summaryrefslogtreecommitdiff
path: root/Master/texmf-dist
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2024-04-18 19:48:25 +0000
committerKarl Berry <karl@freefriends.org>2024-04-18 19:48:25 +0000
commit0c9828a4f12791b59e5d85a69976e43be42bdeae (patch)
treeb74022d5c113a5b06c5797536a5936d36b5acafc /Master/texmf-dist
parent73e8d411206265becaf3dfce8ae2591df9f7e287 (diff)
l3kernel-dev
git-svn-id: svn://tug.org/texlive/trunk@70976 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist')
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/CHANGELOG.md1879
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/README.md96
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/expl3.pdfbin0 -> 610072 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/interface3.pdfbin0 -> 1801762 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/interface3.tex82
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3doc.pdfbin0 -> 565293 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3docstrip.pdfbin0 -> 150996 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news.pdfbin0 -> 402149 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news.tex187
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news01.pdfbin0 -> 122973 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news01.tex102
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news02.pdfbin0 -> 150932 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news02.tex153
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news03.pdfbin0 -> 154694 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news03.tex128
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news04.pdfbin0 -> 114764 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news04.tex123
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news05.pdfbin0 -> 112536 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news05.tex64
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news06.pdfbin0 -> 151602 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news06.tex141
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news07.pdfbin0 -> 128356 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news07.tex111
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news08.pdfbin0 -> 163496 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news08.tex170
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news09.pdfbin0 -> 256114 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news09.tex1433
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news10.pdfbin0 -> 156270 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news10.tex220
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news11.pdfbin0 -> 176082 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news11.tex208
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news12.pdfbin0 -> 210972 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3news12.tex383
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3obsolete.txt743
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3prefixes.csv296
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3prefixes.pdfbin0 -> 82732 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3prefixes.tex91
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3styleguide.pdfbin0 -> 296269 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3styleguide.tex303
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3syntax-changes.pdfbin0 -> 231115 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3syntax-changes.tex190
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3term-glossary.pdfbin0 -> 241905 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/l3term-glossary.tex180
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/source3.pdfbin0 -> 7624184 bytes
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/source3.tex106
-rw-r--r--Master/texmf-dist/doc/latex-dev/l3kernel/source3body.tex620
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/expl3.dtx1798
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3.ins182
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3basics.dtx3507
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3bitset.dtx629
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3bootstrap.dtx357
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3box.dtx2530
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3cctab.dtx975
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3clist.dtx2248
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3coffins.dtx2590
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3color.dtx3246
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3debug.dtx1478
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3deprecation.dtx691
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3doc.dtx4802
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3docstrip.dtx96
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3expan.dtx2398
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3file.dtx4026
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3flag.dtx396
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fp-assign.dtx316
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fp-aux.dtx1291
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fp-basics.dtx2181
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fp-convert.dtx630
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fp-expo.dtx1380
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fp-extended.dtx1275
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fp-functions.dtx288
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fp-logic.dtx750
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fp-parse.dtx2899
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fp-random.dtx629
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fp-round.dtx592
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fp-symbolic.dtx688
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fp-traps.dtx402
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fp-trig.dtx1700
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fp-types.dtx184
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fp.dtx1663
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3fparray.dtx427
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3int.dtx2679
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3intarray.dtx974
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3kernel-functions.dtx739
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3keys.dtx3649
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3legacy.dtx161
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3luatex.dtx750
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3msg.dtx2443
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3names.dtx1568
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3pdf.dtx831
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3prg.dtx1829
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3prop.dtx2602
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3quark.dtx1008
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3regex.dtx8205
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3seq.dtx2681
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3skip.dtx2600
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3sort.dtx1097
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3str-convert.dtx3566
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3str.dtx2226
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3sys.dtx1383
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3text-case.dtx2336
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3text-map.dtx586
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3text-purify.dtx797
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3text.dtx1453
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3tl-analysis.dtx1660
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3tl-build.dtx356
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3tl.dtx3843
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3token.dtx2936
-rw-r--r--Master/texmf-dist/source/latex-dev/l3kernel/l3unicode.dtx1031
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/expl3-code.tex37498
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/expl3-generic.tex110
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/expl3.ltx199
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/expl3.lua618
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/expl3.sty227
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3debug.def1055
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3doc.cls2445
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3docstrip.tex25
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88591.def29
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885910.def75
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885911.def120
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885913.def85
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885914.def60
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885915.def37
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885916.def69
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88592.def86
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88593.def64
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88594.def79
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88595.def123
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88596.def109
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88597.def108
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88598.def96
-rw-r--r--Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88599.def35
131 files changed, 156594 insertions, 0 deletions
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/CHANGELOG.md b/Master/texmf-dist/doc/latex-dev/l3kernel/CHANGELOG.md
new file mode 100644
index 00000000000..e770f3fe368
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/CHANGELOG.md
@@ -0,0 +1,1879 @@
+# Changelog
+All notable changes to the `l3kernel` bundle since the start of 2018
+will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+this project uses date-based 'snapshot' version identifiers.
+
+## [Unreleased]
+
+## [2024-04-11]
+
+### Added
+- `\prop_(g)put_if_not_in:Nnn` (renamed from `\prop_(g)put_if_new:Nnn`)
+- `\intarray_if_exist:N(TF)`
+- `\fparray_if_exist:N(TF)`
+- Support for 'indexed' PDF objects
+
+### Changed
+- Expand object names in `\pdf_object_...` functions (issue \#1521)
+- Adapt `l3doc` for `ltcmd` change to handling of newlines
+ (see latex3/latex2e\#1304)
+- Return active spaces from `\sys_get_query:n(nn)N` (issue \#1529)
+
+### Fixed
+- False `debug` error raised by `\cctab_const:Nn` (issue \#1508)
+- Undefined `\__kernel_iwo_open:Nn` used in `\iow_shell_open:Nn`
+ (issue \#1515)
+- Naming of register functions in LuaMetaTeX 2.11+ (issue \#1518)
+- Catcode applied to letters by `\sys_get_query:n(nn)N`
+
+### Deprecated
+- `\prop_(g)put_if_new:Nnn` (renamed to `\prop_(g)put_if_not_in:Nnn`)
+
+## [2024-03-14]
+
+### Added
+- Macro interface for `l3sys-query`
+
+### Changed
+- Require that `expl3` is loaded at top level in generic mode
+- Ensure that the dimension arguments to `\box_autosize_to_...:Nnn`
+ are properly formed (issue \#1502)
+
+### Fixed
+- Typeset `TF` of internal conditionals in current color (issue \#730)
+- Some functions and variables were defined locally
+
+## [2024-02-20]
+
+### Changed
+- Improve error recovery when an invalid `prop` is used (PR \#1464)
+
+### Fixed
+- Restore `\__kernel_msg_...:nnx(xx)` for older `xparse` support
+- Prevent an infinite loop in `\debug_(on|off):n` (issue \#1442)
+- Require `\sys_ensure_backend:` is used in the preamble (issue \#1442)
+
+## [2024-02-18]
+
+### Changed
+- Work-around to enable `\prop_set_from_keyval:Nn`, etc., to avoid an error if
+ `prop` is not already defined
+
+### Fixed
+- Extend debug checking to cover `\prop_(g)set_from_keyval:Nn`
+
+## [2024-02-13]
+
+### Added
+- Checking missing `\endgroup` at the end of `\DocInclude`
+- Linked storage type for large property lists (issue \#1040, pull \#1059)
+
+### Changed
+- `\meta` now typesets in `\texttt`, along with `\Arg`
+- Improved the performance of `\cs_if_exist:NTF`, `\cs_if_free:NTF`,
+ `\cs_if_exist_use:NTF`, and most notably their `c`-type variants.
+
+### Fixed
+- Inconsistent local/global assignments in `\vcoffin_gset:Nnn` and
+ `\vcoffin_gset:Nnw`
+- Tokenization by `\peek_analysis_map_inline:n` of one additional
+ character after any space or brace
+
+### Deprecated
+- `\msg_gset:nnn(n)`
+
+## [2024-01-22]
+
+### Added
+- `\keys_set_exclude_groups:nnn(nN)` to replace `\keys_set_filter:nnn(nN)`
+- Flags with N-type names, like other variable types
+
+### Changed
+- Set `l3doc` option `kernel` off as-standard (issue \#1403)
+- Moved `\seq_set_filter:NNn` to stable
+
+### Deprecated
+- `\keys_set_filter:nnn(nN)` in favor of `\keys_set_exclude_groups:nnn(nN)`
+
+## [2024-01-04]
+
+### Added
+- Documentation for `\bitset_show_named_index:N` (issue \#1372)
+- `\bitset_log_named_index:N`
+- `\tl_build_get_intermediate:NN`
+- Support for `.default:n` values for the `unknown` handler (see issue \#67)
+
+### Changed
+- Improved method to suppress `l3bitset` where required
+- Clarify behavior of `\tl_if_novalue:n(TF)` (see issue \#1402)
+
+### Deprecated
+- `\tl_build_get:NN` in favor of `\tl_build_get_intermediate:NN`
+
+### Fixed
+- Handling of `documented-as` variants in `l3doc` indexing
+ (see issue \#1345)
+- Missing trailing `TF` in single variant of conditional functions (issue \#1398)
+
+## [2023-12-11]
+
+### Changed
+- Use keyval handler for `l3doc` class options
+
+### Fixed
+- Support for deprecated `pdf` module functions (issue \#1373)
+- Generate stub `l3bitset` for transitional support
+
+## [2023-12-08]
+
+### Added
+- `\c_empty_str` (issue \#1361)
+- `l3bitset`: moved from `l3experimental`
+
+### Fixed
+- Return `str` (not `tl`) in `\bool_to_str:N` and `\bool_to_str:n` (issue \#1331)
+- Catcode of space from `\str_set_convert:Nnnn` (issue \#1344)
+- Tidy up various documentation issues (issue \#1345)
+
+## [2023-11-09]
+
+### Added
+- Documentation for `\c_nan_fp`
+- `\str_case_e:en(TF)`
+
+### Changed
+- Speed up `\str_(g)set:Nn`, `\str_const:Nn`, `\str_(g)put_left:Nn`,
+ and `\str_(g)put_right:Nn`
+
+### Fixed
+- Normalisation of `.inherit:n` key data (issue \#1314)
+
+## [2023-11-01]
+
+### Added
+- `\seq_(g)set_map_e:NNn`
+- Documentation for `\ExplLoaderFileDate` in `expl3.pdf`
+
+### Changed
+- Documentation improvements
+- Refine action of `\text_titlecase_first:n(n)` to be focussed strictly on
+ first (relevant) codepoint in the input
+- Standardize "This is the (e)TeX primitive ..." in `texnote` in documentation
+- Move `\tl_build_(g)begin:N`, `\tl_build_(g)end:N`, `\tl_build_(g)put_left:Nn`
+ and `\tl_build_(g)right:Nn` to stable status
+
+### Deprecated
+- `\seq_(g)set_map_x:NNn` in favor of `e`-type naming
+
+### Fixed
+- `\fp_clear_variable:n` should act locally (issue \#1298)
+- `\fp_clear_function:n` should act locally and correctly
+- Support for `\fp_show:N` showing symbolic expressions (issue \#1301)
+- Undefined `\str_case:en(TF)` (excluding `\str_case:en`)
+
+## [2023-10-23]
+
+### Added
+- `\text_titlecase_all:n(n)`
+- `\token_to_catcode:N`
+- Support for symbolic variables in fp input:
+ `\fp_new_variable:n`, `\fp_set_variable:nn` and `\fp_clear_variable:n`
+- Support for user-defined functions in fp expressions:
+ `\fp_new_function:n`, `\fp_set_function:nnn` and `\fp_clear_function:n`
+
+### Changed
+- Clarify action of `\text_titlecase_first:n(n)`
+- Detect letters for titlecasing based on Unicode general category
+
+### Deprecated
+- `\text_titlecase:n(n)` as ambiguous: replaced by `\text_titlecase_all:n(n)`
+- `\tl_build_(g)clear:N` in favour of `\tl_build_(g)begin:N`
+
+### Fixed
+- Support arbitrary BCP-47 locales for case-changing overrides (issue \#1239)
+- Retain braces when ending titlecasing with some input structures
+
+## [2023-10-10]
+
+### Added
+- `\iow_shipout_e:Nn`
+- Documentation for `\sys_if_timer_exist_p:` and `\sys_if_timer_exist:(TF)`.
+ They were supported since l3kernel 2021-05-25, along with `\sys_timer:`.
+- l3doc.dtx: Support for footnotes in the function and variable environment
+- Variants `\tl_if_single:c(TF)` and `\tl_if_single_p:c` (issue \#1272)
+- Key properties `.str_(g)set_e:N` and `.tl_(g)set_e:N`
+
+### Changed
+- Switch generally from `x`- to `e`-type variants
+- Convert `\file_if_exist:n(TF)` to expandable status,
+ adding predicate version
+- Standardise variants for `\prop_(g)pop:NnN(TF)`
+- Standardise variants for `\prop_(g)put:Nnn`
+- Standardise variants for `\prop_(g)put_if_new:Nnn`
+- Standardise variants for `\prop_(g)remove:Nn`
+
+### Deprecated
+- `\iow_shipout_x:Nn` in favor of `e`-type naming
+- Key properties `.str_(g)set_x:N` and `.tl_(g)set_x:N`
+
+### Fixed
+- Rare rounding error in divisions (issue \#1264)
+
+## [2023-08-29]
+
+### Added
+- `\c_sys_timestamp_str`
+
+### Changed
+- Rename `\tex_pdfcreationdate:D` to `\tex_creationdate:D`
+
+## [2023-08-11]
+
+### Changed
+- Wrap long lists of variants in `l3doc` display of functions (issue \#1258)
+
+### Fixed
+- Revert 'Allow for implicit chars when case changing' (issue \#1260)
+
+## [2023-08-03]
+
+### Added
+- `\tex_XeTeXhyphenatablelength:D`, `\tex_XeTeXinterwordspaceshaping:D`,
+ `\tex_XeTeXselectorcode:D`
+- Several missing `\tex_<name>:D` primitives from LuaTeX and (up)TeX
+
+### Changed
+
+- `\tex_protrudechars:D` now defined for XeTeX (to `\XeTeXprotrudechars`)
+- Allow for optional `v` in version string argument to
+ `\ProvidesExpl...`
+
+### Fixed
+- Allow for implicit chars when case changing
+
+## [2023-06-30]
+
+### Added
+- `\codepoint_to_category:n`
+
+### Changed
+- Drop notes about slow `\expanded` emulation. `\expanded` is required since
+ release 2023-05-15
+- Sync engine/primitive requirements in `expl3.dtx` with code and README
+
+### Fixed
+- Case changing of Greek characters with complex NFD representation (issue \#1236)
+- Error recovery when wrongly applying abs to a tuple (issue \#1238)
+
+## [2023-06-16]
+
+### Changed
+- Exclude entries in `\l_text_case_exclude_arg_tl` from expansion in `\text_expand:n`
+ (latex3/latex2e\#904)
+- Generate _dialytika_ if appropriate when uppercasing Greek words starting with a
+ vowel taking a breathing mark (issue \#1228)
+- Retain stress diacritics when uppercasing Greek (issue \#1230)
+- Expand (partial) paths in `\l_file_search_path_seq` and `\input@path`
+ (see latex3/latex2e\#1086)
+
+## [2023-06-05]
+
+### Added
+- `\cctab_gsave_current:N`, `\g_tmpa_cctab` and `\g_tmpb_cctab` (issue \#1089)
+- `\prg_gset_eq_conditional:NNn` (issue \#1212)
+- `\iow_shell_open:Nn` (issue \#1122)
+
+### Deprecated
+- `\tl_case:Nn(TF)`
+- `expl3` package option `enable-debug` (no longer required)
+
+### Fixed
+- Set `\l_keys_value_tl` in `.initial:n` (issue \#1013)
+- Support for boolean values in `expl3` package options
+ `check-declarations` and `log-functions` (issue \#978)
+- Allow for optional `/` at end of `\input@path` entries
+ (see latex3/latex2e\#1076)
+
+## [2023-05-22]
+
+### Added
+- Rounded out the `\use_i:nn...` functions to cover all cases taking
+ one argument from up to nine arguments
+- `\coffin_(g)reset_poles:N`
+- `\dim_to_decimal_in_<unit>:n`, for `<unit>` types
+ `cc`, `cm`, `dd`, `in`, `mm` and `pc`
+- `\file_input_raw:n`
+- `\int_if_zero:n(TF)`
+- `\str_mdfive_hash:n`
+### Changed
+- Remove `\noexpand` inside math mode in `\text_expand:n`
+- Re-implement `\dim_to_decimal_in_bp:n` and
+ `\dim_to_decimal_in_unit:nn` to be re-entrant (issue \#954)
+
+### Fixed
+- Omit `0123456789` from URL escaping (issue \#838)
+- Leave implicit chars unchanged in `\text_expand:n` (issue \#874)
+- Filtering/grouping applied to meta keys (issue \#940)
+- Issue correct error message if `bool` key is set
+ incorrectly using `.initial:n` (issue \#1013)
+- Set `\l_keys_key_str` in `.initial:n` (issue \#1013)
+- Handling of implicit chars in `\text_purify:n` (issue \#1208)
+
+## [2023-05-15]
+
+### Changed
+- Require `\expanded` and 'pdfTeX extension' primitives,
+ and thus TeX distributions from mid-2019 onward
+
+### Fixed
+- Treatment of trailing `/` in `\input@path`
+
+## [2023-05-11]
+
+### Added
+- `\seq_map_pairwise_function:NNN` (renamed from `\seq_mapthread_function:NNN`)
+
+### Changed
+- Moved `\seq_set_item:Nnn` to stable status
+- Track seen filenames to improve performance of
+ `\file_full_name:n` and dependent file operations
+
+### Removed
+- Experimental function `\seq_pop_item:NnN` from `l3candidates`
+- Experimental function `\seq_mapthread_function:NNN` now renamed
+ `\seq_map_pairwise_function:NNN`
+
+## [2023-05-05]
+
+### Added
+- `\bool_case:n(TF)` (renamed from `\bool_case_true:n(TF)`)
+- `\flag_ensure_raised:n` (renamed from `\flag_raise_if_clear:n`)
+- `\iow_wrap_allow_break:` (renamed from `\iow_allow_break:`)
+
+### Changed
+- Moved to stable status:
+ - `\bool_(g)set_inverse:N`
+ - `\c_catcode_active_space_tl`
+ - `\c_sys_engine_version_str`
+ - `\exp_args_generate:n`
+ - `\fp_if_nan:n(TF)`
+ - `\ior_get_term:nN` and `\ior_str_get_term:nN`
+ - `\ior_shell_open:Nn`
+ - `\msg_show_item:n`, `\msg_show_item_unbraced:n`,
+ `\msg_show_item:nn`, `\msg_show_item_unbraced:nn`
+- Require random number support from engine
+
+### Removed
+- Experimental functions from `l3candidates`:
+ - `\bool_case_false:n(TF)`
+ - `\intarray_gset_rand:Nn(n)`
+ - `\intarray_to_clist:N`
+ - `\peek_catcode_collect_inline:Nn`, `\peek_charcode_collect_inline:Nn` and
+ `\peek_meaning_collect_inline:Nn`
+ - `\prop_rand_key_value:N`
+ - `\seq_set_from_function:Nnn` and `\seq_set_from_inline_x:Nnn`
+ - `\sys_if_rand_exist:(TF)`
+ - `\tl_range_braced:Nnn` and `\tl_range_unbraced:Nnn`
+- Experimental functions now renamed
+ - `\bool_case_true:n(TF)` (now `\bool_case:n(TF)`)
+ - `\flag_raise_if_clear:n` (now `\flag_ensure_raised:n`)
+ - `\iow_allow_break:` (now `\iow_wrap_allow_break:`)
+- Experimental functions moved to kernel-internal:
+ - `\msg_log_eval:Nn` and `\msg_show_eval:Nn`
+
+## [2023-04-20]
+
+### Changed
+- Re-order arguments of `\text_declare_lowercase_mapping:nnn`,
+ `\text_declare_titlecase_mapping:nnn` and
+ `\text_declare_uppercase_mapping:nnn`
+
+## [2023-04-19]
+
+### Added
+- `\box_set_clipped:N` and variants (renamed from `\box_clip:N`)
+- `\text_declare_lowercase_mapping:nn(n)`,
+ `\text_declare_titlecase_mapping:nn(n)`
+ and `\text_declare_uppercase_mapping:nn(n)` to allow customization of
+ case changing outcomes
+
+### Changed
+- `\box_set_trim:Nnnnn` and `\box_set_viewport:Nnnnn` moved to
+ stable kernel
+
+### Fixed
+- Incorrect internal name in `\pdf_pagesize_gset:nn`
+
+### Removed
+- Experimental `\box_clip:N` and variants (renamed to
+ `\box_set_clipped:N`)
+
+## [2023-03-30]
+
+### Added
+- `\tex_endlocalcontrol:D` as expl3 name for the LuaTeX primitive
+
+### Changed
+- Allow non-integer values in color expressions
+- Restrict color expressions values to the range [0, 100]
+- Leave Greek spacing diacritics unchanged in uppercasing
+
+### Fixed
+- Fix minor version in `\c_sys_engine_version_str` for pdfTeX and LuaTeX
+ (issue [\#1186](https://github.com/latex3/latex3/issues/1186))
+
+## [2023-02-22]
+
+### Changed
+- More defensive approach in `\codepoint_to_nfd:n`
+
+### Fixed
+- CJK character handling for (u)pTeX (issue
+ [\#1171](https://github.com/latex3/latex3/issues/1171))
+
+## [2023-02-07]
+
+### Changed
+- Swap meaning of `el` and `el-x-iota` when case changing
+ Greek: match traditional LaTeX approach
+
+### Deprecated
+- `\l_text_accents_tl` and `\l_text_letterlike_tl`
+
+## [2023-02-02]
+
+### Fixed
+- Handling of multiple accents when uppercasing Greek
+ (see LaTeX2e issue
+ [\#987](https://github.com/latex3/latex2e/issues/987))
+
+## [2023-02-01]
+
+### Fixed
+- Allow for expressions within color `RGB`/`HSB` input
+
+## [2023-01-24]
+
+### Added
+- Complete list of pdfTeX primitives
+
+### Fixed
+- Allow for multi-part BCP47 strings in case changing
+- Loading with plain TeX due to `\outer` status of `^^L`
+ issue ([\#1161](https://github.com/latex3/latex3/issues/1161))
+
+### Changed
+- Leave catcode of `&` unchanged in code environment
+
+## [2023-01-16]
+
+### Added
+- `\pdf_pagesize_gset:nn`
+
+### Fixed
+- Enable `\char_generate:nn` to create active tokens which are `\outer`
+ or equal to `\fi`
+
+## [2022-12-17]
+
+### Added
+- `\codepoint_to_nfd:n`
+- `\codepoint_generate:n` and `\codepoint_str_generate:n`
+- `\str_casefold:n`
+
+### Changed
+- Protect `babel` shorthands from expansion
+
+### Fixed
+- Appearance of `\DescribeOption` (issue
+ [\#1111](https://github.com/latex3/latex3/issues/1111))
+
+## [2022-11-02]
+
+### Added
+- `\prg_gset_conditional:N(p)nn` and
+ `\prg_gset_protected_conditional:N(p)nn`
+
+### Fixed
+- Loading of `expl3` with ConTeXt MkIV (issue
+ [\#1142](https://github.com/latex3/latex3/issues/1142))
+
+## [2022-10-26]
+
+### Changed
+- Usage of `\exp_not:n`/`\exp_not:N` in `\peek_analysis_map_inline:n` output
+
+### Fixed
+- `\peek_analysis_map_inline:n` support for macro parameter characters (issue
+ [\#1109](https://github.com/latex3/latex3/issues/1109)), for many expandable
+ tokens (issue [\#1110](https://github.com/latex3/latex3/issues/1110)), and an
+ esoteric case (issue [\#1113](https://github.com/latex3/latex3/issues/1113))
+- Creation of a message with some engines (issue
+ [\#1139](https://github.com/latex3/latex3/issues/1139))
+
+### Deprecated
+- `\char_to_nfd:N`, `\char_to_nfd:n`
+- `\char_foldcase:N`, `\char_lowercase:N`, `\char_titlecase:N`,
+ `\char_uppercase:N` and `str` variants
+- `\char_to_utfviii_bytes:n`
+- `\str_foldcase:n`
+
+## [2022-09-28]
+
+### Added
+- Set media box for test phase documents
+
+### Fixed
+- Text case switching in `\text_titlecase_first:n` (issue
+ [\#1130](https://github.com/latex3/latex3/issues/1130))
+
+## [2022-08-30]
+
+### Added
+- `\pdf_object_new:n` and `\pdf_object_write:nnn` - support assignment of object
+ type at point-of-writing (issue
+ [\#1123](https://github.com/latex3/latex3/issues/1123))
+- `\text_map_function:nN` and `\text_map_inline:nn` for mapping to
+ graphemes in textual input
+- Support for medieval Latin case changing
+- `\char_to_nfd:n` to extend NFD support to 8-bit engines
+
+### Deprecated
+- `\pdf_object_new:nn` and `\pdf_object_write:nn`, in favor of
+ `\pdf_object_new:n` and `\pdf_object_write:nnn`, respectively
+
+## [2022-08-23]
+
+### Added
+- Shortened range of chars which are left unchanged when converted to
+ strings with `\str_(g)set_convert:Nnnn`.
+- `\color_if_exist:n(TF)`
+- Support for case changing Vietnamese characters with 8-bit engines
+- Parsing of `cmy`, `tHsb` and `&spot` color models to support data exchange with
+ `xcolor`
+
+### Changed
+- Replace `LaTeX3` in message text by `LaTeX`
+
+### Changed
+- Approach to setting current color data from LaTeX2e
+## [2022-08-05]
+
+### Added
+- Support for case changing Croatian diagraph with 8-bit engines
+- Support accent removal when uppercasing Greek with 8-bit engines
+- Function `\sys_ensure_backend:`
+
+### Fixed
+- Behavior of `\color_math:nn` in alignments
+- Expansion of text containing unterminated `\romannumeral` primitives
+
+## [2022-07-21]
+
+### Fixed
+- `\iow_open:N` in ConTeXt MkII
+
+## [2022-07-15]
+
+### Fixed
+- Correct argument order in `\text_case_switch:nnnn`
+
+## [2022-07-14]
+
+### Changed
+- Improved approach to `\text_case_switch:nnnn` expansion
+
+## [2022-07-04]
+
+### Added
+- `\text_declare_case_equivalent:Nn`, `\text_case_switch:nnnn` and
+ related mechanism to allow specialisation of case changing output
+ for selected commands
+
+## [2022-07-01]
+
+### Added
+- `\cs_parameter_spec:N`
+
+### Changed
+- `\text_expand:n` now acts on active chars to support legacy input encodings
+
+### Deprecated
+- `\cs_argument_spec:N`
+
+### Fixed
+- Correct validity check performed by `\regex_show:N` (issue [\#1093](https://github.com/latex3/latex3/issues/1093))
+- Closing of file handles (issue [\#1105](https://github.com/latex3/latex3/issues/1105))
+
+## [2022-06-16]
+
+### Fixed
+- Made `\peek_analysis_map_inline:n` alignment-safe (issue [\#1090](https://github.com/latex3/latex3/issues/1090))
+- Setting a boolean to itself no longer errors (issue [\#1055](https://github.com/latex3/latex3/issues/1055))
+
+## [2022-06-02]
+
+### Changed
+- Exclude only first mandatory argument of entries in
+ `\l_text_case_exclude_arg_tl` from case changing
+
+## [2022-05-30]
+
+### Added
+- Add `\lua_load_module:n`
+
+### Fixed
+- Typo in implementation of titlecase `hy-x-yiwn`
+- Definition order issue with `\str_case:Nn(TF)`
+
+## [2022-05-04]
+
+### Added
+- Language settings `hy` and `hy-x-yiwn` for handling of ech-yiwn ligature
+ uppercasing
+
+### Changed
+- Support BCP 47 properly in case changer language argument
+
+### Fixed
+- Correct `el-xiota` and `de-xeszett` to `el-x-iota` and `de-x-eszett`
+
+## [2022-04-29]
+
+### Added
+- Language setting `el-xiota` for retention of ypogegrammeni when uppercasing
+ Greek
+
+### Changed
+- Rename case-changing variant `de-alt` to `de-xeszett` to align with
+ `luaotfload`
+- Allow for `\lccode`/`\uccode` changes in `\char_...case:n` functions
+
+### Fixed
+- Support for ypogegrammeni in case changing Greek (see issue [\#1088](https://github.com/latex3/latex3/issues/1088))
+
+## [2022-04-20]
+
+### Changed
+- Collect some common code from `l3backend-color`
+
+## [2022-04-10]
+
+### Added
+- `\keys_precompile:nnN` for conversion of keyvals to fast-to-apply token
+ lists
+- Missing `\str_if_empty:n(TF)` (see issue [\#1071](https://github.com/latex3/latex3/issues/1071))
+- Missing `\str_case:Nn(TF)` (see issue [\#1071](https://github.com/latex3/latex3/issues/1071))
+- `\tex_...:D` names for primitives added in TeX Live 2022
+
+### Changed
+- Definition of `\legacy_if:n(TF)` to support primitive conditionals
+- `\str_<type>case:n` now case changes codepoints above 127 with all engines
+- `\char_generate:nn` now also allows to generate category 10 tokens (spaces)
+ except for char code 0
+
+### Fixed
+- Handling of 'misplaced' `\protect` by `\text_expand:n`
+- Nesting of `\tl_analysis_map_inline:nn`
+- Naming of an error message
+
+## [2022-02-24]
+
+### Changed
+- Better support for `\cite`, _etc._, in case changing
+
+## [2022-02-21]
+
+### Fixed
+- Use of `\@uclclist` for case changing
+
+## [2022-02-05]
+
+### Added
+- Distribute `l3doc.pdf` with a prominent warning about future changes
+- `\color_math:nn(n)` as a functional equivalent of the new `\mathcolor`
+ command in LaTeX2e
+
+### Changed
+- Documentation for horizontal coffin poles (see issue [\#1041](https://github.com/latex3/latex3/issues/1041))
+- Update primitive requirements to enable loading with Prote/HINT
+
+## [2022-01-21]
+
+### Changed
+- Auto-generate legacy switch if required in `.legacy_set_if:n`
+ key property
+
+### Fixed
+- Correct creation of `.if` property
+- Handling of colors created in a group once they go out-of-scope
+
+## [2022-01-12]
+
+### Added
+- Support for validity scope for keys
+- `\peek_remove_filler:n`
+- `\prop_to_keyval:N`
+- `\regex_match_case:nn(TF)`, `\regex_replace_case_once:nN(TF)`,
+ `\regex_replace_case_all:nN(TF)`
+
+### Changed
+- Policy change: functions will no longer be removed after deprecation,
+ thus the Lua functions noted below are the *last* 'stable' code to be
+ removed from `l3kernel` after deprecation
+- Allow indirect conversions between colorspaces through fallback models
+- Move some color functions from `l3backend`
+
+### Deprecated
+- `\peek_..._ignore_spaces:N(TF)` functions
+- `\sys_load_deprecation:`
+- Option `undo-recent-deprecations`
+
+### Removed
+- Lua functions in `l3kernel` table
+
+## [2021-11-22]
+
+### Added
+- Support for legacy `if` switches in `l3keys`
+
+### Changed
+- Documentation improvements
+- Implementation of `intarray` data type with LuaTeX
+- Better support for LuaMetaTeX
+
+## [2021-11-12]
+
+### Fixed
+- DeviceN colorspace conversions with alternative model RGB
+
+### Added
+- `.str_set:N`, etc., key properties (issue [\#1007](https://github.com/latex3/latex3/issues/1007))
+- `\bool_to_str:n` (issue [\#1010](https://github.com/latex3/latex3/issues/1010))
+
+### Changed
+- `\prop_..._from_keyval:Nn` functions now support active comma or
+ equal sign (pull \#1012)
+
+## [2021-10-18]
+
+### Added
+- Support for ICC-based color profiles
+- `\color_profile_apply:nn`
+
+## [2021-10-17]
+
+### Changed
+- Better DeviceN support
+
+## [2021-10-12]
+
+### Fixed
+- Global assignments for `\box_gresize_to_ht_plus_dp:Nn`
+ and `\coffin_gattach:NnnNnnnn`
+- Conversion of DeviceN colors to device fallback
+
+## [2021-08-27]
+
+### Changed
+- Formatting of expandable errors (issue [\#931](https://github.com/latex3/latex3/issues/931))
+- Internal code for kernel messages
+
+## [2021-07-12]
+
+### Fixed
+- Handling of multiple color models (issue [\#962](https://github.com/latex3/latex3/issues/962))
+
+### Removed
+- Functions marked for removal end-2020
+
+## [2021-06-18]
+
+### Fixed
+- Local assignment to `\g__sys_backend_tl`
+- Incorrect internal function name (issue [\#939](https://github.com/latex3/latex3/issues/939))
+- Case-changing exceptions for (u)pTeX (issue [\#939](https://github.com/latex3/latex3/issues/939))
+- Low-level error if accent commands are not followed by
+ letter when case changing (see \#946)
+
+## [2021-06-01]
+
+### Fixed
+- Loading when `\expanded` is not available
+
+## [2021-05-27]
+
+### Fixed
+- Correctly detect local formats in `Mismatched LaTeX support files` error.
+
+## [2021-05-25]
+
+### Added
+- `\msg_note:nnnnnn` (issue [\#911](https://github.com/latex3/latex3/issues/911))
+- `\str_compare:nNnTF` (issue [\#927](https://github.com/latex3/latex3/issues/927))
+- `\sys_timer:`
+- `\prop_concat:NNN`, `\prop_put_from_keyval:Nn` (issue [\#924](https://github.com/latex3/latex3/issues/924))
+- Functions to show and log various datatypes (issue [\#241](https://github.com/latex3/latex3/issues/241)):
+ `\coffin_show:Nnn`, `\coffin_show:N`, `\coffin_log:Nnn`, `\coffin_log:N`,
+ `\color_log:n`, `\group_show_list:`, `\group_log_list:`,
+ `\ior_show:N`, `\ior_log:N`, `\iow_show:N`, `\iow_log:N`,
+ `\tl_log_analysis:N`, `\tl_log_analysis:n`
+- `\legacy_if_set_true:n`, `\legacy_if_set_false:n`, `\legacy_if_set:nn`
+- Matching multiple regex at the same time (issue [\#433](https://github.com/latex3/latex3/issues/433)):
+ `\regex_case_match:nn(TF)`,
+ `\regex_case_replace_once:nN(TF)`,
+ `\regex_case_replace_all:nN(TF)`
+
+### Fixed
+- Checking brace balance in all regex functions (issue [\#377](https://github.com/latex3/latex3/issues/377))
+- Removing duplicates in clists when items contain commas (issue [\#917](https://github.com/latex3/latex3/issues/917))
+
+### Changed
+- Slight speed up in some elementary int/dim/skip/muskip operations and
+ in setting tl or clist variables equal.
+- Speed up mapping functions in l3clist, l3prop, l3seq, l3tl
+
+## [2021-05-11]
+
+### Added
+- `\cctab_item:Nn` (issue [\#880](https://github.com/latex3/latex3/issues/880))
+- `\clist_use:nnnn` and `\clist_use:nn` (issue [\#561](https://github.com/latex3/latex3/issues/561))
+
+### Fixed
+- Loading of backend in generic DVI mode (issue [\#905](https://github.com/latex3/latex3/issues/905))
+- Make `\keyval_parse:nnn` alignment-safe (issue [\#896](https://github.com/latex3/latex3/issues/896))
+- Control sequences and category codes in regex replacements (issue [\#909](https://github.com/latex3/latex3/issues/909))
+
+### Changed
+- Speed up `\group_align_safe_begin:` (pull \#906)
+
+## [2021-05-07]
+
+### Added
+- Color export in comma-separated format
+- `\ur{...}` escape in `l3regex` to compose regexes
+- `\seq_set_split_keep_spaces:Nnn` (see \#784)
+- `\seq_set_item:Nnn(TF)` and `\seq_pop_item:NnN(TF)`
+- `\box_ht_plus_dp:N` (issue [\#899](https://github.com/latex3/latex3/issues/899))
+- `\clist_map_tokens:nn`, `\clist_map_tokens:Nn`,
+ `\str_map_tokens:nn`, `\str_map_tokens:Nn`
+
+### Changed
+- Use prevailing catcodes instead of string in regex replacement (issue [\#621](https://github.com/latex3/latex3/issues/621))
+ (*Breaking change*)
+- `\__kernel_file_name_sanitize:n` now uses a faster `\csname`-based
+ approach to expand the file name
+- Improved performance for basic conditionals
+- `\pdf_version_gset:n` support for `dvips`
+- Improve handling of `\exp_not:n` in `\text_expand:n` (issue [\#875](https://github.com/latex3/latex3/issues/875))
+- `\file_full_name:n` now avoids calling `\pdffilesize` primitive multiple times
+ on the same file
+- Show printable characters explicitly in `\regex_show:n`
+- Regex replacement now errors when using a submatch (`\1` etc) for which
+ the regex has too few groups
+- Showing complex datatypes now validates their internal structure (issue [\#884](https://github.com/latex3/latex3/issues/884))
+- Indexing in l3doc: all page references before codeline references,
+ improve target placement, solve pdfTeX and makeindex warnings
+
+### Fixed
+- Evalutate integer constants only once (issue [\#861](https://github.com/latex3/latex3/issues/861))
+- Detect `\ior_map_inline:Nn` calls on undefined streams (issue [\#194](https://github.com/latex3/latex3/issues/194))
+
+### Deprecated
+- `l3docstrip` converted to a stub which simply loads DocStrip: use
+ the latter directly
+
+## [2021-02-18]
+
+### Added
+- `l3color`: Moved from `l3experimental`
+- `l3pdf`: Moved from `l3experimental`
+- `default` alias to str_convert
+
+### Changed
+- Re-ordered `interface3` documentation
+- Moved `msg_show:nn(nnnn)` to stable
+
+## [2021-02-06]
+
+### Changed
+- Use new (internal) interface for kerns
+
+## [2021-02-02]
+
+### Added
+- `\c_zero_str`
+
+## [2021-01-09]
+
+### Added
+- `\keyval_parse:nnn`
+
+### Changed
+- `\keyval_parse:NNn` is set equal to `\keyval_parse:nnn`
+
+### Fixed
+- Handling of encoding-specfic commands in `\text_purify:n`
+
+## [2020-12-07]
+
+### Fixed
+- `\peek_analysis_map_inline:n` with spaces and braces
+
+## [2020-12-05]
+
+### Fixed
+- Setting of line width in vertical coffins in LaTeX
+
+## [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
+- Avoid relying on braced `\input` primitive syntax
+- Correct expansion of environments in `\text_purify:n`
+- Some aspects of `cctab` setup with 8-bit engines(issue [\#814](https://github.com/latex3/latex3/issues/814))
+
+### Changed
+- Improved performance for `tl` functions
+- Extend case changer to cover all of Greek with pdfTeX
+
+## [2020-10-05]
+
+### Fixed
+- Correctly detect LaTeX when pre-loading expl3 and setting up
+ case changer
+- Lua emulation of \strcmp (issue [\#813](https://github.com/latex3/latex3/issues/813))
+
+## [2020-09-24]
+
+### Changed
+- Use Lua pseudo-primitives instead of `\directlua`
+- `\token_if_primitive:N(TF)` now reports pseudo-primitives as primitives in LuaTeX
+
+## [2020-09-06]
+
+### Fixed
+- Loading in generic mode (issue [\#800](https://github.com/latex3/latex3/issues/800))
+
+## [2020-09-03]
+
+### Fixed
+- Save primitive definition of `\pdfoutput` with CSLaTeX
+
+## [2020-09-01]
+
+### Added
+- `\hbox_overlap_center:n`
+
+### Changed
+- Backend setting for direct PDF output
+- Backend setting for XeTeX support
+
+### Deprecated
+- Backend setting `pdfmode`
+
+### Fixed
+- `\file_compare_timestamp:nNn(TF)` in LuaTeX (issue [\#792](https://github.com/latex3/latex3/issues/792))
+- Text case changing and expansion where an excluded command is equivalent
+ to `\use:n`
+
+## [2020-08-07]
+
+### Changed
+- Color selection implementation
+- Performance enhancements for `\keys_set:nn`
+
+### Fixed
+- Loading generically on ConTeXt (issue [\#783](https://github.com/latex3/latex3/issues/783))
+
+## [2020-07-17]
+
+### Added
+- `l3cctab` module for using category code tables
+- `\file_parse_full_name:n` and `\file_parse_full_name_apply:nN`
+- Additional `\prop_put:Nnn` variants
+- `\seq_set_map_x:NNn`
+- `\msg_term:nn(nnnn)`
+
+### Fixed
+- File lookup with `\input@path`
+- 8-bit encodings in `\str_set_convert:Nnnn`
+
+### Changed
+- Implementation of `\file_parse_full_name:nNNN` now uses
+ `\file_parse_full_name:n` internally
+- `\seq_set_map:NNn` no longer `x`-expands `<inline function>`
+ (`\seq_set_map_x:NNn` now does that). Both moved to stable.
+
+### Removed
+- Functions deprecated at end of 2019
+
+### Deprecated
+- `\str_declare_eight_bit_encoding:nnn`
+
+## [2020-06-18]
+
+### Changed
+- Use `scn` operator for separations
+- Internal color model
+- Internal performance enhancements
+- Moved `\msg_expandable_error:nn(nnnn)` to stable.
+- Moved `\seq_indexed_map_inline:Nn` and `\seq_indexed_map_function:Nn`
+ to stable as `\seq_map_indexed_inline:Nn` and `\seq_map_indexed_function:Nn`.
+- Internal changes to `expl3` to allow loading earlier in LaTeX2e.
+
+## [2020-06-03]
+
+### Added
+- `\str_convert_pdfname:n`
+
+## [2020-05-15]
+
+### Changed
+- Make `\text_purify:n` `f`-type expandable
+
+## [2020-05-14]
+
+### Changed
+- Performance improvements in keyval processing
+
+## [2020-05-11]
+
+### Changed
+- Internal changes to quark handling
+
+## [2020-05-05]
+
+### Added
+- Recognize the exponent marker `E` (same as `e`) in floating point numbers
+
+### Fixed
+- Leave active characters untouched when case-changing (see \#715)
+
+## [2020-04-06]
+
+### Added
+- Control for start-of-titlecasing: see `\l_text_titlecase_check_letter_bool`
+
+### Fixed
+- Nesting of `\seq_shuffle:N` in another sequence mapping (issue [\#687](https://github.com/latex3/latex3/issues/687))
+- `\ior_shell_open:Nn` in engines other than LuaTeX - shell commands didn't
+ execute, plus the command call would be left in the input stream.
+
+## [2020-03-06]
+
+### Added
+- `\text_purify:n`
+
+### Fixed
+- Issue with case-changing Turkish
+
+## [2020-03-03]
+
+### Added
+- `\tex...:D` coverage for TeX Live 2020 engine changes
+
+### Changed
+- New implementation for `\keyval_parse:NNn` - around 40% speed improvement,
+ also *expandable*
+
+### Fixed
+- Make `expl3` reload-safe for `latexrelease` (see latex3/latex2e#295)
+
+## [2020-02-25]
+
+### Changed
+- Extend case-changing encoding support to Cyrillic and Greek
+
+## [2020-02-21]
+
+### Added
+- Option `suppress-backend-headers` (see matching change in
+ `l3backend`)
+
+### Changed
+- Allow `dvisvgm` driver with XeTeX (issue [\#677](https://github.com/latex3/latex3/issues/677))
+
+### Fixed
+- `undo-recent-deprecations` would not reload the deprecation code
+
+## [2020-02-14]
+
+### Fixed
+- Interaction with some `babel` languages at the start of the document
+
+## [2020-02-13]
+
+### Changed
+- Leave implicit tokens unchanged by `\text_expand:n`
+- Extend the `de-alt` case changing locale to 8-bit engines
+
+## [2020-02-11]
+
+### Added
+- Key property `.cs_set:Np` and variants
+
+### Changed
+- Support `\@uclclist` entries when case-changing
+
+### Fixed
+- Allow for full range of encodings when expanding text (issue [\#671](https://github.com/latex3/latex3/issues/671))
+- Support `\begin`/`\end` in text expansion
+
+## [2020-02-08]
+
+### Added
+- `\l_keys_key_str` and `\l_keys_path_str`
+
+### Deprecated
+- `\l_keys_key_tl` and `\l_keys_path_tl`, replaced by `\l_keys_key_str` and
+ `\l_keys_path_str`, respectively
+
+## [2020-02-03]
+
+### Changed
+- Minor edits to LaTeX3 News
+
+## [2020-01-31]
+
+### Added
+- Table of Contents for combined LaTeX3 News
+
+### Changed
+- Use Lua `utf8` library if available
+
+### Fixed
+- Undefined command in box debugging code
+
+## [2020-01-22]
+
+### Added
+- Support for command replacement in text expansion
+
+### Changed
+- Require key values for numerical key types (dim, int, etc.) (see \#661)
+
+### Fixed
+- Issue with keys where some leading spaces could be left in key names
+
+## [2020-01-12]
+
+### Added
+- `\bool_case_true:n(TF)` and `\bool_case_false:n(TF)`
+- `\file_hex_dump:n(nn)` and `\file_get_hex_dump:n(nn)N(TF)`
+- `\str_<type>case:n`
+- `\text_<type>case:n(n)`
+- `\text_expand:n` and supporting data structures
+
+### Changed
+- Distribute LaTeX3 News
+- Moved `\char_<type>case:N` to stable
+- Documentation improvements
+
+### Fixed
+- Inherit key required/forbidden properties (see \#653)
+- Set backend at the beginning of `\document` (see \#657)
+
+### Deprecated
+- `\str_<type>_case:n` replaced by `\str_<type>case:n`
+ except `\str_mixed_case:n` replaced by `\str_titlecase:n`
+- `\tl_<type>_case:n(n)` replaced by `\text_<type>case:n(n)`,
+ except `\tl_mixed_case:n(n)` replaced by `\text_titlecase:n(n)`
+
+## [2019-11-07]
+
+### Fixed
+- Handling of repeated loading of a backend (issue [\#646](https://github.com/latex3/latex3/issues/646))
+- Handling of repeated loading of deprecated functions
+
+## [2019-10-28]
+
+### Fixed
+- File searching when `\(pdf)filesize` is not available (issue \#644)
+
+## [2019-10-27]
+
+### Changed
+- Internal structure of `\c_sys_jobname_str` altered
+- Update upTeX test to follow guidance from developers
+
+## [2019-10-24]
+
+### Changed
+- File names are now returned without quotes by `\file_full_name:n`
+
+### Fixed
+- `\file_if_exist:n(TF)`, etc., when dealing with file names containing
+ spaces (see \#642)
+
+## [2019-10-21]
+
+### Added
+- Lua function `l3kernel.shellescape()`
+
+### Changed
+- Better coverage of (u)pTeX primitives following publication of
+ pTeX manual in English
+- Trim spaces surrounding file names
+
+### Removed
+- HarfTeX primitives
+
+## [2019-10-14]
+
+### Fixed
+- Correct handling of 'traditional' class options for backend
+
+## [2019-10-11]
+
+### Changed
+- Standard backend for (u)pTeX is now `dvips`
+- Minimum LuaTeX version now v0.95
+- Moved `\debug_on:n`, `\debug_off:n`, `\debug_suspend:` and `\debug_resume:`
+ to stable
+- Accept 'traditional' class options for backend (`dvipdfmx`, `dvips`, etc.)
+- Performance enhancements when loading `expl3`
+
+### Fixed
+- Handling of files with no extension
+- Behaviour of Lua function `l3kernel.charcat` in some circumstances
+- Loading under ConTeXt
+
+## [2019-10-02]
+
+### Fixed
+- Variants using `\exp_args` functions with more than 9 arguments (see \#636)
+
+## [2019-09-30]
+
+### Fixed
+- File searching using `\file_full_name:n` (see \#634)
+
+## [2019-09-28]
+
+### Changed
+- Speed up variants and reduce their `\tracingall` output
+- Debug and deprecation code are now loaded independently of expl3 core
+- `\file_compare_timestamp:nNn(TF)` now usable in expansion contexts
+- Moved to stable:
+ - `\bool_const:Nn`
+ - `\dim_sign:n`
+ - `\file_compare_timestamp:nNn(TF)`
+ - FP `logb` operator
+ - `\fp_sign:n`
+ - `fparray` module
+ - `\int_sign:n`
+ - `\intarray_const_from_clist:Nn`
+ - `\intarray_show:N`
+ - `\ior_map_variable:NNn`
+ - `\ior_str_map_variable:NNn`
+ - `\mode_leave_vertical:`
+ - `\prop_(g)set_from_clist:Nn`
+ - `\prop_const_from_clist:Nn`
+ - `\seq_const_from_clist:Nn`
+ - `\seq_(g)shuffle:N`
+ - `\sys_if_platform_unix:(TF)`
+ - `\sys_if_platform_windows:(TF)`
+ - `\sys_gset_rand_seed:`
+ - `\sys_rand_seed:`
+ - Shell access functions
+
+### Fixed
+- Key `.initial:n` property when combined with inherited keys (see \#631)
+
+## [2019-09-19]
+
+### Fixed
+- Loading Unicode data when some chars may be active (see \#627)
+
+## [2019-09-08]
+
+### Fixed
+- Missing internal variant (issue \#624)
+
+## [2019-09-05]
+
+### Added
+- `\file_full_name:n`, `\file_mdfive_hash:n`, `\file_size:n`,
+ `\file_timestamp:n`
+- `\seq_map_tokens:Nn`, `\tl_map_tokens:nn`, `\tl_map_tokens:Nn`
+
+### Changed
+- Moved `\prop_map_tokens:Nn` to stable
+- Generate chars with catcode as-supplied when case changing
+
+## [2019-08-25]
+
+### Added
+- `\fp_if_nan:nTF`
+
+### Changed
+- Make round(.,nan)=nan with no "Invalid operation" error
+
+### Fixed
+- `\tl_rescan:nn` and `\tl_(g)set_rescan:Nnn` when single-line input
+ contains a comment character (see \#607)
+- Final value of the variable in `\tl_map_variable:NNn` and
+ `\clist_map_variable:NNn`.
+- Remove duplicate keys in `\prop_set_from_keyval:Nn` (see \#572)
+
+## [2019-08-14]
+
+### Deprecated
+- `\c_term_ior`
+
+### Fixed
+- Coffin pole intersection in some cases (see \#605)
+
+## [2019-07-25]
+
+### Fixed
+- Loading for `expl3` with plain TeX
+
+## [2019-07-01]
+
+### Added
+- Moved `l3str-convert` module to `l3kernel`
+
+### Changed
+- Ensure `\msg_fatal:nn` ends the TeX run if used inside an
+ hbox (see \#587)
+- Moved backend code to a separate release schedule
+
+### Fixed
+- Handling of control sequences in key names (see \#594)
+
+## [2019-05-28]
+
+### Added
+- Experimental `\file_compare_timestamp:nNn(TF)`
+
+### Changed
+- Precedence of juxtaposition (implicit multiplication) in `l3fp`
+ now different for keywords/variables and factors in parentheses
+
+## [2019-05-09]
+
+### Added
+- Experimental driver-level interfaces for image inclusion
+- Experimental `\ior_shell_open:Nn`
+
+### Fixed
+- Some issues in `dvisvgm` driver
+
+## [2019-05-07]
+
+### Added
+- `.muskip_set:N` property
+
+### Changed
+- Experimental `\driver_pdf_compress_objects:n` replaces
+ `\driver_pdf_objects_(en|dis)able:`
+
+## [2019-05-05]
+
+### Added
+- `\char_str_<target>_case:N`
+
+### Fixed
+- Infinite loop in some cases in DVI mode due to link-breaking code
+ (see \#570)
+- Category code of output from `\char_<target>_case:N`, and
+ same issue in `\str_<target>_case:n`
+
+## [2019-05-03]
+
+### Added
+- New `l3legacy` module containing
+ - `\legacy_if:n(TF)`
+
+### Changed
+- Moved `\file_get_mdfive_hash:nN(TF)`, `\file_get_size:nN(TF)`
+ and `\file_get_timestamp:nN(TF)` to stable
+- Moved `\file_if_exist_input:n` and `\file_if_exist_input:nF` to stable
+- Moved `\file_input_stop:` to stable
+- Moved `\peek_N_type:TF` to stable
+
+## [2019-04-21]
+
+### Added
+- Experimental support for a range of PDF concepts at the lowest
+ (driver abstraction) level
+
+## [2019-04-06]
+
+### Changed
+- Moved `\tl_if_single_token:n(TF)` to stable
+
+### Fixed
+- Support for ConTeXt from mid-December 2018
+
+## [2019-03-26]
+
+### Fixed
+- Loading when pre-TL'18 XeTeX is in use (see \#555)
+
+## [2019-03-05]
+
+### Added
+- `\str_log:n`, `\str_log:N`
+- `TF` versions for `\file_get_...:nN` and `\ior_(str_)get:NN` functions
+- `\cs_prefix_spec:N`, `\cs_argument_spec:N`, `\cs_replacement_spec:N`
+- `undo-recent-deprecations` option
+- `factorial` function in `l3fp`
+
+### Changed
+- Return values from `\file_get:nnN`, `\file_get_...:nN`, `\ior_get:NN`,
+ `\sys_shell_get:nnN`
+- Moved coffin affine transformations to stable
+- Moved `\prop_count:N` to stable
+- Moved `\tl_count_tokens:n` to stable
+- Completed emulation of e-type argument when `\expanded` is unavailable
+- Made expandable messages expand their result, like usual messages
+- Made deprecation errors less intrusive by default
+- Stopped providing do-nothing `\color` macro when undefined
+
+### Deprecated
+- `\token_get_prefix_spec:N`, `\token_get_arg_spec:N`,
+ `\token_get_replacement_spec:N` replaced by `\cs_prefix_spec:N`,
+ `\cs_argument_spec:N`, `\cs_replacement_spec:N`, respectively
+
+### Fixed
+- Treatment of inherited keys when setting only known keys (see \#548)
+
+### Removed
+- Experimental `\skip_split_finite_else_action:nnNN`
+- Experimental `\tl_reverse_tokens:n`
+
+## [2019-02-15]
+
+### Changed
+- Defensive code for redefinition of `\time`, `\day`, `\month` and `\year`
+
+### Fixed
+- Resetting of key inheritance (see \#535)
+- Issue in deprecated command `\tl_set_from_file:Nnn`
+ (see https://tex.stackexchange.com/q/474813/)
+
+## [2019-02-03]
+
+### Added
+- Support for return of whole path by `\keys_set_known:nnN`-like
+ function `\keys_set_known:nnnN` (see \#508)
+- `.prop_(g)put:N` key property (see \#444)
+
+### Fixed
+- Handling of nested key setting when filtering, _etc._ (see \#526)
+- Inheritance of default values (see \#504)
+
+## [2019-01-28]
+
+### Added
+- Global versions of box affine functions, e.g. `\box_grotate:Nn`
+- Global versions of box size adjustment functions
+- `\box_(g)set_eq_drop:NN`, `\(h|v)box_unpack_drop:N`
+- `\file_get:nnN` and `\file_get:nnNTF`
+- Experimental functions `\sys_shell_get:nnN` and `\sys_shell_get:nnNTF`
+
+### Changed
+- `\char_generate:nn` now always takes exactly two expansions
+- Move `\prg_generate_conditional_variant:Nnn` to stable
+- Renamed experimental `\box_trim:Nnnnn` and `\box_viewport:Nnnnn` as
+ `\box_set_trim:Nnnnn` and `\box_set_viewport:Nnnnn`, respectively
+
+### Deprecated
+- `\box_(g)set_eq_clear:NN`, replaced by `\box_(g)set_eq_drop:NN`
+- `\(h|v)box_unpack_clear:N`, replaced by `\(h|v)box_unpack_drop:N`
+- `\tl_(g)set_from_file(_x):Nnn`, replaced by `\file_get:nnN`
+
+### Fixed
+- Scope treatment of `\box_set_dp:N`, _etc._
+- In (u)platex: detection of spaces in `\tl_rescan:nn` and related functions
+
+### Removed
+- Experimental function family `\tl_(g)set_from_shell:(N|c)nn`
+ (replaced by `\sys_shell_get:nnN`)
+
+## [2019-01-13]
+
+### Added
+- `\ior_map_variable:NNn` and `\ior_str_map_variable:NNn`
+
+### Fixed
+- Unclosed conditional with Unicode engines
+
+## [2019-01-12]
+
+### Changed
+- Improved `expl3` loading time with LuaTeX and XeTeX
+- Improved performance of `\ior_map_inline:Nn` and related functions
+
+### Fixed
+- Handling of accented characters under mixed case changing in 8-bit engines
+ (see \#514)
+
+## [2019-01-01]
+
+### Added
+- `\iow_allow_break:`
+
+### Fixed
+- Correct fp randint with zero argument (see \#507)
+- Handling of `\current@color` with `(x)dvipdfmx` (see \#510)
+
+### Removed
+- Support for stand-alone `l3regex`, `l3sort`, `l3srt`, `l3tl-analysis`,
+ `l3tl-build`
+- `\box_resize:Nnn`
+- `\box_use_clear:N`
+- `\c_minus_one`
+- `\file_add_path:nN`
+- `\file_list:`
+- `\file_path_include:n` and `\file_path_remove:n`
+- `\io(r|w)_list_streams:` and `\io(r|w)_log_streams:`
+- `\sort_ordered:` and `\sort_reversed:`
+- `\token_new:Nn`
+- Generation of invalid variants from `n`/`N` base types
+
+## [2018-12-12]
+
+### Changed
+- Move `\tl_range:nnn` to stable
+
+### Fixed
+- Loading in ConTeXt MkIV
+
+## [2018-12-11]
+
+### Changed
+- Enable `\char_generate:nn` to create active tokens with XeTeX
+
+## [2018-12-06]
+
+### Changed
+- Apply `\par` only at the end of vertical boxes
+- Move `\int_rand:n` to stable
+- Move `\<var>_rand_item:N` to stable
+
+### Fixed
+- Typo in `\lua_shipout_e:n` (see \#503)
+
+## [2018-11-19]
+
+### Added
+- Support for cross-compatibility primitives in XeTeX
+- `\int_sign:n`, `\dim_sign:n` and `\fp_sign:n`
+
+## [2018-10-19]
+
+### Fixed
+- Wrapping of text in messages, etc., for some line lengths (issue \#491)
+
+## [2018-10-17]
+
+### Added
+- `\g_msg_module_documentation_prop` (see \#471)
+- `\peek_remove_spaces:n`
+
+### Changed
+- Formatting of messages: now follows LaTeX2e closely
+
+### Deprecated
+- `\msg_interrupt:nn`, `\msg_log:n` and `\msg_term:n`
+
+### Fixed
+- Handling of inheritance for choice keys (issue \#483)
+
+## [2018-09-24]
+
+### Added
+- Some driver-level support for PDF features
+- `\peek_catcode_collect_inline:Nn`, `\peek_charcode_collect_inline:Nn`,
+ `\peek_meaning_collect_inline:Nn`
+
+### Fixed
+- Handling of unknown keys when inheritance is active (issue \#481)
+
+## [2018-08-23]
+
+### Added
+- `\lua_escape:e`, `\lua_now:e` and `\lua_shipout_e:n`
+- `\str_case_e:nn(TF)` and `\str_if_eq:ee(TF)`
+- `\sys_if_platform_unix:(TF)` and `\sys_if_platform_windows:(TF)`
+- `\tl_(g)set_from_shell:Nnn`
+
+### Deprecated
+- `\lua_escape_x:n`, `\lua_now_x:n` and `\lua_shipout_x:n`
+- `\str_case_x:nn(TF)` and `\str_if_eq_x:nn(TF)`
+
+## [2018-06-14]
+
+### Added
+- Support for `e`-type argument using `\expanded` or macro emulation
+
+## [2018-06-01]
+
+### Added
+- `CHANGELOG.md` (issue \#460)
+
+### Fixed
+- Loading `expl3` with LuaTeX/XeTeX and certain letter tokens set
+ to be active (see \#462)
+
+### Changed
+- Alter `\char_codepoint_from_bytes:n` to produce four groups in all
+ cases; make `f`-type expandable
+
+## [2018-05-13]
+
+### Fixed
+- Correct date string in `expl3.dtx`
+- Correct `\c_sys_engine_version_str` when using XeTeX
+
+## [2018-05-12]
+
+### Added
+- Define `\c_zero_int` and `\c_one_int`
+- Implement `\c_sys_engine_version_str`
+- Implement `\seq_indexed_map_function/inline`
+- Implement `\intarray_gzero:N`
+- Implement `\intarray_const_from_clist:Nn`
+- Implement `\bool_set_inverse:N`
+- Implement `\bool_xor:nnTF` instead of just `\bool_xor_p:nn`
+- Implement candidate `\int_rand:n`
+- Implement `\intarray_gset_rand:Nnn`
+- Implement candidate `l3fparray` module
+
+### Changed
+- Update minimal required versions of XeTeX and LuaTeX
+- Deprecate named integer constants `\c_zero`. etc.
+- Move all primitives to `\tex_...:D namespace`,
+ deprecating older engine-dependent prefixes
+- Several internal optimisations
+
+### Fixed
+- Expand boolean expression before calling `\chardef` (issue \#461)
+
+### Removed
+- Remove undocumented `\fp_function:Nw` and `\fp_new_function:Npn`
+
+## [2018-04-30]
+
+### Added
+- Implement `\tl_analysis_map_inline:nn`
+- Implement `\exp_args_generate:n` to define new `\exp_args:N...`
+ functions
+- Low-level `\int_value:w` function
+- New experimental functions for
+ - Building token lists piecewise
+ - Fast manipulation of integer arrays
+ - Sequence shuffling
+ - `\seq_set_from_function:NnN`
+ - `\char_codepoint_to_bytes:n`
+
+### Changed
+- Significant internal revision to use only internal functions
+ 'private' to specific modules
+- Better documentation of cross-module kernel-internal functions
+- Enable `\char_generate:nn` for active chars
+- Renamed `\tl_show_analysis:(N|n)n` as `\tl_analysis_show:(N|n)n`
+- Change `\int_rand:nn` (and rand_item functions) to better use
+ the RNG
+- Make prg break functions public
+- Make scan marks mechanism public
+- Make `\prg_do_nothing:` long rather than nopar (issue \#455)
+- Several performance improvements
+- Documentation improvements
+
+### Fixed
+- Only index TF, T, F functions together if they are `expl3`
+ functions (issue \#453)
+- Make `\infty` and `\pi` into errors in fp expressions
+ (issue \#357)
+
+### Removed
+- Deprecated functions expiring at end of 2017
+- Old module `.sty` files
+
+## [2018-03-05]
+
+### Changes
+- Adjustments to `l3drivers` to support `l3draw` development in
+ `l3experimental` bundle
+
+## [2018-02-21]
+
+### Added
+- Tuple support in fp expressions
+- Step functions have been added for dim variables,
+ e.g. `\dim_step_inline:nnnn`
+
+[Unreleased]: https://github.com/latex3/latex3/compare/2024-04-11...HEAD
+[2024-04-11]: https://github.com/latex3/latex3/compare/2024-03-14...2024-04-11
+[2024-03-14]: https://github.com/latex3/latex3/compare/2024-02-20...2024-03-14
+[2024-02-20]: https://github.com/latex3/latex3/compare/2024-02-18...2024-02-20
+[2024-02-18]: https://github.com/latex3/latex3/compare/2024-02-13...2024-02-18
+[2024-02-13]: https://github.com/latex3/latex3/compare/2024-01-22...2024-02-13
+[2024-01-22]: https://github.com/latex3/latex3/compare/2024-01-04...2024-01-22
+[2024-01-04]: https://github.com/latex3/latex3/compare/2023-12-11...2024-01-04
+[2023-12-11]: https://github.com/latex3/latex3/compare/2023-12-08...2023-12-11
+[2023-12-08]: https://github.com/latex3/latex3/compare/2023-11-09...2023-12-08
+[2023-11-09]: https://github.com/latex3/latex3/compare/2023-11-01...2023-11-09
+[2023-11-01]: https://github.com/latex3/latex3/compare/2023-10-23...2023-11-01
+[2023-10-23]: https://github.com/latex3/latex3/compare/2023-10-10...2023-10-23
+[2023-10-10]: https://github.com/latex3/latex3/compare/2023-08-29...2023-10-10
+[2023-08-29]: https://github.com/latex3/latex3/compare/2023-08-11...2023-08-29
+[2023-08-11]: https://github.com/latex3/latex3/compare/2023-08-03...2023-08-11
+[2023-08-03]: https://github.com/latex3/latex3/compare/2023-06-30...2023-08-03
+[2023-06-30]: https://github.com/latex3/latex3/compare/2023-06-16...2023-06-30
+[2023-06-16]: https://github.com/latex3/latex3/compare/2023-06-05...2023-06-16
+[2023-06-05]: https://github.com/latex3/latex3/compare/2023-05-22...2023-06-05
+[2023-05-22]: https://github.com/latex3/latex3/compare/2023-05-15...2023-05-22
+[2023-05-15]: https://github.com/latex3/latex3/compare/2023-05-11...2023-05-15
+[2023-05-11]: https://github.com/latex3/latex3/compare/2023-05-05...2023-05-11
+[2023-05-05]: https://github.com/latex3/latex3/compare/2023-04-20...2023-05-05
+[2023-04-20]: https://github.com/latex3/latex3/compare/2023-04-19...2023-04-20
+[2023-04-19]: https://github.com/latex3/latex3/compare/2023-03-30...2023-04-19
+[2023-03-30]: https://github.com/latex3/latex3/compare/2023-02-22...2023-03-30
+[2023-02-22]: https://github.com/latex3/latex3/compare/2023-02-07...2023-02-22
+[2023-02-07]: https://github.com/latex3/latex3/compare/2023-02-02...2023-02-07
+[2023-02-02]: https://github.com/latex3/latex3/compare/2023-02-01...2023-02-02
+[2023-02-01]: https://github.com/latex3/latex3/compare/2023-01-24...2023-02-01
+[2023-01-24]: https://github.com/latex3/latex3/compare/2023-01-16...2023-01-24
+[2023-01-16]: https://github.com/latex3/latex3/compare/2022-12-17...2023-01-16
+[2022-12-17]: https://github.com/latex3/latex3/compare/2022-11-02...2022-12-17
+[2022-11-02]: https://github.com/latex3/latex3/compare/2022-10-26...2022-11-02
+[2022-10-26]: https://github.com/latex3/latex3/compare/2022-09-28...2022-10-26
+[2022-09-28]: https://github.com/latex3/latex3/compare/2022-08-30...2022-09-28
+[2022-08-30]: https://github.com/latex3/latex3/compare/2022-08-23...2022-08-30
+[2022-08-23]: https://github.com/latex3/latex3/compare/2022-08-05...2022-08-23
+[2022-08-05]: https://github.com/latex3/latex3/compare/2022-07-15...2022-08-05
+[2022-07-15]: https://github.com/latex3/latex3/compare/2022-07-14...2022-07-15
+[2022-07-14]: https://github.com/latex3/latex3/compare/2022-07-04...2022-07-14
+[2022-07-04]: https://github.com/latex3/latex3/compare/2022-07-01...2022-07-04
+[2022-07-01]: https://github.com/latex3/latex3/compare/2022-06-16...2022-07-01
+[2022-06-16]: https://github.com/latex3/latex3/compare/2022-06-02...2022-06-16
+[2022-06-02]: https://github.com/latex3/latex3/compare/2022-05-30...2022-06-02
+[2022-05-30]: https://github.com/latex3/latex3/compare/2022-05-04...2022-05-30
+[2022-05-04]: https://github.com/latex3/latex3/compare/2022-04-29...2022-05-04
+[2022-04-29]: https://github.com/latex3/latex3/compare/2022-04-20...2022-04-29
+[2022-04-20]: https://github.com/latex3/latex3/compare/2022-04-10...2022-04-20
+[2022-04-10]: https://github.com/latex3/latex3/compare/2022-02-24...2022-04-10
+[2022-02-24]: https://github.com/latex3/latex3/compare/2022-02-21...2022-02-24
+[2022-02-21]: https://github.com/latex3/latex3/compare/2022-02-05...2022-02-21
+[2022-02-05]: https://github.com/latex3/latex3/compare/2022-01-21...2022-02-05
+[2022-01-21]: https://github.com/latex3/latex3/compare/2022-01-12...2022-01-21
+[2022-01-12]: https://github.com/latex3/latex3/compare/2021-11-22...2022-01-12
+[2021-11-22]: https://github.com/latex3/latex3/compare/2021-11-12...2021-11-22
+[2021-11-12]: https://github.com/latex3/latex3/compare/2021-10-18...2021-11-12
+[2021-10-18]: https://github.com/latex3/latex3/compare/2021-10-17...2021-10-18
+[2021-10-17]: https://github.com/latex3/latex3/compare/2021-10-12...2021-10-17
+[2021-10-12]: https://github.com/latex3/latex3/compare/2021-08-27...2021-10-12
+[2021-08-27]: https://github.com/latex3/latex3/compare/2021-07-12...2021-08-27
+[2021-07-12]: https://github.com/latex3/latex3/compare/2021-06-18...2021-07-12
+[2021-06-18]: https://github.com/latex3/latex3/compare/2021-06-01...2021-06-18
+[2021-06-01]: https://github.com/latex3/latex3/compare/2021-05-27...2021-06-01
+[2021-05-27]: https://github.com/latex3/latex3/compare/2021-05-25...2021-05-27
+[2021-05-25]: https://github.com/latex3/latex3/compare/2021-05-11...2021-05-25
+[2021-05-11]: https://github.com/latex3/latex3/compare/2021-05-07...2021-05-11
+[2021-05-07]: https://github.com/latex3/latex3/compare/2021-02-18...2021-05-07
+[2021-02-18]: https://github.com/latex3/latex3/compare/2021-02-06...2021-02-18
+[2021-02-06]: https://github.com/latex3/latex3/compare/2021-02-02...2021-02-06
+[2021-02-02]: https://github.com/latex3/latex3/compare/2021-01-09...2021-02-02
+[2021-01-09]: https://github.com/latex3/latex3/compare/2020-12-07...2021-01-09
+[2020-12-07]: https://github.com/latex3/latex3/compare/2020-12-05...2020-12-07
+[2020-12-05]: https://github.com/latex3/latex3/compare/2020-12-03...2020-12-05
+[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
+[2020-09-06]: https://github.com/latex3/latex3/compare/2020-09-03...2020-09-06
+[2020-09-03]: https://github.com/latex3/latex3/compare/2020-09-01...2020-09-03
+[2020-09-01]: https://github.com/latex3/latex3/compare/2020-08-07...2020-09-01
+[2020-08-07]: https://github.com/latex3/latex3/compare/2020-07-17...2020-08-07
+[2020-07-17]: https://github.com/latex3/latex3/compare/2020-06-18...2020-07-17
+[2020-06-18]: https://github.com/latex3/latex3/compare/2020-06-03...2020-06-18
+[2020-06-03]: https://github.com/latex3/latex3/compare/2020-05-15...2020-06-03
+[2020-05-15]: https://github.com/latex3/latex3/compare/2020-05-14...2020-05-15
+[2020-05-14]: https://github.com/latex3/latex3/compare/2020-05-11...2020-05-14
+[2020-05-11]: https://github.com/latex3/latex3/compare/2020-05-05...2020-05-11
+[2020-05-05]: https://github.com/latex3/latex3/compare/2020-04-06...2020-05-05
+[2020-04-06]: https://github.com/latex3/latex3/compare/2020-03-06...2020-04-06
+[2020-03-06]: https://github.com/latex3/latex3/compare/2020-03-03...2020-03-06
+[2020-03-03]: https://github.com/latex3/latex3/compare/2020-02-25...2020-03-03
+[2020-02-25]: https://github.com/latex3/latex3/compare/2020-02-21...2020-02-25
+[2020-02-21]: https://github.com/latex3/latex3/compare/2020-02-14...2020-02-21
+[2020-02-14]: https://github.com/latex3/latex3/compare/2020-02-13...2020-02-14
+[2020-02-13]: https://github.com/latex3/latex3/compare/2020-02-11...2020-02-13
+[2020-02-11]: https://github.com/latex3/latex3/compare/2020-02-08...2020-02-11
+[2020-02-08]: https://github.com/latex3/latex3/compare/2020-02-03...2020-02-08
+[2020-02-03]: https://github.com/latex3/latex3/compare/2020-01-31...2020-02-03
+[2020-01-31]: https://github.com/latex3/latex3/compare/2020-01-22...2020-01-31
+[2020-01-22]: https://github.com/latex3/latex3/compare/2020-01-12...2020-01-22
+[2020-01-12]: https://github.com/latex3/latex3/compare/2019-11-07...2020-01-12
+[2019-11-07]: https://github.com/latex3/latex3/compare/2019-10-28...2019-11-07
+[2019-10-28]: https://github.com/latex3/latex3/compare/2019-10-27...2019-10-28
+[2019-10-27]: https://github.com/latex3/latex3/compare/2019-10-24...2019-10-27
+[2019-10-24]: https://github.com/latex3/latex3/compare/2019-10-21...2019-10-24
+[2019-10-21]: https://github.com/latex3/latex3/compare/2019-10-14...2019-10-21
+[2019-10-14]: https://github.com/latex3/latex3/compare/2019-10-11...2019-10-14
+[2019-10-11]: https://github.com/latex3/latex3/compare/2019-10-02...2019-10-11
+[2019-10-02]: https://github.com/latex3/latex3/compare/2019-09-30...2019-10-02
+[2019-09-30]: https://github.com/latex3/latex3/compare/2019-09-28...2019-09-30
+[2019-09-28]: https://github.com/latex3/latex3/compare/2019-09-19...2019-09-28
+[2019-09-19]: https://github.com/latex3/latex3/compare/2019-09-08...2019-09-19
+[2019-09-08]: https://github.com/latex3/latex3/compare/2019-09-05...2019-09-08
+[2019-09-05]: https://github.com/latex3/latex3/compare/2019-08-25...2019-09-05
+[2019-08-25]: https://github.com/latex3/latex3/compare/2019-08-14...2019-08-25
+[2019-08-14]: https://github.com/latex3/latex3/compare/2019-07-25...2019-08-14
+[2019-07-25]: https://github.com/latex3/latex3/compare/2019-07-01...2019-07-25
+[2019-07-01]: https://github.com/latex3/latex3/compare/2019-05-28...2019-07-01
+[2019-05-28]: https://github.com/latex3/latex3/compare/2019-05-09...2019-05-28
+[2019-05-09]: https://github.com/latex3/latex3/compare/2019-05-07...2019-05-09
+[2019-05-07]: https://github.com/latex3/latex3/compare/2019-05-05...2019-05-07
+[2019-05-05]: https://github.com/latex3/latex3/compare/2019-05-03...2019-05-05
+[2019-05-03]: https://github.com/latex3/latex3/compare/2019-04-21...2019-05-03
+[2019-04-21]: https://github.com/latex3/latex3/compare/2019-04-06...2019-04-21
+[2019-04-06]: https://github.com/latex3/latex3/compare/2019-03-26...2019-04-06
+[2019-03-26]: https://github.com/latex3/latex3/compare/2019-03-05...2019-03-26
+[2019-03-05]: https://github.com/latex3/latex3/compare/2019-02-15...2019-03-05
+[2019-02-15]: https://github.com/latex3/latex3/compare/2019-02-03...2019-02-15
+[2019-02-03]: https://github.com/latex3/latex3/compare/2019-01-28...2019-02-03
+[2019-01-28]: https://github.com/latex3/latex3/compare/2019-01-13...2019-01-28
+[2019-01-13]: https://github.com/latex3/latex3/compare/2019-01-12...2019-01-13
+[2019-01-12]: https://github.com/latex3/latex3/compare/2019-01-01...2019-01-12
+[2019-01-01]: https://github.com/latex3/latex3/compare/2018-12-12...2019-01-01
+[2018-12-12]: https://github.com/latex3/latex3/compare/2018-12-11...2018-12-12
+[2018-12-11]: https://github.com/latex3/latex3/compare/2018-12-06...2018-12-11
+[2018-12-06]: https://github.com/latex3/latex3/compare/2018-11-19...2018-12-06
+[2018-11-19]: https://github.com/latex3/latex3/compare/2018-10-31...2018-11-19
+[2018-10-31]: https://github.com/latex3/latex3/compare/2018-10-26...2018-10-31
+[2018-10-26]: https://github.com/latex3/latex3/compare/2018-10-19...2018-10-26
+[2018-10-19]: https://github.com/latex3/latex3/compare/2018-10-17...2018-10-19
+[2018-10-17]: https://github.com/latex3/latex3/compare/2018-09-24...2018-10-17
+[2018-09-24]: https://github.com/latex3/latex3/compare/2018-08-23...2018-09-24
+[2018-08-23]: https://github.com/latex3/latex3/compare/2018-06-14...2018-08-23
+[2018-06-14]: https://github.com/latex3/latex3/compare/2018-06-01...2018-06-14
+[2018-06-01]: https://github.com/latex3/latex3/compare/2018-05-13...2018-06-01
+[2018-05-13]: https://github.com/latex3/latex3/compare/2018-05-12...2018-05-13
+[2018-05-12]: https://github.com/latex3/latex3/compare/2018-04-30...2018-05-12
+[2018-04-30]: https://github.com/latex3/latex3/compare/2018-03-05...2018-04-30
+[2018-03-05]: https://github.com/latex3/latex3/compare/2018-02-21...2018-03-05
+[2018-02-21]: https://github.com/latex3/latex3/compare/2017-12-16...2018-02-21
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/README.md b/Master/texmf-dist/doc/latex-dev/l3kernel/README.md
new file mode 100644
index 00000000000..70d106b4967
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/README.md
@@ -0,0 +1,96 @@
+LaTeX3 Programming Conventions
+==============================
+
+Release 2024-04-11
+
+Overview
+--------
+
+The files of the `l3kernel` bundle provide an API for TeX programmers
+with defined syntax conventions, completely separating it from document level
+syntax. Hence, the commands provided are not intended for use at the document
+level nor for use in describing design layouts in document class files.
+
+This API provides the foundation on which new additions to the LaTeX kernel and
+other advanced extensions are built. The programming layer is designed to be
+loaded as part of LaTeX2e format building or as a loaded package with plain TeX
+or other formats.
+
+The syntax and functionality provided by `l3kernel` is regarded by the LaTeX
+team as stable. There may still be changes to some functions, but these will be
+very minor when compared to the scope of `l3kernel`. In particular, no functions
+will be removed, although some may be deprecated.
+
+
+Requirements
+------------
+
+The `l3kernel` bundle requires the e-TeX extensions and a number of additional
+'utility' primitives, almost all of which were first added to pdfTeX. In
+particular, the functionality equivalent to the following pdfTeX primitives must
+be available
+
+- `\ifincsname`
+- `\ifpdfprimitive`
+- `\pdfcreationdate`
+- `\pdfelapsedtime`
+- `\pdffiledump`
+- `\pdffilemoddate`
+- `\pdffilesize`
+- `\pdflastxpos`
+- `\pdflastypos`
+- `\pdfmdfivesum`
+- `\pdfnormaldeviate`
+- `\pdfpageheight`
+- `\pdfpagewidth`
+- `\pdfprimitive`
+- `\pdfrandomseed`
+- `\pdfresettimer`
+- `\pdfsavepos`
+- `\pdfsetrandomseed`
+- `\pdfshellescape`
+- `\pdfstrcmp`
+- `\pdfuniformdeviate`
+
+For ease of reference, these primitives will be referred to as the 'pdfTeX
+utilities'. With the exception of `\expanded`, these have been present in pdfTeX
+since the release of version 1.40.0 in 2007; `\expanded` was added for TeX Live
+2019. Similarly, the full set of these utility primitives has been available in
+XeTeX from the 2019 TeX Live release, and has always been available in LuaTeX
+(some by Lua emulation). The Japanese pTeX and upTeX gained all of the above
+(except `\ifincsname`) for TeX Live 2019 and `\ifincsname` for TeX Live 2020.
+
+Starting from release 2023-05-15, the `\expanded` primitive is *required*.
+Its slow emulation has been removed.
+
+In addition to the above, engines which are fully Unicode-compatible
+must provide the functionality of the following primitives, documented in the
+LuaTeX manual
+
+- `\Uchar`
+- `\Ucharcat`
+- `\Umathcode`
+
+The existence of the primitive `\Umathcode` is used as the marker for Unicode
+support.
+
+Issues
+------
+
+The issue tracker for LaTeX3 is currently located
+[on GitHub](https://github.com/latex3/latex3/issues).
+
+The LaTeX Project
+------------------
+
+Development of LaTeX3 is carried out by
+[The LaTeX Project](https://www.latex-project.org/latex3/).
+
+The development team can be contacted
+by e-mail: <latex-team@latex-project.org>.
+
+-----
+
+<p>Copyright (C) 1998-2012,2015-2024 The LaTeX Project <br />
+<a href="http://latex-project.org/">http://latex-project.org/</a> <br />
+All rights reserved.</p>
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/expl3.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/expl3.pdf
new file mode 100644
index 00000000000..892886a3620
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/expl3.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/interface3.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/interface3.pdf
new file mode 100644
index 00000000000..a866590ab0d
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/interface3.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/interface3.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/interface3.tex
new file mode 100644
index 00000000000..1bf31032ee8
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/interface3.tex
@@ -0,0 +1,82 @@
+\iffalse meta-comment
+
+File: interface3.tex
+
+Copyright (C) 1990-2011,2017-2024 The LaTeX Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "l3kernel bundle" (The Work in LPPL)
+and all files in that bundle must be distributed together.
+
+The released version of this bundle is available from CTAN.
+
+-----------------------------------------------------------------------
+
+The development version of the bundle can be found at
+
+ https://github.com/latex3/latex3
+
+for those people who are interested.
+
+\fi
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% This document typesets the LaTeX3 interface descriptions a single document.
+% This produces quite a large file (more than 360 pages as of Dec 2023).
+%
+% There is also a full version of the sources (source3.tex) which additionally
+% also typesets the command implementations.
+%
+% Do not forget to generate the index (as explained on the terminal output
+% near the end of the run)!
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+\documentclass[kernel]{l3doc}
+
+\newif\ifinterface
+\interfacetrue
+
+\listfiles
+
+\begin{document}
+
+\title{The \LaTeX3 Interfaces}
+\author{%
+ The \LaTeX{} Project\thanks
+ {%
+ E-mail:
+ \href{mailto:latex-team@latex-project.org}
+ {latex-team@latex-project.org}%
+ }%
+}
+\date{Released 2024-04-11}
+
+\pagenumbering{roman}
+\maketitle
+
+%
+% First load all modules and typeset the documentation parts
+%
+
+\input{source3body} % all the individual modules
+
+\clearpage
+
+\begingroup
+ \def\endash{--}
+ \catcode`\-\active
+ \def-{\futurelet\temp\indexdash}
+ \def\indexdash{\ifx\temp-\endash\fi}
+ \DelayPrintIndex
+\endgroup
+
+\end{document}
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3doc.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3doc.pdf
new file mode 100644
index 00000000000..604acf43b6d
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3doc.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3docstrip.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3docstrip.pdf
new file mode 100644
index 00000000000..e68f61dcc58
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3docstrip.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news.pdf
new file mode 100644
index 00000000000..591ab927d36
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news.tex
new file mode 100644
index 00000000000..b8f0de5c72b
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news.tex
@@ -0,0 +1,187 @@
+% Copyright 2019-2021 The LaTeX Project
+
+\providecommand*{\lastissue}{12}
+
+\documentclass{ltnews}
+
+\AtBeginDocument{%
+ \renewcommand{\LaTeXNews}{\LaTeX3~News}%
+ \RaggedRight
+ \setlength\parindent{1em}%
+}
+\usepackage{catchfile}
+\usepackage{csquotes}
+\usepackage{enumitem}
+\usepackage{fancyvrb}
+\usepackage{hologo}
+\usepackage{metalogo}
+\usepackage{multicol}
+\usepackage{ragged2e}
+\usepackage{siunitx}
+\usepackage{tikz}
+\usepackage{underscore}
+\usepackage{xparse}
+\usepackage{hyperref}
+\hypersetup{colorlinks}
+\usepackage{bookmark}
+
+\MakeOuterQuote{"}
+
+\pdfstringdefDisableCommands
+ {%
+ \RenewExpandableDocumentCommand \cs { O{} m }
+ {\textbackslash#2}%
+ }
+
+\makeatletter
+\newcounter{issue}
+\renewcommand*{\theissue}{%
+ \ifnum\value{issue}<10 0\fi
+ \number\value{issue}%
+}
+\newcommand*{\MonthJanuary}{01}
+\newcommand*{\MonthFebruary}{02}
+\newcommand*{\MonthMarch}{03}
+\newcommand*{\MonthApril}{04}
+\newcommand*{\MonthMay}{05}
+\newcommand*{\MonthJune}{06}
+\newcommand*{\MonthJuly}{07}
+\newcommand*{\MonthAugust}{08}
+\newcommand*{\MonthSeptember}{09}
+\newcommand*{\MonthOctober}{10}
+\newcommand*{\MonthNovember}{11}
+\newcommand*{\MonthDecember}{12}
+\newcommand*{\printissue}{%
+ Issue %
+ \texorpdfstring{\number\value{issue}}{\theissue}, %
+ \texorpdfstring{\@month\space\@year}{\@year/\@nameuse{Month\@month}}%
+}
+\let\l@part\l@section
+\let\l@section\l@subsection
+\let\l@subsection\l@subsubsection
+\let\l@subsubsection\l@paragraph
+\let\l@paragraph\l@subparagraph
+\newcommand*{\makefirsttitle}{%
+ \twocolumn[{%
+ \parbox[t][4\baselineskip]{\textwidth}{%
+ \@titlefont\@title, Issues 1--\lastissue
+ }%
+ }]%
+}
+
+% show the page number on first page of issues:
+
+\renewcommand{\ps@titlepage}{%
+ \renewcommand{\@oddhead}{\hfill}%
+ \renewcommand{\@evenhead}{\hfill}%
+ \renewcommand{\@oddfoot}{\@indiciafont\@indicia\hfill --\thepage}%
+ \renewcommand{\@evenfoot}{\@indiciafont\@indicia\hfill --\thepage}%
+}
+
+% only needed if the old ltnews class is used
+\renewcommand{\@oddfoot}{\@indiciafont\mbox{}\hfill --\thepage}%
+\renewcommand{\@evenfoot}{\@indiciafont\mbox{}\hfill --\thepage}%
+
+
+\makeatother
+
+\begin{document}
+
+%%% Title and Table of Contents
+
+\makefirsttitle
+\pdfbookmark[0]{\contentsname}{toc}
+\tableofcontents
+\clearpage
+
+\begingroup
+ \renewcommand*{\AtBeginDocument}[1]{#1}%
+ \renewcommand*{\PassOptionsToPackage}[2]{}%
+ \renewcommand*{\usepackage}[2][]{}%
+ \renewcommand*{\RequirePackage}[2][]{}%
+ \renewcommand*{\documentclass}[2][]{%
+ \setcounter{footnote}{0}%
+ \gobbleopt
+ }%
+ \newcommand*{\gobbleopt}[1][]{}%
+ \makeatletter
+ \let\org@twocolumn\twocolumn
+ \renewenvironment{document}{%
+ \clearpage
+ \ifnum\@issue=20 %
+ \addtocontents{toc}{\protect\setcounter{tocdepth}{5}}%
+ \addtocontents{toc}{%
+ \protect\makeatletter
+ }%
+ \addtocontents{toc}{%
+ \let\protect\saved@l@paragraph\protect\l@paragraph
+ }%
+ \addtocontents{toc}{%
+ \let\protect\l@paragraph\protect\l@subsection
+ }%
+ \fi
+ \ifnum\@issue>9\space % we may have issue tocs ...
+ \addtocontents{toc}{%
+ \let\protect\l@paragraph\protect\saved@l@paragraph
+ }%
+ \def\toclevel@subsection{1}%
+ \def\toclevel@subsubsection{2}%
+ \addtocontents{toc}{\protect\setcounter{tocdepth}{4}}%
+ \renewcommand*{\tableofcontents}{%
+ \section*{\contentsname}%
+ \@starttoc{toc\theissue}%
+ }%
+ \let\saved@addtocontents\addtocontents
+ \renewcommand*{\addtocontents}[2]{%
+ \saved@addtocontents{##1}{##2}%
+ \def\temp@toc{toc}%
+ \def\temp@param{##1}%
+ \ifx\temp@toc\temp@param
+ \saved@addtocontents{toc\theissue}{##2}%
+ \fi
+ }%
+ \let\l@subsubsection\l@subsection
+ \let\l@subsection\l@section
+ \let\l@section\l@part
+ \let\l@part\@gobbletwo
+ \fi
+ \def\twocolumn[{%
+ \let\twocolumn\org@twocolumn
+ \org@twocolumn[%
+ {% because of optional argument of \twocolumn
+ \pdfbookmark[0]{\printissue}{issue\theissue}%
+ }%
+ \begingroup
+ \let\WriteBookmarks\relax
+ \@firstofone{% inside opt. arg. of \twocolumn: protect "]"
+ \renewcommand*{\Hy@writebookmark}[5]{}%
+ }%
+ \phantomsection
+ \addcontentsline{toc}{part}{\printissue}%
+ \endgroup
+ }%
+ }{%
+ \clearpage
+ }%
+ \def\MakeOuterQuote#1{%
+ \if\noexpand"\noexpand#1%
+ \else
+ \@latex@error{Unexpected \string\MakeOuterQuote
+ {\detokenize{#1}}\MessageBreak%
+ Check if output is valid in `l3news\theissue.tex'}%
+ \fi}
+ \makeatother
+ \loop
+ \ifnum\value{issue}<\lastissue
+ \stepcounter{issue}%
+ \input{l3news\theissue}%
+ \repeat
+ \stepcounter{issue}%
+ \IfFileExists{l3news\theissue}{%
+ \@latex@error{\jobname.tex is out of date,\MessageBreak
+ there is `l3news\theissue.tex'%
+ }\@ehc
+ }{}%
+\endgroup
+
+\end{document}
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news01.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news01.pdf
new file mode 100644
index 00000000000..c8a490c9cb7
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news01.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news01.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news01.tex
new file mode 100644
index 00000000000..b4841ac24bf
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news01.tex
@@ -0,0 +1,102 @@
+% Copyright 2009 The LaTeX Project
+\documentclass{ltnews}
+\PassOptionsToPackage{colorlinks}{hyperref}
+
+\usepackage{metalogo,ragged2e}
+
+\AtBeginDocument{
+ \renewcommand{\LaTeXNews}{\LaTeX3~News}
+ \RaggedRight
+}
+
+\publicationmonth{February}
+\publicationyear{2009}
+\publicationissue{1}
+
+\begin{document}
+\maketitle
+
+\section{Welcome to \LaTeX3}
+
+Momentum is again starting to build behind The \LaTeX{} Project. For the
+last few releases of \TeX~Live, the experimental programming foundation for
+\LaTeX3 has been available under the name \package{expl3}. Despite large
+warnings that the code would probably change in the future, we wanted to show
+that there was progress being made, no matter how slowly. Since then, some
+people have looked at the code, provided feedback, and~--- most
+importantly~--- actually tried using it. Although it is yet early days, we
+believe that the ideas behind the code are sound and there are only `cosmetic
+improvements' that need to be made before \package{expl3} is ready for the
+\LaTeX~package author masses.
+
+\section{What currently exists}
+
+The current \LaTeX3 code consists of two main branches: the
+\package{expl3} modules that define the underlying programming environment,
+and the `\package{xpackage}s', which are a suite of packages that are written
+with the \package{expl3} programming interface and provide some higher-level
+functionality for what will one day become \LaTeX3 proper. Both \package{expl3} and
+parts of the \package{xpackages} are designed to be used \emph{on top} of
+\LaTeXe, so new packages can take advantage of the new features while still
+allowing to be used alongside many of the vast number of \LaTeXe\ packages on
+\textsc{ctan}.
+
+\section{What's happening now}
+
+In preparation for a minor overhaul of the \package{expl3} code, we are
+writing a comprehensive test suite for each module. These tests allow us to
+make implementation changes and then test if the code still works as before.
+They are also highlighting any minor shortcomings or omissions in the code.
+As the tests are being written, our assumptions about what should be called
+what and the underlying naming conventions for the functions and datatypes are
+being questioned, challenged, and noted for further rumination.
+
+At the time of writing, we are approximately half-way through writing the test
+suite. Once this task is complete, which we plan for the first
+half of 2009, we will be ready to make changes without worrying about breaking
+anything.
+
+\section{What's happening soon}
+
+So what do we want to change? The current \package{expl3} codebase has
+portions that date to the pre-\LaTeXe\ days, while other modules have been
+more recently conceived. It is quite apparent when reading through the sources
+that some unification and tidying up would improve the simplicity and
+consistency of the code. In many cases, such changes will mean nothing more
+than a tweak or a rename.
+
+Beyond these minor changes, we are also re-thinking the exact notation behind
+the way functions are defined. There are currently a handful of different
+types of arguments that functions may be passed (from an untouched single
+token to a complete expansion of a token list) and we're not entirely happy
+with how the original choices have evolved now that the system has grown
+somewhat. We have received good feedback from several people on ways that we
+could improve the argument syntax, and as part of the upcoming changes to the
+\package{expl3} packages we hope to address the problems that we currently
+perceive in the present syntax.
+
+\section{What's happening later}
+
+After the changes discussed above are finished, we will begin freezing the core
+interface of the \package{expl3} modules, and we hope that more package
+authors will be interested in using the new ideas to write their own code.
+While the core functions will then remain unchanged, more features and new
+modules will be added as \LaTeX3 starts to grow.
+
+Some new and/or experimental packages will be changing to use the
+\package{expl3} programming interface, including \package{breqn},
+\package{mathtools}, \package{empheq}, \package{fontspec}, and
+\package{unicode-math}. (Which is one reason for the lack of progress in
+these latter two in recent times.) There will also be a version of the
+\package{siunitx} package written in \package{expl3}, in parallel to the
+current \LaTeXe\ version. These developments will provide improvements to
+everyday \LaTeX\ users who haven't even heard of The \LaTeX{} Project.
+
+Looking towards the long term, \LaTeX3 as a document preparation system needs
+to be written almost from scratch. A high-level user syntax needs to be
+designed and scores of packages will be used as inspiration for the
+`out-of-the-box' default document templates. \LaTeXe\ has stood up to the test
+of time~--- some fifteen years and still going strong~--- and it is now time
+to write a successor that will survive another score.
+
+\end{document}
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news02.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news02.pdf
new file mode 100644
index 00000000000..f3ab2dbd184
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news02.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news02.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news02.tex
new file mode 100644
index 00000000000..a3d5467c8ef
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news02.tex
@@ -0,0 +1,153 @@
+% Copyright 2009 The LaTeX Project
+\documentclass{ltnews}
+\PassOptionsToPackage{colorlinks}{hyperref}
+
+\usepackage{metalogo,ragged2e}
+
+\AtBeginDocument{
+ \renewcommand{\LaTeXNews}{\LaTeX3~News}
+ \RaggedRight
+}
+
+\usepackage{url}
+\newcommand\TUG[1]{\textsc{tug}~#1}
+
+\publicationmonth{June}
+\publicationyear{2009}
+\publicationissue{2}
+
+\begin{document}
+\maketitle
+
+\section{\TeX~Live and the \textsf{expl3} code}
+
+\TeX~Live 2009 is almost upon us, and the \LaTeX3 team have been
+readying a new release of the experimental \LaTeX3 code for this.
+Very dramatic changes have occurred since the
+last public release of the code in \TeX~Live~2008; no backwards compatibility has been
+maintained (as warned in the beginning of the documentation) but we
+believe the changes made are all much for the better. Almost every
+single part of \textsf{expl3} has been scrutinized, resulting in a far
+more coherent code base.
+
+The \textsf{expl3} code is now considered to be much more stable than
+it was before; a comprehensive test suite has been written that helps
+to ensure that we don't make any mistakes as we change things in the
+future. In the process of writing the test
+suite, many minor bugs were fixed; we recommend such test suites for
+all similar developmental projects!
+Some small underlying changes are still expected in the
+\textsf{expl3} code, but major, disruptive, changes aren't planned.
+
+\section{Planned updates}
+
+Until now, the last update to \textsc{ctan} of the \textsf{expl3}
+bundle was for \TeX~Live~2008. Now that work on the code is happening
+on a semi-steady basis, we plan to keep updates rolling out to
+\textsc{ctan} more frequently. This will allow anyone who wishes to
+experiment with the new code to use the \TeX~Live or MiK\TeX\
+updaters to install a recent version without having to `check out' the
+\textsc{svn} repository and install the packages manually.
+
+\section{New members}
+
+We didn't say anything about it in the last status update, but Joseph
+Wright and Will Robertson are now members of the \LaTeX\ Team. They
+have been working fairly exclusively on the \textsf{expl3} code.
+
+It's worth repeating that \LaTeXe\ is essentially frozen in order to
+prevent any backwards compatibility problems. As desirable as it is
+to benefit from the new features offered by new engines \XeTeX\ and
+Lua\TeX, we cannot risk the stability of production servers running
+older versions of \LaTeXe\ which will inevitably end up processing
+documents written into the future.
+
+\LaTeX3 will not be inheriting the same restraints, so stay tuned.
+
+\newpage
+
+\section{Some specifics}
+
+Morten H\o gholm will be presenting the recent changes in much more detail at
+\TUG{2009}. Here are some quick specifics for those interested. New code written
+and broad changes made to the \textsf{expl3} modules:
+\begin{description}
+\item [More logical function names]
+ Many function names that were hold-outs from the \TeX\ naming system
+ have been changed to fit into the more logical scheme of \textsf{expl3}; e.g.,
+ \verb|\def:Npn| and \verb|\let:NN| are now \verb|\cs_set:Npn| and
+ \verb|\cs_set_eq:NN|.
+
+\item [Defining functions and conditionals]
+ Much thought was put into new ways to define
+ functions and conditionals with a minimum of code.
+ See \verb|\cs_set:Nn| and \verb|\prg_set_conditional:Nnn|.
+
+\item [Smart comparisons]
+ Comparisons can be made much more easily now, with familiar notation such as
+ \verb|\intexpr_compare_p:n{ #1+3 != \l_tmpa_int }|.
+
+\item [Data from variables] A new function argument specifier \texttt{V} has
+ been added for extracting information from variables of different types,
+ without needing to know the underlying variable structure. Some other
+ tidy-ups on the argument specifiers offered, partially as a result of the
+ addition of this new one.
+
+\item [l3msg] New module to deal with communication between \LaTeX3 code
+ and the user (info messages, warnings, and errors), including message
+ filtering partially inspired by the \textsf{silence} package.
+ \end{description}
+
+\section{The next six months}
+
+Having overhauled the \textsf{expl3} code, we now plan to perform an
+analogous process with the foundations of the
+\textsf{xpackages}. These are the higher-level packages that will
+provide the basic needs such as control of the page layout and rich
+document-level interaction with the user. As the groundwork for this
+layer of the document processing matures, we will be able to start
+building more packages for a \LaTeX3 kernel; these packages will also
+be usable on top of \LaTeXe\ and serve as broadly customisable
+templates for future document design.
+
+As gaps in the functionality offered by \textsf{expl3} are found (in
+some cases, we know that they exist already), the programming layer
+will be extended to support our needs. In other cases, wrappers around
+\TeX\ functions that can be more usefully handled at a higher level
+will be written.
+
+In terms of what we're planning to work on next, three \textsf{xpackage}s
+will take the focus of our attention.
+\begin{description}
+\item [xbase]
+ `\textsf{xbase}' is actually two packages: \textsf{xparse} and
+ \textsf{template}. These contain code for, respectively, defining new
+ document commands (such that a user would use; e.g., \verb|\section|,
+ \verb|\makebox|, \dots) and for handling keyval lists for user input and
+ document specification. \textsf{xparse} was presented at \TUG{1999}%
+ \footnote{\url{http://www.latex-project.org/papers/tug99.pdf}}
+ and Lars Hellstr\"om wrote some notes on \textsf{template} in 2000%
+ \footnote{\url{http://www.latex-project.org/papers/template-notes.pdf}}.
+ Functionality coverage for these packages is good but concepts need a good
+ ``airing''. There are various approaches taken for keyval input, some more
+ recent than the \textsf{template} code, so there are some alternatives to
+ evaluate.
+\item [galley2] Sophisticated handling for \mbox{constructing} paragraphs and
+ other document elements. Morten spoke on this at \TUG{2008}%
+ \footnote{\url{http://river-valley.tv/the-galley-module/}}.
+ Design needs to be revisited after some stress testing.
+\item [xor] This is the \LaTeX3 output routine for splitting the galley into
+ page and sub-page sized chunks. Ideas and code need work to move to
+ ``production ready'' status. Early developments with this package were
+ published by Frank in 2000%
+ \footnote{\url{http://www.latex-project.org/papers/xo-pfloat.pdf}}.
+\end{description}
+
+Expect to hear again from us at Christmas. If you'd like to discuss any
+of these ideas, please join us on the \textsc{latex-l} mailing list%
+\footnote{\url{http://www.latex-project.org/code.html}}.
+
+\end{document}
+
+
+
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news03.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news03.pdf
new file mode 100644
index 00000000000..268bfdca04b
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news03.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news03.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news03.tex
new file mode 100644
index 00000000000..e3e245f051f
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news03.tex
@@ -0,0 +1,128 @@
+% Copyright 2009,2010 The LaTeX Project
+\documentclass{ltnews}
+\PassOptionsToPackage{colorlinks}{hyperref}
+
+\usepackage{metalogo,ragged2e}
+
+\AtBeginDocument{
+ \renewcommand{\LaTeXNews}{\LaTeX3~News}
+ \RaggedRight
+}
+
+\usepackage{url}
+
+\publicationmonth{January}
+\publicationyear{2010}
+\publicationissue{3}
+
+\begin{document}
+\maketitle
+\raisefirstsection
+
+\section{Happy New Year}
+
+Welcome to the holiday season edition of `news of our activities' for
+the \LaTeX3 team.
+
+\section{Recent developments}
+
+The last six months has seen two significant releases in the \LaTeX3 code.
+In the \textsc{ctan} repository for the \pkg{xpackages},\footnote{\url{http://mirror.ctan.org/tex-archive/macros/latex/contrib/xpackages/}} you'll find two items of interest:
+\begin{itemize}
+\item A revised version of \textsf{xparse}; and
+\item The new package \textsf{xtemplate}, a re-implementation of \textsf{template} with a new syntax.
+\end{itemize}
+Special thanks to Joseph Wright who handled the implementations above almost single-handedly (with lots of input and feedback from other members of the team and members of the \textsc{latex-l} mailing list).
+
+These two packages are designed for the \LaTeX\ package author who wishes to define document commands and designer interfaces in a high-level manner.
+
+\paragraph{\textsf{xparse}}
+This package allows complex document commands to be constructed with all sorts of optional arguments and flags. Think of how \verb|\newcommand| allows you to create a command with a single optional argument and \textsf{xparse} is a generalisation of that idea.
+
+\paragraph{\textsf{xtemplate}}
+This package requires more explanation.
+\textsf{Xtemplate} is designed to separate the logical information in a
+document from its visual representation. `Templates' are constructed to fulfil
+individual typesetting requirements for each set of arguments; to change
+the look of a certain part of a document, instantiations of templates can be
+swapped out for another without (a) having to change the markup of the source
+document, or (b) having to edit some internal \LaTeX\ macro.
+
+
+\LaTeXe{} packages, such as \textsf{geometry} or \textsf{titlesec}, already provide
+parameterized interfaces to specific document elements. For example,
+one may use \textsf{titlesec} to change the
+layout of a \verb|\section|: one modifies its layout parameters via \verb|\titleformat| and \verb|\titlespacing|.
+In a way, such packages define a template for a specific document element and
+some manipulation commands to instantiate it.
+\pagebreak
+However, the moment the intended
+layout is not achievable with one package you are on your own: either you have
+to resort to low-level programming or find some other high-level package which, of course, comes with its own set of conventions and manipulation commands.
+
+The \textsf{xtemplate} package can be thought of a generalization of such
+ideas. It provides a uniform interface for defining and managing templates for
+any kind of document element and most importantly provides a uniform interface
+for instantiating the layout.
+
+Thus the designer activity of defining or modifying a document class is
+limited to selecting the document elements that should be provided by the
+class (e.g., \verb|\chapter|, \verb|\section| \verb|\footnote|, lists, \ldots), selecting
+appropriate ``named'' templates for each of them, and instantiating these
+templates by specifying values for their layout parameters. If a desired
+layout can't be achieved with a given template a different template for the
+same document element can be selected.
+
+Programming is only necessary
+if no suitable template for the intended layout is available.
+It is then that a \LaTeX{} programmer has to build a new template
+that supports the layout requirements. Once this task is complete, the
+template may be
+added to the selection of templates that designers and users may choose from
+to define or adjust document layouts seamlessly.
+
+This is a slight gloss over the complexities of the package itself, which you can read about in the documentation. We've tried to document \textsf{xtemplate} clearly but we'd love feedback on whether the ideas make sense to you.
+
+As an addendum to the introduction of \textsf{xtemplate}, the older \textsf{template} package will be retired in the near future. To our knowledge there is only a single package on \textsc{ctan} that uses \textsf{template}, namely \textsf{xfrac}, and members of the \LaTeX\ team are in the process of switching this package over to \textsf{xtemplate}. If you have any private code that uses \textsf{template}, please let us know!
+
+
+\section{Upcoming plans}
+
+Having announced the updated \textsf{xparse} and the new \textsf{xtemplate}, the next stage of development will revolve around using these two systems in the other components of the \textsf{xpackages}, feeding back our experience in practise with the original ideas behind the designs and evaluating if the packages are meeting our expectations.
+
+\subsection{Packages to tackle}
+
+\paragraph{\textsf{xhead}}
+The first work will be to create a new \textsf{xpackage} (probably called \textsf{xhead}), for typesetting section headings and other document divisions.
+Section headings are one of the more complex areas to work with, so
+the work should stress \textsf{xtemplate} enough to know if its
+current design is sufficient for most needs. Nothing has been
+released yet, but we'll announce further developments on the
+\textsc{latex-l} mailing
+list\footnote{For details, see
+ \url{http://www.latex-project.org/code.html}} in the
+mean time.
+
+
+\paragraph{\textsf{galley}}
+We also need to give \textsf{galley} the same treatment as
+\textsf{xparse} and \textsf{xtemplate} have already had. That is, we have an
+older implementation (in fact two) that needs some work before we're ready to release it to \textsc{ctan}.
+
+The \textsf{galley} package is used to place material into the
+vertical list while typesetting but before page breaks occur. Since it
+works at such a low level, it is important to solidify this package
+before writing higher level design templates.
+
+An issue we have to face is that to achieve best results,
+\textsf{galley} cannot be used in concert with \LaTeXe\ code. This
+could limit its usefulness, and we may decide that it's better to
+scale back the features we're attempting, to allow better
+interoperability for existing packages and documents. More work
+remains before we can decide between these options.
+
+
+\end{document}
+
+
+
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news04.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news04.pdf
new file mode 100644
index 00000000000..b70a04f2bd8
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news04.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news04.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news04.tex
new file mode 100644
index 00000000000..a0fc82f3ded
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news04.tex
@@ -0,0 +1,123 @@
+% Copyright 2009,2010 The LaTeX Project
+\documentclass{ltnews}
+\PassOptionsToPackage{colorlinks}{hyperref}
+
+\usepackage{metalogo,ragged2e}
+
+\AtBeginDocument{
+ \renewcommand{\LaTeXNews}{\LaTeX3~News}
+ \RaggedRight
+}
+
+\usepackage{url,hologo}
+
+\publicationmonth{July}
+\publicationyear{2010}
+\publicationissue{4}
+
+\begin{document}
+\maketitle
+
+\noindent
+Now that we're back from the \TeX\ Users Group conference in San
+Francisco, it's time to discuss what's been going on over the last six months.
+Due to some extra travel plans after the conference, this issue is slightly
+late in coming out.
+
+
+\section{\textsf{expl3} in practice}
+
+Joseph Wright and Will Robertson have both released significant new versions
+of their packages, resp., \textsf{siunitx} and \textsf{fontspec}. These have
+been re-written in the \LaTeX3 programming language \textsf{expl3}, which we
+have discussed here previously. Using \textsf{expl3} for production code has
+been very successful, both in demonstrating that the concepts are sound and
+highlighting areas that still need some attention.
+% The \textsf{expl3}
+% code will continue to evolve as more people use it for their work.
+
+In the case of \textsf{fontspec}, \textsf{expl3} programming is being used to
+target \LaTeX\ running on either \XeTeX\ and \hologo{LuaTeX}. In the latter
+case, the package is a mixture of Lua code and \pkg{expl3} code; Will
+presented the \pkg{unicode-math} package at TUG~2010, which is developed in
+the same style.
+
+
+\section{New \textsf{xpackages}}
+
+Frank Mittelbach has started to work on a new experimental \LaTeX3 package
+\textsf{xhead} that provides templates for one of the most complex areas of
+document design: section headings and document divisions. This
+is the beginning of an ambitious idea to map out the requirements for
+typesetting most documents currently processed with \LaTeX.
+
+One of the challenges here is providing a ``natural'' design language for
+describing the two-dimensional spatial relationships of objects participating
+in the design, e.g., the placement of a heading number in relation to the
+heading title, a possible sub-title, etc. In answer to this challenge Frank
+developed the \textsf{xcoffin} package, which he presented at TUG~2010. It is
+designed as a high-level interface for placing and aligning boxes on a page,
+allowing a `designer's approach' for indicating the positional relationship
+between boxes. (A `coffin' is a box with handles.) As an example, it is
+possible to represent ideas such as `align the lower-left corner of box A with
+the upper-right corner of box B after rotating it ninety degrees', without
+having to calculate the intermediate positions.
+
+We expect a future version of \textsf{xcoffin} (after some further work on its
+interface layer and its internal implementation) to play a major role in all
+packages providing layout templates for higher-level document objects, such as
+table of contents designs, floats, etc.
+
+Finally,
+Joseph Wright has begun work with the current `galley' packages, producing the
+new, minimal, \textsf{xgalley} based on \textsf{xfm-galley} as a testbed for
+what we need and what will work.
+
+
+\section{Developments with \textsf{expl3}}
+
+Meanwhile, Joseph's \emph{also} been writing a new floating-point calculation
+module, called \textsf{l3fp}, for \pkg{expl3}. This module allows manipulation
+and calculation of numbers with a much larger range than \TeX\ allows
+naturally. The \textsf{l3fp} module has already been utilised in the
+\pkg{xcoffin} code for calculatations such as coordinate rotations and
+intersection points of vectors.
+
+The modules \pkg{l3io} and \pkg{l3file} have been revised, rethinking the way
+that read and write streams are dealt with. \TeX\ has a hard limit of sixteen
+input and output streams open at any one time, and the new implementation for
+\pkg{expl3} provides more flexibility in how they are allocated; there's now
+much less chance of running into a `\verb|No room for a new \read|' (or
+\verb|\write|) error.
+
+Sometimes we discuss ideas for \textsf{expl3} that \emph{don't} end up making
+it into the final code. One example of this is the concept of having `local
+registers' for integers, boxes, and so on, that do not survive outside of the
+group they are defined in (in contrast to Plain \TeX\ and \LaTeX, where
+allocators such as \verb|\newcount| and \verb|\newbox| are always global).
+Despite the scope for some small benefit, we decided that the extra complexity
+that the additional functions required, in both syntax and documentation, was
+not justified.
+
+
+\section{TUG 2010 reflections}
+
+% added most of it back in: -fmi
+
+%% Most of the active \LaTeX3 Project team were able to attend the TUG~2010
+%% conference and (for Will) meet everyone in person for the first time.
+
+Our interpretation of the broad themes discussed at the conference are that
+\TeX-based systems are still thriving and there are some big problems to solve
+with robust solutions to transform \LaTeX\ source, including mathematics, into
+a form such as HTML. While there are big pushes for standardising various aspects
+of the \LaTeX\ syntax, we also believe that it is \LaTeX's very
+flexibility---its inherently non-standardised markup---that has allowed it to
+survive for so many years. There is a delicate trade-off here between moving
+forward into more standards-based territory while also retaining the
+extensibility of the third-party package system.
+
+\end{document}
+
+
+
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news05.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news05.pdf
new file mode 100644
index 00000000000..87c46a0c2a5
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news05.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news05.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news05.tex
new file mode 100644
index 00000000000..43ec2a8d193
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news05.tex
@@ -0,0 +1,64 @@
+% Copyright 2011 The LaTeX Project
+\documentclass{ltnews}
+\PassOptionsToPackage{colorlinks}{hyperref}
+
+\usepackage{metalogo,ragged2e}
+
+\AtBeginDocument{
+ \renewcommand{\LaTeXNews}{\LaTeX3~News}
+ \RaggedRight
+}
+
+\usepackage{hologo}
+
+\publicationmonth{January}
+\publicationyear{2011}
+\publicationissue{5}
+
+\begin{document}
+\maketitle
+
+
+\section{Happy new year}
+
+Seasons greetings for 2011!
+As the previous news issue was released late, this season's issue will be shorter than usual.
+
+\section{The LPPL is now OSI-approved}
+
+We are happy to report that earlier this year the \LaTeX\ Project Public License (LPPL) has been approved by the OSI as an open source licence.\footnote{\url{http://www.opensource.org/licenses/lppl}} Frank Mittelbach will be publishing further details on this news in a retrospective on the LPPL in an upcoming TUGboat article.
+
+\section{Reflections on 2010}
+
+We are pleased to see the continued development and discussion in the \TeX\ world.
+The \LaTeX\ ecosystem continues to see new developments and a selection of notable news from the second half of last year include:
+\begin{itemize}
+\item[June] The TUG~2010 conference was held very successfully in San
+Francisco; videos, slides, and papers from \LaTeX3 Project members are available from our website.\footnote{\url{http://www.latex-project.org/papers/}}
+\item[Aug.]
+The \TeX\ Stack Exchange\footnote{\url{http://tex.stackexchange.com}} question\,\&\,answer website was created and has since grown quickly. At time of writing, some 2800 people have asked 2600 questions with 5600 answers total, and 2200 users are currently visiting daily.
+\item[Sept.] \TeX\ Live 2010 was released: each year the shipping date is earlier; the production process is becoming more streamlined and we congratulate all involved for their hard work. One of the most notable new components of \TeX\ Live 2010 includes the `restricted shell escape' feature to allow, among other things, automatic EPS figure conversion for pdf\LaTeX\ documents.
+\item[Oct.] TLContrib\footnote{\url{http://tlcontrib.metatex.org/}} was opened by Taco Hoekwater as a way to update a \TeX~Live installation with material that is not distributable through \verb|tlmgr| itself. Such material includes executables (e.g., new versions of Lua\TeX), non-free code, or test versions of packages.
+\item[Nov.] Philipp Lehman released the first stable version of \textsf{biblatex}. One of the most ambitious \LaTeX\ packages in recent memory, \textsf{biblatex} is a highly flexible package for managing citation cross-referencing and bibliography typesetting. In `beta' status for some years now, reaching this point is a great milestone.
+\item[Dec.] Lua\TeX\ 0.65. We are happy to see Lua\TeX\ development steadily continuing. \LaTeX\ users may use Lua\TeX\ with the \verb|lualatex| program. Like \verb|xelatex|, this allows \LaTeX\ documents to use multilingual OpenType fonts and Unicode text input.
+\end{itemize}
+
+\section{Current progress}
+
+The \textsf{expl3} programming modules continue to see revision and expansion; we have added a Lua\TeX\ module, but \textsf{expl3} continues to support all three of pdf\LaTeX, \XeLaTeX, and Lua\LaTeX\ equally.
+
+The \textsf{l3fp} module for performing floating-point arithmetic has been extended and improved. Floating point maths is important for some of the calculations required for complex box typesetting performed in the new `coffins' code.
+The \textsf{l3coffin} module has been added based on the original \textsf{xcoffins} package introduced at TUG~2010 as reported in the last news issue; this code is now available from CTAN for testing and feedback.
+
+We have consolidated the \textsf{l3int} and \textsf{l3intexpr} modules (which were separate for historical purposes); all integer/count-related functions are now contained within the `\textsf{int}' code and have prefix \verb|\int_|. Backwards compatibility is provided for, but eventually we will drop support for the older \verb|\intexpr_| function names.
+
+\section{Plans for 2011}
+
+In the following year, we plan to use the current \LaTeX3 infrastructure to continue work in building high-level code for designing \LaTeX\ documents using the \textsf{xtemplate} package. Our first priority is to look at section headings and document divisions, as we see this area as one of the most difficult, design-wise, of the areas to address. From there we will broaden our scope to more document elements.
+
+We will also do some low-level work on the `galley', which is the code that \LaTeX3 uses to build material for constructing pages, and we will continue to extend \textsf{expl3} into a more complete system from which we can, one day, create a pure \LaTeX3 format.
+
+\end{document}
+
+
+
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news06.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news06.pdf
new file mode 100644
index 00000000000..d4c6ce71a10
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news06.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news06.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news06.tex
new file mode 100644
index 00000000000..63ee6c7c75a
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news06.tex
@@ -0,0 +1,141 @@
+% Copyright 2011 The LaTeX Project
+\documentclass{ltnews}
+\PassOptionsToPackage{colorlinks}{hyperref}
+
+\usepackage{metalogo,ragged2e}
+
+\AtBeginDocument{
+ \renewcommand{\LaTeXNews}{\LaTeX3~News}
+ \RaggedRight
+}
+
+\usepackage{hologo}
+
+\publicationmonth{June}
+\publicationyear{2011}
+\publicationissue{6}
+
+\begin{document}
+\maketitle
+
+\noindent
+A key aim of releasing `stable' \LaTeX3 material to CTAN is to allow users to
+benefit from new ideas \emph{now}, and also to raise the profile of usable
+\LaTeX3 ideas. This is clearly being successful, with \pkg{xparse} being of
+particular utility to end users. This increase in interest has been
+particularly notable on the new
+\href{http://tex.stackexchange.com/}{TeX.SX Q\&A site}.
+
+\section{The \LaTeX3 Team expands}
+
+Raising interest in \LaTeX3 developments has inevitably led to feedback on
+cases where the code base has required attention. It has also attracted new
+programmers to using \LaTeX3 ideas, some more than others! Bruno Le Floch has
+over the past few months made many useful contributions to \LaTeX3, and we are
+very pleased that he has recently joined The \LaTeX{} Project.
+
+Bruno has taken a particular interest in improving the performance and
+reliability of the \pkg{expl3} language. This has already resulted in new
+implementations for the \texttt{prop} and \texttt{seq} data types. At the same
+time, he has identified and fixed several edge-case issues in core \pkg{expl3}
+macros.
+
+\section{The `Big Bang'}
+
+In parallel to Bruno's improvements, Joseph Wright initiated a series of `Big
+Bang' improvements to \LaTeX3. The aim of the Big Bang was to address a number
+of long-standing issues with the \LaTeX3 code base. Development has taken place
+over many years, with the status of some of the resulting code being less
+than clear, even to members of The \LaTeX{} Project! At the same time, different
+conventions had been applied to different parts of the code, which made reading
+some of the code rather `interesting'. A key part of the Big Bang has been to
+address these issues, cleaning up the existing code and ensuring that the
+status of each part is clear.
+
+The arrangement of \LaTeX3 code is now the same in the development
+repository and on CTAN, and splits the code into three parts.
+\begin{description}
+ \item[\pkg{l3kernel}] The core of \LaTeX3, code which
+ is expected to be used in a \LaTeX3 kernel in more or less the
+ current form. Currently, this part is made up of the \LaTeX3
+ programming layer, \pkg{expl3}.
+ \item[\pkg{l3packages}] \LaTeXe{} packages making use of \LaTeX3
+ concepts and with stable interfaces. The \pkg{xparse} and
+ \pkg{xtemplate} packages are the core of this area. While many of
+ the \emph{ideas} explored here may eventually appear in a \LaTeX3
+ kernel, the interfaces here are tied to \LaTeXe{}.
+ \item[\pkg{l3experimental}] \LaTeXe{} packages which explore more
+ experimental \LaTeX3 ideas, and which may see interface changes as
+ development continues. Over time, we expect code to move from this area
+ to either \pkg{l3kernel} or \pkg{l3packages}, as appropriate.
+\end{description}
+
+In addition to these release areas, the development code also features a
+\pkg{l3trial} section for exploring code ideas. Code in \pkg{l3trial} may be
+used to improve or replace other parts of \LaTeX3, or may simply be dropped!
+
+As well as these improvements to the \emph{code} used in \LaTeX3, much of the
+documentation for \pkg{expl3} has been made more precise as part of the Big
+Bang. This means that \texttt{source3.pdf} is now rather longer than it was
+previously, but also should mean that many of the inaccuracies in earlier
+versions have been removed. Of course, we are very pleased to receive
+suggestions for further improvement.
+
+\section{\LaTeX3 on GitHub}
+
+The core development repository for \LaTeX3 is held in an SVN repository, which
+is \href{http://www.latex-project.org/code.html}{publicly viewable \emph{via}
+the Project website}. However, this interface misses out on some of the `bells
+and whistles' of newer code-hosting sites such as
+\href{http://gitbug.com/}{GitHub} and \href{http://bitbucket.org/}{BitBucket}.
+We have therefore established a mirror of the master repository on GitHub%
+\footnote{\url{http://github.com/latex3/svn-mirror}}. This is kept in
+synchronisation with the main SVN repository by Will Robertson (or at least
+by his laptop!).
+
+The GitHub mirror offers several useful features for people who wish to
+follow the \LaTeX3 code changes. GitHub offers facilities such as highlighted
+differences and notification of changes. It also makes it possible for
+non-Team members to submit patches for \LaTeX3 as `pull requests' on
+GitHub.
+
+As well as offering a convenient interface to the \LaTeX3 code, the GitHub
+site also includes an issue database\footnote{%
+\url{http://github.com/latex3/svn-mirror/issues}}. Given the very
+active nature of \LaTeX3 development, and the transitory nature of many
+of the issues, this provides a better approach to tracking issues than
+the main \LaTeX{} bug database\footnote{\url{http://www.latex-project.org/bugs.html}}.
+Developers and users are
+therefore asked to report any issues with \LaTeX3 code \emph{via} the GitHub
+database, rather than on the main Project homepage.
+Discussion on the \href{http://www.latex-project.org/code.html}{\mbox{LaTeX-L} mailing list}
+is also encouraged.
+
+\section{Next steps}
+
+The `Big Bang' involves making a number of changes to \pkg{expl3} function
+names, and is likely to break at least some third-party code. As a result,
+the updates will not appear on the \TeX{} Live 2011 DVD release, but will
+instead be added to \TeX{} Live once regular updates restart (probably
+August).
+
+Bruno is working on a significant overhaul of the \pkg{l3fp} floating-point
+unit for \LaTeX3. He has developed an approach which allows expandable
+parsing of floating-point expressions, which will eventually allow syntax
+such as
+\begin{verbatim}
+ \fp_parse:n { 3 * 4 ( ln(5) + 1 ) }
+\end{verbatim}
+This will result in some changes in the interface for floating-point numbers, but
+we feel that the long-term benefit is worth a small amount of recoding in other
+areas.
+
+Joseph has completed documentation of the \pkg{xgalley} module, and this is
+currently being discussed. Joseph is hoping to move on to implement other
+more visible ideas based on the \pkg{xtemplate} concept over the next few
+months.
+
+\end{document}
+
+
+
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news07.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news07.pdf
new file mode 100644
index 00000000000..f11e905a9be
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news07.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news07.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news07.tex
new file mode 100644
index 00000000000..2b433da4c41
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news07.tex
@@ -0,0 +1,111 @@
+% Copyright 2012 The LaTeX3 Project
+\documentclass{ltnews}
+\PassOptionsToPackage{colorlinks}{hyperref}
+
+\usepackage{metalogo,ragged2e}
+
+\AtBeginDocument{
+ \renewcommand{\LaTeXNews}{\LaTeX3~News}
+ \RaggedRight
+}
+
+\publicationmonth{February}
+\publicationyear{2012}
+\publicationissue{7}
+
+\begin{document}
+\maketitle
+
+\section{After the `Big Bang'}
+
+The last \LaTeX3 News gave details of the `Big Bang', in which the team have
+revised the layout and coverage of the \LaTeX3 codebase. This process has
+made the status of different modules clearer, so that both the team themselves
+and everyone else know what is going on.
+
+The `Big Bang' changes were not shipped to \textsc{ctan} until after the \TeX{}
+Live 2011 freeze, as we did not want to end up with a \textsc{dvd} containing
+badly broken code. The update went to \textsc{ctan} soon after \TeX{} Live 2011
+shipped, and has now propagated around the world. The new package naming
+(\pkg{l3kernel}, \pkg{l3packages} and \pkg{l3experimental}) has caused some
+surprises for a small number of users, but there have not been any major
+issues with the changes at the code level.
+
+The `Big Bang' has attracted attention from programmers outside of the
+\LaTeX3 team, with useful feedback arriving on the
+\texttt{LaTeX-L} list and TeX.sx, in particular. One area that this has
+highlighted is the need to document carefully when changes to the `stable'
+parts of the \LaTeX3 codebase occur. All changes to \pkg{l3kernel} now
+come with an explicit date for the change in the documentation, which means
+that programmers can check exactly when the features they want were introduced.
+
+Another key part of supporting \LaTeX3 use beyond the team is making it easy
+to check on the version of \LaTeX3 installed. To support that, the file date
+of the main \pkg{expl3} package is now set each time there is a release of the
+\LaTeX3 material to \textsc{ctan}. This means that the \LaTeXe{}
+\cs{@ifpackagelater} test can be used reliably to detect if the installed
+version of \LaTeX3 is going to supply the functions that a programmer is
+using.
+
+\section{Deforming boxes}
+
+Additions to both the \LaTeX3 stable material and more experimental modules
+continue. Joseph Wright has been working on adding `native' drivers for
+\LaTeX3 to support box transformations. These allow box rotation, clipping
+and scaling with the drivers \texttt{dvips}, \texttt{xdvipdfmx} and direct
+\textsc{pdf} output.
+
+The development of clipping support for the \texttt{xdvipdfmx} driver has also allowed
+us to suggest improvements to the \LaTeXe{} graphics drivers, enabling clipping
+with the \XeTeX{} engine.
+
+\section{A \TeX{}-based regex engine}
+
+Bruno Le Floch has been %working across the codebase, %% omitted to avoid page break
+improving the efficiency
+and robustness of a number of \LaTeX3 functions. Most notably, he has
+created a purely \TeX{}-based regular expression (regex) system for \LaTeX3.
+This is currently experimental, but is already proving useful and will
+hopefully stabilise over the coming months.
+
+Bruno's regex system works with all of the supported engines (pdf\TeX{},
+\XeTeX{} and \LuaTeX{}). He has implemented the core ideas of standard
+regex systems, along with some \TeX{}-specifics to allow matching and
+replacing
+the content of token lists % correct?
+by category code.
+
+\section{\pkg{xparse} improves}
+
+The \pkg{xparse} module has been overhauled, making the internal code
+more efficient and adding additional argument types. This has also allowed
+us to deal with a number of internal bugs, meaning that argument grabbing
+is now more reliable.
+
+The argument grabbers themselves have been reworked so that in the event of an error,
+the user will normally get a meaningful message from \TeX{} rather than
+one pointing to \pkg{xparse} internal function names. This should help
+in tracking down erroneous input in real documents.
+
+\section{The galley}
+
+As detailed in the last issue, work on the galley module has been continuing.
+Discussion of Joseph's reimplementation of the galley concepts highlighted
+some important areas to work on, with the nature of the template concept
+being particularly significant.
+
+More work is still needed to finalise the galley concepts, but it is clear that
+some of this will require feedback from other areas. Joseph therefore hopes
+to finish work on the current round of galley improvements by the end of
+February, and to return to them once some other areas have been addressed.
+
+\section{Relationships between document items}
+
+The \textsc{tug}2011 meeting took place in October in India. Frank Mittelbach
+spoke there about ideas for describing the design relationship between document elements.
+% omit calling it "ldb" here because that name will likely change (last I heard?)
+These ideas allow a document designer to specify the design of a document element
+based on its context within a document, and progress in this area will likely
+lead to an extension in the \pkg{xtemplate} system.
+
+\end{document}
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news08.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news08.pdf
new file mode 100644
index 00000000000..a56eff92d23
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news08.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news08.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news08.tex
new file mode 100644
index 00000000000..17107f83602
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news08.tex
@@ -0,0 +1,170 @@
+% Copyright 2012 The LaTeX3 Project
+\documentclass{ltnews}
+
+\PassOptionsToPackage{colorlinks}{hyperref}
+
+\usepackage{metalogo,ragged2e}
+
+\AtBeginDocument{
+ \renewcommand{\LaTeXNews}{\LaTeX3~News}
+ \RaggedRight
+}
+
+\usepackage{expl3,siunitx,catchfile}
+\usepackage{hologo}
+\usepackage{verbatim}% avoid ltnews' "special" verbatim processing
+
+\publicationmonth{July}
+\publicationyear{2012}
+\publicationissue{8}
+
+\def\DTX{\texttt{.dtx}}
+\def\STY{\texttt{.sty}}
+
+\ExplSyntaxOn
+\NewDocumentCommand { \calcnum } { m }
+ { \num { \fp_to_scientific:n {#1} } }
+\ExplSyntaxOff
+
+\begin{document}
+\maketitle
+
+\raisefirstsection
+\section{Extended floating point support}
+
+Bruno Le Floch has been re-writing the floating point module to function in an `expandable' manner. This allows floating point calculations to be computed far more flexibly and efficiently than before.
+The expandable nature of the new code allows its use inside operations such as writing to external files, for which previously any such calculations would have to be pre-calculated before any of the writing operations began.
+
+Bruno's work on the floating point module has been officially released into the main \textsc{svn} repository for \texttt{l3kernel}; \TeX\ Live 2012 will contain the `old' code for stability while this year is spent testing the new code in production environments and ironing out any wrinkles.
+
+Here's a neat example as suggested in the documentation, which produces `\calcnum { round ( 200 pi * sin ( 2.3 ^ 5 ) , 2 ) }':
+
+\begin{verbatim}
+\usepackage{xparse, siunitx}
+\ExplSyntaxOn
+\NewDocumentCommand { \calcnum } { m }
+ { \num { \fp_to_scientific:n {#1} } }
+\ExplSyntaxOff
+
+\calcnum {
+ round ( 200 pi * sin ( 2.3 ^ 5 ) , 2 )
+}
+\end{verbatim}
+
+This feature is invaluable for simple (and not-so-simple) calculations in document and package authoring, and has been lacking a robust solution for many years.
+While \hologo{LuaLaTeX} can perform similar tasks within its Lua environment, the floating point support is written using the \texttt{expl3} programming language only, and is thus available in \hologo{pdfLaTeX} and \hologo{XeLaTeX} as well.
+
+\section{Regular expressions in \TeX}
+
+As if expandable floating point support wasn't enough, Bruno has also written a complete regular expression engine in \texttt{expl3} as well.
+% [Ed: Consider my mind blown.]
+Many reading this will be familiar with the quote attributed to Jamie Zawinski:
+\begin{quote}\itshape
+Some people, when confronted with a problem, think
+``I know, I'll use regular expressions.''
+Now they have two problems.
+\end{quote}
+And as humorous as the saying is, it's still fair to say that regular expressions are a great tool for manipulating streams of text.
+We desperately hope that people will \emph{not} start using the regex code to do things like parse \textsc{xml} documents; however, for general search--replace duties, there's never been anything like \texttt{l3regex} for the \LaTeX\ world.
+As a trivial example, there are
+\CatchFileDef\thisfile{\jobname.tex}{}%
+\ExplSyntaxOn
+\exp_args:Nno \regex_count:nnN { \b (?i) W e \b } {\thisfile} \l_tmpa_int
+\int_use:N \l_tmpa_int
+\ExplSyntaxOff
+~instances of the word `We' or `we' in this document (including those two).
+This value is counted automatically in two lines of code.
+
+And again, it is available for \hologo{pdfLaTeX} and \hologo{XeLaTeX} users as well as \hologo{LuaLaTeX} ones; it also bears noting that this provides an easy solution for applying regular expression processing to \LaTeX\ documents and text data even on the Windows operating system that does not have native support for such things.
+
+
+\section{Separating internal and external code}
+
+\LaTeX\ packages are written by a wide range of package authors and consist of code and commands for various purposes.
+Some of these commands will be intended for use by document authors (such as \verb|\pbox| from the \textsf{pbox} package); others are intended for use by other package writers (such as \verb|\@ifmtarg| from the \textsf{ifmtarg} package).
+
+However, a large portion of them are internal, i.e., are intended to
+be used only within the package or within the \LaTeX{} kernel and
+should not be used elsewhere. For example, \verb|\@float| is the
+\LaTeX{} kernel interface for floats to be used in class files, but
+the actual work is done by a command called \verb|\@xfloat| which
+should not be used directly. Unfortunately the \LaTeXe{} language
+makes no clear distinction between the two, so it is tempting for
+programmers to directly call the latter to save some ``unnecessary''
+parsing activity.
+
+The downside of this is that the ``internal'' commands suddenly act as
+interfaces and a reimplementation or fix in any of them would then
+break add-on packages as they rely on internal behavior. Over the
+course of the last twenty years probably 80\% of such ``internal''
+commands have found their way into one or another package. The
+consequences of this is that nowadays it is next to impossible to
+change anything in the \LaTeXe{} kernel (even if it is clearly just an
+internal command) without breaking something.
+
+
+In \LaTeX3 we hope to improve this situation drastically by
+clearly separating public interfaces (that extension packages can use
+and rely on) and private functions and variables (that should not
+appear outside of their module). There is (nearly) no way to enforce
+this without severe computing overhead, so we implement it only
+through a naming convention, and some support mechanisms. However, we
+think that this naming convention is easy to understand and to follow,
+so that we are confident that this will be adopted and provides the
+desired results.
+
+\subsection{Naming convention for internals}
+
+We've been throwing around some ideas for this for a number of years but nothing quite fit; the issue comes down to the fact that \TeX\ does not have a `name-spacing' mechanism so any internal command needs to have a specific prefix to avoid clashing with other packages' commands.
+The prefix we have finally decided on for \textsf{expl3} code is a double underscore, such that functions like \verb|\seq_count:N| are intended for external use and \verb|\__seq_item:n| is an internal command that should not be used or relied upon by others.
+
+All this is well and good, but it can be inconvenient to type long prefixes such as \verb|\__seq_| before all command names, especially in a package for which nearly \emph{all} package functions are internal.
+
+We therefore also extended \textsf{DocStrip} slightly by adding a `shorthand' for internal package prefixes.
+Commands and variables in \DTX\ code may now contain \texttt{@@} which is expanded to the function prefix when the \STY\ file is extracted.
+As an example, writing
+\begin{verbatim}
+ %<@@=seq>
+ \cs_new:Npn \@@_item:n
+ ...
+\end{verbatim}
+is equivalent to
+\begin{verbatim}
+ \cs_new:Npn \__seq_item:n
+ ...
+\end{verbatim}
+There are clear advantages to this syntax.
+Function names are shorter and therefore easier to type, and code can still be prototyped using the \texttt{@@} syntax
+(e.g., pasting code between a \DTX\ file and a regular \texttt{.tex} document).
+Most importantly, it is explicitly clear from the code source which commands are intended to be used externally and which should be avoided.
+
+We hope that this syntax will prove popular; in our initial experiments we think it works very well. In fact we found a good number of smaller errors when being forced to think about what is internal and what is an external function.
+
+\section{Continual revolution---the `small bang'}
+
+In addition to the major additions introduced above, Frank Mittelbach has been examining \texttt{expl3} with a fresh eye to resolve any outstanding issues in the consistency or logic of the names of functions.
+
+We are very mindful of the fact that for people to find \texttt{expl3} a useful tool, it must have a stable interface.
+This said, there are still some musty corners that we can show where people simply haven't been using certain functions.
+In select cases, we're re-assessing whether all of the (sometimes esoteric) odds and ends that have been added to \texttt{expl3} really belong; in other cases, it's now clear that some naming or behaviour choices weren't correct the first time around.
+
+To address this tarnish, we're carefully making some minor changes to parts of the \texttt{expl3} interface and we'd like to allay any fears that \texttt{expl3} isn't stable.
+The \texttt{expl3} language now offers a wide range of functions plus their variants, and we're talking about changing but a very small percentage of these, and not common ones at that.
+We don't want it to become a mess, so we think it's better to tidy things up as we go.
+Follow the \texttt{LaTeX-L} mailing list for such details as they arise.
+
+\end{document}
+
+
+dropped/replaced:
+
+The \LaTeX3 codebases ranges between these two extremes; the packages
+in \texttt{l3packages} are largely the former while the modules
+composing \textsf{expl3} are largely the latter type. In both cases,
+the `external' commands (whether for document author or package
+writer) are usually defined in terms of other internal package
+commands that should not be used by anyone else, but often when
+reading the internal package code it's not always clear which is
+which.
+
+For \LaTeX3 we are experimenting with an extension to the \textsf{DocStrip} mechanism to provide a clear distinction between internal and external package commands.
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news09.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news09.pdf
new file mode 100644
index 00000000000..660eae9025b
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news09.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news09.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news09.tex
new file mode 100644
index 00000000000..55ed700a2cf
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news09.tex
@@ -0,0 +1,1433 @@
+% Copyright 2014 The LaTeX3 Project
+\documentclass{ltnews}
+
+\PassOptionsToPackage{colorlinks}{hyperref}
+\usepackage{metalogo,ragged2e}
+
+\AtBeginDocument{
+ \renewcommand{\LaTeXNews}{\LaTeX3~News}
+ \RaggedRight
+ \setlength\parindent{1em}
+}
+
+\hbadness=10000
+\def\Dash{\,---\,}
+
+\providecommand\LuaLaTeX{Lua\LaTeX}
+\setcounter{tocdepth}{2}
+
+\usepackage{enumitem,csquotes}
+\MakeOuterQuote{"}
+\usepackage{fancyvrb}% to allow \Verb"<ul>" to typeset properly since ltnews patches \verb
+
+\makeatletter
+\renewcommand{\@subheadingfont}{%
+ \sffamily\slshape
+ \let\LaTeX\cmssLaTeX\let\TeX\cmssTeX
+}
+\renewcommand{\subsection}{%
+ \@startsection
+ {subsection}{2}{\z@}{-1.3ex \@plus -1ex \@minus -.2ex}%
+ {0.8ex \@plus.2ex}{\@subheadingfont}%
+}
+\makeatother
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\RequirePackage{tikz}
+\definecolor{basecolor}{RGB}{0,128,128}
+\definecolor{maincolor}{RGB}{55,200,113}
+
+
+\newcommand \explbanner{%
+\begin{tikzpicture}[y=0.80pt,x=0.80pt,yscale=-1, inner sep=0pt, outer sep=0pt]
+ \path[fill=basecolor,rounded corners=0.0000cm] (248.7785,295.7529) rectangle
+ (465.3586,478.5802);
+ \path[fill=white,rounded corners=0.0000cm] (257.9199,304.5427) rectangle
+ (456.2172,469.7905);
+ \path[fill=basecolor] (283.2386,478.5760) -- (289.5259,453.4125) .. controls
+ (302.2770,439.6121) and (314.0060,425.3008) .. (319.5695,407.9067) .. controls
+ (355.0759,401.0374) and (383.7853,403.5174) .. (413.7371,404.2858) .. controls
+ (411.0072,403.8309) and (322.2852,388.7992) .. (322.2852,388.7992) .. controls
+ (298.7181,356.5528) and (264.6380,373.5409) .. (249.0663,403.2205) --
+ (248.7470,478.6397) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=1.253pt]
+ (288.4249,394.4251) -- (288.2061,394.8313) .. controls (287.2860,396.5455) and
+ (285.7381,397.6863) .. (283.5499,398.3938) .. controls (281.3616,399.1013) and
+ (278.5647,399.3476) .. (275.2999,399.2063) -- (273.2374,399.1126) --
+ (274.7061,400.5501) .. controls (275.4640,401.2791) and (275.6960,401.8541) ..
+ (275.7061,402.3626) .. controls (275.7162,402.8712) and (275.5103,403.4284) ..
+ (275.0186,404.0501) .. controls (274.0353,405.2935) and (272.0593,406.6472) ..
+ (270.2374,408.0189) -- (269.6749,408.4564) -- (270.0499,409.0501) .. controls
+ (271.5698,411.3818) and (272.2617,413.2035) .. (272.4249,414.5814) .. controls
+ (272.5880,415.9592) and (272.2796,416.9162) .. (271.6436,417.7689) .. controls
+ (270.3717,419.4743) and (267.6257,420.6198) .. (265.1124,422.0501) --
+ (264.6124,422.3314) -- (264.7061,422.8626) .. controls (264.9221,424.1742) and
+ (264.5793,425.2316) .. (263.6436,426.3001) .. controls (262.7079,427.3687) and
+ (261.1571,428.4020) .. (259.0498,429.3001) -- (258.4873,429.5189) --
+ (258.5810,430.1126) .. controls (259.0013,432.9207) and (259.8134,435.3978) ..
+ (261.5810,436.9564) .. controls (263.1945,438.3790) and (265.6272,438.7418) ..
+ (268.7373,438.1126) .. controls (269.2811,441.1124) and (270.0885,443.8463) ..
+ (271.8935,445.6439) .. controls (273.7365,447.4793) and (276.5967,448.0631) ..
+ (280.5185,447.0189) .. controls (281.3879,449.0885) and (282.6104,450.5771) ..
+ (284.0498,451.5501) .. controls (285.6426,452.6269) and (287.4312,453.1746) ..
+ (289.1435,453.7064) -- (289.5810,452.2376) .. controls (287.8816,451.7099) and
+ (286.2778,451.1522) .. (284.9248,450.2376) .. controls (283.5718,449.3230) and
+ (282.4412,448.0628) .. (281.6748,445.8939) -- (281.4248,445.1751) --
+ (280.7060,445.3939) .. controls (276.7309,446.6155) and (274.5047,446.0613) ..
+ (272.9873,444.5501) .. controls (271.4699,443.0389) and (270.6337,440.3179) ..
+ (270.1123,437.1439) -- (269.9873,436.3001) -- (269.1435,436.5189) .. controls
+ (265.8672,437.3737) and (263.9323,436.9328) .. (262.6123,435.7689) .. controls
+ (261.3919,434.6928) and (260.6559,432.7860) .. (260.2373,430.4251) .. controls
+ (262.1657,429.5404) and (263.7732,428.5395) .. (264.8310,427.3314) .. controls
+ (265.8589,426.1576) and (266.3097,424.7444) .. (266.2373,423.2376) .. controls
+ (268.5457,421.9725) and (271.2938,420.8514) .. (272.8935,418.7064) .. controls
+ (273.7344,417.5788) and (274.1920,416.1226) .. (273.9873,414.3939) .. controls
+ (273.8023,412.8317) and (273.0178,411.0042) .. (271.7060,408.8626) .. controls
+ (273.3411,407.6578) and (275.1128,406.4408) .. (276.2373,405.0189) .. controls
+ (276.8610,404.2302) and (277.3196,403.3287) .. (277.2998,402.3314) .. controls
+ (277.2890,401.7896) and (277.0912,401.2653) .. (276.7998,400.7376) .. controls
+ (279.5102,400.7514) and (281.9952,400.5582) .. (284.0498,399.8939) .. controls
+ (286.3053,399.1646) and (288.0426,397.8495) .. (289.2060,396.0189) .. controls
+ (292.5635,396.0371) and (296.0636,396.7806) .. (299.5810,397.2376) .. controls
+ (303.1955,397.7073) and (306.8484,397.8601) .. (310.3310,396.3314) --
+ (309.7060,394.8939) .. controls (306.6247,396.2464) and (303.2846,396.1632) ..
+ (299.7685,395.7064) .. controls (296.2524,395.2495) and (292.5819,394.3984) ..
+ (288.8935,394.4251) -- (288.4248,394.4251) -- cycle;
+ \path[fill=white] (301.5159,393.8637) .. controls (301.5159,397.5244) and
+ (298.5484,400.4920) .. (294.8877,400.4920) .. controls (291.2270,400.4920) and
+ (288.2595,397.5244) .. (288.2595,393.8637) .. controls (288.2595,390.2031) and
+ (291.2270,387.2355) .. (294.8877,387.2355) .. controls (298.5484,387.2355) and
+ (301.5159,390.2031) .. (301.5159,393.8637) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=1.253pt]
+ (321.0186,387.9251) .. controls (315.0680,395.6662) and (313.8600,393.4088) ..
+ (310.2374,394.7063) -- (309.6124,394.9251) -- (309.7374,395.5813) .. controls
+ (311.1321,403.3566) and (315.5160,405.7238) .. (319.1124,408.5501) --
+ (320.0811,407.3313) .. controls (316.4798,404.5012) and (312.9249,402.6094) ..
+ (311.4874,396.0188) .. controls (314.1361,395.3813) and (316.6278,396.2007) ..
+ (322.2686,388.8626) -- (321.0186,387.9251) -- cycle;
+ \path[fill=maincolor] (301.5159,393.8637) .. controls (301.5159,397.5244) and
+ (298.5484,400.4920) .. (294.8877,400.4920) .. controls (291.2270,400.4920) and
+ (288.2595,397.5244) .. (288.2595,393.8637) .. controls (288.2595,390.2031) and
+ (291.2270,387.2355) .. (294.8877,387.2355) .. controls (298.5484,387.2355) and
+ (301.5159,390.2031) .. (301.5159,393.8637) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=1.253pt]
+ (294.8936,386.4563) .. controls (290.8098,386.4563) and (287.4874,389.7788) ..
+ (287.4874,393.8626) .. controls (287.4874,397.9464) and (290.8098,401.2688) ..
+ (294.8936,401.2688) .. controls (298.9774,401.2688) and (302.2999,397.9464) ..
+ (302.2999,393.8626) .. controls (302.2999,389.7788) and (298.9774,386.4563) ..
+ (294.8936,386.4563) -- cycle(294.8936,388.0188) .. controls
+ (298.1312,388.0188) and (300.7374,390.6250) .. (300.7374,393.8626) .. controls
+ (300.7374,397.1002) and (298.1312,399.7063) .. (294.8936,399.7063) .. controls
+ (291.6560,399.7063) and (289.0499,397.1002) .. (289.0499,393.8626) .. controls
+ (289.0499,390.6250) and (291.6560,388.0188) .. (294.8936,388.0188) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=1.253pt]
+ (291.5499,370.7063) .. controls (288.4841,370.7722) and (285.4193,371.2673) ..
+ (282.4249,372.1438) .. controls (269.6164,375.8932) and (256.6677,387.2311) ..
+ (248.7622,400.8596) -- (248.7662,404.0176) .. controls (256.4832,389.5161) and
+ (269.8419,377.4553) .. (282.8625,373.6438) .. controls (286.1465,372.6825) and
+ (289.4914,372.1880) .. (292.8312,372.2376) .. controls (302.8507,372.3862) and
+ (312.9146,377.4187) .. (321.6437,389.3626) -- (321.8312,389.6126) --
+ (322.1437,389.6751) .. controls (322.1437,389.6751) and (383.4924,400.0668) ..
+ (402.2375,403.2376) .. controls (376.3035,402.3430) and (350.4290,401.2333) ..
+ (319.3937,407.2376) -- (318.9250,407.3313) -- (318.8000,407.7688) .. controls
+ (313.2958,424.9775) and (301.6553,439.2096) .. (288.9250,452.9875) --
+ (288.8000,453.1125) -- (288.7375,453.3313) -- (282.4611,478.5810) --
+ (283.9307,478.5810) -- (290.2375,453.8937) .. controls (302.8375,440.2444) and
+ (314.4843,426.0036) .. (320.1437,408.7062) .. controls (355.3332,401.9651) and
+ (383.8501,404.4090) .. (413.7062,405.1750) -- (413.8312,403.6125) .. controls
+ (413.8282,403.6120) and (413.7403,403.6129) .. (413.7375,403.6125) .. controls
+ (410.8928,403.1379) and (323.2219,388.2676) .. (322.6750,388.1750) .. controls
+ (313.7728,376.1342) and (303.2902,370.9006) .. (292.8625,370.7062) .. controls
+ (292.4247,370.6982) and (291.9880,370.6972) .. (291.5500,370.7062) -- cycle;
+ \path[fill=maincolor,miter limit=4.00,line width=0.977pt] (370.0353,433.6332) --
+ (333.5033,433.6332) -- (333.5033,428.6205) .. controls (334.6678,428.5193) and
+ (335.9083,428.4180) .. (337.2248,428.3167) .. controls (338.5413,428.1648) and
+ (339.6299,427.9623) .. (340.4907,427.7091) .. controls (341.9084,427.2534) and
+ (342.9717,426.4686) .. (343.6806,425.3547) .. controls (344.3895,424.1901) and
+ (344.7439,422.6964) .. (344.7439,420.8736) -- (344.7439,334.1385) .. controls
+ (344.7439,332.3158) and (344.3388,330.6195) .. (343.5287,329.0498) .. controls
+ (342.7692,327.4296) and (341.7565,326.1638) .. (340.4907,325.2523) .. controls
+ (339.5793,324.6448) and (337.9084,324.0878) .. (335.4780,323.5814) .. controls
+ (333.0476,323.0752) and (331.0982,322.7714) .. (329.6298,322.6700) --
+ (329.6298,317.8092) -- (357.8073,316.0623) -- (358.8706,317.2016) --
+ (358.8706,419.9622) .. controls (358.8706,421.7344) and (359.1997,423.2028) ..
+ (359.8580,424.3673) .. controls (360.5162,425.4813) and (361.5795,426.3420) ..
+ (363.0479,426.9496) .. controls (364.1618,427.4560) and (365.2504,427.8357) ..
+ (366.3138,428.0889) .. controls (367.4277,428.3420) and (368.6682,428.5193) ..
+ (370.0353,428.6205) -- (370.0353,433.6332);
+ \path[color=black,fill=white,nonzero rule,line width=0.977pt]
+ (357.7812,315.4375) -- (329.5938,317.1875) -- (329.0312,317.2500) --
+ (329.0312,317.8125) -- (329.0312,322.6562) -- (329.0312,323.2500) --
+ (329.5938,323.2813) .. controls (331.0155,323.3795) and (332.9294,323.6847) ..
+ (335.3438,324.1875) .. controls (337.7273,324.6842) and (339.3512,325.2404) ..
+ (340.1250,325.7500) .. controls (340.1300,325.7530) and (340.1517,325.7471) ..
+ (340.1563,325.7500) .. controls (341.3216,326.5942) and (342.2496,327.7784) ..
+ (342.9688,329.3125) -- (343.0001,329.3438) .. controls (343.7660,330.8280) and
+ (344.1251,332.3928) .. (344.1251,334.1251) -- (344.1251,420.8751) .. controls
+ (344.1251,422.6188) and (343.7916,423.9876) .. (343.1563,425.0313) .. controls
+ (342.5262,426.0216) and (341.6206,426.7047) .. (340.3126,427.1251) .. controls
+ (339.5087,427.3615) and (338.4639,427.5701) .. (337.1876,427.7188) .. controls
+ (337.1753,427.7198) and (337.1687,427.7178) .. (337.1563,427.7188) .. controls
+ (335.8502,427.8194) and (334.5947,427.8995) .. (333.4375,428.0001) --
+ (332.9063,428.0626) -- (332.9063,428.6251) -- (332.9063,433.6251) --
+ (332.9063,434.2501) -- (333.5000,434.2501) -- (370.0313,434.2501) --
+ (370.0313,433.6251) -- (370.6563,433.6251) -- (370.6563,428.6251) --
+ (370.6563,428.0626) -- (370.0938,428.0001) .. controls (368.7631,427.9015) and
+ (367.5404,427.7420) .. (366.4688,427.5001) .. controls (366.4598,427.4981) and
+ (366.4466,427.5021) .. (366.4375,427.5001) .. controls (365.4251,427.2570) and
+ (364.3851,426.8939) .. (363.3125,426.4063) -- (363.2812,426.3750) .. controls
+ (361.9048,425.8055) and (360.9567,425.0472) .. (360.3750,424.0625) .. controls
+ (359.7820,423.0135) and (359.4687,421.6654) .. (359.4687,419.9688) --
+ (359.4687,317.1875) -- (359.4687,316.9688) -- (359.3125,316.7813) --
+ (358.2500,315.6563) -- (358.0625,315.4375) -- (357.7812,315.4375) --
+ cycle(357.5625,316.6875) -- (358.2500,317.4062) -- (358.2500,419.9688) ..
+ controls (358.2500,421.8165) and (358.5889,423.3762) .. (359.3125,424.6562) --
+ (359.3438,424.6875) .. controls (360.0742,425.9238) and (361.2340,426.8546) ..
+ (362.7813,427.5000) .. controls (362.7903,427.5041) and (362.8037,427.4960) ..
+ (362.8126,427.5000) .. controls (363.9485,428.0145) and (365.0908,428.4264) ..
+ (366.1876,428.6875) .. controls (367.1668,428.9101) and (368.2823,429.0175) ..
+ (369.4376,429.1250) -- (369.4376,433.0313) -- (334.1251,433.0313) --
+ (334.1251,429.1875) .. controls (335.1314,429.1017) and (336.1652,429.0234) ..
+ (337.2813,428.9375) .. controls (338.6211,428.7830) and (339.7464,428.5489) ..
+ (340.6563,428.2813) -- (340.6876,428.2813) .. controls (342.2151,427.7903) and
+ (343.4000,426.9252) .. (344.1876,425.6875) .. controls (344.9701,424.4021) and
+ (345.3439,422.7770) .. (345.3439,420.8750) -- (345.3439,334.1250) .. controls
+ (345.3439,332.2119) and (344.9169,330.4366) .. (344.0626,328.7813) --
+ (344.0939,328.7813) .. controls (343.2978,327.0829) and (342.2020,325.7280) ..
+ (340.8439,324.7500) .. controls (339.7893,324.0471) and (338.0571,323.4821) ..
+ (335.5939,322.9688) .. controls (333.3923,322.5102) and (331.6955,322.2871) ..
+ (330.2502,322.1563) -- (330.2502,318.3750) -- (357.5627,316.6875) -- cycle;
+ \path[fill=basecolor,miter limit=4.00,line width=0.977pt] (411.1244,461.3551) ..
+ controls (406.3648,461.3550) and (402.0103,460.7474) .. (398.0609,459.5323) ..
+ controls (394.1115,458.3677) and (390.7191,456.7727) .. (387.8836,454.7474) ..
+ controls (385.0481,452.7220) and (382.8456,450.3929) .. (381.2759,447.7600) ..
+ controls (379.7569,445.1270) and (378.9974,442.4181) .. (378.9974,439.6333) ..
+ controls (378.9974,437.1523) and (379.6050,434.9244) .. (380.8202,432.9497) ..
+ controls (382.0354,430.9244) and (383.9848,429.9117) .. (386.6684,429.9117) ..
+ controls (389.7064,429.9117) and (392.0102,430.6712) .. (393.5799,432.1902) ..
+ controls (395.2001,433.6586) and (396.0103,435.3548) .. (396.0103,437.2789) ..
+ controls (396.0103,438.8991) and (395.7318,440.9498) .. (395.1748,443.4308) ..
+ controls (394.6685,445.9118) and (394.2634,447.6840) .. (393.9596,448.7473) ..
+ controls (394.3140,449.2030) and (394.9470,449.8106) .. (395.8584,450.5701) ..
+ controls (396.8204,451.3296) and (397.9850,452.0132) .. (399.3521,452.6208) ..
+ controls (400.9723,453.3803) and (402.6686,453.9879) .. (404.4408,454.4436) ..
+ controls (406.2129,454.8993) and (408.7192,455.1271) .. (411.9598,455.1272) ..
+ controls (414.9978,455.1271) and (417.9092,454.6208) .. (420.6941,453.6081) ..
+ controls (423.5295,452.5955) and (426.0106,450.9752) .. (428.1372,448.7473) ..
+ controls (430.3650,446.4182) and (432.0866,443.6840) .. (433.3018,440.5447) ..
+ controls (434.5676,437.4561) and (435.2005,433.3801) .. (435.2006,428.3167) ..
+ controls (435.2005,425.5825) and (434.8967,422.8736) .. (434.2892,420.1901) ..
+ controls (433.7322,417.5065) and (432.6689,415.1774) .. (431.0993,413.2026) ..
+ controls (429.5296,411.2280) and (427.3777,409.7090) .. (424.6435,408.6456) ..
+ controls (421.9599,407.5317) and (418.5421,406.9747) .. (414.3902,406.9747) --
+ (404.4408,406.9747) -- (404.4408,398.2404) -- (411.0484,398.2404) .. controls
+ (418.0358,398.2405) and (423.0738,396.3164) .. (426.1625,392.4682) .. controls
+ (429.2511,388.6201) and (430.7954,382.8226) .. (430.7955,375.0756) .. controls
+ (430.7954,368.7465) and (429.3017,363.9870) .. (426.3144,360.7970) .. controls
+ (423.3270,357.5565) and (419.0991,355.9363) .. (413.6307,355.9362) .. controls
+ (410.9978,355.9363) and (408.8205,356.2654) .. (407.0990,356.9235) .. controls
+ (405.4281,357.5312) and (404.0610,358.1388) .. (402.9977,358.7463) .. controls
+ (401.7318,359.4553) and (400.6939,360.2401) .. (399.8838,361.1008) .. controls
+ (399.0736,361.9616) and (398.4660,362.5946) .. (398.0609,362.9995) .. controls
+ (398.4153,364.6199) and (398.8457,366.6199) .. (399.3521,368.9996) .. controls
+ (399.8584,371.3288) and (400.1116,373.5567) .. (400.1116,375.6832) .. controls
+ (400.1116,377.5567) and (399.3014,379.2529) .. (397.6812,380.7719) .. controls
+ (396.1115,382.2403) and (393.7824,382.9745) .. (390.6938,382.9744) .. controls
+ (388.0102,382.9745) and (386.0355,382.0125) .. (384.7697,380.0883) .. controls
+ (383.5544,378.1643) and (382.9468,375.9365) .. (382.9469,373.4047) .. controls
+ (382.9468,370.7718) and (383.6810,368.0883) .. (385.1494,365.3540) .. controls
+ (386.6178,362.6199) and (388.7950,360.0629) .. (391.6811,357.6830) .. controls
+ (394.5672,355.3033) and (398.0862,353.3793) .. (402.2382,351.9108) .. controls
+ (406.3901,350.4425) and (411.1750,349.7083) .. (416.5928,349.7083) .. controls
+ (422.8207,349.7083) and (427.9599,350.6451) .. (432.0107,352.5184) .. controls
+ (436.1119,354.3413) and (439.3271,356.5692) .. (441.6564,359.2020) .. controls
+ (443.9348,361.7844) and (445.5044,364.4933) .. (446.3653,367.3287) .. controls
+ (447.2260,370.1642) and (447.6564,372.5947) .. (447.6564,374.6199) .. controls
+ (447.6563,377.1517) and (447.3019,379.7087) .. (446.5931,382.2909) .. controls
+ (445.8842,384.8226) and (444.7196,387.1771) .. (443.0994,389.3543) .. controls
+ (441.3272,391.7341) and (438.9727,393.9113) .. (436.0360,395.8860) .. controls
+ (433.1499,397.8101) and (429.5043,399.3797) .. (425.0992,400.5949) --
+ (425.0992,401.8101) .. controls (427.9346,402.0633) and (430.9473,402.6962) ..
+ (434.1373,403.7089) .. controls (437.3778,404.7216) and (440.2892,406.2152) ..
+ (442.8716,408.1899) .. controls (445.5551,410.2659) and (447.7576,412.9495) ..
+ (449.4792,416.2406) .. controls (451.2007,419.4812) and (452.0615,423.6078) ..
+ (452.0615,428.6205) .. controls (452.0615,438.3422) and (448.1627,446.2156) ..
+ (440.3652,452.2410) .. controls (432.5676,458.3170) and (422.8207,461.3550) ..
+ (411.1244,461.3551);
+ \path[color=black,fill=white,nonzero rule,line width=0.977pt]
+ (416.5938,349.0938) .. controls (411.1208,349.0938) and (406.2619,349.8476) ..
+ (402.0312,351.3438) .. controls (397.8235,352.8319) and (394.2307,354.7869) ..
+ (391.2812,357.2188) .. controls (388.3458,359.6393) and (386.1373,362.2465) ..
+ (384.6250,365.0625) .. controls (383.1180,367.8687) and (382.3437,370.6599) ..
+ (382.3438,373.4062) .. controls (382.3437,376.0357) and (382.9702,378.3800) ..
+ (384.2500,380.4062) -- (384.2500,380.4375) .. controls (385.6247,382.5272) and
+ (387.8580,383.5938) .. (390.6875,383.5938) .. controls (393.8729,383.5938) and
+ (396.3782,382.8237) .. (398.0938,381.2188) .. controls (399.8054,379.6142) and
+ (400.7187,377.7371) .. (400.7188,375.6875) .. controls (400.7187,373.5126) and
+ (400.4520,371.2419) .. (399.9375,368.8750) .. controls (399.4598,366.6301) and
+ (399.0615,364.7892) .. (398.7188,363.2188) .. controls (399.1249,362.8074) and
+ (399.6263,362.2937) .. (400.3438,361.5313) .. controls (401.1024,360.7253) and
+ (402.0588,359.9659) .. (403.2812,359.2813) -- (403.3125,359.2813) .. controls
+ (404.3323,358.6986) and (405.6639,358.0996) .. (407.3125,357.5000) .. controls
+ (408.9371,356.8790) and (411.0429,356.5314) .. (413.6250,356.5313) .. controls
+ (418.9728,356.5314) and (423.0134,358.1148) .. (425.8750,361.2188) .. controls
+ (428.7247,364.2617) and (430.1875,368.8295) .. (430.1875,375.0625) .. controls
+ (430.1875,382.7284) and (428.6509,388.4017) .. (425.6875,392.0938) .. controls
+ (422.7395,395.7667) and (417.9378,397.6251) .. (411.0625,397.6250) --
+ (404.4375,397.6250) -- (403.8438,397.6250) -- (403.8438,398.2500) --
+ (403.8438,406.9688) -- (403.8438,407.5938) -- (404.4375,407.5938) --
+ (414.3750,407.5938) .. controls (418.4724,407.5938) and (421.8256,408.1476) ..
+ (424.4063,409.2188) -- (424.4376,409.2188) .. controls (427.0895,410.2502) and
+ (429.1296,411.7125) .. (430.6251,413.5938) .. controls (432.1327,415.4906) and
+ (433.1477,417.7115) .. (433.6876,420.3125) .. controls (434.2853,422.9526) and
+ (434.5938,425.6236) .. (434.5938,428.3125) .. controls (434.5938,433.3250) and
+ (433.9715,437.3322) .. (432.7501,440.3125) -- (432.7188,440.3125) .. controls
+ (431.5305,443.3821) and (429.8590,446.0423) .. (427.6875,448.3125) .. controls
+ (425.6226,450.4758) and (423.2475,452.0501) .. (420.5000,453.0313) .. controls
+ (417.7822,454.0196) and (414.9389,454.5313) .. (411.9688,454.5313) .. controls
+ (408.7568,454.5313) and (406.2790,454.2771) .. (404.5938,453.8438) .. controls
+ (402.8574,453.3973) and (401.2103,452.8056) .. (399.6250,452.0625) --
+ (399.5938,452.0625) .. controls (398.2643,451.4717) and (397.1638,450.8152) ..
+ (396.2500,450.0938) .. controls (395.4421,449.4205) and (394.9503,448.9158) ..
+ (394.6250,448.5313) .. controls (394.9285,447.4218) and (395.3093,445.8751) ..
+ (395.7812,443.5625) .. controls (396.3444,441.0538) and (396.6250,438.9715) ..
+ (396.6250,437.2813) .. controls (396.6250,435.1859) and (395.7186,433.3075) ..
+ (394.0000,431.7500) .. controls (392.2889,430.0942) and (389.7960,429.3125) ..
+ (386.6562,429.3125) .. controls (383.8191,429.3125) and (381.5985,430.4297) ..
+ (380.2812,432.6250) -- (380.3125,432.6250) .. controls (379.0375,434.6970) and
+ (378.3750,437.0443) .. (378.3750,439.6250) .. controls (378.3750,442.5209) and
+ (379.1845,445.3489) .. (380.7500,448.0625) .. controls (382.3672,450.7752) and
+ (384.6374,453.1829) .. (387.5313,455.2500) .. controls (390.4308,457.3211) and
+ (393.8715,458.9445) .. (397.8750,460.1250) .. controls (401.8903,461.3605) and
+ (406.3135,461.9688) .. (411.1250,461.9688) .. controls (422.9271,461.9688) and
+ (432.8357,458.8858) .. (440.7500,452.7188) .. controls (448.6732,446.5963) and
+ (452.6875,438.5099) .. (452.6875,428.6250) .. controls (452.6875,423.5424) and
+ (451.8139,419.3245) .. (450.0313,415.9688) .. controls (448.2737,412.6089) and
+ (446.0071,409.8517) .. (443.2500,407.7188) .. controls (440.6098,405.6999) and
+ (437.6168,404.1577) .. (434.3125,403.1250) .. controls (431.2903,402.1656) and
+ (428.4437,401.5684) .. (425.7188,401.2813) -- (425.7188,401.0313) .. controls
+ (429.9462,399.8234) and (433.5316,398.3019) .. (436.3750,396.4063) .. controls
+ (439.3588,394.4000) and (441.7734,392.1633) .. (443.5938,389.7188) .. controls
+ (445.2562,387.4849) and (446.4583,385.0731) .. (447.1875,382.4688) --
+ (447.1875,382.4375) .. controls (447.9098,379.8061) and (448.2812,377.2137) ..
+ (448.2813,374.6250) .. controls (448.2812,372.5132) and (447.8100,370.0304) ..
+ (446.9375,367.1562) .. controls (446.0482,364.2269) and (444.4516,361.4494) ..
+ (442.1250,358.8125) .. controls (439.7295,356.1046) and (436.4304,353.8164) ..
+ (432.2813,351.9688) .. controls (432.2733,351.9647) and (432.2585,351.9728) ..
+ (432.2500,351.9688) .. controls (428.0952,350.0532) and (422.8701,349.0938) ..
+ (416.5938,349.0938) -- cycle(416.5938,350.3125) .. controls
+ (422.7604,350.3126) and (427.8075,351.2392) .. (431.7500,353.0625) .. controls
+ (435.7908,354.8585) and (438.9298,357.0417) .. (441.1875,359.5938) .. controls
+ (443.4178,362.1216) and (444.9489,364.7586) .. (445.7812,367.5000) .. controls
+ (446.6302,370.2969) and (447.0312,372.6862) .. (447.0312,374.6250) .. controls
+ (447.0312,377.0998) and (446.6953,379.5919) .. (446.0000,382.1250) .. controls
+ (445.3114,384.5842) and (444.2030,386.8795) .. (442.6250,389.0000) .. controls
+ (440.9009,391.3151) and (438.5771,393.4320) .. (435.6875,395.3750) .. controls
+ (432.8713,397.2525) and (429.2959,398.7977) .. (424.9375,400.0000) --
+ (424.5000,400.1250) -- (424.5000,400.5938) -- (424.5000,401.8125) --
+ (424.5000,402.3750) -- (425.0312,402.4063) .. controls (427.8151,402.6549) and
+ (430.7833,403.2800) .. (433.9375,404.2813) -- (433.9688,404.2813) .. controls
+ (437.1455,405.2741) and (439.9756,406.7571) .. (442.5000,408.6875) .. controls
+ (445.1100,410.7067) and (447.2519,413.3089) .. (448.9375,416.5313) .. controls
+ (450.5978,419.6567) and (451.4375,423.6822) .. (451.4375,428.6250) .. controls
+ (451.4375,438.1834) and (447.6718,445.8218) .. (440.0000,451.7500) .. controls
+ (432.3192,457.7351) and (422.7156,460.7500) .. (411.1250,460.7500) .. controls
+ (406.4174,460.7500) and (402.1335,460.1325) .. (398.2500,458.9375) --
+ (398.2188,458.9375) .. controls (394.3234,457.7889) and (391.0214,456.2296) ..
+ (388.2500,454.2500) .. controls (385.4806,452.2719) and (383.3338,450.0134) ..
+ (381.8125,447.4688) .. controls (381.8075,447.4598) and (381.8175,447.4462) ..
+ (381.8125,447.4375) .. controls (380.3501,444.8937) and (379.5938,442.2897) ..
+ (379.5938,439.6250) .. controls (379.5938,437.2436) and (380.1884,435.1587) ..
+ (381.3438,433.2812) -- (381.3438,433.2500) .. controls (382.4569,431.3946) and
+ (384.1263,430.5312) .. (386.6562,430.5312) .. controls (389.5925,430.5312) and
+ (391.7280,431.2428) .. (393.1562,432.6250) -- (393.1562,432.6562) .. controls
+ (394.6782,434.0355) and (395.4062,435.5285) .. (395.4062,437.2812) .. controls
+ (395.4062,438.8316) and (395.1445,440.8592) .. (394.5938,443.3125) --
+ (394.5625,443.3125) .. controls (394.0575,445.7868) and (393.6689,447.5649) ..
+ (393.3750,448.5938) -- (393.2812,448.8750) -- (393.4688,449.1250) .. controls
+ (393.8779,449.6511) and (394.5411,450.2582) .. (395.4688,451.0312) --
+ (395.4688,451.0625) .. controls (396.4790,451.8601) and (397.6889,452.5632) ..
+ (399.0938,453.1875) .. controls (400.7490,453.9634) and (402.4733,454.5664) ..
+ (404.2812,455.0313) .. controls (406.1403,455.5093) and (408.6995,455.7500) ..
+ (411.9688,455.7500) .. controls (415.0746,455.7500) and (418.0544,455.2246) ..
+ (420.9062,454.1875) .. controls (423.8297,453.1434) and (426.4054,451.4488) ..
+ (428.5938,449.1563) .. controls (430.8716,446.7748) and (432.6333,443.9792) ..
+ (433.8750,440.7813) .. controls (433.8780,440.7723) and (433.8720,440.7589) ..
+ (433.8750,440.7500) .. controls (435.1781,437.5565) and (435.8124,433.4132) ..
+ (435.8125,428.3125) .. controls (435.8124,425.5331) and (435.4924,422.7896) ..
+ (434.8750,420.0625) .. controls (434.3008,417.2964) and (433.1940,414.8651) ..
+ (431.5625,412.8125) .. controls (429.9186,410.7444) and (427.6914,409.1578) ..
+ (424.8750,408.0625) -- (424.8750,408.0938) .. controls (422.0884,406.9371) and
+ (418.5815,406.3751) .. (414.3750,406.3750) -- (405.0625,406.3750) --
+ (405.0625,398.8438) -- (411.0625,398.8438) .. controls (418.1619,398.8438) and
+ (423.3956,396.8673) .. (426.6250,392.8438) .. controls (429.8388,388.8397) and
+ (431.4062,382.8907) .. (431.4062,375.0625) .. controls (431.4062,368.6374) and
+ (429.8750,363.7121) .. (426.7500,360.3750) .. controls (423.6367,356.9981) and
+ (419.2140,355.3126) .. (413.6250,355.3125) .. controls (410.9412,355.3126) and
+ (408.6935,355.6486) .. (406.8750,356.3438) .. controls (405.1817,356.9596) and
+ (403.7942,357.5864) .. (402.6875,358.2188) .. controls (401.3782,358.9521) and
+ (400.2991,359.7722) .. (399.4375,360.6875) .. controls (398.6294,361.5462) and
+ (398.0210,362.1666) .. (397.6250,362.5625) -- (397.4062,362.8125) --
+ (397.4688,363.1250) .. controls (397.8228,364.7437) and (398.2438,366.7461) ..
+ (398.7500,369.1250) .. controls (399.2481,371.4166) and (399.5000,373.6095) ..
+ (399.5000,375.6875) .. controls (399.5000,377.3850) and (398.7789,378.8793) ..
+ (397.2500,380.3125) .. controls (395.8262,381.6445) and (393.6794,382.3751) ..
+ (390.6875,382.3750) .. controls (388.1498,382.3751) and (386.4382,381.5087) ..
+ (385.2812,379.7500) .. controls (384.1306,377.9283) and (383.5625,375.8403) ..
+ (383.5625,373.4063) .. controls (383.5625,370.8869) and (384.2578,368.3186) ..
+ (385.6875,365.6563) .. controls (387.1119,363.0040) and (389.2257,360.4955) ..
+ (392.0625,358.1563) .. controls (394.8853,355.8288) and (398.3413,353.9488) ..
+ (402.4375,352.5000) .. controls (406.5107,351.0596) and (411.2311,350.3126) ..
+ (416.5938,350.3125) -- cycle;
+\end{tikzpicture}
+}
+
+
+
+\newcommand \explogo{%
+\begin{tikzpicture}[y=0.45pt,x=0.45pt,yscale=-1, inner sep=0pt, outer sep=0pt]
+ \path[fill=black] (188.1546,427.4287) .. controls (190.5345,431.7164) and
+ (193.0099,435.4077) .. (195.5921,438.5850) -- (195.6233,438.6163) .. controls
+ (195.6323,438.6274) and (195.6455,438.6365) .. (195.6545,438.6476) .. controls
+ (194.4650,438.4740) and (193.2397,438.3529) .. (191.9670,438.3663) .. controls
+ (189.4530,438.3928) and (186.7534,438.8247) .. (183.8420,439.7413) .. controls
+ (188.8419,443.9283) and (193.0993,446.9893) .. (196.8420,449.3663) .. controls
+ (196.7505,449.3362) and (196.6520,449.3022) .. (196.5607,449.2726) --
+ (196.8420,449.4289) .. controls (191.0442,447.6143) and (184.7895,447.3923) ..
+ (177.4670,450.8664) .. controls (182.7247,454.1565) and (187.1369,456.5570) ..
+ (191.0295,458.3664) .. controls (190.9155,458.3495) and (190.7995,458.3198) ..
+ (190.6857,458.3039) -- (190.9670,458.4601) .. controls (184.8999,457.4153) and
+ (178.6035,458.0026) .. (171.7482,462.5226) .. controls (176.2179,464.5144) and
+ (180.1011,466.0179) .. (183.5607,467.1789) -- (183.5919,467.1789) --
+ (183.8107,467.3039) .. controls (178.0253,467.5288) and (172.2917,469.3302) ..
+ (166.6857,474.6476) .. controls (171.5829,475.8624) and (175.7757,476.6736) ..
+ (179.4669,477.2101) .. controls (179.3633,477.2310) and (179.2580,477.2503) ..
+ (179.1544,477.2726) .. controls (179.0103,477.2696) and (178.8604,477.2746) ..
+ (178.7169,477.2726) -- (178.8732,477.3351) .. controls (174.5605,478.3144) and
+ (170.3552,480.4767) .. (166.2482,484.6164) .. controls (169.5756,485.3353) and
+ (172.5696,485.8937) .. (175.3107,486.3039) .. controls (171.9361,487.9351) and
+ (168.7309,490.4629) .. (165.8107,494.3351) .. controls (169.6449,494.6328) and
+ (173.0419,494.7600) .. (176.0919,494.7726) .. controls (172.4017,496.4801) and
+ (168.9244,499.1610) .. (165.8419,503.4914) .. controls (171.3184,503.7651) and
+ (175.8960,503.7533) .. (179.8419,503.5226) .. controls (179.7681,503.5528) and
+ (179.6968,503.5855) .. (179.6232,503.6163) .. controls (179.3834,503.6377) and
+ (179.1427,503.6531) .. (178.9044,503.6788) -- (179.2482,503.8038) .. controls
+ (175.1462,505.6048) and (171.3495,508.5232) .. (168.0919,513.4600) .. controls
+ (174.8980,513.5594) and (180.2892,513.2381) .. (184.8107,512.6788) .. controls
+ (184.6730,512.7162) and (184.5417,512.7645) .. (184.4044,512.8038) .. controls
+ (184.2299,512.8144) and (184.0455,512.8223) .. (183.8732,512.8350) --
+ (184.0919,512.8975) .. controls (178.7598,514.4860) and (173.8363,517.5457) ..
+ (169.7482,523.7412) .. controls (175.2461,523.8214) and (179.8367,523.6133) ..
+ (183.7794,523.2412) .. controls (183.6938,523.2879) and (183.6146,523.3500) ..
+ (183.5294,523.3975) .. controls (178.8190,526.0253) and (174.7697,529.9721) ..
+ (172.0919,536.6475) .. controls (176.9272,535.7317) and (180.9801,534.7653) ..
+ (184.4982,533.7412) .. controls (184.4398,533.7875) and (184.3686,533.8191) ..
+ (184.3107,533.8662) -- (184.5294,533.8350) .. controls (180.9081,536.5374) and
+ (177.8636,540.3240) .. (175.8107,545.9288) .. controls (179.1397,545.1929) and
+ (182.1140,544.4194) .. (184.7794,543.6475) .. controls (182.3867,546.5404) and
+ (180.5386,550.1515) .. (179.4982,554.8975) .. controls (183.1131,553.5663) and
+ (186.2487,552.2854) .. (189.0294,551.0225) .. controls (188.9958,551.0619) and
+ (188.9691,551.1079) .. (188.9357,551.1475) .. controls (188.8789,551.1923) and
+ (188.8034,551.2277) .. (188.7482,551.2725) -- (188.8419,551.2413) .. controls
+ (186.2910,554.2981) and (184.3447,558.1421) .. (183.3732,563.2413) .. controls
+ (188.4628,561.2062) and (192.6027,559.2529) .. (196.0919,557.3976) .. controls
+ (195.9933,557.4930) and (195.8722,557.5825) .. (195.7794,557.6788) --
+ (195.8731,557.6476) .. controls (192.7873,561.0321) and (190.4833,565.3800) ..
+ (189.5606,571.3664) .. controls (195.5682,568.7142) and (200.1972,566.2494) ..
+ (203.9981,563.9289) .. controls (203.9580,563.9700) and (203.9127,564.0127) ..
+ (203.8731,564.0539) -- (204.1231,563.9289) .. controls (199.8075,567.6248) and
+ (196.4969,572.5288) .. (195.3419,580.0226) .. controls (203.4387,576.4482) and
+ (209.1016,573.2112) .. (213.4981,570.2101) .. controls (211.6101,574.0286) and
+ (210.6125,578.4777) .. (211.0919,584.0539) .. controls (218.9172,578.0502) and
+ (223.8713,573.2098) .. (227.5606,568.9289) .. controls (227.5546,568.9906) and
+ (227.5656,569.0545) .. (227.5606,569.1164) .. controls (227.3369,571.8296) and
+ (227.5060,574.7714) .. (228.3106,578.0226) .. controls (232.9500,570.0520) and
+ (237.5899,566.7480) .. (242.3106,564.0226) .. controls (236.4069,567.8493) and
+ (237.4669,570.9503) .. (237.4669,574.4914) .. controls (244.4870,567.0983) and
+ (254.2537,559.2459) .. (257.2481,554.3351) .. controls (253.2445,560.3478) and
+ (251.6457,566.0579) .. (250.5919,571.0851) .. controls (230.1065,581.9187) and
+ (217.6060,584.7541) .. (203.6231,589.0851) .. controls (209.0471,591.0745) and
+ (214.8691,593.0070) .. (212.4669,595.7726) -- (206.0606,602.1789) .. controls
+ (210.1567,600.9056) and (214.3405,599.0697) .. (217.9669,601.5539) .. controls
+ (217.6110,604.9137) and (213.3733,606.6264) .. (209.7481,608.5851) .. controls
+ (216.1304,606.2854) and (217.5075,606.8270) .. (219.1856,607.3664) .. controls
+ (220.0240,611.3923) and (217.1123,613.5511) .. (215.2169,616.2101) .. controls
+ (231.2927,603.9867) and (252.2574,594.3357) .. (267.0294,590.3039) .. controls
+ (287.8076,583.3065) and (314.6175,576.0122) .. (321.9044,558.2726) --
+ (329.1856,529.0226) .. controls (337.7280,519.7775) and (345.5838,510.2065) ..
+ (349.3108,498.5539) .. controls (373.0973,493.9520) and (392.3393,495.6016) ..
+ (412.4046,496.1164) .. controls (410.5758,495.8116) and (351.1546,485.7414) ..
+ (351.1546,485.7414) .. controls (333.7856,461.9756) and (307.8721,478.1391) ..
+ (299.3421,501.6164) .. controls (297.1891,507.5420) and (284.9022,507.5289) ..
+ (281.3421,501.5852) .. controls (267.2670,471.2820) and (224.5286,439.2367) ..
+ (188.1546,427.4289) -- cycle;
+ \path[fill=basecolor] (187.7346,425.7215) .. controls (190.1312,430.0392) and
+ (192.6231,433.7232) .. (195.2248,436.9158) .. controls (194.0353,436.7423) and
+ (192.8098,436.6402) .. (191.5371,436.6536) .. controls (189.0231,436.6801) and
+ (186.3356,437.0974) .. (183.4241,438.0140) .. controls (188.5047,442.2685) and
+ (192.7982,445.3733) .. (196.5852,447.7660) .. controls (190.7343,445.8972) and
+ (184.4338,445.6311) .. (177.0321,449.1427) .. controls (182.4382,452.5257) and
+ (186.9760,454.9705) .. (190.9471,456.7968) .. controls (184.7555,455.6556) and
+ (178.3356,456.1756) .. (171.3284,460.7959) .. controls (175.9548,462.8576) and
+ (179.9558,464.4054) .. (183.5061,465.5818) .. controls (177.6784,465.7855) and
+ (171.9101,467.5854) .. (166.2639,472.9408) .. controls (171.1611,474.1557) and
+ (175.3568,474.9612) .. (179.0480,475.4977) .. controls (174.5307,476.4104) and
+ (170.0996,478.5772) .. (165.8050,482.9059) .. controls (169.1324,483.6248) and
+ (172.1275,484.1674) .. (174.8686,484.5777) .. controls (171.4940,486.2088) and
+ (168.2990,488.7365) .. (165.3788,492.6087) .. controls (169.2130,492.9064) and
+ (172.6053,493.0386) .. (175.6553,493.0512) .. controls (171.9650,494.7587) and
+ (168.5104,497.4567) .. (165.4280,501.7871) .. controls (170.9044,502.0608) and
+ (175.4790,502.0342) .. (179.4250,501.8034) .. controls (175.0884,503.5763) and
+ (171.0915,506.5720) .. (167.6734,511.7521) .. controls (174.4795,511.8515) and
+ (179.8697,511.5247) .. (184.3911,510.9654) .. controls (178.7800,512.4888) and
+ (173.5859,515.5521) .. (169.3124,522.0286) .. controls (174.8103,522.1088) and
+ (179.3994,521.9089) .. (183.3421,521.5369) .. controls (178.5221,524.1635) and
+ (174.3823,528.1477) .. (171.6562,534.9438) .. controls (176.6186,534.0040) and
+ (180.7774,532.9979) .. (184.3583,531.9445) .. controls (180.6281,534.6591) and
+ (177.4926,538.4719) .. (175.3930,544.2041) .. controls (178.7220,543.4682) and
+ (181.6765,542.6978) .. (184.3419,541.9259) .. controls (181.9491,544.8188) and
+ (180.1048,548.4397) .. (179.0644,553.1858) .. controls (182.6793,551.8545) and
+ (185.8062,550.5643) .. (188.5869,549.3014) .. controls (185.9451,552.3928) and
+ (183.9273,556.3063) .. (182.9324,561.5282) .. controls (188.0220,559.4931) and
+ (192.1617,557.5487) .. (195.6510,555.6934) .. controls (192.4524,559.1128) and
+ (190.0726,563.5113) .. (189.1278,569.6412) .. controls (195.3243,566.9057) and
+ (200.0785,564.3715) .. (203.9442,561.9871) .. controls (199.5047,565.7077) and
+ (196.0910,570.6550) .. (194.9134,578.2951) .. controls (203.0102,574.7206) and
+ (208.6604,571.4786) .. (213.0570,568.4775) .. controls (211.1690,572.2961) and
+ (210.1683,576.7672) .. (210.6477,582.3434) .. controls (218.4730,576.3397) and
+ (223.4302,571.4800) .. (227.1195,567.1991) .. controls (226.8707,569.9634) and
+ (227.0669,572.9701) .. (227.8899,576.2955) .. controls (233.5078,566.6437) and
+ (239.0905,563.8256) .. (244.8678,560.6045) .. controls (235.6654,565.2375) and
+ (237.0296,568.7253) .. (237.0296,572.7723) .. controls (244.0497,565.3793) and
+ (253.8400,557.5393) .. (256.8344,552.6285) .. controls (252.8308,558.6412) and
+ (251.2011,564.3353) .. (250.1474,569.3626) .. controls (229.6620,580.1962) and
+ (217.1896,583.0276) .. (203.2067,587.3587) .. controls (208.6306,589.3481) and
+ (214.4431,591.2965) .. (212.0409,594.0622) -- (205.6324,600.4542) .. controls
+ (209.7285,599.1810) and (213.9051,597.3636) .. (217.5315,599.8478) .. controls
+ (217.1756,603.2077) and (212.9289,604.9040) .. (209.3037,606.8627) .. controls
+ (215.6860,604.5630) and (217.0662,605.0941) .. (218.7443,605.6335) .. controls
+ (219.5827,609.6594) and (216.6733,611.8250) .. (214.7780,614.4840) .. controls
+ (230.8537,602.2606) and (251.8308,592.6034) .. (266.6028,588.5716) .. controls
+ (287.3810,581.5741) and (314.1729,574.3016) .. (321.4598,556.5621) --
+ (328.7697,527.3061) .. controls (337.3119,518.0610) and (345.1694,508.4736) ..
+ (348.8965,496.8210) .. controls (372.6829,492.2191) and (391.9159,493.8805) ..
+ (411.9812,494.3952) .. controls (410.1524,494.0905) and (350.7157,484.0204) ..
+ (350.7157,484.0204) .. controls (333.3467,460.2547) and (307.4537,476.4086) ..
+ (298.9237,499.8859) .. controls (296.7707,505.8115) and (284.4877,505.8133) ..
+ (280.9276,499.8695) .. controls (266.8525,469.5663) and (224.1086,437.5293) ..
+ (187.7346,425.7216) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (328.0690,487.7425) -- (327.9127,487.9925) .. controls (327.2963,489.1409) and
+ (326.2537,489.9248) .. (324.7877,490.3988) .. controls (323.3218,490.8728) and
+ (321.4437,491.0247) .. (319.2565,490.9300) -- (317.8815,490.8675) --
+ (318.8815,491.8363) .. controls (319.3892,492.3246) and (319.5310,492.7144) ..
+ (319.5377,493.0550) .. controls (319.5447,493.3957) and (319.3983,493.7636) ..
+ (319.0690,494.1800) .. controls (318.4103,495.0130) and (317.1020,495.9174) ..
+ (315.8815,496.8363) -- (315.4752,497.1175) -- (315.7565,497.5238) .. controls
+ (316.7747,499.0858) and (317.2409,500.2883) .. (317.3502,501.2113) .. controls
+ (317.4595,502.1343) and (317.2450,502.7963) .. (316.8190,503.3675) .. controls
+ (315.9669,504.5101) and (314.0965,505.2843) .. (312.4127,506.2425) --
+ (312.1002,506.3988) -- (312.1627,506.7738) .. controls (312.3075,507.6524) and
+ (312.0708,508.3704) .. (311.4440,509.0863) .. controls (310.8171,509.8021) and
+ (309.7619,510.4847) .. (308.3502,511.0863) -- (307.9752,511.2425) --
+ (308.0377,511.6488) .. controls (308.3193,513.5300) and (308.8848,515.1671) ..
+ (310.0690,516.2113) .. controls (311.1523,517.1665) and (312.7911,517.4184) ..
+ (314.8815,516.9925) .. controls (315.2459,518.9979) and (315.7682,520.8217) ..
+ (316.9752,522.0238) .. controls (318.1986,523.2421) and (320.1355,523.6024) ..
+ (322.7252,522.9300) .. controls (323.3080,524.3346) and (324.1596,525.3355) ..
+ (325.1315,525.9925) .. controls (326.1986,526.7139) and (327.5930,527.0049) ..
+ (328.7401,527.3612) -- (329.4197,526.6303) .. controls (328.2812,526.2767) and
+ (326.6004,525.7302) .. (325.6940,525.1175) .. controls (324.7876,524.5048) and
+ (324.0512,523.6643) .. (323.5377,522.2113) -- (323.3815,521.7113) --
+ (322.8815,521.8675) .. controls (320.2185,522.6859) and (318.7418,522.3174) ..
+ (317.7252,521.3050) .. controls (316.7087,520.2927) and (316.1370,518.4626) ..
+ (315.7877,516.3363) -- (315.6940,515.7738) -- (315.1315,515.9300) .. controls
+ (312.9366,516.5027) and (311.6408,516.2098) .. (310.7565,515.4300) .. controls
+ (309.9304,514.7016) and (309.4415,513.4103) .. (309.1628,511.8050) .. controls
+ (310.4344,511.2171) and (311.5245,510.5741) .. (312.2253,509.7738) .. controls
+ (312.9099,508.9919) and (313.2063,508.0267) .. (313.1628,507.0238) .. controls
+ (314.7142,506.1705) and (316.5854,505.4372) .. (317.6628,503.9925) .. controls
+ (318.2261,503.2372) and (318.5187,502.2444) .. (318.3815,501.0863) .. controls
+ (318.2568,500.0326) and (317.7408,498.8152) .. (316.8503,497.3675) .. controls
+ (317.9434,496.5623) and (319.1300,495.7554) .. (319.8815,494.8050) .. controls
+ (320.2994,494.2767) and (320.6136,493.6919) .. (320.6003,493.0238) .. controls
+ (320.5933,492.6554) and (320.2387,492.3198) .. (320.0378,491.9613) .. controls
+ (321.9389,491.9861) and (323.6709,491.8609) .. (325.1003,491.3988) .. controls
+ (326.6062,490.9119) and (327.7892,490.0245) .. (328.5690,488.8050) .. controls
+ (330.8191,488.8168) and (333.1492,489.3112) .. (335.5065,489.6175) .. controls
+ (337.9280,489.9322) and (340.3922,490.0166) .. (342.7253,488.9925) --
+ (342.2878,488.0238) .. controls (340.2235,488.9299) and (338.0183,488.8923) ..
+ (335.6628,488.5863) .. controls (333.3073,488.2802) and (330.8524,487.7246) ..
+ (328.3815,487.7425) -- cycle;
+ \path[draw=white,fill=white,miter limit=4.00,line width=0.839pt]
+ (336.8186,487.3469) .. controls (336.8186,489.7993) and (334.8306,491.7873) ..
+ (332.3782,491.7873) .. controls (329.9259,491.7873) and (327.9379,489.7993) ..
+ (327.9379,487.3469) .. controls (327.9379,484.8946) and (329.9259,482.9065) ..
+ (332.3782,482.9065) .. controls (334.8306,482.9065) and (336.8186,484.8945) ..
+ (336.8186,487.3469) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (350.0784,483.1829) .. controls (346.0920,488.3688) and (345.0896,487.0295) ..
+ (342.6627,487.8988) -- (342.2565,488.0550) -- (342.3190,488.4925) .. controls
+ (343.2534,493.7013) and (346.3404,495.3564) .. (348.7497,497.2498) --
+ (348.8842,496.8285) -- (349.5255,496.7063) .. controls (347.1103,494.8083) and
+ (344.4675,493.2007) .. (343.5065,488.7738) .. controls (345.2738,488.3557) and
+ (346.9603,488.8903) .. (350.7252,483.9925) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (196.5832,447.7548) .. controls (204.1159,452.4256) and (216.5138,457.2421) ..
+ (224.4752,462.2738) .. controls (228.8554,465.0628) and (232.7896,467.8418) ..
+ (235.8502,470.2425) .. controls (238.9108,472.6433) and (241.1740,475.1236) ..
+ (242.0065,475.9300) .. controls (242.8390,476.7364) and (243.4041,476.3554) ..
+ (242.8502,475.3050) .. controls (242.2964,474.2547) and (239.5701,471.8264) ..
+ (236.4752,469.3988) .. controls (233.3804,466.9712) and (229.4438,464.2043) ..
+ (225.0377,461.3988) .. controls (216.2255,455.7878) and (205.8833,450.7708) ..
+ (196.5832,447.7548) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (195.2242,436.9074) .. controls (198.3819,437.6366) and (203.0426,440.3298) ..
+ (208.2252,443.2113) .. controls (213.4079,446.0928) and (219.0564,449.6386) ..
+ (224.2252,453.0863) .. controls (229.3940,456.5340) and (233.3791,459.5515) ..
+ (236.5649,461.9823) .. controls (239.5902,464.5711) and (241.0838,466.2475) ..
+ (241.6627,466.2425) .. controls (241.7421,466.2585) and (241.8203,466.2597) ..
+ (241.9440,466.2425) .. controls (242.0677,466.2253) and (242.2764,466.1299) ..
+ (242.3815,465.9300) .. controls (242.4865,465.7302) and (242.4459,465.5623) ..
+ (242.4128,465.4613) .. controls (242.3796,465.3603) and (242.3618,465.3067) ..
+ (242.3191,465.2425) .. controls (242.1482,464.9858) and (241.8873,464.7454) ..
+ (241.5066,464.3988) .. controls (240.7451,463.7056) and (239.4870,462.6862) ..
+ (237.8816,461.4613) .. controls (234.6708,459.0114) and (230.0051,455.6705) ..
+ (224.8191,452.2113) .. controls (219.6331,448.7521) and (213.9447,445.1757) ..
+ (208.7253,442.2738) .. controls (206.8700,441.5863) and (197.7714,436.7746) ..
+ (195.2243,436.9074) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (190.9046,456.7968) .. controls (200.5728,460.7431) and (214.3261,465.3785) ..
+ (223.5065,471.1488) .. controls (228.4392,474.2811) and (232.7356,477.4014) ..
+ (235.9440,479.9613) .. controls (237.5482,481.2412) and (238.8881,482.3917) ..
+ (239.8815,483.3050) .. controls (240.8749,484.2184) and (241.4352,485.0475) ..
+ (241.7565,485.2738) .. controls (242.4289,485.7475) and (242.9436,485.4328) ..
+ (242.6315,484.6800) .. controls (242.3958,484.1116) and (241.6181,483.4596) ..
+ (240.6002,482.5238) .. controls (239.5823,481.5880) and (238.2198,480.4410) ..
+ (236.6002,479.1488) .. controls (233.3611,476.5644) and (229.0363,473.3968) ..
+ (224.0690,470.2425) .. controls (214.1344,463.9341) and (202.2573,458.3877) ..
+ (190.9046,456.7968) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (183.4711,465.5716) .. controls (191.0060,465.5766) and (206.5713,472.4735) ..
+ (213.1315,475.9300) .. controls (223.7689,481.6031) and (233.4415,488.8954) ..
+ (238.7877,493.9925) .. controls (239.3805,494.5577) and (240.2465,494.0588) ..
+ (239.5690,493.2738) .. controls (234.6033,487.5198) and (224.3477,480.7388) ..
+ (213.6315,475.0238) .. controls (202.9153,469.3087) and (191.8189,465.5632) ..
+ (183.4711,465.5716) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (179.1133,475.4956) .. controls (190.4411,477.1531) and (204.0196,481.7255) ..
+ (213.9127,486.6800) .. controls (219.4336,489.4832) and (224.3566,492.4856) ..
+ (228.1315,495.1488) .. controls (231.9064,497.8120) and (234.6937,500.8104) ..
+ (235.3815,501.5863) .. controls (236.0693,502.3622) and (236.8886,502.3945) ..
+ (236.2565,501.0238) .. controls (235.6244,499.6531) and (232.5499,496.9721) ..
+ (228.7252,494.2738) .. controls (224.9006,491.5755) and (219.9456,488.5676) ..
+ (214.3815,485.7425) .. controls (203.2533,480.0924) and (190.5800,475.6473) ..
+ (179.1133,475.4956) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (182.8502,483.9300) .. controls (180.0362,483.6845) and (177.5032,484.2615) ..
+ (174.9224,484.5841) .. controls (184.9876,483.6409) and (199.0622,488.2631) ..
+ (208.4440,492.5863) .. controls (219.4960,497.7676) and (228.6734,504.8810) ..
+ (233.0690,509.3988) .. controls (233.7572,510.1061) and (234.2435,509.2048) ..
+ (233.8815,508.7425) .. controls (229.7881,503.5164) and (220.0397,496.8487) ..
+ (208.8815,491.6175) .. controls (200.5128,487.6942) and (191.2924,484.6668) ..
+ (182.8502,483.9300) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (175.7074,492.9973) .. controls (185.6124,493.7315) and (198.8738,496.1032) ..
+ (208.1627,500.3675) .. controls (217.5053,504.7372) and (224.5953,510.4032) ..
+ (228.1627,513.6175) .. controls (228.7116,514.1121) and (229.6823,513.7617) ..
+ (228.9127,512.8675) .. controls (225.6572,509.0849) and (218.0877,503.8529) ..
+ (208.6315,499.4300) .. controls (199.1753,495.0072) and (187.6892,492.1744) ..
+ (175.7074,492.9973) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (184.6002,501.2425) .. controls (182.5105,501.1718) and (181.5301,501.5628) ..
+ (179.5141,501.7805) .. controls (186.2367,502.0768) and (196.9195,503.8647) ..
+ (202.8190,505.8675) .. controls (210.4747,508.5236) and (216.7699,512.5245) ..
+ (220.3502,515.6488) .. controls (221.0416,516.2521) and (221.5570,515.4427) ..
+ (221.1002,514.9300) .. controls (217.7467,511.1667) and (210.9312,507.5627) ..
+ (203.1627,504.8675) .. controls (197.3364,502.8461) and (190.8693,501.4545) ..
+ (184.6002,501.2425) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (184.3157,510.9926) .. controls (198.6871,511.9623) and (207.3770,514.4944) ..
+ (214.0377,520.4613) .. controls (214.5368,520.9084) and (215.3247,520.2930) ..
+ (214.7877,519.7113) .. controls (207.9520,512.3054) and (194.4933,510.2417) ..
+ (184.3157,510.9926) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (195.3815,519.6175) .. controls (191.0251,519.2671) and (187.3797,520.2191) ..
+ (183.4094,521.5412) .. controls (195.0282,519.6893) and (199.8590,520.5773) ..
+ (208.6627,524.7113) .. controls (209.6662,525.1825) and (209.6073,524.0723) ..
+ (209.1627,523.8050) .. controls (204.4864,520.9942) and (199.7378,519.9679) ..
+ (195.3815,519.6175) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (194.9584,527.7065) .. controls (190.8394,528.0717) and (187.3420,529.4861) ..
+ (184.4584,531.8315) .. controls (195.9645,527.3967) and (199.1447,528.2791) ..
+ (207.9440,530.7738) .. controls (209.1013,531.1019) and (208.7417,529.9871) ..
+ (208.2877,529.8050) .. controls (203.0183,527.6924) and (199.0774,527.3413) ..
+ (194.9584,527.7065) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (217.7565,534.8363) .. controls (211.6177,534.1725) and (204.7011,534.9170) ..
+ (198.6315,536.0238) .. controls (192.5618,537.1306) and (187.3415,539.3794) ..
+ (184.3947,541.7762) .. controls (194.4866,537.5140) and (196.3258,537.5231) ..
+ (198.8190,537.0550) .. controls (204.7989,535.9646) and (210.7264,535.5394) ..
+ (217.6940,535.8675) .. controls (218.3258,535.8973) and (218.2935,534.8944) ..
+ (217.7565,534.8363) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (221.3190,539.8050) .. controls (214.6662,539.5537) and (207.7944,541.2000) ..
+ (201.8502,542.8988) .. controls (195.9061,544.5976) and (191.4940,546.9158) ..
+ (188.5846,549.2822) .. controls (194.4716,546.4504) and (199.9499,544.5335) ..
+ (202.1315,543.8988) .. controls (207.9926,542.2237) and (213.9052,540.7736) ..
+ (221.3815,540.8675) .. controls (221.9623,540.8745) and (222.3377,539.8435) ..
+ (221.3190,539.8050) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (226.7565,543.8988) .. controls (220.4907,544.8239) and (213.9208,546.6745) ..
+ (208.1940,548.6175) .. controls (202.4671,550.5606) and (198.3906,552.8682) ..
+ (195.7313,555.6308) .. controls (202.5433,551.7206) and (206.2895,550.3495) ..
+ (208.5065,549.5863) .. controls (214.1631,547.6670) and (219.4450,546.3315) ..
+ (226.9440,544.9300) .. controls (227.8440,544.7618) and (227.1212,543.8449) ..
+ (226.7565,543.8988) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (229.9440,549.1175) .. controls (217.3782,551.1190) and (209.2362,556.5404) ..
+ (204.0606,561.9168) .. controls (212.0814,555.8737) and (217.6610,552.6260) ..
+ (230.1627,550.1488) .. controls (230.9540,549.9920) and (230.7150,548.9947) ..
+ (229.9440,549.1175) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (236.0690,553.7113) .. controls (223.4309,555.6581) and (216.1430,563.3408) ..
+ (213.1075,568.4120) .. controls (221.1036,560.4242) and (227.3269,556.0769) ..
+ (236.2877,554.7425) .. controls (237.1946,554.6075) and (236.9632,553.5735) ..
+ (236.0690,553.7113) -- cycle;
+ \path[color=black,fill=white,nonzero rule,line width=0.839pt]
+ (239.8815,557.7113) .. controls (237.9104,557.9574) and (235.2282,559.3533) ..
+ (232.6940,560.9613) .. controls (230.1598,562.5693) and (228.2477,564.5258) ..
+ (227.1075,567.0466) .. controls (230.1601,563.8962) and (231.3821,563.0437) ..
+ (233.2565,561.8363) .. controls (235.6896,560.2924) and (238.1414,559.3201) ..
+ (240.1315,558.7425) .. controls (241.0644,558.4718) and (240.3594,557.6516) ..
+ (239.8815,557.7113) -- cycle;
+ \path[draw=white,fill=white,miter limit=4.00,line width=0.839pt]
+ (336.8186,487.3469) .. controls (336.8186,489.7993) and (334.8306,491.7873) ..
+ (332.3782,491.7873) .. controls (329.9259,491.7873) and (327.9379,489.7993) ..
+ (327.9379,487.3469) .. controls (327.9379,484.8946) and (329.9259,482.9065) ..
+ (332.3782,482.9065) .. controls (334.8306,482.9065) and (336.8186,484.8945) ..
+ (336.8186,487.3469) -- cycle;
+ \path[draw=white,fill=maincolor,miter limit=4.00,line width=0.839pt]
+ (336.8186,487.3469) .. controls (336.8186,489.7993) and (334.8306,491.7873) ..
+ (332.3782,491.7873) .. controls (329.9259,491.7873) and (327.9379,489.7993) ..
+ (327.9379,487.3469) .. controls (327.9379,484.8946) and (329.9259,482.9065) ..
+ (332.3782,482.9065) .. controls (334.8306,482.9065) and (336.8186,484.8945) ..
+ (336.8186,487.3469) -- cycle;
+ \path[fill=black] (240.9977,587.6605) .. controls (237.8583,593.6353) and
+ (233.6558,598.3948) .. (228.3899,601.9391) .. controls (223.1240,605.4835) and
+ (217.0733,607.2556) .. (210.2379,607.2556) .. controls (204.2124,607.2556) and
+ (198.9212,606.1923) .. (194.3643,604.0657) .. controls (189.8579,601.9391) and
+ (186.1363,599.0530) .. (183.1996,595.4074) .. controls (180.2628,591.7618) and
+ (178.0603,587.5086) .. (176.5919,582.6478) .. controls (175.1742,577.7870) and
+ (174.4653,572.6477) .. (174.4653,567.2299) .. controls (174.4653,562.3691) and
+ (175.2248,557.6096) .. (176.7438,552.9512) .. controls (178.3134,548.2424) and
+ (180.5919,544.0145) .. (183.5793,540.2676) .. controls (186.4654,536.6727) and
+ (190.0351,533.7866) .. (194.2883,531.6092) .. controls (198.5921,529.3814) and
+ (203.4023,528.2675) .. (208.7188,528.2674) .. controls (214.1872,528.2675) and
+ (218.8708,529.1283) .. (222.7696,530.8497) .. controls (226.6684,532.5713) and
+ (229.8329,534.9258) .. (232.2634,537.9131) .. controls (234.5925,540.7486) and
+ (236.3140,544.0905) .. (237.4280,547.9385) .. controls (238.5925,551.7867) and
+ (239.1748,555.9640) .. (239.1749,560.4703) -- (239.1749,565.4830) --
+ (189.5794,565.4830) .. controls (189.5794,570.3945) and (190.0351,574.8503) ..
+ (190.9465,578.8503) .. controls (191.9085,582.8503) and (193.4022,586.3694) ..
+ (195.4276,589.4073) .. controls (197.4022,592.3441) and (199.9845,594.6732) ..
+ (203.1745,596.3948) .. controls (206.3644,598.0657) and (210.1619,598.9011) ..
+ (214.5670,598.9011) .. controls (219.0733,598.9011) and (222.9468,597.8631) ..
+ (226.1874,595.7872) .. controls (229.4785,593.6606) and (232.6431,589.8631) ..
+ (235.6812,584.3946) -- (240.9977,587.6605)(224.2127,559.1032) .. controls
+ (224.2126,556.2678) and (223.9595,553.2298) .. (223.4532,549.9892) .. controls
+ (222.9974,546.7487) and (222.2379,544.0398) .. (221.1747,541.8625) .. controls
+ (220.0101,539.5334) and (218.4151,537.6347) .. (216.3898,536.1663) .. controls
+ (214.3644,534.6980) and (211.7568,533.9638) .. (208.5669,533.9637) .. controls
+ (203.2504,533.9638) and (198.8200,536.1916) .. (195.2757,540.6473) .. controls
+ (191.7819,545.0525) and (189.8832,551.2044) .. (189.5794,559.1032) --
+ (224.2127,559.1032);
+ \path[fill=black] (321.9606,605.2809) -- (287.6311,605.2809) --
+ (287.6311,600.2682) .. controls (289.9096,600.0151) and (291.8336,599.7619) ..
+ (293.4033,599.5087) .. controls (295.0236,599.2556) and (295.8337,598.5973) ..
+ (295.8337,597.5340) .. controls (295.8337,597.0783) and (295.5805,596.3695) ..
+ (295.0742,595.4074) .. controls (294.5679,594.4454) and (294.0615,593.5593) ..
+ (293.5552,592.7492) .. controls (292.2893,590.7745) and (290.5425,588.1415) ..
+ (288.3147,584.8503) .. controls (286.1374,581.5086) and (283.4538,577.5338) ..
+ (280.2639,572.9262) .. controls (277.6310,576.2174) and (275.2006,579.4326) ..
+ (272.9727,582.5718) .. controls (270.7448,585.6605) and (268.4157,588.9517) ..
+ (265.9853,592.4454) .. controls (265.6309,592.9517) and (265.2511,593.5846) ..
+ (264.8460,594.3441) .. controls (264.4916,595.1036) and (264.3144,595.7112) ..
+ (264.3144,596.1669) .. controls (264.3144,597.2809) and (265.0232,598.2176) ..
+ (266.4410,598.9771) .. controls (267.9093,599.6860) and (270.0866,600.1670) ..
+ (272.9727,600.4201) -- (272.9727,605.2809) -- (245.2509,605.2809) --
+ (245.2509,600.5720) .. controls (248.6940,599.5594) and (251.4535,598.3442) ..
+ (253.5295,596.9264) .. controls (255.6561,595.4581) and (257.6054,593.6606) ..
+ (259.3776,591.5340) .. controls (260.9472,589.5086) and (263.1245,586.7238) ..
+ (265.9093,583.1794) .. controls (268.7448,579.6351) and (272.3904,574.8249) ..
+ (276.8462,568.7489) .. controls (274.2638,565.0527) and (271.0993,560.5463) ..
+ (267.3524,555.2298) .. controls (263.6561,549.8627) and (260.5928,545.4069) ..
+ (258.1624,541.8625) .. controls (256.8459,539.9385) and (255.3269,538.3942) ..
+ (253.6054,537.2296) .. controls (251.9345,536.0144) and (249.5547,535.4068) ..
+ (246.4661,535.4068) -- (246.4661,530.3940) -- (279.0487,530.3940) --
+ (279.0487,535.4068) .. controls (277.4284,535.4068) and (275.8335,535.5081) ..
+ (274.2639,535.7106) .. controls (272.6942,535.9132) and (271.9094,536.3689) ..
+ (271.9094,537.0777) .. controls (271.9094,537.5334) and (272.0613,538.0398) ..
+ (272.3651,538.5967) .. controls (272.6689,539.1537) and (273.0486,539.7613) ..
+ (273.5044,540.4195) .. controls (274.6183,542.1411) and (276.3398,544.7993) ..
+ (278.6690,548.3942) .. controls (281.0487,551.9893) and (283.6057,555.7868) ..
+ (286.3400,559.7868) .. controls (288.1121,557.3564) and (290.1627,554.5209) ..
+ (292.4919,551.2803) .. controls (294.8716,548.0399) and (297.0489,544.9512) ..
+ (299.0236,542.0144) .. controls (299.2261,541.6600) and (299.4286,541.2297) ..
+ (299.6312,540.7233) .. controls (299.8337,540.1664) and (299.9350,539.6347) ..
+ (299.9350,539.1283) .. controls (299.9350,538.0651) and (299.0489,537.2296) ..
+ (297.2768,536.6220) .. controls (295.5552,535.9638) and (293.9096,535.5081) ..
+ (292.3400,535.2549) -- (292.3400,530.3940) -- (319.9859,530.3940) --
+ (319.9859,535.1030) .. controls (316.0870,536.1157) and (313.0490,537.3309) ..
+ (310.8719,538.7486) .. controls (308.6946,540.1157) and (306.8464,541.7107) ..
+ (305.3275,543.5334) .. controls (303.6565,545.6095) and (301.6565,548.1664) ..
+ (299.3274,551.2044) .. controls (297.0489,554.2425) and (293.8337,558.4957) ..
+ (289.6818,563.9640) .. controls (294.2894,570.4451) and (298.0362,575.7617) ..
+ (300.9224,579.9136) .. controls (303.8084,584.0655) and (306.7199,588.3441) ..
+ (309.6567,592.7492) .. controls (311.1250,594.9770) and (312.7452,596.7492) ..
+ (314.5175,598.0657) .. controls (316.3402,599.3315) and (318.8212,600.0657) ..
+ (321.9606,600.2682) -- (321.9606,605.2809);
+ \path[fill=black] (396.9235,539.2802) .. controls (399.9107,542.8246) and
+ (402.2146,546.9512) .. (403.8349,551.6601) .. controls (405.5057,556.3184) and
+ (406.3412,561.6602) .. (406.3413,567.6856) .. controls (406.3412,573.6097) and
+ (405.3538,579.0275) .. (403.3792,583.9389) .. controls (401.4044,588.7998) and
+ (398.7968,592.9517) .. (395.5563,596.3948) .. controls (392.1638,599.8885) and
+ (388.3916,602.5721) .. (384.2398,604.4455) .. controls (380.0878,606.3189) and
+ (375.6826,607.2556) .. (371.0244,607.2556) .. controls (366.5180,607.2556) and
+ (362.8218,606.7493) .. (359.9357,605.7366) .. controls (357.0495,604.7240) and
+ (354.6951,603.5341) .. (352.8723,602.1670) -- (352.2647,602.1670) --
+ (352.2647,624.6482) .. controls (352.2647,626.4710) and (352.6191,627.9900) ..
+ (353.3280,629.2053) .. controls (354.0369,630.4204) and (355.1255,631.3065) ..
+ (356.5939,631.8635) .. controls (357.8090,632.3192) and (359.5306,632.7496) ..
+ (361.7585,633.1547) .. controls (364.0370,633.6103) and (365.9104,633.8635) ..
+ (367.3788,633.9142) -- (367.3788,639.0028) -- (325.5302,639.0028) --
+ (325.5302,633.9142) .. controls (327.0999,633.8129) and (328.6189,633.6863) ..
+ (330.0873,633.5344) .. controls (331.5556,633.3825) and (332.8721,633.1293) ..
+ (334.0367,632.7749) .. controls (335.5557,632.2686) and (336.6190,631.3825) ..
+ (337.2266,630.1167) .. controls (337.8342,628.8508) and (338.1380,627.3571) ..
+ (338.1380,625.6356) -- (338.1380,545.9638) .. controls (338.1380,544.4449) and
+ (337.7582,542.9765) .. (336.9987,541.5587) .. controls (336.2392,540.1410) and
+ (335.2519,539.0018) .. (334.0367,538.1410) .. controls (333.1253,537.5334) and
+ (331.8594,537.0777) .. (330.2392,536.7739) .. controls (328.6189,536.4195) and
+ (327.0492,536.1916) .. (325.5302,536.0903) -- (325.5302,531.1535) --
+ (351.0495,529.4826) -- (352.1128,530.3940) -- (352.1128,538.8245) --
+ (352.7204,538.9764) .. controls (355.3533,535.9385) and (358.6192,533.4068) ..
+ (362.5180,531.3814) .. controls (366.4167,529.3561) and (370.2902,528.3435) ..
+ (374.1384,528.3434) .. controls (378.7966,528.3435) and (383.0498,529.3308) ..
+ (386.8980,531.3054) .. controls (390.7967,533.2802) and (394.1385,535.9385) ..
+ (396.9234,539.2802)(384.5436,593.1289) .. controls (386.5688,589.9896) and
+ (388.0625,586.3440) .. (389.0246,582.1921) .. controls (389.9866,578.0402) and
+ (390.4676,573.4072) .. (390.4677,568.2932) .. controls (390.4676,564.5464) and
+ (390.0625,560.7235) .. (389.2525,556.8247) .. controls (388.4423,552.9260) and
+ (387.1764,549.5335) .. (385.4550,546.6474) .. controls (383.7334,543.7107) and
+ (381.4802,541.3309) .. (378.6954,539.5081) .. controls (375.9105,537.6853) and
+ (372.5687,536.7739) .. (368.6700,536.7739) .. controls (364.9230,536.7739) and
+ (361.7331,537.6094) .. (359.1002,539.2802) .. controls (356.4673,540.9005) and
+ (354.1888,542.8499) .. (352.2647,545.1284) -- (352.2647,589.4073) .. controls
+ (352.7204,590.7745) and (353.4546,592.1922) .. (354.4673,593.6606) .. controls
+ (355.5305,595.1289) and (356.6951,596.3948) .. (357.9610,597.4581) .. controls
+ (359.5306,598.7239) and (361.2268,599.7619) .. (363.0496,600.5720) .. controls
+ (364.8724,601.3315) and (367.1003,601.7113) .. (369.7333,601.7113) .. controls
+ (372.5687,601.7113) and (375.3029,600.9771) .. (377.9359,599.5087) .. controls
+ (380.5688,597.9897) and (382.7713,595.8631) .. (384.5436,593.1289);
+ \path[fill=black] (452.5949,605.2809) -- (416.0629,605.2809) --
+ (416.0629,600.2682) .. controls (417.2275,600.1670) and (418.4680,600.0657) ..
+ (419.7845,599.9644) .. controls (421.1009,599.8125) and (422.1895,599.6100) ..
+ (423.0503,599.3568) .. controls (424.4680,598.9011) and (425.5313,598.1163) ..
+ (426.2402,597.0024) .. controls (426.9491,595.8378) and (427.3035,594.3441) ..
+ (427.3035,592.5213) -- (427.3035,505.7862) .. controls (427.3035,503.9635) and
+ (426.8985,502.2672) .. (426.0883,500.6975) .. controls (425.3288,499.0773) and
+ (424.3161,497.8115) .. (423.0503,496.9000) .. controls (422.1389,496.2925) and
+ (420.4680,495.7355) .. (418.0376,495.2291) .. controls (415.6072,494.7229) and
+ (413.6578,494.4191) .. (412.1894,494.3177) -- (412.1894,489.4569) --
+ (440.3670,487.7100) -- (441.4303,488.8493) -- (441.4303,591.6099) .. controls
+ (441.4302,593.3821) and (441.7593,594.8505) .. (442.4176,596.0150) .. controls
+ (443.0758,597.1290) and (444.1391,597.9897) .. (445.6075,598.5973) .. controls
+ (446.7214,599.1037) and (447.8100,599.4834) .. (448.8734,599.7366) .. controls
+ (449.9873,599.9897) and (451.2278,600.1670) .. (452.5949,600.2682) --
+ (452.5949,605.2809);
+ \path[fill=black] (493.6840,633.0028) .. controls (488.9244,633.0027) and
+ (484.5700,632.3951) .. (480.6206,631.1800) .. controls (476.6711,630.0154) and
+ (473.2787,628.4204) .. (470.4432,626.3951) .. controls (467.6078,624.3697) and
+ (465.4052,622.0406) .. (463.8356,619.4077) .. controls (462.3166,616.7747) and
+ (461.5571,614.0658) .. (461.5571,611.2810) .. controls (461.5571,608.8000) and
+ (462.1647,606.5721) .. (463.3799,604.5974) .. controls (464.5951,602.5721) and
+ (466.5445,601.5594) .. (469.2280,601.5594) .. controls (472.2660,601.5594) and
+ (474.5699,602.3189) .. (476.1395,603.8379) .. controls (477.7598,605.3063) and
+ (478.5699,607.0025) .. (478.5699,608.9266) .. controls (478.5699,610.5468) and
+ (478.2914,612.5975) .. (477.7345,615.0785) .. controls (477.2281,617.5596) and
+ (476.8230,619.3317) .. (476.5193,620.3950) .. controls (476.8737,620.8507) and
+ (477.5066,621.4583) .. (478.4180,622.2178) .. controls (479.3800,622.9773) and
+ (480.5446,623.6609) .. (481.9117,624.2685) .. controls (483.5320,625.0280) and
+ (485.2282,625.6356) .. (487.0004,626.0913) .. controls (488.7725,626.5470) and
+ (491.2789,626.7748) .. (494.5195,626.7749) .. controls (497.5574,626.7748) and
+ (500.4689,626.2685) .. (503.2537,625.2559) .. controls (506.0892,624.2432) and
+ (508.5702,622.6229) .. (510.6969,620.3950) .. controls (512.9247,618.0659) and
+ (514.6462,615.3317) .. (515.8615,612.1924) .. controls (517.1272,609.1038) and
+ (517.7602,605.0278) .. (517.7602,599.9644) .. controls (517.7602,597.2302) and
+ (517.4564,594.5213) .. (516.8488,591.8378) .. controls (516.2918,589.1542) and
+ (515.2285,586.8251) .. (513.6589,584.8503) .. controls (512.0892,582.8757) and
+ (509.9373,581.3566) .. (507.2031,580.2933) .. controls (504.5195,579.1794) and
+ (501.1018,578.6224) .. (496.9499,578.6224) -- (487.0004,578.6224) --
+ (487.0004,569.8881) -- (493.6081,569.8881) .. controls (500.5954,569.8882) and
+ (505.6335,567.9641) .. (508.7221,564.1159) .. controls (511.8107,560.2678) and
+ (513.3551,554.4703) .. (513.3551,546.7233) .. controls (513.3551,540.3942) and
+ (511.8614,535.6347) .. (508.8740,532.4447) .. controls (505.8866,529.2042) and
+ (501.6587,527.5840) .. (496.1904,527.5839) .. controls (493.5574,527.5840) and
+ (491.3801,527.9131) .. (489.6586,528.5712) .. controls (487.9877,529.1789) and
+ (486.6206,529.7865) .. (485.5573,530.3940) .. controls (484.2915,531.1030) and
+ (483.2535,531.8878) .. (482.4434,532.7485) .. controls (481.6332,533.6093) and
+ (481.0256,534.2423) .. (480.6206,534.6472) .. controls (480.9750,536.2676) and
+ (481.4054,538.2676) .. (481.9117,540.6473) .. controls (482.4180,542.9765) and
+ (482.6712,545.2044) .. (482.6712,547.3309) .. controls (482.6712,549.2044) and
+ (481.8611,550.9006) .. (480.2408,552.4196) .. controls (478.6712,553.8880) and
+ (476.3420,554.6222) .. (473.2534,554.6222) .. controls (470.5698,554.6222) and
+ (468.5951,553.6602) .. (467.3293,551.7360) .. controls (466.1141,549.8120) and
+ (465.5065,547.5842) .. (465.5065,545.0524) .. controls (465.5065,542.4195) and
+ (466.2407,539.7360) .. (467.7090,537.0017) .. controls (469.1774,534.2676) and
+ (471.3546,531.7106) .. (474.2408,529.3307) .. controls (477.1268,526.9510) and
+ (480.6459,525.0270) .. (484.7978,523.5585) .. controls (488.9497,522.0902) and
+ (493.7346,521.3560) .. (499.1524,521.3560) .. controls (505.3803,521.3560) and
+ (510.5196,522.2928) .. (514.5703,524.1661) .. controls (518.6716,525.9890) and
+ (521.8868,528.2169) .. (524.2160,530.8497) .. controls (526.4944,533.4321) and
+ (528.0641,536.1410) .. (528.9249,538.9764) .. controls (529.7856,541.8119) and
+ (530.2160,544.2424) .. (530.2161,546.2676) .. controls (530.2160,548.7994) and
+ (529.8615,551.3563) .. (529.1527,553.9386) .. controls (528.4438,556.4703) and
+ (527.2792,558.8248) .. (525.6590,561.0020) .. controls (523.8868,563.3818) and
+ (521.5323,565.5590) .. (518.5957,567.5337) .. controls (515.7095,569.4578) and
+ (512.0639,571.0274) .. (507.6588,572.2426) -- (507.6588,573.4578) .. controls
+ (510.4943,573.7110) and (513.5070,574.3439) .. (516.6969,575.3566) .. controls
+ (519.9374,576.3693) and (522.8488,577.8629) .. (525.4312,579.8376) .. controls
+ (528.1147,581.9136) and (530.3172,584.5972) .. (532.0389,587.8883) .. controls
+ (533.7603,591.1289) and (534.6211,595.2555) .. (534.6212,600.2682) .. controls
+ (534.6211,609.9898) and (530.7223,617.8633) .. (522.9248,623.8887) .. controls
+ (515.1272,629.9647) and (505.3803,633.0027) .. (493.6840,633.0028);
+ \path[fill=maincolor] (239.7096,586.3724) .. controls (236.5703,592.3472) and
+ (232.3677,597.1067) .. (227.1019,600.6510) .. controls (221.8359,604.1954) and
+ (215.7852,605.9676) .. (208.9498,605.9676) .. controls (202.9244,605.9676) and
+ (197.6332,604.9043) .. (193.0762,602.7777) .. controls (188.5698,600.6510) and
+ (184.8482,597.7649) .. (181.9115,594.1193) .. controls (178.9748,590.4737) and
+ (176.7722,586.2205) .. (175.3038,581.3597) .. controls (173.8861,576.4989) and
+ (173.1772,571.3596) .. (173.1772,565.9418) .. controls (173.1772,561.0810) and
+ (173.9367,556.3215) .. (175.4557,551.6632) .. controls (177.0254,546.9543) and
+ (179.3039,542.7264) .. (182.2913,538.9795) .. controls (185.1773,535.3846) and
+ (188.7470,532.4985) .. (193.0002,530.3212) .. controls (197.3040,528.0934) and
+ (202.1142,526.9794) .. (207.4308,526.9794) .. controls (212.8991,526.9794) and
+ (217.5827,527.8402) .. (221.4816,529.5617) .. controls (225.3803,531.2833) and
+ (228.5449,533.6377) .. (230.9753,536.6250) .. controls (233.3044,539.4606) and
+ (235.0259,542.8024) .. (236.1399,546.6505) .. controls (237.3045,550.4987) and
+ (237.8867,554.6759) .. (237.8868,559.1822) -- (237.8868,564.1950) --
+ (188.2913,564.1950) .. controls (188.2913,569.1064) and (188.7470,573.5622) ..
+ (189.6584,577.5622) .. controls (190.6204,581.5623) and (192.1141,585.0813) ..
+ (194.1395,588.1193) .. controls (196.1142,591.0560) and (198.6965,593.3852) ..
+ (201.8864,595.1067) .. controls (205.0763,596.7776) and (208.8738,597.6131) ..
+ (213.2789,597.6130) .. controls (217.7853,597.6131) and (221.6587,596.5751) ..
+ (224.8993,594.4991) .. controls (228.1904,592.3725) and (231.3550,588.5750) ..
+ (234.3931,583.1066) -- (239.7096,586.3724)(222.9246,557.8151) .. controls
+ (222.9246,554.9797) and (222.6714,551.9417) .. (222.1651,548.7011) .. controls
+ (221.7094,545.4606) and (220.9499,542.7517) .. (219.8866,540.5744) .. controls
+ (218.7220,538.2454) and (217.1270,536.3466) .. (215.1017,534.8782) .. controls
+ (213.0764,533.4099) and (210.4687,532.6757) .. (207.2789,532.6756) .. controls
+ (201.9623,532.6757) and (197.5319,534.9036) .. (193.9876,539.3592) .. controls
+ (190.4939,543.7644) and (188.5951,549.9164) .. (188.2913,557.8151) --
+ (222.9246,557.8151);
+ \path[fill=maincolor] (320.6725,603.9929) -- (286.3430,603.9929) --
+ (286.3430,598.9801) .. controls (288.6215,598.7270) and (290.5456,598.4738) ..
+ (292.1152,598.2206) .. controls (293.7355,597.9675) and (294.5456,597.3093) ..
+ (294.5457,596.2459) .. controls (294.5456,595.7902) and (294.2924,595.0814) ..
+ (293.7862,594.1193) .. controls (293.2798,593.1573) and (292.7734,592.2712) ..
+ (292.2672,591.4611) .. controls (291.0013,589.4864) and (289.2544,586.8535) ..
+ (287.0266,583.5623) .. controls (284.8493,580.2205) and (282.1657,576.2457) ..
+ (278.9759,571.6381) .. controls (276.3429,574.9293) and (273.9125,578.1445) ..
+ (271.6846,581.2838) .. controls (269.4567,584.3724) and (267.1276,587.6636) ..
+ (264.6972,591.1573) .. controls (264.3428,591.6636) and (263.9630,592.2965) ..
+ (263.5580,593.0560) .. controls (263.2035,593.8155) and (263.0263,594.4231) ..
+ (263.0263,594.8788) .. controls (263.0263,595.9928) and (263.7352,596.9295) ..
+ (265.1529,597.6890) .. controls (266.6213,598.3979) and (268.7985,598.8789) ..
+ (271.6846,599.1321) -- (271.6846,603.9929) -- (243.9628,603.9929) --
+ (243.9628,599.2840) .. controls (247.4059,598.2713) and (250.1654,597.0561) ..
+ (252.2414,595.6383) .. controls (254.3680,594.1700) and (256.3174,592.3725) ..
+ (258.0896,590.2459) .. controls (259.6592,588.2206) and (261.8364,585.4357) ..
+ (264.6213,581.8914) .. controls (267.4567,578.3470) and (271.1023,573.5369) ..
+ (275.5581,567.4608) .. controls (272.9758,563.7646) and (269.8112,559.2582) ..
+ (266.0643,553.9417) .. controls (262.3681,548.5746) and (259.3047,544.1188) ..
+ (256.8744,540.5744) .. controls (255.5579,538.6504) and (254.0389,537.1061) ..
+ (252.3173,535.9415) .. controls (250.6464,534.7263) and (248.2667,534.1187) ..
+ (245.1780,534.1187) -- (245.1780,529.1060) -- (277.7607,529.1060) --
+ (277.7607,534.1187) .. controls (276.1404,534.1187) and (274.5454,534.2200) ..
+ (272.9758,534.4225) .. controls (271.4061,534.6251) and (270.6213,535.0808) ..
+ (270.6213,535.7896) .. controls (270.6213,536.2453) and (270.7732,536.7517) ..
+ (271.0770,537.3086) .. controls (271.3808,537.8656) and (271.7606,538.4732) ..
+ (272.2163,539.1314) .. controls (273.3302,540.8530) and (275.0517,543.5112) ..
+ (277.3809,547.1062) .. controls (279.7606,550.7012) and (282.3176,554.4987) ..
+ (285.0519,558.4987) .. controls (286.8240,556.0683) and (288.8747,553.2329) ..
+ (291.2038,549.9923) .. controls (293.5836,546.7518) and (295.7608,543.6631) ..
+ (297.7356,540.7263) .. controls (297.9380,540.3720) and (298.1406,539.9416) ..
+ (298.3432,539.4352) .. controls (298.5456,538.8783) and (298.6469,538.3466) ..
+ (298.6470,537.8402) .. controls (298.6469,536.7770) and (297.7608,535.9415) ..
+ (295.9887,535.3339) .. controls (294.2671,534.6757) and (292.6215,534.2200) ..
+ (291.0519,533.9668) -- (291.0519,529.1060) -- (318.6978,529.1060) --
+ (318.6978,533.8149) .. controls (314.7990,534.8276) and (311.7610,536.0428) ..
+ (309.5838,537.4605) .. controls (307.4065,538.8277) and (305.5584,540.4226) ..
+ (304.0394,542.2453) .. controls (302.3685,544.3214) and (300.3684,546.8784) ..
+ (298.0394,549.9163) .. controls (295.7608,552.9544) and (292.5456,557.2076) ..
+ (288.3937,562.6760) .. controls (293.0013,569.1571) and (296.7482,574.4736) ..
+ (299.6343,578.6255) .. controls (302.5204,582.7775) and (305.4318,587.0560) ..
+ (308.3686,591.4611) .. controls (309.8369,593.6890) and (311.4572,595.4611) ..
+ (313.2294,596.7776) .. controls (315.0521,598.0434) and (317.5332,598.7776) ..
+ (320.6725,598.9801) -- (320.6725,603.9929);
+ \path[fill=maincolor] (395.6354,537.9921) .. controls (398.6227,541.5365) and
+ (400.9265,545.6632) .. (402.5468,550.3720) .. controls (404.2177,555.0303) and
+ (405.0531,560.3722) .. (405.0532,566.3975) .. controls (405.0531,572.3216) and
+ (404.0658,577.7394) .. (402.0911,582.6508) .. controls (400.1164,587.5117) and
+ (397.5087,591.6636) .. (394.2683,595.1067) .. controls (390.8758,598.6004) and
+ (387.1036,601.2840) .. (382.9517,603.1574) .. controls (378.7997,605.0308) and
+ (374.3946,605.9676) .. (369.7363,605.9676) .. controls (365.2299,605.9676) and
+ (361.5337,605.4612) .. (358.6476,604.4486) .. controls (355.7615,603.4359) and
+ (353.4070,602.2460) .. (351.5843,600.8789) -- (350.9766,600.8789) --
+ (350.9766,623.3602) .. controls (350.9766,625.1829) and (351.3311,626.7019) ..
+ (352.0400,627.9172) .. controls (352.7488,629.1323) and (353.8374,630.0184) ..
+ (355.3058,630.5754) .. controls (356.5210,631.0311) and (358.2425,631.4615) ..
+ (360.4704,631.8666) .. controls (362.7489,632.3223) and (364.6223,632.5754) ..
+ (366.0907,632.6261) -- (366.0907,637.7147) -- (324.2422,637.7147) --
+ (324.2422,632.6261) .. controls (325.8118,632.5248) and (327.3308,632.3982) ..
+ (328.7992,632.2463) .. controls (330.2676,632.0944) and (331.5840,631.8412) ..
+ (332.7486,631.4868) .. controls (334.2676,630.9805) and (335.3309,630.0944) ..
+ (335.9385,628.8286) .. controls (336.5461,627.5627) and (336.8499,626.0690) ..
+ (336.8499,624.3475) -- (336.8499,544.6758) .. controls (336.8499,543.1568) and
+ (336.4701,541.6884) .. (335.7107,540.2706) .. controls (334.9511,538.8530) and
+ (333.9638,537.7137) .. (332.7486,536.8529) .. controls (331.8372,536.2453) and
+ (330.5714,535.7896) .. (328.9511,535.4858) .. controls (327.3308,535.1314) and
+ (325.7612,534.9036) .. (324.2422,534.8022) -- (324.2422,529.8655) --
+ (349.7614,528.1945) -- (350.8247,529.1060) -- (350.8247,537.5364) --
+ (351.4323,537.6883) .. controls (354.0653,534.6504) and (357.3311,532.1187) ..
+ (361.2299,530.0933) .. controls (365.1287,528.0680) and (369.0021,527.0554) ..
+ (372.8503,527.0553) .. controls (377.5085,527.0554) and (381.7617,528.0427) ..
+ (385.6099,530.0174) .. controls (389.5087,531.9921) and (392.8505,534.6504) ..
+ (395.6354,537.9921)(383.2555,591.8408) .. controls (385.2808,588.7016) and
+ (386.7744,585.0560) .. (387.7365,580.9040) .. controls (388.6985,576.7521) and
+ (389.1795,572.1191) .. (389.1796,567.0051) .. controls (389.1795,563.2583) and
+ (388.7745,559.4354) .. (387.9644,555.5366) .. controls (387.1542,551.6379) and
+ (385.8884,548.2455) .. (384.1669,545.3593) .. controls (382.4453,542.4226) and
+ (380.1921,540.0428) .. (377.4073,538.2200) .. controls (374.6224,536.3972) and
+ (371.2806,535.4858) .. (367.3819,535.4858) .. controls (363.6350,535.4858) and
+ (360.4451,536.3213) .. (357.8122,537.9921) .. controls (355.1792,539.6125) and
+ (352.9007,541.5618) .. (350.9766,543.8403) -- (350.9766,588.1193) .. controls
+ (351.4323,589.4864) and (352.1665,590.9041) .. (353.1792,592.3725) .. controls
+ (354.2425,593.8409) and (355.4070,595.1067) .. (356.6729,596.1700) .. controls
+ (358.2425,597.4358) and (359.9387,598.4738) .. (361.7616,599.2839) .. controls
+ (363.5843,600.0434) and (365.8122,600.4232) .. (368.4452,600.4232) .. controls
+ (371.2806,600.4232) and (374.0148,599.6890) .. (376.6478,598.2206) .. controls
+ (379.2807,596.7016) and (381.4833,594.5750) .. (383.2555,591.8408);
+ \path[fill=basecolor] (451.3069,603.9929) -- (414.7748,603.9929) --
+ (414.7748,598.9801) .. controls (415.9394,598.8789) and (417.1799,598.7776) ..
+ (418.4964,598.6763) .. controls (419.8128,598.5245) and (420.9015,598.3219) ..
+ (421.7622,598.0687) .. controls (423.1800,597.6130) and (424.2433,596.8282) ..
+ (424.9521,595.7143) .. controls (425.6610,594.5497) and (426.0154,593.0560) ..
+ (426.0155,591.2332) -- (426.0155,504.4981) .. controls (426.0154,502.6754) and
+ (425.6104,500.9792) .. (424.8002,499.4094) .. controls (424.0407,497.7893) and
+ (423.0281,496.5234) .. (421.7622,495.6119) .. controls (420.8508,495.0044) and
+ (419.1799,494.4475) .. (416.7495,493.9410) .. controls (414.3191,493.4348) and
+ (412.3697,493.1310) .. (410.9014,493.0296) -- (410.9014,488.1688) --
+ (439.0789,486.4219) -- (440.1422,487.5612) -- (440.1422,590.3218) .. controls
+ (440.1422,592.0940) and (440.4713,593.5624) .. (441.1295,594.7269) .. controls
+ (441.7877,595.8409) and (442.8510,596.7016) .. (444.3195,597.3092) .. controls
+ (445.4334,597.8156) and (446.5220,598.1953) .. (447.5853,598.4485) .. controls
+ (448.6992,598.7017) and (449.9397,598.8789) .. (451.3069,598.9801) --
+ (451.3069,603.9929);
+ \path[fill=basecolor] (492.3959,631.7147) .. controls (487.6364,631.7147) and
+ (483.2819,631.1071) .. (479.3325,629.8919) .. controls (475.3831,628.7273) and
+ (471.9906,627.1323) .. (469.1552,625.1070) .. controls (466.3197,623.0817) and
+ (464.1171,620.7525) .. (462.5475,618.1196) .. controls (461.0285,615.4866) and
+ (460.2690,612.7778) .. (460.2690,609.9929) .. controls (460.2690,607.5119) and
+ (460.8766,605.2840) .. (462.0918,603.3093) .. controls (463.3070,601.2840) and
+ (465.2564,600.2713) .. (467.9400,600.2713) .. controls (470.9780,600.2713) and
+ (473.2818,601.0308) .. (474.8514,602.5498) .. controls (476.4717,604.0182) and
+ (477.2818,605.7144) .. (477.2819,607.6385) .. controls (477.2818,609.2587) and
+ (477.0033,611.3094) .. (476.4464,613.7904) .. controls (475.9400,616.2715) and
+ (475.5350,618.0436) .. (475.2312,619.1069) .. controls (475.5856,619.5626) and
+ (476.2185,620.1702) .. (477.1300,620.9298) .. controls (478.0920,621.6892) and
+ (479.2565,622.3728) .. (480.6237,622.9804) .. controls (482.2439,623.7399) and
+ (483.9401,624.3475) .. (485.7123,624.8032) .. controls (487.4845,625.2589) and
+ (489.9908,625.4868) .. (493.2314,625.4868) .. controls (496.2694,625.4868) and
+ (499.1808,624.9804) .. (501.9657,623.9678) .. controls (504.8011,622.9551) and
+ (507.2821,621.3348) .. (509.4088,619.1069) .. controls (511.6366,616.7778) and
+ (513.3581,614.0436) .. (514.5734,610.9043) .. controls (515.8392,607.8157) and
+ (516.4721,603.7397) .. (516.4721,598.6763) .. controls (516.4721,595.9421) and
+ (516.1683,593.2333) .. (515.5607,590.5497) .. controls (515.0037,587.8661) and
+ (513.9404,585.5370) .. (512.3708,583.5622) .. controls (510.8011,581.5876) and
+ (508.6492,580.0686) .. (505.9151,579.0052) .. controls (503.2314,577.8913) and
+ (499.8137,577.3344) .. (495.6618,577.3343) -- (485.7123,577.3343) --
+ (485.7123,568.6001) -- (492.3200,568.6001) .. controls (499.3074,568.6001) and
+ (504.3454,566.6760) .. (507.4341,562.8279) .. controls (510.5227,558.9797) and
+ (512.0670,553.1822) .. (512.0670,545.4352) .. controls (512.0670,539.1061) and
+ (510.5733,534.3466) .. (507.5860,531.1566) .. controls (504.5985,527.9162) and
+ (500.3707,526.2959) .. (494.9023,526.2958) .. controls (492.2693,526.2959) and
+ (490.0921,526.6250) .. (488.3706,527.2831) .. controls (486.6996,527.8908) and
+ (485.3325,528.4984) .. (484.2693,529.1060) .. controls (483.0034,529.8149) and
+ (481.9654,530.5997) .. (481.1553,531.4604) .. controls (480.3451,532.3213) and
+ (479.7375,532.9542) .. (479.3325,533.3592) .. controls (479.6869,534.9795) and
+ (480.1173,536.9795) .. (480.6237,539.3592) .. controls (481.1300,541.6884) and
+ (481.3831,543.9163) .. (481.3832,546.0429) .. controls (481.3831,547.9164) and
+ (480.5730,549.6126) .. (478.9527,551.1315) .. controls (477.3831,552.5999) and
+ (475.0539,553.3341) .. (471.9653,553.3341) .. controls (469.2817,553.3341) and
+ (467.3070,552.3721) .. (466.0412,550.4480) .. controls (464.8260,548.5240) and
+ (464.2184,546.2961) .. (464.2184,543.7643) .. controls (464.2184,541.1315) and
+ (464.9526,538.4479) .. (466.4210,535.7136) .. controls (467.8893,532.9795) and
+ (470.0666,530.4225) .. (472.9527,528.0426) .. controls (475.8388,525.6630) and
+ (479.3578,523.7389) .. (483.5098,522.2704) .. controls (487.6617,520.8021) and
+ (492.4465,520.0680) .. (497.8643,520.0679) .. controls (504.0922,520.0680) and
+ (509.2315,521.0047) .. (513.2822,522.8780) .. controls (517.3835,524.7009) and
+ (520.5987,526.9288) .. (522.9279,529.5617) .. controls (525.2064,532.1440) and
+ (526.7760,534.8529) .. (527.6368,537.6883) .. controls (528.4975,540.5239) and
+ (528.9279,542.9543) .. (528.9280,544.9795) .. controls (528.9279,547.5113) and
+ (528.5735,550.0683) .. (527.8647,552.6505) .. controls (527.1557,555.1822) and
+ (525.9912,557.5367) .. (524.3710,559.7139) .. controls (522.5987,562.0937) and
+ (520.2443,564.2709) .. (517.3076,566.2456) .. controls (514.4214,568.1697) and
+ (510.7758,569.7393) .. (506.3708,570.9545) -- (506.3708,572.1697) .. controls
+ (509.2062,572.4229) and (512.2189,573.0558) .. (515.4088,574.0685) .. controls
+ (518.6493,575.0812) and (521.5607,576.5749) .. (524.1431,578.5495) .. controls
+ (526.8266,580.6255) and (529.0292,583.3091) .. (530.7508,586.6003) .. controls
+ (532.4722,589.8408) and (533.3330,593.9674) .. (533.3331,598.9801) .. controls
+ (533.3330,608.7018) and (529.4342,616.5753) .. (521.6368,622.6007) .. controls
+ (513.8391,628.6766) and (504.0922,631.7147) .. (492.3959,631.7147);
+\end{tikzpicture}
+}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\publicationmonth{March}
+\publicationyear{2014}
+\publicationissue{9}
+
+\usepackage{multicol}
+\usepackage{xparse}
+\newcommand\ctanpkg[1]{\href{http://ctan.org/pkg/#1}{\pkg{#1}}}
+
+\begin{document}
+\maketitle
+
+\raisefirstsection
+
+\tableofcontents % could drop this and raise first section instead
+
+\section{Hiatus?}
+
+Well, it's been a busy couple of years.
+Work has slowed on the \LaTeX3 codebase as all active members of the team have been\Dash shall we say\Dash busily occupied with more pressing concerns in their day-to-day activities.
+
+Nonetheless, Joseph and Bruno have continued to fine-tune the \LaTeX3 kernel and add-on packages.
+Browsing through the commit history shows bug fixes and improvements to documentation, test files, and internal code across the entire breadth of the codebase.
+
+Members of the team have presented at two TUG conferences since the last \LaTeX3 news. (Has it really been so long?)
+In July 2012, Frank and Will travelled to Boston; Frank discussed the challenges faced in the past and continuing to the present day due to the limits of the various \TeX\ engines; and, Frank and Will together covered a brief history and recent developments of the \pkg{expl3} code.
+
+In 2013, Joseph and Frank wrote a talk on complex layouts, and the "layers" ideas discussed in \LaTeX3; Frank went to Tokyo in October to present the work.
+Slides of and recordings from these talks are available on the \LaTeX3 website.
+
+These conferences are good opportunities to introduce the \pkg{expl3} language to a wider group of people; in many cases, explaining the rationale behind why \pkg{expl3} looks a little strange at first helps to convince the audience that it's not so weird after all.
+In our experience, anyone that's been exposed to some of the more awkward expansion aspects of \TeX\ programming appreciates how \pkg{expl3} makes life much easier for us.
+
+\section{\pkg{expl3} in the community}
+
+While things have been slightly quieter for the team, more and more people are adopting \pkg{expl3} for their own use.
+A search on the \TeX\ Stack Exchange website for either "\texttt{expl3}" or "\texttt{latex3}" at time of writing yield around one thousand results each.
+
+In order to help standardise the prefixes used in \pkg{expl3} modules, we have developed a registration procedure for package authors (which amounts to little more than notifying us that their package uses a specific prefix, which will often be the name of the package itself).
+Please contact us via the \texttt{latex-l} mailing list to register your module prefixes and package names; we ask that you avoid using package names that begin with \texttt{l3...}\ since \pkg{expl3} packages use this internally.
+Some authors have started using the package prefix \texttt{lt3...}\ as a way of indicating their package builds on \pkg{expl3} in some way but is not maintained by the \LaTeX3 team.
+
+In the prefix database at present, some thirty package prefixes are registered by fifteen separate individuals (unrelated to The \LaTeX{} Project\Dash the number of course grows if you include packages by members of the team).
+These packages cover a broad range of functionality:
+\begin{description}
+\item[\ctanpkg{acro}] Interface for creating (classes of) acronyms
+\item[\ctanpkg{hobby}]
+Hobby's algorithm in PGF/TiKZ for drawing optimally smooth curves.
+\item[\ctanpkg{chemmacros}] Typesetting in the field of chemistry.
+\item[\ctanpkg{classics}] Traditional-style citations for the classics.
+\item[\ctanpkg{conteq}] Continued (in)equalities in mathematics.
+\item[\ctanpkg{ctex}] A collection of macro packages and document classes for Chinese typesetting.
+\item[\ctanpkg{endiagram}] Draw potential energy curve diagrams.
+\item[\ctanpkg{enotez}] Support for end-notes.
+\item[\ctanpkg{exsheets}] Question sheets and exams with metadata.
+%(Note to self: remember to use this for teaching this semester!)
+\item[\ctanpkg{lt3graph}] A graph data structure.
+\item[\ctanpkg{newlfm}] The venerable class for memos and letters.
+\item[\ctanpkg{fnpct}] Interaction between footnotes and punctuation.
+\item[\ctanpkg{GS1}] Barcodes and so forth.
+\item[\ctanpkg{hobete}] Beamer theme for the Univ.\ of Hohenheim.
+\item[\ctanpkg{kantlipsum}] Generate sentences in Kant's style.
+\item[\ctanpkg{lualatex-math}] Extended support for mathematics in \LuaLaTeX.
+\item[\ctanpkg{media9}] Multimedia inclusion for Adobe Reader.
+\item[\ctanpkg{pkgloader}] Managing the options and loading order of other packages.
+\item[\ctanpkg{substances}] Lists of chemicals, etc., in a document.
+\item[\ctanpkg{withargs}] Ephemeral macro use.
+\item[\ctanpkg{xecjk}] Support for CJK documents in \XeLaTeX.
+\item[\ctanpkg{xpatch}, \ctanpkg{regexpatch}] Patch command definitions.
+\item[\ctanpkg{xpeek}] Commands that peek ahead in the input stream.
+\item[\ctanpkg{xpinjin}] Automatically add pinyin to Chinese characters
+\item[\ctanpkg{zhnumber}] Typeset Chinese representations of numbers
+\item[\ctanpkg{zxjatype}] Standards-conforming typesetting of Japanese for \XeLaTeX.
+\end{description}
+Some of these packages are marked by their authors as experimental, but it is clear that these packages have been developed to solve specific needs for typesetting and document production.
+
+The \pkg{expl3} language has well and truly gained traction after many years of waiting patiently.
+
+\section[Logo for the \LaTeX3 Programming Language]
+ {A logo for the \LaTeX3 Programming Language}
+
+To show that \pkg{expl3} is ready for general use Paulo Cereda drew up a nice logo for us, showing a \mbox{hummingbird} (agile and fast\Dash but needs huge amounts of energy) picking at "l3". Big thanks to Paulo!
+\begin{center}
+\explogo
+\end{center}
+
+\section{Recent activity}
+
+\LaTeX3 work has only slowed, not ground to a halt.
+While changes have tended to be minor in recent times, there are a number of improvements worth discussing explicitly.
+\begin{enumerate}
+\item
+Bruno has extended the floating point code to cover additional functions such as inverse trigonometric functions.
+These additions round out the functionality well and make it viable for use in most cases needing floating point mathematics.
+\item
+Joseph's refinement of the experimental galley code now allows separation of paragraph shapes from margins/cutouts.
+This still needs some testing!
+\item
+For some time now \pkg{expl3} has provided "native" drivers although they have not been selected by default in most cases.
+These have been revised to improve robustness, which makes them probably ready to enable by default.
+The improvements made to the drivers have also fed back to more "general" \LaTeX\ code.
+\end{enumerate}
+
+
+\section{Work in progress}
+
+We're still actively discussing a variety of areas to tackle next.
+We are aware of various "odds and ends" in \pkg{expl3} that still need sorting out.
+In particular, some experimental functions have been working quite well and it's time to assess moving them into the "stable" modules, in particular the \pkg{l3str} module for dealing with catcode-twelve token lists more commonly known in \pkg{expl3} as \emph{strings}.
+
+Areas of active discussion including issues around uppercasing and lowercasing (and the esoteric ways that this can be achieved in \TeX) and space skipping (or not) in commands and environments with optional arguments.
+These two issues are discussed next.
+
+\subsection{Uppercasing and lowercasing}
+
+The commands \verb"\tl_to_lowercase:n" and \verb"\tl_to_uppercase:n" have long been overdue for a good hard look.
+From a traditional \TeX\ viewpoint, these commands are simply the primitive \verb"\lowercase" and \verb"\uppercase", and in practice it's well known that there are various limitations and peculiarities associated with them.
+We know these commands are good, to one extent or another, for three things:
+\begin{enumerate}
+\item
+Uppercasing text for typesetting purposes such as all-uppercase titles.
+\item
+Lowercasing text for normalisation in sorting and other applications such as filename comparisons.
+\item
+Achieving special effects, in concert with manipulating \verb"\uccode" and the like, such as defining commands that contain characters with different catcodes than usual.
+\end{enumerate}
+We are working on providing a set of commands to achieve all three of these functions in a more direct and easy-to-use fashion, including support for Unicode in \LuaLaTeX\ and \XeLaTeX.
+
+\pagebreak
+
+\subsection{Space-skipping in \pkg{xparse}}
+
+We have also re-considered the behaviour of space-skipping in \pkg{xparse}.
+Consider the following examples:
+\begin{verbatim}
+\begin{dmath} \begin{dmath}[label=foo]
+[x y z] = [1 2 3] x^2 + y^2 = z^2
+\end{dmath} \end{dmath}
+\end{verbatim}
+In the first case, we are typesetting some mathematics that contains square brackets.
+In the second, we are assigning a label to the equation using an optional argument, which also uses brackets.
+The fact that both work correctly is due to behaviour that is specifically programmed into the workings of the \texttt{dmath} environment of \pkg{breqn}: spaces before an optional argument are explicitly forbidden.
+At present, this is also how commands and environments defined using \pkg{xparse} behave.
+But consider a \pkg{pgfplots} environment:
+\begin{verbatim}
+\begin{pgfplot}
+ [
+ % plot options
+ ]
+ \begin{axis}
+ [
+ % axis options
+ ]
+ ...
+ \end{axis}
+\end{pgfplot}
+\end{verbatim}
+This would seem like quite a natural way to write such environments, but with the current state of \pkg{xparse} this syntax would be incorrect. One would have to write either of these instead:
+\begin{multicols}{2}
+\begin{verbatim}
+\begin{pgfplot}%
+ [
+ % plot options
+ ]
+\end{verbatim}
+\begin{verbatim}
+\begin{pgfplot}[
+ % plot options
+ ]
+\end{verbatim}
+\end{multicols}
+Is this an acceptable compromise?
+We're not entirely sure here\Dash we're in a corner because the humble \texttt{[} has ended up being part of both the syntax and semantics of a \LaTeX\ document.
+
+%There is one further matter to consider and that is how "control symbols" such as \verb"\\" behave in such matters.
+%Because \TeX\ does not skip spaces after control symbols, neither does \pkg{xparse}; therefore control symbols are a solution if one desires a command to use in a mathematics context where an optional argument could cause issues.
+%An example for this from \pkg{amsmath} is line breaks in multi-line display equations:
+%\begin{verbatim}
+%\begin{align}
+%a &= b+c \\
+%[x y z] &= [1 2 3]
+%\end{align}
+%\end{verbatim}
+%In \pkg{amsmath} this requires specific measures to be taken to allow \verb"\\" to behave correctly; with \pkg{xparse} this functionality is built in.
+
+Despite the current design covering most regular use-cases, we have considered adding a further option to \pkg{xparse} to define the space-skipping behaviour as desired by a package author.
+But at this very moment we've rejected adding this additional complexity, because environments that change their parsing behaviour based on their intended use make a \LaTeX-based language more difficult to predict; one could imagine such behaviour causing difficulties down the road for automatic syntax checkers and so forth.
+However, we don't make such decisions in a vacuum and we're always happy to continue to discuss such matters.
+
+
+\section{\ldots and for 2014 onwards}
+
+There is one (understandable) misconception that shows up once in a while with people claiming that
+\[
+ \textrm{\pkg{expl3}} = \textrm{\LaTeX3}.
+\]
+However, the correct relation would be a subset,
+\[
+ \textrm{\pkg{expl3}} \subset \textrm{\LaTeX3},
+\]
+with \pkg{expl3} forming the Core Language Layer on which there will eventually be several other layers on top that \mbox{provide}
+\begin{itemize}
+\item higher-level concepts for typesetting (Typesetting Foundation Layer),
+\item a designer interface for specifying document structures and layouts (Designer Layer),
+\item and finally a Document Representation Layer that implements document level syntax.
+\end{itemize}
+Of those four layers, the lowest one\Dash\pkg{expl3}\Dash is available for use and with \pkg{xparse} we have an instance of the Document Representation Layer modeled largely after \LaTeXe{} syntax (there could be others). Both can be successfully used within the current \LaTeXe{} framework and as mentioned above this is increasingly happening.
+
+The middle layers, however, where the rubber meets the road, are still at the level of prototypes and ideas (templates, \pkg{ldb}, galley, \pkg{xor} and all the good stuff) that need to be revised and further developed to arrive at a \LaTeX3 environment that can stand on its own and that is to where we want to return in 2014.
+
+An overview on this can be found in the answer to "What can *I* do to help The \LaTeX{} Project?"\ on Stack Exchange,\footnote{\url{http://tex.stackexchange.com/questions/45838}}
+which is reproduced below in slightly abridged form.
+This is of course not the first time that we have discussed such matters, and you can find similar material in other publications such as those at \url{http://latex-project.org}; e.g., the architecture talk given at the TUG 2011 conference.
+
+\bigskip
+
+\begin{center}
+\explbanner
+\end{center}
+
+\newpage
+\section{What can you do for The \LaTeX{} Project?}
+\textbf{By Frank Mittelbach}
+
+My vision of \LaTeX3 is really a system with multiple layers that provide interfaces for different kinds of roles. These layers are
+\begin{itemize}
+\item the underlying engine (some \TeX\ variant)
+\item the programming layer (the core language, i.e., \pkg{expl3})
+\item the typesetting foundation layer (providing higher-level concepts for typesetting)
+\item the typesetting element layer (templates for all types of document elements)
+\item the designer interface foundation layer
+\item the class designer layer (where instances of document elements with specific settings are defined)
+\item document representation layer (that provides the input syntax, i.e., how the author uses elements)
+\end{itemize}
+If you look at it from the perspective of user roles then there are at least three or four roles that you can clearly distinguish:
+\begin{itemize}
+\item The Programmer (template and functionality provider)
+\item The Document Type Designer (defines which elements are available; abstract syntax and semantics)
+\item The Designer (typography and layout)
+\item The Author (content)
+\end{itemize}
+As a consequence The \LaTeX{} Project needs different kinds of help depending on what layer or role we are looking at.
+
+The "Author" is using, say, list structures by specifying something like \verb"\begin{itemize} \item" in his documents. Or perhaps by writing \Verb|<ul> ... </ul>| or whatever the UI representation offers to him.
+
+The "Document Type Designer" defines what kind of abstract document elements are available, and what attributes or arguments those elements provide at the author level. E.g., he may specify that a certain class of documents provides the display lists "enumerate", "itemize" and "description".
+
+The "Programmer" on the other hand implements templates (that offer customizations) for such document elements, e.g., for display lists. What kind of customization possibilities should be provided by the "Programmer" is the domain of the "Document Designer"; he drives what kind of flexibility he needs for the design. In most cases the "Document Designer" should be able to simply select templates (already written) from a template library and only focus on the design, i.e., instantiating the templates with values so that the desired layout for "itemize" lists, etc., is created.
+
+In real life a single person may end up playing more than one role, but it is important to recognise that all of them come with different requirements with respect to interfaces and functionality.
+
+\subsection{Programming Layer}
+
+The programming layer consists of a core language layer (called \pkg{expl3} (EXP erimental L aTeX 3) for historical reasons and now we are stuck with it \texttt{:-)}) and two more components: the "Typesetting Foundation Layer" that we are currently working on and the "Typesetting Element Layer" that is going to provide customizable objects for the design layer. While \pkg{expl3} is in many parts already fairly complete and usable the other two are under construction.
+
+Help is needed for the programming layer in
+\begin{itemize}
+\item helping by extending and completing the regression test suite for \pkg{expl3}
+\item helping with providing good or better documentation, including tutorials
+\item possibly helping in coding additional core functionality\Dash but that requires, in contrast to the first two points, a good amount of commitment and experience with the core language as otherwise the danger is too high that the final results will end up being inconsistent
+\end{itemize}
+Once we are a bit further along with the "Typesetting Foundation Layer" we would need help in
+providing higher-level functionality, perhaps rewriting existing packages/code for elements making use of extended possibilities.
+Two steps down the road (once the "\mbox{Designer} Layer" is closer to being finalized) we would need help with developing templates for all kinds of elements.
+
+In summary for this part, we need help from people interested in programming in \TeX\ and \pkg{expl3} and/or interested in providing documentation (but for this a thorough understanding of the programming concepts is necessary too).
+
+\subsection{Design Layer}
+
+The intention of the design layer is to provide interfaces that allow specifying layout and typography styles in a declarative way. On the implementation side there are a number of prototypes (most notably \pkg{xtemplate} and the recent reimplementation of \pkg{ldb}).
+These need to be unified into a common model which requires some more experimentation and probably also some further thoughts.
+
+But the real importance of this layer is not the implementation of its interfaces but the conceptual view of it: provisioning a rich declarative method (or methods) to describe design and layout. I.e., enabling a designer to think not in programs but in visual representations and relationships.
+
+So here is the big area where people who do not feel they can or want to program \TeX's bowels can help. What would be extremely helpful (and in fact not just for \LaTeX3) would be
+\begin{itemize}
+\item collecting and classifying a \emph{huge} set of layouts and designs
+\begin{itemize}[nosep]
+\item designs for individual document elements (such as headings, TOCs, etc)
+\item document designs that include relationships between document elements
+\end{itemize}
+\item thinking about good, declarative ways to specify such designs
+\begin{itemize}[nosep]
+\item what needs to be specified
+\item to what extent and with what flexibility
+\end{itemize}
+\end{itemize}
+I believe that this is a huge task (but rewarding in itself) and already the first part of collecting existing design specifications will be a major undertaking and will need coordination and probably a lot of work. But it will be a huge asset towards testing any implementations and interfaces for this layer later on.
+
+\subsection{Document Interface Layer}
+
+If we get the separation done correctly, then this layer should effectively offer nothing more than a front end for parsing the document syntax and transforming it into an internal standardised form. This means that on this layer one should not see any (or not much) coding or computation.
+
+It is envisioned that alternative document syntax models can be provided.
+At the moment we have a draft solution in \pkg{xparse}.
+This package offers a document syntax in the style of \LaTeXe, that is with \verb|*|-forms, optional arguments in brackets, etc., but with a few more bells and whistles such as a more generalized concept of default values, support for additional delimiters for arguments, verbatim-style arguments, and so on.
+It is fairly conventional though.
+In addition when it was written the clear separation of layers wasn't well-defined and so the package also contains components for conditional programming that I no longer think should be there.
+
+Bottom line on what is needed for this layer is to
+\begin{itemize}
+\item think about good syntax for providing document content from "the author" perspective
+\item think about good syntax for providing document content from an "application to typesetting" perspective, i.e., the syntax and structure for automated typesetting where the content is prepared by a system/application rather than by a human
+\end{itemize}
+These two areas most likely need strict structure (as automation works much better with structures that do not have a lot of alternative possibilities and shortcuts, etc.) and even when just looking at the human author a lot of open questions need answering.
+And these answers may or may not be to painfully stick with existing \LaTeXe\ conventions in all cases (or perhaps with any?).
+
+None of this requires coding or \pkg{expl3} experience. What it requires is familiarity with existing input concepts, a feel for where the pain points currently are and the willingness to think and discuss what alternatives and extensions could look like.
+
+\subsection{In Summary}
+
+Basically help is possible on any level and it doesn't need to involve programming. Thoughts are sprinkled throughout this article, but here are a few more highlights:
+\begin{itemize}
+\item help with developing/improving the core programming layer by
+\begin{itemize}[nosep]
+\item joining the effort to improve the test suite
+\item help improving the existing (or not existing) documentation
+\item joining the effort to produce core or auxiliary code modules
+\end{itemize}
+\item help on the design layer by
+\begin{itemize}[nosep]
+\item collecting and classifying design tasks
+\item thinking and suggesting ways to describe layout requirements in a declarative manner
+\end{itemize}
+\item help on shaping the document interface layer
+\end{itemize}
+These concepts, as well as their implementation, are under discussion on the list \texttt{latex-l}.\footnote{Instructions for joining and browsing archives at:\\ \url{http://latex-project.org/code.html}}
+The list has only a fairly low level of traffic right now as actual implementation and development tasks are typically discussed directly among the few active implementors. But this might change if more people join.
+
+\subsection{And something else \dots}
+
+The people on the \LaTeX3 team are also committed to keeping \LaTeXe\ stable and even while there isn't that much to do these days there remains the need to resolve bug reports (if they concern the 2e core), provide new distributions once in a while, etc. All this is work that takes effort or remains undone or incomplete. Thus here too, it helps the \LaTeX3 efforts if we get help to free up resources.
+
+\end{document}
+
+
+
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news10.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news10.pdf
new file mode 100644
index 00000000000..f33607fe584
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news10.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news10.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news10.tex
new file mode 100644
index 00000000000..88291e848fa
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news10.tex
@@ -0,0 +1,220 @@
+% Copyright 2016 The LaTeX3 Project
+\documentclass{ltnews}
+
+\PassOptionsToPackage{colorlinks}{hyperref}
+
+\usepackage{csquotes}
+\usepackage{hologo}
+\usepackage{ragged2e}
+\usepackage{underscore}
+
+\AtBeginDocument{%
+ \renewcommand*{\LaTeXNews}{\LaTeX3~News}%
+ \RaggedRight
+ \setlength\parindent{1em}%
+}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\publicationmonth{November}
+\publicationyear{2016}
+\publicationissue{10}
+
+% Avoid hyphenation of csnames
+\makeatletter
+\protected\edef\cs#1{%
+ \noexpand\path{\@backslashchar#1}%
+}
+\makeatother
+
+\begin{document}
+
+\maketitle
+
+There has been something of a gap since the last \LaTeX3 News, but this does
+not mean that work has not been going on. The Team have been working on a
+number of areas, many of which reflect wider take-up of \pkg{expl3}. There have
+also been a number of significant new developments in the \LaTeX3
+\enquote{sphere} in the last two years.
+
+\section{\pkg{l3build}: Testing \LaTeX{} packages}
+
+Testing has been an important part of the work of the team since they assumed
+maintenance of \LaTeX{} over twenty years ago. Various scripts have been used
+over that time by the team for testing, but these have until recently not been
+set up for wider use.
+
+With the general availability of \hologo{LuaTeX} it is now possible to be sure
+that every \TeX{} user has a powerful general scripting language available:
+Lua. The team have used this to create a new general testing system for \TeX{}
+code, \pkg{l3build}. This \emph{is} designed to be used beyond the team, so is
+now available in \TeX{} Live and \hologo{MiKTeX} and is fully documented.
+Testing using \pkg{l3build} makes use of a normalised version of the
+\texttt{.log} file, so can test any aspect of \TeX{} output (e.g., by using
+\cs{showbox}) or its algorithms (by displaying results in the \texttt{.log}).
+
+Part of the remit for creating \pkg{l3build} was to enable the team to work
+truly cross-platform and to allow testing using multiple \TeX{} engines
+(earlier systems were limited to a single engine, normally \eTeX{}). The new
+testing system means we are in a much stronger position to support a variety of
+engines (see below). It has also enabled us to give useful feedback on
+development of the \hologo{LuaTeX} engine.
+
+As well as the core capability in testing, \pkg{l3build} also provides a
+\enquote{one stop} script for creating release bundles. The script is
+sufficiently flexible that for many common \LaTeX{} package structures, setting
+up for creating releases will require only a few lines of configuration.
+
+In addition to the documentation distributed with \pkg{l3build}, the project
+website~\cite[publications in 2014]{10:project-publications} contains some
+articles, videos and conference presentations that explain how to use
+\pkg{l3build} to manage and test any type of (\LaTeX{}) package.
+
+\section{Automating \pkg{expl3} testing}
+
+As well as developing \pkg{l3build} for local use, the team have also set up
+integration testing for \pkg{expl3} using the Travis-CI system. This means that
+\emph{every} commit to the \LaTeX3 code base now results in a full set of tests
+being run. This has allowed us to significantly reduce the number of occasions
+where \pkg{expl3} needs attention before being released to CTAN.
+
+Automated testing has also enabled us to check that \pkg{expl3} updates do not
+break a number of key third-party packages which use the programming
+environment.
+
+\section{Refining \pkg{expl3}}
+
+Work continues to improve \pkg{expl3} both in scope and robustness. Increased
+use of the programming environment means that code which has to-date been
+under-explored is being used, and this sometimes requires changes to the code.
+
+The team have extended formal support in \pkg{expl3} to cover the engines
+p\TeX{} and up\TeX{}, principally used by Japanese \TeX{} users. This has been
+possible in part due to the \pkg{l3build} system discussed above.
+Engine-dependent variations between \hologo{pdfTeX}, \hologo{XeTeX},
+\hologo{LuaTeX} and (u)p\TeX{} are now well-understood and documented. As part
+of this process, the \enquote{low-level} part of \pkg{expl3}, which saves all
+primitives, now covers essentially all primitives found in all of these
+engines.
+
+The code in \pkg{expl3} is now entirely self-contained, loading no other
+third-party packages, and can also be loaded as a generic package with plain
+\TeX{}, \emph{etc.} These changes make it much easier to diagnose problems and
+make \pkg{expl3} more useful. In particular it can be used as a programming
+language for generic packages, that then can run without modifications under
+different formats!
+
+The team have made a range of small refinements to both internals and
+\pkg{expl3} interfaces. Internal self-consistency has also been improved, for
+example removing almost all use of \texttt{nopar} functions. Performance
+enhancements to the \pkg{l3keys} part of \pkg{expl3} are ongoing and should
+result in significantly faster key setting. As keyval methods are increasingly
+widely used in defining behaviours, this will have an impact on compile times
+for end users.
+
+\section{Replacing \cs{lowercase} and \cs{uppercase}}
+
+As discussed in the last \LaTeX3 News, the team have for some time been keen to
+provide new interfaces which do not directly expose (or in some cases even use)
+the \TeX{} primitives \cs{lowercase} and \cs{uppercase}. We have now created a
+series of different interfaces that provide support for the different
+conceptual uses which may flow from the primitives:
+\begin{itemize}
+ \item For case changing text, \cs{tl_upper_case:n}, \cs{tl_lower_case:n},
+ \cs{tl_mixed_case:n} and related language-aware functions. These are
+ Unicode-capable and designed for working with text. They also allow for
+ accents, expansion of stored text and leaving math mode unchanged. At
+ present some of the interface decisions are not finalised so they are
+ marked as experimental, but the team expect the core concept to be stable.
+ \item For case changing programming strings, \cs{str_upper_case:n},
+ \cs{str_lower_case:n} and \cs{str_fold_case:n}. Again these are
+ Unicode-aware, but in contrast to the functions for text are not
+ context-dependent. They are intended for caseless comparisons, constructing
+ command names on-the-fly and so forth.
+ \item For creating arbitrary character tokens, \cs{char_generate:nn}. This
+ is based on the \cs{Ucharcat} primitive introduced by \hologo{XeTeX}, but
+ with the ideas extended to other engines. This function can be used to
+ create almost any reasonable token.
+ \item For defining active characters, \cs{char_set_active_eq:NN} and
+ related functions. The concept here is that active characters should be
+ equivalent to some named function, so one does not directly define the
+ active character.
+\end{itemize}
+
+\section{Extending \pkg{xparse}}
+
+After discussions at TUG2015 and some experimentation, the team have added a
+new argument type, \texttt{e} (\enquote{embellishment}), to \pkg{xparse}.
+This allows arguments similar to
+\TeX{} primitive sub- and superscripts to be accepted. Thus
+\begin{verbatim}
+\DeclareDocumentCommand\foo{e{^_}}
+ {\showtokens{"#1"}}
+\foo^{Hello} world
+\end{verbatim}
+will show
+\begin{verbatim}
+"{Hello}{-NoValue-}".
+\end{verbatim}
+
+At present, this argument type is experimental: there are a number of models
+which may make sense for this interface.
+
+\section{A new \cs{parshape} model}
+
+As part of development of \pkg{l3galley}, Joseph Wright has proposed a new
+model for splitting up the functions of the \cs{parshape} primitive into three
+logical elements:
+\begin{itemize}
+ \item Margins between the edges of the galley and the paragraph (for example
+ an indented block);
+ \item Cut-out sections running over a fixed number of lines, to support
+ \enquote{in place} figures and so forth;
+ \item Running or single-paragraph shape.
+\end{itemize}
+
+There are additional elements to consider here, for example whether lines are
+the best way to model the length of shaping, how to handle headings, cut-outs
+at page breaks, \emph{etc.}
+
+
+\section{Globally optimized pagination of documents}
+
+Throughout 2016 Frank Mittelbach has worked on methods and algorithms for
+globally optimizing the pagination of documents including those that contain
+floats. Early research results have been presented at Bacho\TeX{} 2016, TUG
+2016 in Toronto and later in the year at \mbox{DocEng'16}, the ACM Symposium on
+Document Engineering in Vienna. A link to the ACM paper (that allows a download
+free of charge) can be found on the project
+website~\cite{10:project-publications}. The site also holds the speaker notes from
+Toronto and will host a link to a video of the presentation once it becomes
+available.
+
+The framework developed by Frank is based on the extended functionality
+provided by \hologo{LuaTeX}, in particular its callback functions that allow
+interacting with the typesetting process at various points. The algorithm that
+determines the optimal pagination of a given document is implemented in {Lua}
+and its results are then used to direct the formatting done by the \TeX{}
+engine.
+
+At the current point in time this a working prototype but not yet anywhere near
+a production-ready system. However, the work so far shows great potential and
+Frank is fairly confident that it will eventually become a generally usable
+solution.
+
+\section{Looking forward}
+
+The \hologo{LuaTeX} engine has recently reached version~1.0. This may presage a
+stable \hologo{LuaTeX} and is likely to result in wider use of this engine in
+production documents.If that happens we expect to implement some of the more
+complex functionality (such as complex pagination requirements and models) only
+for \hologo{LuaTeX}.
+
+\begin{thebibliography}{10}
+ \raggedright
+ \bibitem{10:project-publications}
+ Links to various publications by members of the \LaTeX{} Project Team.
+ \newblock \url{https://www.latex-project.org/publications}.
+\end{thebibliography}
+
+\end{document}
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news11.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news11.pdf
new file mode 100644
index 00000000000..192d1d97cc0
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news11.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news11.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news11.tex
new file mode 100644
index 00000000000..a1fd8a129c9
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news11.tex
@@ -0,0 +1,208 @@
+% Copyright 2017-2018 The LaTeX3 Project
+
+\documentclass{ltnews}
+
+\PassOptionsToPackage{colorlinks}{hyperref}
+
+\usepackage{csquotes}
+\usepackage{hologo}
+\usepackage{ragged2e}
+\usepackage{underscore}
+
+
+%%% wrong in ltnews.cls ... so some tmp fix here at the moment:
+\makeatletter
+\renewcommand{\subsubsection}{%
+ \@startsection
+ {subsubsection}{2}{\leftmargini}{-1.5ex \@plus -1ex \@minus -.2ex}%
+ {1sp}{\@subheadingfont}%
+}
+\makeatother
+
+
+\AtBeginDocument{%
+ \renewcommand*{\LaTeXNews}{\LaTeX3~News}%
+ \RaggedRight
+ \setlength\parindent{1em}%
+}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\publicationmonth{February}
+\publicationyear{2018}
+\publicationissue{11}
+
+% Avoid hyphenation of csnames
+\makeatletter
+\protected\edef\cs#1{%
+ \noexpand\path{\@backslashchar#1}%
+}
+\makeatother
+\hyphenpenalty=10000 \exhyphenpenalty=10000
+
+\begin{document}
+
+\maketitle
+
+\tableofcontents
+
+\section{Move of sources from Subversion to Git}
+
+The \LaTeX{} team have used a variety of version control systems over the life
+of the \LaTeX3 sources. For a long time we maintained the \LaTeX3 sources in
+Subversion (\texttt{svn}) but also provided a read-only clone of them on GitHub using
+SubGit from TMate Software~\cite{SubGit} to synchronize the two
+repositories---a solution that worked very well.
+
+We have now retired the Subversion repository and completely moved over to Git,
+with the master \LaTeX3 repository hosted on GitHub:
+\url{https://github.com/latex3/latex3}. This new approach means we are (slowly)
+adopting some new approaches to development, for example branches and accepting
+pull requests.
+
+\subsection{Version identifiers}
+
+Following this change, we have removed Subversion \verb=$Id= lines from the
+\LaTeX3 sources. At present, we will be retaining \cs{GetIdInfo} as there are
+several possible use cases. The \LaTeX3 sources now have only release date
+strings as identifiers. However, the team recommend that package authors
+include version information directly in \cs{ProvidesExplPackage} (or similar)
+lines.
+
+\section{\pkg{expl3} updates and extensions}
+
+Work has continued on the codebase over the last year, with both small
+changes/fixes and more substantial changes taking place. The following sections
+summarise some of the more notable changes.
+
+\subsection{\pkg{l3sort} moves to the kernel}
+
+Sorting is an important ability, and for some time the team have provided a
+stand-alone \pkg{l3sort} to support this. The functionality has seen wide take
+up, and so has now been integrated directly into the kernel. This took place in
+parallel with some interface changes to \enquote{round out} the code.
+
+\subsection{Boolean functions}
+
+For some time, the team have been aware that boolean expressions can fail in
+certain circumstances, leading to low-level errors. This is linked to two
+features of the long-standing \cs{bool_if:n(TF)} function: expandable operation
+and short-circuit evaluation.
+
+Addressing that has meant two changes: altering \cs{bool_if:n(TF)} to
+\emph{always} evaluate each part of the expression, and introducing new
+short-circuit functions without the issue. The latter are \verb|lazy| in \pkg{expl3}
+terms:
+\begin{itemize}
+\item \cs{bool_lazy_all:n(TF)}
+\item \cs{bool_lazy_and:nn(TF)}
+\item \cs{bool_lazy_any:n(TF)}
+\item \cs{bool_lazy_or:nn(TF)}
+\end{itemize}
+These new, stable functions are now the recommended way of handling boolean
+evaluations. Package authors are encouraged to employ these new functions as
+appropriate.
+
+\subsection{Revision of \pkg{l3file}}
+
+Large parts of \pkg{l3file} have been revised to give a better separation of
+path/file/extension. This has resulted in the addition of a number of new support
+functions and variables.
+
+At the same time, new experimental functions have been added to utilise a
+number of useful primitives in \hologo{pdfTeX}{}: \cs{file_get_mdfive_hash:nN},
+\cs{file_get_size:nN} and \cs{file_get_timestamp:nN}. Currently, \hologo{XeTeX}
+does not support getting file size/timestamp information: this is available in
+other engines.
+
+Paralleling these changes, we have added (experimental) support for shell
+escape to the \pkg{l3sys} module, most notably \cs{sys_shell_now:n}. A range of
+test booleans are also available to check whether shell escape is enabled.
+
+\subsection{Detection of \cs{cs_generate_variant:Nn} errors}
+
+The ability to generate variants is an important feature of \pkg{expl3}. At
+the same time, there are crucial aspects of this approach that can be
+misunderstood by users. In particular, the requirement that variants map
+correctly to an underlying \verb|N|- or \verb|n|-type base function is sometimes
+misunderstood.
+
+To help detect and correct these cases, \cs{cs_generate_variant:Nn} now
+carries out error checking on its arguments, and raises a warning where
+it is mis-applied. At present, the team have avoided making this an error
+as it is likely to be seen by end users rather than directly by package
+developers. In time, we are likely to revisit this and tighten up
+further on this key requirement.
+
+\subsection{Accessing random data}
+
+To support randomised data selection, we have introduced a family of
+experimental functions which use under\-lying engine support for random values,
+and provide one entry at random from the data type.
+
+At the same time, we have addressed some issues with uniformity stemming from
+the random number function used by \hologo{pdfTeX}{} and inherited by other
+engines. This means that \pkg{expl3}'s FPU will generate \emph{pseudo}-random
+values across the range of possible outputs.
+
+\subsection{More powerful debugging}
+
+A new set of debugging functions have been added to the kernel. These allow
+debug code to be enabled locally using the new option \verb|enable-debug| along
+with functions \cs{debug_on:n} and \cs{debug_off:n}. Accompanying this change, we
+have improved the handling of global/local consistency in variable setting.
+
+\subsection{Mark-up changes in \pkg{l3doc}}
+
+Since the introduction of the \verb|__| syntax to mark internal functions, the
+need for explicit markup of internal material in sources has been negated.
+As such, we have now dropped the requirement to mark internal material with
+\verb|[aux]| when using \pkg{l3doc}. Instead, the status of functions and
+variables is auto-detected from the presence of \verb|__|. For cases where
+non-standard names are used for internal code, the mark-up \verb|[int]| is
+retained, \emph{e.g.}
+\begin{verbatim}
+\begin{macro}[int]{\l@expl@enable@debug@bool}
+\end{verbatim}
+
+\newpage
+\section{\pkg{l3build} updates}
+
+Work on \pkg{l3build} has continued in parallel with \pkg{expl3} work, in
+particular continuing to develop features to allow wider use of the tool.
+
+Paralleling the move of the \LaTeX3 codebase to Git, \pkg{l3build} now has its
+own separate Git repository: \url{https://github.com/latex3/l3build}. This will
+enable us to involve other developers in the Lua code required for the build
+system. At the same time, we have split the code into a number of small source
+files, again to ease development both for the team ourselves and for potential
+collaborators.
+
+Another major change is that \pkg{l3build} can now retain the structure of
+source repositories when creating a CTAN archive. Whilst the team favor `flat'
+source setups, other users prefer structured approaches. Most notably, this
+new \pkg{l3build} functionality means that it is now used to carry out
+\pkg{beamer} releases.
+
+The other major new feature is a new approach to multiple test setups, which
+replaces the older \verb|--testfiledir| option. In the new approach, separate
+configuration files are listed in the main \verb|build.lua| script, and can be
+selected manually using a new \verb|--config| switch. This new approach allows
+complex test setups to be run in a totally automated fashion, which is
+important for kernel testing.
+
+Some changes to the normalisation routines have been carried out, some to deal
+with upcoming \hologo{LuaTeX} changes, others to address aspects which show up
+only in some tests. This has required \verb|.tlg| updates in some cases: as far
+as possible, we strive to avoid requiring changes to the reference files.
+
+\begin{thebibliography}{10}
+ \raggedright
+ \bibitem{SubGit}
+ \emph{SubGit}, TMate Software, \url{https://subgit.com}
+ \bibitem{11:project-publications}
+ Links to various publications by members of the \LaTeX{} Project Team.
+ \newblock \url{https://www.latex-project.org/publications}
+\end{thebibliography}
+
+\end{document}
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news12.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news12.pdf
new file mode 100644
index 00000000000..c9741946735
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news12.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3news12.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news12.tex
new file mode 100644
index 00000000000..f41e4b36902
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3news12.tex
@@ -0,0 +1,383 @@
+% Copyright 2019 The LaTeX3 Project
+\documentclass{ltnews}
+
+\usepackage{csquotes}
+\usepackage{hologo}
+\usepackage{ragged2e}
+
+\PassOptionsToPackage{colorlinks}{hyperref}
+
+%%% wrong in ltnews.cls ... so some tmp fix here at the moment:
+\makeatletter
+\renewcommand{\subsubsection}{%
+ \@startsection
+ {subsubsection}{2}{\leftmargini}{-1.5ex \@plus -1ex \@minus -.2ex}%
+ {1sp}{\@subheadingfont}%
+}
+\makeatother
+
+\AtBeginDocument{%
+ \renewcommand*{\LaTeXNews}{\LaTeX3~News}%
+ \RaggedRight
+ \setlength\parindent{1em}%
+}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\publicationmonth{January}
+\publicationyear{2020}
+\publicationissue{12}
+
+% Avoid hyphenation of csnames
+\makeatletter
+\protected\edef\cs#1{%
+ \noexpand\path{\@backslashchar#1}%
+}
+\makeatother
+\hyphenpenalty=10000
+\exhyphenpenalty=10000
+\hbadness=2500
+
+\makeatletter
+\renewcommand*\l@section[2]{%
+ \ifnum \c@tocdepth >\z@
+ \addpenalty\@secpenalty
+ \addvspace{0.7em \@plus\p@}% reduce from 1.0em
+ \setlength\@tempdima{1.5em}%
+ \begingroup
+ \parindent \z@ \rightskip \@pnumwidth
+ \parfillskip -\@pnumwidth
+ \leavevmode \bfseries
+ \advance\leftskip\@tempdima
+ \hskip -\leftskip
+ #1\nobreak\hfil
+ \nobreak\hb@xt@\@pnumwidth{\hss #2%
+ \kern-\p@\kern\p@}\par
+ \endgroup
+ \fi}
+\makeatother
+
+\newenvironment{compactitemize}%
+ {\begin{itemize}%
+ \setlength{\itemsep}{0pt}%
+ \setlength{\parskip}{0pt}%
+ \setlength{\parsep} {0pt}%
+ }%
+ {\end{itemize}}
+
+
+\begin{document}
+
+\maketitle
+
+\tableofcontents
+
+\section{Introduction}
+
+There has been quite a gap since the last \emph{\LaTeX3 News} (Issue~11,
+February 2018), and so there is quite a bit to cover here. Luckily, one of the
+things there \emph{is} to cover is that we are using a more formalised approach
+for logging changes, so writing up what has happened is a bit easier.
+(By mistake \LaTeX3 News~11 itself did not get \emph{published} when written, but is
+now available: we have kept the information it contains separate as it
+is a good summary of the work that had happened in 2017.)
+
+Work has continued apace across the \LaTeX3 codebase in the last (nearly) two
+years. A lot of this is ultimately focussed on making the core of \pkg{expl3}
+even more stable: \emph{squeezing} out more experimental ideas, refining
+ones we have and making it a serious option for core \LaTeX{} programming.
+
+As a result of these activities, the \LaTeX3 programming layer will be
+available as part of the kernel of \LaTeXe{} from 2020-02-02 onwards, i.e., can
+be used without explicitly loading \pkg{expl3}. See \emph{\LaTeX{}
+ News~31}~\cite{12:site-news} for more details on this.
+
+\section{New features in \pkg{expl3}}
+
+\subsection{A new argument specifier: \texttt{e}-type}
+
+During 2018, the team worked with the \TeX{} Live, \hologo{XeTeX} and
+(u)p\TeX{} developers to add the \cs{expanded} primitive to \hologo{pdfTeX}{},
+\hologo{XeTeX} and (u)p\TeX{}. This primitive was originally suggested for
+\hologo{pdfTeX}{} v1.50 (never released), and was present in \hologo{LuaTeX}{}
+from the start of that project.
+
+Adding \cs{expanded} lets us create a new argument specifier: \texttt{e}-type
+expansion. This is \emph{almost} the same as \texttt{x}-type, but is itself
+expandable. (It also doesn't need doubled \verb|#| tokens.) That's incredibly
+useful for creating function-like macros: you can ensure that \emph{everything}
+is expanded in an argument before you go near it, with not an \cs{expandafter}
+in sight.
+
+\subsection{New functions}
+
+New programming tools have appeared in various places across \pkg{expl3}.
+The highlights are
+\begin{compactitemize}
+ \item Shuffling of sequences to allow randomization
+ \item Arrays of integers and floating point values; these have constant-time
+ access
+ \item Functions to return values after system shell usage
+ \item Expandable access to file information, including file size, MD5
+ hash and modification date
+\end{compactitemize}
+
+For the latter, we have revised handling of file names considerably. There is
+now support for finding files in expansion contexts (by using the
+\cs{(pdf)filesize} primitive). Spaces and quotes in file names are now fully
+normalised, in a similar manner to the approach used by the latest \LaTeXe{}
+kernel.
+
+\subsection{String conversion moves to \pkg{expl3}}
+
+In addition to entirely new functions, the team have moved the
+\pkg{l3str-convert} module from the \pkg{l3experimental} bundle into the
+\pkg{expl3} core. This module is essential for dealing with the need to produce
+UTF-16 and UTF-32 strings in some contexts, and also offers
+built-in escape for url and PDF strings.
+
+\subsection{Case changing of text}
+
+Within \pkg{expl3}, the team have renamed and reworked the ideas from
+\cs{tl_upper_case:n} and so on, creating a new module \pkg{l3text}. This is a
+\enquote{final} home for functions to manipulate \emph{text}; token lists that
+can reasonably be expected to expand to plain text plus limited markup, for
+example emphasis and labels/references. Moving these functions, we have also
+made a small number of changes in other modules to give consistent names to
+functions: see the change log for full details.
+
+Over time, we anticipate that functions for other textual manipulation will be
+added to this module.
+
+\section{Notable fixes and changes}
+
+\subsection{File name parsing}
+
+The functions for parsing file names have been entirely rewritten, partly as
+this is required for the expandable access to file information mentioned above.
+The new code correctly deals with spaces and quote marks in file names and
+splits the path/name/extension.
+
+\subsection{Message formatting}
+
+The format of messages in \pkg{expl3} was originally quite text-heavy, the idea
+being that they would stand out in the \texttt{.log} file. However, this made
+them hard to find by a regular expression search, and was very different from
+the \LaTeXe{} message approach. The formatting of \pkg{expl3} messages has been
+aligned with that from the \LaTeXe{} kernel, such that IDE scripts and
+similar will be able to find and extract them directly.
+
+\subsection{Key inheritance}
+
+A number of changes have been made to the inheritance code for keys,
+to allow inheritance to work \enquote{as expected} in (almost) all
+cases.
+
+\subsection{Floating point juxtaposition}
+
+Implicit multiplication by juxtaposition, such as \verb|2pi|, is now handled
+separately from parenthetic values. Thus for example \verb|1in/1cm| is treated
+as equal to \verb|(1in)/(1cm)| and thus yields \verb|2.54|, and \verb|1/2(pi+pi)| is
+equal to \verb|pi|.
+
+\subsection{Changing box dimensions}
+
+\TeX{}'s handling of boxes is subtly different from other registers, and this
+shows up in particular when you want to resize a box. To bring treatment of
+boxes, or rather the grouping behavior of boxes, into line with other
+registers, we have made some internal changes to how functions such as
+\cs{box_set_wd:N} are implemented. This will be transparent for
+\enquote{well-behaved} use cases of these functions.
+
+\subsection{More functions moved to stable}
+
+A large number of functions which were introduced as candidates have been
+evaluated and moved to stable status. The team hopes to move all functions
+in \pkg{expl3} to stable status, or move them out of the core, over the coming
+months.
+
+\subsection{Deprecations}
+
+There have been two notable sets of deprecations over the past 18 months.
+First, we have rationalised all of the \enquote{raw} primitive names to the
+form \cs{tex_<name>:D}. This means that the older names, starting
+\mbox{\cs{pdftex_...}}, \cs{xetex_...}, etc., have been removed.
+
+Secondly, the use of integer constants, which dates back to the earliest days
+of \pkg{expl3}, is today more likely to make the code harder to read than
+anything else. Speed improvements in engines mean that the tiny enhancements in
+reading such constants are no longer required. Thus for example \cs{c_two} is
+deprecated in favour of simply using \texttt{2}.
+
+In parallel with this, a number of older \texttt{.sty} files have been removed.
+These older files provided legacy stubs for files which have now been
+integrated in the \pkg{expl3} core. They have now had sufficient time to allow
+users to update their code.
+
+\section{Internal improvements}
+
+\subsection{Cross-module functions}
+
+The team introduced the idea of internal module functions some time ago. Within
+the kernel, there are places where functions need to be used in multiple
+modules. To make the nature of the kernel interactions clearer, we have worked
+on several aspects
+\begin{compactitemize}
+ \item Reducing as far as possible cross-module functions
+ \item Making more generally-useful functions public, for example
+ scan marks
+ \item Creating an explicit cross-kernel naming convention for
+ functions which are internal but are essential to use in multiple
+ kernel modules
+\end{compactitemize}
+
+\subsection{The backend}
+
+Creating graphics, working with color, setting up hyperlinks and so on require
+backend-specific code. Here, backends are for example \texttt{dvips},
+\texttt{xdvipdfmx} and the direct PDF mode in \hologo{pdfTeX} and
+\hologo{LuaTeX}. These functions are needed across the \LaTeX3 codebase and
+have to be updated separately from the \pkg{expl3} core. To facilitate that, we
+have split those sources into a separate bundle, which can be updated as
+required.
+
+At the same time, the code these files contain is very low-level and is best
+described as internal. We have re-structured how the entire set of functions
+are referred to such that they are now internal for the area they implement,
+for example image inclusion, box affine transformations, etc.
+
+\section{Better support for (u)p\texorpdfstring{\kern-.05em}{}\TeX{}}
+
+The developers behind (u)p\TeX{} (Japanese \TeX{}) have recently enhanced their
+English documentation (see \url{https://github.com/texjporg/ptex-manual}).
+Using this new information, we have been able to make internal adjustments to
+\pkg{expl3} to better support these engines.
+
+\section{Options}
+
+A new option \texttt{undo-recent-deprecations} is now available for cases where
+a document (or package) requires some \pkg{expl3} functions that have been
+formally removed after deprecation. This is to allow \emph{temporary}
+work-arounds for documents to be compiled whilst code is begin updated.
+
+The \enquote{classical} options for selecting backends (\texttt{dvips},
+\texttt{pdftex}, etc.)\ are now recognised in addition to the native key--value
+versions. This should make it much easier to use the \pkg{expl3} image and color
+support as it is brought up to fully-workable standards.
+
+\section{Engine requirements}
+
+The minimum engine versions needed to use \pkg{expl3} have been
+incremented a little:
+\begin{compactitemize}
+ \item \hologo{pdfTeX} v1.40
+ \item \hologo{XeTeX} v0.99992
+ \item \hologo{LuaTeX} v0.95
+ \item $\varepsilon$-(u)p\TeX{} mid-2012
+\end{compactitemize}
+
+The team have also worked with the \hologo{XeTeX} and (u)p\TeX{} developers to
+standardise the set of post-\eTeX{} utility primitives that are available: the
+so-called \enquote{\hologo{pdfTeX} utilities}. These are now available in all
+supported engines, and in time will all be \emph{required}. This primarily
+impacts \hologo{XeTeX}, which gained most of these primitives in the 2019
+\TeX{} Live cycle. (Examples are the random number primitives and expandable
+file data provision.) See \emph{\LaTeX{} News~31}~\cite{12:site-news}
+for more.
+
+\section{Documentation}
+
+\subsection{News}
+
+The \emph{\LaTeX3 News} files were until recently only used to create
+PDF files on the team website~\cite{12:site}. We have now
+integrated those into the \pkg{l3kernel} (\pkg{expl3} core)
+bundle. The news files cover all of \LaTeX3 files, as the core files
+are always available.
+
+\subsection{ChangeLog}
+
+Since the start of 2018, the team have commenced a comprehensive
+change log for each of the bundles which make up the \LaTeX3 code.
+These are simple Markdown text files, which means that they can be displayed
+formatted in web views.
+
+\section{Changes in \pkg{xparse}}
+
+A number of new features have been added to \pkg{xparse}. To allow
+handling of the fact that skipping spaces may be required only in
+some cases when searching for optional arguments, a new modifier
+\texttt{!} is available in argument specifiers. This causes
+\pkg{xparse} to \emph{require} that an optional argument follows
+immediately with no intervening spaces.
+
+There is a new argument type purely for environments: \texttt{b}-type
+for collecting a \cs{begin}\texttt{...}\cs{end} pair, i.e., collecting the body of
+an environment. This is similar in concept to the \pkg{environ} package,
+but is integrated directly into \pkg{xparse}.
+
+Finally, it is now possible to refer to one argument as the
+default for another optional one, for example
+\begin{verbatim}
+\NewDocumentCommand{\caption}{O{#2} m} ...
+\end{verbatim}
+
+\section{New experimental modules}
+
+A number of new experimental modules have been added within the
+\pkg{l3experimental} bundle:
+\begin{description}
+ \item[\pkg{l3benchmark}] Performance-testing system using the timing
+ function in modern \TeX{} engines
+ \item[\pkg{l3cctab}] Category code tables for all engines, not
+ just \hologo{LuaTeX}
+ \item[\pkg{l3color}] Color support, similar in interface to \pkg{xcolor}
+ \item[\pkg{l3draw}] Creation of drawings, inspired by \pkg{pgf}, but
+ using the \LaTeX3 FPU for calculations
+ \item[\pkg{l3pdf}] Support for PDF features such as compression,
+ hyperlinks, etc.
+ \item[\pkg{l3sys-shell}] Shell escape functions for file manipulation
+\end{description}
+
+\section{\pkg{l3build} changes}
+
+The \pkg{l3build} tool for testing and releasing \TeX{} packages
+has seen a number of incremental improvements. It is now available directly
+as a script in \TeX{} Live and MiK\TeX{}, meaning you can call it simply
+as
+\begin{verbatim}
+l3build <target>
+\end{verbatim}
+Accompanying this, we have added support for installing scripts and script
+\texttt{man} files.
+
+There is a new \texttt{upload} target that can take a zip file and send it to
+CTAN: you just have to fill in release information for \emph{this}
+upload at the prompts.
+
+Testing using PDF files rather than logs has been heavily revised:
+this is vital for work on PDF tagging.
+
+There is also better support for complex directory structures, including the
+ability to manually specify TDS location for all installed files. This
+is particularly targeted at packages with both generic and format-specific files
+to install.
+
+
+\begin{thebibliography}{9}
+
+\fontsize{9.9}{11.9}\selectfont
+
+
+\bibitem{12:site}
+ \emph{\LaTeX{} Project Website}.
+ \hfil\break\url{https://latex-project.org/}
+
+\bibitem{12:site-news}
+ \emph{\LaTeXe{} release newsletters on the \LaTeX{} Project Website}.
+ \url{https://latex-project.org/news/latex2e-news/}
+
+
+\end{thebibliography}
+
+\end{document}
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3obsolete.txt b/Master/texmf-dist/doc/latex-dev/l3kernel/l3obsolete.txt
new file mode 100644
index 00000000000..0403ec5225e
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3obsolete.txt
@@ -0,0 +1,743 @@
+This file lists functions and variables which were part of l3kernel
+but which are either deprecated or which have been removed entirely.
+The file is split into two parts. The first part lists functions
+which are deprecated but are still currently included in the
+extracted files. The second part lists functions which have been
+removed from the distribution entirely. Functions which were added
+with 'experimental' status only are not listed.
+
+Deprecated functions and variables
+==================================
+
+These functions are deprecated and should be removed from packages.
+
+---------------------------------------------------
+Function Date deprecated
+---------------------------------------------------
+\char_fold_case:N 2020-01-03
+\char_foldcase:N 2022-10-17
+\char_lower_case:N 2020-01-03
+\char_lowercase:N 2022-10-17
+\char_mixed_case:N 2020-01-03
+\char_titlecase:N 2022-10-17
+\char_upper_case:N 2020-01-03
+\char_uppercase:N 2022-10-17
+\char_str_fold_case:N 2020-01-03
+\char_str_foldcase:N 2022-10-17
+\char_str_lower_case:N 2020-01-03
+\char_str_lowercase:N 2022-10-17
+\char_str_mixed_case:N 2020-01-03
+\char_str_titlecase:N 2022-10-17
+\char_str_upper_case:N 2020-01-03
+\char_str_uppercase:N 2022-10-17
+\char_to_utfviii_bytes:n 2022-10-09
+\char_to_nfd:N 2022-10-09
+\cs_argument_spec:N 2022-06-24
+\iow_shipout_x:cn 2023-09-17
+\iow_shipout_x:cx 2023-09-17
+\iow_shipout_x:Nn 2023-09-17
+\iow_shipout_x:Nx 2023-09-17
+\keys_set_filter:nnn 2024-01-10
+\keys_set_filter:nnV 2024-01-10
+\keys_set_filter:nnv 2024-01-10
+\keys_set_filter:nno 2024-01-10
+\keys_set_filter:nnnN 2024-01-10
+\keys_set_filter:nnVN 2024-01-10
+\keys_set_filter:nnvN 2024-01-10
+\keys_set_filter:nnoN 2024-01-10
+\keys_set_filter:nnnnN 2024-01-10
+\keys_set_filter:nnVnN 2024-01-10
+\keys_set_filter:nnvnN 2024-01-10
+\keys_set_filter:nnonN 2024-01-10
+\l_keys_key_tl 2020-02-08
+\l_keys_path_tl 2020-02-08
+\l_text_accents_tl 2023-02-07
+\l_text_letterlike_tl 2023-02-07
+\msg_gset:nnn 2024-02-13
+\msg_gset:nnnn 2024-02-13
+\peek_catcode_ignore_spaces:N 2022-01-11
+\peek_catcode_remove_ignore_spaces:N 2022-01-11
+\peek_charcode_ignore_spaces:N 2022-01-11
+\peek_charcode_remove_ignore_spaces:N 2022-01-11
+\peek_meaning_ignore_spaces:N 2022-01-11
+\peek_meaning_remove_ignore_spaces:N 2022-01-11
+\pdf_object_new:nn 2022-08-23
+\pdf_object_write:nn 2022-08-23
+\pdf_object_write:nx 2022-08-23
+\prop_gput_if_new:cnn 2024-03-30
+\prop_gput_if_new:cnV 2024-03-30
+\prop_gput_if_new:cVn 2024-03-30
+\prop_gput_if_new:Nnn 2024-03-30
+\prop_gput_if_new:NnV 2024-03-30
+\prop_gput_if_new:NVn 2024-03-30
+\prop_put_if_new:cnn 2024-03-30
+\prop_put_if_new:cnV 2024-03-30
+\prop_put_if_new:cVn 2024-03-30
+\prop_put_if_new:Nnn 2024-03-30
+\prop_put_if_new:NnV 2024-03-30
+\prop_put_if_new:NVn 2024-03-30
+\str_declare_eight_bit_encoding:nnn 2020-08-20
+\str_fold_case:n 2020-01-03
+\str_fold_case:V 2020-01-03
+\str_foldcase:n 2022-10-17
+\str_foldcase:V 2022-10-17
+\str_lower_case:f 2020-01-03
+\str_lower_case:n 2020-01-03
+\str_upper_case:f 2020-01-03
+\str_upper_case:n 2020-01-03
+\seq_indexed_map_inline:Nn 2020-06-18
+\seq_indexed_map_function:NN 2020-06-18
+\seq_gset_map_x:NNn 2023-10-26
+\seq_set_map_x:NNn 2023-10-26
+\sys_load_deprecation: 2021-01-11
+\text_titlecase:n 2023-07-08
+\text_titlecase:nn 2023-07-08
+\tl_build_clear:N 2023-10-18
+\tl_build_gclear:N 2023-10-18
+\tl_build_get:NN 2023-12-14
+\tl_case:cn 2023-05-23
+\tl_case:cnF 2023-05-23
+\tl_case:cnT 2023-05-23
+\tl_case:cnTF 2023-05-23
+\tl_case:Nn 2023-05-23
+\tl_case:NnF 2023-05-23
+\tl_case:NnT 2023-05-23
+\tl_case:NnTF 2023-05-23
+\tl_lower_case:n 2020-01-03
+\tl_lower_case:nn 2020-01-03
+\tl_mixed_case:n 2020-01-03
+\tl_mixed_case:nn 2020-01-03
+\tl_upper_case:n 2020-01-03
+\tl_upper_case:nn 2020-01-03
+[key property] .str_gset_x:c 2023-09-27
+[key property] .str_gset_x:N 2023-09-27
+[key property] .str_set_x:c 2023-09-27
+[key property] .str_set_x:N 2023-09-27
+[key property] .tl_gset_x:c 2023-09-27
+[key property] .tl_gset_x:N 2023-09-27
+[key property] .tl_set_x:c 2023-09-27
+[key property] .tl_set_x:N 2023-09-27
+[x-type variants] 2023-09-27
+---------------------------------------------------
+
+Removed functions and variables
+===============================
+
+The 'Date removed' column details when the functions were
+removed from the sources.
+
+----------------------------------------------------------
+Function Date removed
+----------------------------------------------------------
+\box_gset_eq_clear:cc 2021-07-07
+\box_gset_eq_clear:cN 2021-07-07
+\box_gset_eq_clear:Nc 2021-07-07
+\box_gset_eq_clear:NN 2021-07-07
+\box_resize:cnn 2018-12-27
+\box_resize:Nnn 2018-12-27
+\box_set_eq_clear:cc 2021-07-07
+\box_set_eq_clear:cN 2021-07-07
+\box_set_eq_clear:Nc 2021-07-07
+\box_set_eq_clear:NN 2021-07-07
+\box_use_clear:c 2018-12-27
+\box_use_clear:N 2018-12-27
+\c_active_char_token 2011-09-08
+\c_alignment_tab_token 2011-09-08
+\c_eight 2020-01-01
+\c_eleven 2020-01-01
+\c_empty_toks 2011-09-08
+\c_fifteen 2020-01-01
+\c_five 2020-01-01
+\c_four 2020-01-01
+\c_fourteen 2020-01-01
+\c_term_ior 2021-07-07
+\c_job_name_tl 2017-01-01
+\c_keys_code_root_tl 2013-01-08
+\c_letter_token 2011-09-08
+\c_luatex_is_engine_bool 2011-12-30
+\c_math_shift_token 2011-09-08
+\c_minus_one 2018-12-27
+\c_nine 2020-01-01
+\c_one 2020-01-01
+\c_one_hundred 2020-01-01
+\c_one_thousand 2020-01-01
+\c_other_char_token 2011-09-08
+\c_pdftex_is_engine_bool 2011-12-30
+\c_seven 2020-01-01
+\c_six 2020-01-01
+\c_sixteen 2020-01-01
+\c_string_cctab 2012-06-30
+\c_thirteen 2020-01-01
+\c_thirty_two 2020-01-01
+\c_three 2020-01-01
+\c_ten 2020-01-01
+\c_ten_thousand 2020-01-01
+\c_twelve 2020-01-01
+\c_two 2020-01-01
+\c_two_hundred_fifty_five 2020-01-01
+\c_two_hundred_fifty_six 2020-01-01
+\c_undefined_fp 2014-05-28
+\c_xetex_is_engine_bool 2011-12-30
+\c_zero 2020-01-01
+\char_make_active:N 2011-09-08
+\char_make_active:n 2011-09-08
+\char_make_alignment_tab:N 2011-09-08
+\char_make_alignment_tab:n 2011-09-08
+\char_make_begin_group:N 2011-09-08
+\char_make_begin_group:n 2011-09-08
+\char_make_comment:N 2011-09-08
+\char_make_comment:n 2011-09-08
+\char_make_end_group:N 2011-09-08
+\char_make_end_group:n 2011-09-08
+\char_make_end_line:N 2011-09-08
+\char_make_end_line:n 2011-09-08
+\char_make_escape:N 2011-09-08
+\char_make_escape:n 2011-09-08
+\char_make_ignore:N 2011-09-08
+\char_make_ignore:n 2011-09-08
+\char_make_invalid:N 2011-09-08
+\char_make_invalid:n 2011-09-08
+\char_make_letter:N 2011-09-08
+\char_make_letter:n 2011-09-08
+\char_make_math_shift:N 2011-09-08
+\char_make_math_shift:n 2011-09-08
+\char_make_math_subscript:N 2011-09-08
+\char_make_math_subscript:n 2011-09-08
+\char_make_math_superscript:N 2011-09-08
+\char_make_math_superscript:n 2011-09-08
+\char_make_other:N 2011-09-08
+\char_make_other:n 2011-09-08
+\char_make_parameter:N 2011-09-08
+\char_make_parameter:n 2011-09-08
+\char_make_space:N 2011-09-08
+\char_make_space:n 2011-09-08
+\char_set_catcode:w 2011-09-08
+\char_set_lccode:w 2011-09-08
+\char_set_mathcode:w 2011-09-08
+\char_set_sfcode:w 2011-09-08
+\char_set_uccode:w 2011-09-08
+\char_show_value_catcode:w 2011-09-08
+\char_show_value_lccode:w 2011-09-08
+\char_show_value_mathcode:w 2011-09-08
+\char_show_value_sfcode:w 2011-09-08
+\char_show_value_uccode:w 2011-09-08
+\char_value_catcode:w 2011-09-08
+\char_value_lccode:w 2011-09-08
+\char_value_mathcode:w 2011-09-08
+\char_value_sfcode:w 2011-09-08
+\char_value_uccode:w 2011-09-08
+\chk_if_free_cs:N 2013-01-08
+\clist_display:c 2011-09-08
+\clist_display:N 2011-09-08
+\clist_gremove_element:Nn 2011-09-08
+\clist_gtrim_spaces:c 2011-12-20
+\clist_gtrim_spaces:N 2011-12-20
+\clist_if_eq_p:cc 2013-01-08
+\clist_if_eq:ccF 2013-01-08
+\clist_if_eq:ccT 2013-01-08
+\clist_if_eq:ccTF 2013-01-08
+\clist_if_eq_p:cN 2013-01-08
+\clist_if_eq:cNF 2013-01-08
+\clist_if_eq:cNT 2013-01-08
+\clist_if_eq:cNTF 2013-01-08
+\clist_if_eq_p:Nc 2013-01-08
+\clist_if_eq:NcF 2013-01-08
+\clist_if_eq:NcT 2013-01-08
+\clist_if_eq:NcTF 2013-01-08
+\clist_length:c 2013-01-08
+\clist_length:N 2013-01-08
+\clist_length:n 2013-01-08
+\clist_remove_element:Nn 2011-09-08
+\clist_top:cN 2011-09-08
+\clist_trim_spaces:c 2011-12-20
+\clist_trim_spaces:N 2011-12-20
+\clist_use:c 2013-01-08
+\clist_use:N 2013-01-08
+\cs_gnew_eq:cc 2011-09-08
+\cs_gnew_eq:cN 2011-09-08
+\cs_gnew_eq:Nc 2011-09-08
+\cs_gnew_nopar:cpn 2011-09-08
+\cs_gnew_nopar:cpx 2011-09-08
+\cs_gnew_nopar:Npn 2011-09-08
+\cs_gnew_nopar:Npx 2011-09-08
+\cs_gnew_protected_nopar:cpn 2011-09-08
+\cs_gnew_protected_nopar:cpx 2011-09-08
+\cs_gnew_protected_nopar:Npn 2011-09-08
+\cs_gnew_protected_nopar:Npx 2011-09-08
+\cs_gnew_protected:cpn 2011-09-08
+\cs_gnew_protected:cpx 2011-09-08
+\cs_gnew_protected:Npn 2011-09-08
+\cs_gnew_protected:Npx 2011-09-08
+\cs_gnew:cpn 2011-09-08
+\cs_gnew:cpx 2011-09-08
+\cs_gnew:Npn 2011-09-08
+\cs_gnew:Npx 2011-09-08
+\cs_gundefine:c 2011-09-08
+\cs_gundefine:N 2011-09-08
+\cs_set_eq:NwN 2011-12-30
+\dim_case:nnn 2015-07-14
+\dim_eval:w 2013-01-08
+\dim_eval_end: 2013-01-08
+\dim_gset_max:cn 2013-01-08
+\dim_gset_max:Nn 2013-01-08
+\dim_gset_min:cn 2013-01-08
+\dim_gset_min:Nn 2013-01-08
+\dim_set_max:cn 2013-01-08
+\dim_set_max:Nn 2013-01-08
+\dim_set_min:cn 2013-01-08
+\dim_set_min:Nn 2013-01-08
+\etex_....:D 2020-01-01
+\ExplSyntaxNamesOff 2013-01-08
+\ExplSyntaxNamesOn 2013-01-08
+\file_add_path:nN 2018-12-27
+\file_if_exist_input:nT 2018-03-05
+\file_if_exist_input:nTF 2018-03-05
+\file_list: 2018-12-27
+\file_path_include:n 2018-12-27
+\file_path_remove:n 2018-12-27
+\fp_abs:c 2014-05-28
+\fp_abs:N 2014-05-28
+\fp_compare:NNNF 2014-05-28
+\fp_compare:NNNT 2014-05-28
+\fp_compare:NNNTF 2014-05-28
+\fp_cos:cn 2014-05-28
+\fp_cos:Nn 2014-05-28
+\fp_div:cn 2014-05-28
+\fp_div:Nn 2014-05-28
+\fp_exp:cn 2014-05-28
+\fp_exp:Nn 2014-05-28
+\fp_gabs:c 2014-05-28
+\fp_gabs:N 2014-05-28
+\fp_gcos:cn 2014-05-28
+\fp_gcos:Nn 2014-05-28
+\fp_gdiv:cn 2014-05-28
+\fp_gdiv:Nn 2014-05-28
+\fp_gexp:cn 2014-05-28
+\fp_gexp:Nn 2014-05-28
+\fp_gln:cn 2014-05-28
+\fp_gln:Nn 2014-05-28
+\fp_gmul:cn 2014-05-28
+\fp_gmul:Nn 2014-05-28
+\fp_gneg:c 2014-05-28
+\fp_gneg:N 2014-05-28
+\fp_gpow:cn 2014-05-28
+\fp_gpow:Nn 2014-05-28
+\fp_ground_figures:cn 2014-05-28
+\fp_ground_figures:Nn 2014-05-28
+\fp_ground_places:cn 2014-05-28
+\fp_ground_places:Nn 2014-05-28
+\fp_gsin:cn 2014-05-28
+\fp_gsin:Nn 2014-05-28
+\fp_gtan:cn 2014-05-28
+\fp_gtan:Nn 2014-05-28
+\fp_if_undefined_p:N 2014-05-28
+\fp_if_undefined:NF 2014-05-28
+\fp_if_undefined:NT 2014-05-28
+\fp_if_undefined:NTF 2014-05-28
+\fp_if_zero_p:N 2014-05-28
+\fp_if_zero:NF 2014-05-28
+\fp_if_zero:NT 2014-05-28
+\fp_if_zero:NTF 2014-05-28
+\fp_ln:cn 2014-05-28
+\fp_ln:Nn 2014-05-28
+\fp_mul:cn 2014-05-28
+\fp_mul:Nn 2014-05-28
+\fp_neg:c 2014-05-28
+\fp_neg:N 2014-05-28
+\fp_pow:cn 2014-05-28
+\fp_pow:Nn 2014-05-28
+\fp_round_figures:cn 2014-05-28
+\fp_round_figures:Nn 2014-05-28
+\fp_round_places:cn 2014-05-28
+\fp_round_places:Nn 2014-05-28
+\fp_sin:cn 2014-05-28
+\fp_sin:Nn 2014-05-28
+\fp_tan:cn 2014-05-28
+\fp_tan:Nn 2014-05-28
+\g_file_current_name_tl 2018-12-27
+\g_tmpa_toks 2011-09-08
+\g_tmpb_toks 2011-09-08
+\g_tmpc_toks 2011-09-08
+\group_execute_after:N 2011-09-08
+\hbox_gset_inline_begin:c 2014-08-09
+\hbox_gset_inline_begin:N 2014-08-09
+\hbox_gset_inline_end: 2014-08-09
+\hbox_set_inline_begin:c 2014-08-09
+\hbox_set_inline_begin:N 2014-08-09
+\hbox_set_inline_end: 2014-08-09
+\hbox_unpack_clear:c 2021-07-07
+\hbox_unpack_clear:N 2021-07-07
+\if_num:w 2013-01-08
+\int_case:nnn 2015-07-14
+\int_convert_from_base_ten:nn 2011-11-22
+\int_convert_from_symbols:nn 2011-11-22
+\int_convert_to_base_ten:nn 2011-11-22
+\int_eval:w 2013-01-08
+\int_eval_end: 2013-01-08
+\int_from_binary:n 2016-01-05
+\int_from_hexadecimal:n 2016-01-05
+\int_from_octal:n 2016-01-05
+\int_to_binary:n 2016-01-05
+\int_to_hexadecimal:n 2016-01-05
+\int_to_octal:n 2016-01-05
+\int_to_symbol:n 2011-11-22
+\int_value:w 2013-01-08
+\ior_get_str:NN 2018-03-05
+\ior_gto:NN 2013-07-28
+\ior_list_streams: 2018-12-27
+\ior_log_streams: 2018-12-27
+\ior_open_streams: 2011-09-08
+\ior_str_gto:NN 2013-07-28
+\ior_str_to:NN 2013-07-28
+\ior_to:NN 2013-07-28
+\iow_list_streams: 2018-12-27
+\iow_log_streams: 2018-12-27
+\iow_now_buffer_safe:Nn 2011-09-08
+\iow_now_buffer_safe:Nx 2011-09-08
+\iow_now_when_avail:Nn 2012-06-05
+\iow_now_when_avail:Nx 2012-06-05
+\iow_open_streams: 2011-09-08
+\iow_wrap:xnnnN 2013-01-08
+\KV_process_no_space_removal_no_sanitize:NNn 2011-09-08
+\KV_process_space_removal_no_sanitize:NNn 2011-09-08
+\KV_process_space_removal_sanitize:NNn 2011-09-08
+\l_iow_line_length_int 2013-01-08
+\l_last_box 2012-05-11
+\l_tl_replace_toks 2011-09-08
+\l_tmpa_toks 2011-09-08
+\l_tmpb_toks 2011-09-08
+\l_tmpc_int 2013-01-08
+\l_tmpc_toks 2011-09-08
+\lua_escape_x:n 2020-01-01
+\lua_now_x:n 2020-01-01
+\lua_shipout_x:n 2020-01-01
+\luatex_...:D 2020-01-01
+\luatex_if_engine_p: 2017-01-01
+\luatex_if_engine:F 2017-01-01
+\luatex_if_engine:T 2017-01-01
+\luatex_if_engine:TF 2017-01-01
+\msg_class_new:nn 2011-09-08
+\msg_class_set:nn 2013-01-08
+\msg_direct_interrupt:xxxxx 2011-09-08
+\msg_direct_log:xx 2011-09-08
+\msg_direct_term:xx 2011-09-08
+\msg_generic_new:nn 2011-09-08
+\msg_generic_new:nnn 2011-09-08
+\msg_generic_set:nn 2011-09-08
+\msg_generic_set:nnn 2011-09-08
+\msg_interrupt:nn 2020-01-01
+\msg_interrupt:xxx 2013-01-08
+\msg_log:n 2020-01-01
+\msg_log:x 2013-01-08
+\msg_newline: 2013-01-08
+\msg_term:n 2020-01-01
+\msg_term:x 2013-01-08
+\msg_trace:nn 2011-09-08
+\msg_trace:nnx 2011-09-08
+\msg_trace:nnxx 2011-09-08
+\msg_trace:nnxxx 2011-09-08
+\msg_trace:nnxxxx 2011-09-08
+\msg_two_newlines: 2013-01-08
+\pdftex_...:D 2020-01-01
+\pdftex_if_engine_p: 2017-01-01
+\pdftex_if_engine:F 2017-01-01
+\pdftex_if_engine:T 2017-01-01
+\pdftex_if_engine:TF 2017-01-01
+\peek_after:NN 2011-09-08
+\peek_gafter:NN 2011-09-08
+\prg_case_dim:nnn 2013-01-08
+\prg_case_int:nnn 2013-01-08
+\prg_case_str:nnn 2013-01-08
+\prg_case_str:onn 2013-01-08
+\prg_case_str:xxn 2013-01-08
+\prg_case_tl:cnn 2013-01-08
+\prg_case_tl:Nnn 2013-01-08
+\prg_new_map_functions:Nn 2011-09-08
+\prg_set_map_functions:Nn 2011-09-08
+\prg_stepwise_function:nnnN 2013-01-08
+\prg_stepwise_inline:nnnn 2013-01-08
+\prg_stepwise_variable:nnnNn 2013-01-08
+\prop_del:cn 2013-01-08
+\prop_del:cV 2013-01-08
+\prop_del:Nn 2013-01-08
+\prop_del:NV 2013-01-08
+\prop_display:c 2011-09-08
+\prop_display:N 2011-09-08
+\prop_gdel:cn 2013-01-08
+\prop_gdel:cV 2013-01-08
+\prop_gdel:Nn 2013-01-08
+\prop_gdel:NV 2013-01-08
+\prop_get:cn 2016-01-05
+\prop_get:Nn 2016-01-05
+\prop_get_gdel:NnN 2011-09-08
+\prop_gget:cnN 2011-09-08
+\prop_gget:cVN 2011-09-08
+\prop_gget:NnN 2011-09-08
+\prop_gget:NVN 2011-09-08
+\prop_gput:ccx 2011-09-08
+\prop_if_eq_p:cc 2011-09-08
+\prop_if_eq_p:cN 2011-09-08
+\prop_if_eq_p:Nc 2011-09-08
+\prop_if_eq_p:NN 2011-09-08
+\prop_if_eq:ccF 2011-09-08
+\prop_if_eq:ccT 2011-09-08
+\prop_if_eq:ccTF 2011-09-08
+\prop_if_eq:cNF 2011-09-08
+\prop_if_eq:cNT 2011-09-08
+\prop_if_eq:cNTF 2011-09-08
+\prop_if_eq:NcF 2011-09-08
+\prop_if_eq:NcT 2011-09-08
+\prop_if_eq:NcTF 2011-09-08
+\prop_if_eq:NNF 2011-09-08
+\prop_if_eq:NNT 2011-09-08
+\prop_if_eq:NNTF 2011-09-08
+\prop_if_in:ccF 2011-09-08
+\prop_if_in:ccT 2011-09-08
+\prop_if_in:ccTF 2011-09-08
+\ptex_...:D 2020-01-01
+\quark_if_recursion_tail_break:N 2015-07-14
+\quark_if_recursion_tail_break:n 2015-07-14
+\scan_align_safe_stop: 2017-01-01
+\seq_display:c 2011-09-08
+\seq_display:N 2011-09-08
+\seq_length:c 2013-01-08
+\seq_length:N 2013-01-08
+\seq_top:cN 2011-09-08
+\seq_top:NN 2011-09-08
+\seq_use:c 2013-01-08
+\seq_use:N 2013-01-08
+\skip_if_infinite_glue_p:n 2013-01-08
+\skip_if_infinite_glue:nF 2013-01-08
+\skip_if_infinite_glue:nT 2013-01-08
+\skip_if_infinite_glue:nTF 2013-01-08
+\sort_ordered: 2018-12-27
+\sort_reversed: 2018-12-27
+\str_case:nnn 2015-07-14
+\str_case:onn 2015-07-14
+\str_case_x:nn 2020-01-01
+\str_case_x:nnF 2020-01-01
+\str_case_x:nnn 2015-07-14
+\str_case_x:nnT 2020-01-01
+\str_case_x:nnTF 2020-01-01
+\str_if_eq_p:xx 2013-01-08
+\str_if_eq_x:nnF 2020-01-01
+\str_if_eq_x:nnT 2020-01-01
+\str_if_eq_x:nnTF 2020-01-01
+\str_if_eq_x_p:nn 2020-01-01
+\str_if_eq:xxF 2013-01-08
+\str_if_eq:xxT 2013-01-08
+\str_if_eq:xxTF 2013-01-08
+\tl_case:cnn 2015-07-14
+\tl_case:Nnn 2015-07-14
+\tl_elt_count:c 2011-09-08
+\tl_elt_count:n 2011-09-08
+\tl_elt_count:N 2011-09-08
+\tl_elt_count:o 2011-09-08
+\tl_elt_count:V 2011-09-08
+\tl_gremove_all_in:cn 2011-09-08
+\tl_gremove_all_in:Nn 2011-09-08
+\tl_gremove_in:cn 2011-09-08
+\tl_gremove_in:Nn 2011-09-08
+\tl_greplace_all_in:cnn 2011-09-08
+\tl_greplace_all_in:Nnn 2011-09-08
+\tl_greplace_in:cnn 2011-09-08
+\tl_greplace_in:Nnn 2011-09-08
+\tl_gset:Nc 2011-09-08
+\tl_gset_from_file:cnn 2021-07-07
+\tl_gset_from_file:Nnn 2021-07-07
+\tl_gset_from_file_x:cnn 2021-07-07
+\tl_gset_from_file_x:Nnn 2021-07-07
+\tl_head_i:n 2011-09-08
+\tl_head_i:w 2011-09-08
+\tl_head_iii:f 2011-09-08
+\tl_head_iii:n 2011-09-08
+\tl_head_iii:w 2011-09-08
+\tl_if_empty:xF 2013-01-08
+\tl_if_empty:xT 2013-01-08
+\tl_if_empty:xTF 2013-01-08
+\tl_if_head_group_p:n 2013-01-08
+\tl_if_head_group:nF 2013-01-08
+\tl_if_head_group:nT 2013-01-08
+\tl_if_head_group:nTF 2013-01-08
+\tl_if_head_N_type_p:n 2013-01-08
+\tl_if_head_N_type:nF 2013-01-08
+\tl_if_head_N_type:nT 2013-01-08
+\tl_if_head_N_type:nTF 2013-01-08
+\tl_if_head_space_p:n 2013-01-08
+\tl_if_head_space:nF 2013-01-08
+\tl_if_head_space:nT 2013-01-08
+\tl_if_head_space:nTF 2013-01-08
+\tl_length:c 2013-01-08
+\tl_length:n 2013-01-08
+\tl_length:N 2013-01-08
+\tl_length:o 2013-01-08
+\tl_length:V 2013-01-08
+\tl_new:cn 2011-09-08
+\tl_new:Nn 2011-09-08
+\tl_new:Nx 2011-09-08
+\tl_remove_all_in:cn 2011-09-08
+\tl_remove_all_in:Nn 2011-09-08
+\tl_remove_in:cn 2011-09-08
+\tl_remove_in:Nn 2011-09-08
+\tl_replace_all_in:cnn 2011-09-08
+\tl_replace_all_in:Nnn 2011-09-08
+\tl_replace_in:cnn 2011-09-08
+\tl_replace_in:Nnn 2011-09-08
+\tl_set:Nc 2011-09-08
+\tl_set_from_file:cnn 2021-07-07
+\tl_set_from_file:Nnn 2021-07-07
+\tl_set_from_file_x:cnn 2021-07-07
+\tl_set_from_file_x:Nnn 2021-07-07
+\tl_show_analysis:N 2020-01-01
+\tl_show_analysis:n 2020-01-01
+\tl_tail:w 2013-01-08
+\tl_to_lowercase:n 2018-03-05
+\tl_to_uppercase:n 2018-03-05
+\token_get_arg_spec:N 2021-07-07
+\token_get_prefix_spec:N 2021-07-07
+\token_get_replacement_spec:N 2021-07-07
+\token_if_active_char_p:N 2011-09-08
+\token_if_active_char:NF 2011-09-08
+\token_if_active_char:NT 2011-09-08
+\token_if_active_char:NTF 2011-09-08
+\token_if_alignment_tab_p:N 2011-09-08
+\token_if_alignment_tab:NF 2011-09-08
+\token_if_alignment_tab:NT 2011-09-08
+\token_if_alignment_tab:NTF 2011-09-08
+\token_if_math_shift_p:N 2011-09-08
+\token_if_math_shift:NF 2011-09-08
+\token_if_math_shift:NT 2011-09-08
+\token_if_math_shift:NTF 2011-09-08
+\token_if_other_char_p:N 2011-09-08
+\token_if_other_char:NF 2011-09-08
+\token_if_other_char:NT 2011-09-08
+\token_if_other_char:NTF 2011-09-08
+\token_new:Nn 2018-12-29
+\toks_clear:c 2011-09-08
+\toks_clear:N 2011-09-08
+\toks_gclear:c 2011-09-08
+\toks_gclear:N 2011-09-08
+\toks_gput_left:cn 2011-09-08
+\toks_gput_left:co 2011-09-08
+\toks_gput_left:cV 2011-09-08
+\toks_gput_left:Nn 2011-09-08
+\toks_gput_left:No 2011-09-08
+\toks_gput_left:NV 2011-09-08
+\toks_gput_left:Nx 2011-09-08
+\toks_gput_right:cn 2011-09-08
+\toks_gput_right:co 2011-09-08
+\toks_gput_right:cV 2011-09-08
+\toks_gput_right:Nn 2011-09-08
+\toks_gput_right:No 2011-09-08
+\toks_gput_right:NV 2011-09-08
+\toks_gput_right:Nx 2011-09-08
+\toks_gset_eq:cc 2011-09-08
+\toks_gset_eq:cN 2011-09-08
+\toks_gset_eq:Nc 2011-09-08
+\toks_gset_eq:NN 2011-09-08
+\toks_gset:cn 2011-09-08
+\toks_gset:co 2011-09-08
+\toks_gset:cV 2011-09-08
+\toks_gset:cx 2011-09-08
+\toks_gset:Nn 2011-09-08
+\toks_gset:No 2011-09-08
+\toks_gset:NV 2011-09-08
+\toks_gset:Nx 2011-09-08
+\toks_if_empty_p:c 2011-09-08
+\toks_if_empty_p:N 2011-09-08
+\toks_if_empty:cF 2011-09-08
+\toks_if_empty:cT 2011-09-08
+\toks_if_empty:cTF 2011-09-08
+\toks_if_empty:NF 2011-09-08
+\toks_if_empty:NT 2011-09-08
+\toks_if_empty:NTF 2011-09-08
+\toks_if_eq_p:cc 2011-09-08
+\toks_if_eq_p:cN 2011-09-08
+\toks_if_eq_p:Nc 2011-09-08
+\toks_if_eq_p:NN 2011-09-08
+\toks_if_eq:ccF 2011-09-08
+\toks_if_eq:ccT 2011-09-08
+\toks_if_eq:ccTF 2011-09-08
+\toks_if_eq:cNF 2011-09-08
+\toks_if_eq:cNT 2011-09-08
+\toks_if_eq:cNTF 2011-09-08
+\toks_if_eq:NcF 2011-09-08
+\toks_if_eq:NcT 2011-09-08
+\toks_if_eq:NcTF 2011-09-08
+\toks_if_eq:NNF 2011-09-08
+\toks_if_eq:NNT 2011-09-08
+\toks_if_eq:NNTF 2011-09-08
+\toks_new:c 2011-09-08
+\toks_new:N 2011-09-08
+\toks_put_left:cn 2011-09-08
+\toks_put_left:co 2011-09-08
+\toks_put_left:cV 2011-09-08
+\toks_put_left:Nn 2011-09-08
+\toks_put_left:No 2011-09-08
+\toks_put_left:NV 2011-09-08
+\toks_put_left:Nx 2011-09-08
+\toks_put_right:cn 2011-09-08
+\toks_put_right:co 2011-09-08
+\toks_put_right:cV 2011-09-08
+\toks_put_right:Nf 2011-09-08
+\toks_put_right:Nn 2011-09-08
+\toks_put_right:No 2011-09-08
+\toks_put_right:NV 2011-09-08
+\toks_put_right:Nx 2011-09-08
+\toks_set_eq:cc 2011-09-08
+\toks_set_eq:cN 2011-09-08
+\toks_set_eq:Nc 2011-09-08
+\toks_set_eq:NN 2011-09-08
+\toks_set:cf 2011-09-08
+\toks_set:cn 2011-09-08
+\toks_set:co 2011-09-08
+\toks_set:cV 2011-09-08
+\toks_set:cv 2011-09-08
+\toks_set:cx 2011-09-08
+\toks_set:Nf 2011-09-08
+\toks_set:Nn 2011-09-08
+\toks_set:No 2011-09-08
+\toks_set:NV 2011-09-08
+\toks_set:Nv 2011-09-08
+\toks_set:Nx 2011-09-08
+\toks_show:c 2011-09-08
+\toks_show:N 2011-09-08
+\toks_use_clear:c 2011-09-08
+\toks_use_clear:N 2011-09-08
+\toks_use_gclear:c 2011-09-08
+\toks_use_gclear:N 2011-09-08
+\toks_use:c 2011-09-08
+\toks_use:N 2011-09-08
+\uptex_...:D 2020-01-01
+\use_i_after_else:nw 2011-12-30
+\use_i_after_fi:nw 2011-12-30
+\use_i_after_or:nw 2011-12-30
+\use_i_after_orelse:nw 2011-12-30
+\utex_...:D 2020-01-01
+\vbox_gset_inline_begin:c 2014-08-09
+\vbox_gset_inline_begin:N 2014-08-09
+\vbox_gset_inline_end: 2014-08-09
+\vbox_set_inline_begin:c 2014-08-09
+\vbox_set_inline_begin:N 2014-08-09
+\vbox_set_inline_end: 2014-08-09
+\vbox_unpack_clear:c 2021-07-07
+\vbox_unpack_clear:N 2021-07-07
+\xetex_...:D 2020-01-01
+\xetex_if_engine_p: 2017-01-01
+\xetex_if_engine:F 2017-01-01
+\xetex_if_engine:T 2017-01-01
+\xetex_if_engine:TF 2017-01-01
+[fp function] round+ 2018-03-05
+[fp function] round- 2018-03-05
+[fp function] round0 2018-03-05
+[key property] .choice_code:n 2015-07-14
+[key property] .choice_code:x 2015-07-14
+[key property] .code:x 2013-07-10
+[key property] .generate_choices:n 2015-07-14
+[key property] .meta:x 2013-07-10
+[key property] .value_forbidden: 2017-01-01
+[key property] .value_required: 2017-01-01
+Variants of n into N/c 2018-12-27
+Variants of N into n/o/V/v/f/x 2018-12-27
+Lua l3kernel table and functions therein 2022-01-06
+----------------------------------------------------------
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3prefixes.csv b/Master/texmf-dist/doc/latex-dev/l3kernel/l3prefixes.csv
new file mode 100644
index 00000000000..54fb41bdeb2
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3prefixes.csv
@@ -0,0 +1,296 @@
+Prefix,Module or package,Registrant,Homepage,Source repository,Issue tracker,First registered,Last update,Notes
+ENdiagram,endiagram,Clemens Niederberger,https://bitbucket.org/cgnieder/endiagram/,git@bitbucket.org:cgnieder/endiagram.git,https://bitbucket.org/cgnieder/endiagram/issues,2013-03-16,2013-03-16,
+GS,gs1,Markus Kohm,,,,2013-03-16,2013-03-16,
+MOdiagram,modiagram,Clemens Niederberger,https://bitbucket.org/cgnieder/modiagram/,git@bitbucket.org:cgnieder/modiagram.git,https://bitbucket.org/cgnieder/modiagram/issues,2013-03-16,2013-03-16,
+UFca,citeall,Ulrike Fischer,,,,2015-04-09,2016-02-26,
+acro,acro,Clemens Niederberger,https://github.com/cgnieder/acro/,https://github.com/cgnieder/acro.git,https://github.com/cgnieder/acro/issues,2013-03-16,2020-04-14,
+affiliations,langsci-affiliations,Felix Kopecky,https://ctan.org/pkg/langsci-affiliations,https://github.com/langsci/langsci-affiliations,https://github.com/langsci/langsci-affiliations/issues,2021-02-18,2021-02-18,
+akshar,akshar,Vu Van Dung,https://github.com/joulev/akshar,https://github.com/joulev/akshar.git,https://github.com/joulev/akshar/issues,2020-05-27,2020-05-27,
+algobox,algobox,Julien Rivaud,,,,2018-06-13,2018-06-13,
+alignment,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+alloc,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,Internal only but reserved
+ampersand,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+apfs,apfontspec,Qing Lee,https://github.com/CTeX-org/apfontspec,https://github.com/CTeX-org/apfontspec.git,https://github.com/CTeX-org/apfontspec/issues,2020-05-17,2020-05-17,
+arch,archaeologie,Lukas C. Bossert,http://www.biblatex-archaeologie.de,https://github.com/LukasCBossert/biblatex-archaeologie.git,https://github.com/LukasCBossert/biblatex-archaeologie/issues,2017-03-24,2017-03-24,
+array,hobby,Andrew Stacey,https://github.com/loopspace/hobby,https://github.com/loopspace/hobby,https://github.com/loopspace/hobby/issues,2013-03-16,2020-10-29,
+arsenal,arsenal,Boris Veytsman,https://github.com/borisveytsman/arsenal,https://github.com/borisveytsman/arsenal,https://github.com/borisveytsman/arsenal/issues,2023-09-04,2023-09-04,
+atsign,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+avm,langsci-avm,Felix Kopecky,https://ctan.org/pkg/langsci-avm,https://github.com/langsci/langsci-avm,https://github.com/langsci/langsci-avm/issues,2020-03-11,2020-03-11,
+babellatin,babel-latin,Keno Wehr,https://ctan.org/pkg/babel-latin,https://github.com/wehro/babel-latin,https://github.com/wehro/babel-latin/issues,2021-08-23,2021-08-23,
+backend,l3backend,The LaTeX 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,
+backslash,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+bearwear,bearwear,Ulrike Fischer,https://github.com/u-fischer/bearwear,https://github.com/u-fischer/bearwear,https://github.com/u-fischer/bearwear/issues,2020-04-24,2020-04-24,
+beuron,beuron,Keno Wehr,https://ctan.org/pkg/beuron,,,2021-08-23,2021-08-23,
+bitset,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2020-12-26,2020-12-26,
+block,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2023-10-17,2023-10-17,
+bool,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+box,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+bxjh,BXjaholiday,Takuto Asakura,https://github.com/wtsnjp/BXjaholiday,https://github.com/wtsnjp/BXjaholiday.git,https://github.com/wtsnjp/BXjaholiday/issues,2018-02-02,2019-02-02,
+cascade,cascade,F. Pantigny,,,,2020-07-21,2020-07-21,
+catcode,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+cctab,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28,
+cellprops,cellprops,Julien Rivaud,,,,2018-06-13,2018-06-13,
+chaos,"chaos,schleuderpackung",Marei Peischl,https://ds.ccc.de/,,,2021-05-28,2021-05-28,
+char,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+chemformula,chemformula,Clemens Niederberger,https://github.com/cgnieder/chemformula/,https://github.com/cgnieder/chemformula.git,https://github.com/cgnieder/chemformula/issues,2013-03-16,2020-04-14,
+chemmacros,chemmacros,Clemens Niederberger,https://github.com/cgnieder/chemmacros/,https://github.com/cgnieder/chemmacros.git,https://github.com/cgnieder/chemmacros/issues,2013-03-16,2020-04-14,
+chemnum,chemnum,Clemens Niederberger,https://github.com/cgnieder/chemnum/,https://github.com/cgnieder/chemnum.git,https://github.com/cgnieder/chemnum/issues,2013-03-16,2020-04-14,
+chk,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,Currently internal-only but reserved
+circumflex,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+classics,classics,Eduardo C. Lourenço de Lima,,,,2013-03-16,2013-03-16,
+clist,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+cmd,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2021-01-20,2021-03-03,
+code,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2021-04-23,2021-04-23,
+codedesc,codedescribe,Alceu Frigeri,https://github.com/alceu-frigeri/codedescribe,https://github.com/alceu-frigeri/codedescribe,https://github.com/alceu-frigeri/codedescribe/issues,2023-05-15,2023-05-15,
+codedoc,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,Somewhat experimental: may change
+codehigh,codehigh,Jianrui Lyu,https://github.com/lvjr/codehigh,https://github.com/lvjr/codehigh.git,https://github.com/lvjr/codehigh/issues,2022-04-02,2022-04-02,
+codelist,codelisting,Alceu Frigeri,https://github.com/alceu-frigeri/codedescribe,https://github.com/alceu-frigeri/codedescribe,https://github.com/alceu-frigeri/codedescribe/issues,2023-05-15,2023-05-15,
+codepoint,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+coffin,"l3kernel,xcoffins",The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+colon,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+color,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+conteq,conteq,Joachim Breitner,https://github.com/nomeata/conteq,https://github.com/nomeata/conteq.git,https://github.com/nomeata/conteq/issues,2013-05-26,2013-05-27,
+cookingunits,cooking-units,Ben Vitecek,https://github.com/Vidabe/cooking-units,https://github.com/Vidabe/cooking-units.git,https://github.com/Vidabe/cooking-units/issues,2018-09-26,2018-09-26,
+cs,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+csl,citation-style-language,Zeping Lee,https://github.com/zepinglee/citeproc-lua,https://github.com/zepinglee/citeproc-lua.git,https://github.com/zepinglee/citeproc-lua/issues,2022-05-09,2022-05-09,
+csvsim,csvsimple,Thomas F. Sturm,https://github.com/T-F-S/csvsimple,https://github.com/T-F-S/csvsimple.git,https://github.com/T-F-S/csvsimple/issues,2020-02-19,2020-02-19,
+ctex,ctex,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,2014-03-08,2014-03-08,
+ctuthesis,ctuthesis,Tom Hejda,https://github.com/tohecz/ctuthesis,https://github.com/tohecz/ctuthesis.git,https://github.com/tohecz/ctuthesis/issues,2015-07-26,2015-07-26,
+debug,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2017-07-15,2017-07-15,
+denisbdoc,denisbdoc,Denis Bitouzé,https://github.com/dbitouze/denisbdoc,git@github.com:dbitouze/denisbdoc.git,,2020-05-13,2020-05-13,
+deriv,derivative,Simon Jensen,,,,2019-07-24,2019-07-24,
+didec,didec,Thomas F. Sturm,https://github.com/T-F-S/didec,https://github.com/T-F-S/didec.git,https://github.com/T-F-S/didec/issues,2024-02-02,2024-02-02,
+diffcoeff,diffcoeff,Andrew Parsloe,,,,2019-08-26,2019-08-26,
+dim,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+document,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2019-06-02,2019-06-02,
+dollar,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+driver,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+dry,dry,Michiel Helvensteijn,,,,2013-01-18,2013-01-18,
+ducksay,ducksay,Jonathan P. Spratte,https://github.com/Skillmon/ltx_ducksay,git@github.com:Skillmon/ltx_ducksay.git,https://github.com/Skillmon/ltx_ducksay/issues,2019-06-07,2019-06-07,
+duckuments,duckuments,Jonathan P. Spratte,https://github.com/Skillmon/ltx_duckuments,git@github.com:Skillmon/ltx_duckuments.git,https://github.com/Skillmon/ltx_duckuments/issues,2019-06-07,2019-06-07,
+e,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+else,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28,
+emoji,emoji,Xiangdong Zeng,https://github.com/stone-zeng/latex-emoji,https://github.com/stone-zeng/latex-emoji.git,https://github.com/stone-zeng/latex-emoji/issues,2020-03-08,2020-03-08,
+emojicite,emojicite,Leon Sixt,https://github.com/berleon/emojicite,https://github.com/berleon/emojicite.git,https://github.com/berleon/emojicite/issues/,2020-04-14,2020-04-20,
+empty,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+enotez,enotez,Clemens Niederberger,https://github.com/cgnieder/enotez/,https://github.com/cgnieder/enotez.git,https://github.com/cgnieder/enotez/issues,2013-03-16,2020-04-14,
+etex,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+etl,etl,Jonathan P. Spratte,https://github.com/Skillmon/ltx_etl,git@github.com:Skillmon/ltx_etl.git,https://github.com/Skillmon/ltx_etl/issues,2021-08-16,2021-08-16,
+exp,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+expl,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+exsheets,exsheets,Clemens Niederberger,https://bitbucket.org/cgnieder/exsheets/,git@bitbucket.org:cgnieder/exsheets.git,https://bitbucket.org/cgnieder/exsheets/issues,2013-03-16,2013-03-16,
+extblx,biblatex-ext,Moritz Wemheuer,https://github.com/moewew/biblatex-ext/,https://github.com/moewew/biblatex-ext.git,https://github.com/moewew/biblatex-ext/issues,2020-02-09,2020-02-09,
+exwf,exwrapfig,Takuto Asakura,https://github.com/wtsnjp/exwrapfig,https://github.com/wtsnjp/exwrapfig.git,https://github.com/wtsnjp/exwrapfig/issues,2018-06-07,2018-06-07,
+false,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2021-04-23,2021-04-23,
+fdu,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,
+fdudoc,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,
+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 LaTeX 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 LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+filehook,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2020-10-01,2021-03-03,
+fingering,recorder-fingering,Alan Munn,https://github.com/amunn/recorder-fingering,https://github.com/amunn/recorder-fingering,https://github.com/amunn/recorder-fingering/issues,2023-02-17,2023-02-17,
+flag,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,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-dashundergaps.git,https://github.com/FrankMittelbach/fmitex-dashundergaps/issues,2018-06-24,2021-10-11,
+fmuft,unicodefonttable,Frank Mittelbach,https://www.latex-project.org/,https://github.com/FrankMittelbach/fmitex-unicodefonttable.git,https://github.com/FrankMittelbach/fmitex-unicodefonttable/issues,2020-02-17,2021-10-11,
+fmwao,widows-and-orphans,Frank Mittelbach,https://www.latex-project.org/,https://github.com/FrankMittelbach/fmitex-widows-and-orphans.git,https://github.com/FrankMittelbach/fmitex-widows-and-orphans/issues,2018-09-26,2018-09-26,
+fnote,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2023-10-17,2023-10-17,
+fnpct,fnpct,Clemens Niederberger,https://github.com/cgnieder/fnpct/,https://github.com/cgnieder/fnpct.git,https://github.com/cgnieder/fnpct/issues,2013-03-16,2020-04-14,
+fontsizes,fontsizes,Julien Rivaud,,,,,2018-06-13,
+fontspec,fontspec,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/fontspec.git,https://github.com/latex3/fontspec/issues,2013-03-16,2024-02-15,
+fp,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+fun,functional,Jianrui Lyu,https://github.com/lvjr/functional,https://github.com/lvjr/functional.git,https://github.com/lvjr/functional/issues,2022-04-02,2022-04-02,
+galley,l3galley,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+gatherenum,gatherenum,Julien Rivaud,,,,2018-06-13,2018-06-14,
+getree,genealogytree,Thomas F. Sturm,https://github.com/T-F-S/genealogytree,https://github.com/T-F-S/genealogytree.git,https://github.com/T-F-S/genealogytree/issues,2020-02-19,2020-02-19,
+ghsystem,ghsystem,Clemens Niederberger,https://github.com/cgnieder/ghsystem/,https://github.com/cgnieder/ghsystem.git,https://github.com/cgnieder/ghsystem/issues,2013-03-16,2020-04-14,
+graph,lt3graph,Michiel Helvensteijn,,,,2014-02-05,2014-02-05,
+group,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+gtl,gtl,Bruno Le Floch,https://github.com/blefloch/latex-gtl,https://github.com/blefloch/latex-gtl.git,https://github.com/blefloch/latex-gtl/issues,2015-09-22,2015-09-22,
+gzt,gzt,Denis Bitouzé,https://github.com/dbitouze/gzt,git@github.com:dbitouze/gzt.git,,2020-05-13,2020-05-13,
+hash,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+hbox,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+hcoffin,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28,
+hobete,hobete,Tobias Görlach,http://www.disk0s1.de,,,2012-11-07,2012-11-07,
+hook,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2019-06-03,2021-03-03,
+hyp,hyperref,The LaTeX 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 LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+incgra,incgraph,Thomas F. Sturm,https://github.com/T-F-S/incgraph,https://github.com/T-F-S/incgraph.git,https://github.com/T-F-S/incgraph/issues,2021-10-11,2021-10-11,
+inf,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+initex,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2021-04-23,2021-04-23,
+insert,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28,
+int,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+intarray,l3kernel,The LaTeX 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 LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+iot,iot,Island of TeX,https://islandoftex.gitlab.io,https://gitlab.com/islandoftex/texmf,https://gitlab.com/groups/islandoftex/texmf/-/issues,2023-07-18,2023-07-18,
+iow,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+iwonamath,iwonamath,Boris Veytsman,https://github.com/borisveytsman/iwonamath,https://github.com/borisveytsman/iwonamath,https://github.com/borisveytsman/iwonamath/issues,2023-09-04,2023-09-04,
+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,
+job,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2021-04-23,2021-04-23,
+kernel,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+keys,"l3kernel,l3keys2e,ltkeys",The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+keyval,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+kgl,kantlipsum,Enrico Gregorio,,,,2013-03-16,2013-03-16,
+kivitendo,"kiviletter, kivitables",Marei Peischl for Kivitendo,https://www.kivitendo.de/,https://github.com/kivitendo/kivitendo-erp,https://forum.kivitendo.de/,2021-05-28,2021-05-28,
+langsci,langscibook,Language Science Press,https://langsci-press.org,https://github.com/langsci/langscibook,https://github.com/langsci/langscibook/issues,2021-07-20,2021-07-21,
+left,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+lltxmath,lualatex-math,Philipp Stephani,https://github.com/phst/lualatex-math,https://github.com/phst/lualatex-math.git,https://github.com/phst/lualatex-math/issues,2012-11-07,2012-11-07,
+log,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+lua,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+luabridge,lt3luabridge,Vít Novotný,https://ctan.org/pkg/lt3luabridge,https://github.com/witiko/lt3luabridge.git,https://github.com/witiko/lt3luabridge/issues,2022-06-25,2022-06-25,
+luatex,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+lwc,lua-widow-control,Max Chernoff,https://github.com/gucci-on-fleek/lua-widow-control,https://github.com/gucci-on-fleek/lua-widow-control.git,https://github.com/gucci-on-fleek/lua-widow-control/issues,2022-02-24,2022-02-24,
+mark,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+markdown,markdown,Vít Novotný,https://ctan.org/pkg/markdown,https://github.com/witiko/markdown.git,https://github.com/witiko/markdown/issues,2021-09-08,2021-09-08,
+marks,l3kernel/xmarks,The LaTeX 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,
+marks,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2021-03-03,2021-03-03,
+math,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+mathcolor,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2022-01-20,2022-01-20,
+max,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+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,
+meta,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2022-01-20,2022-01-20,
+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 LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+mix,media9,Alexander Grahn,,https://gitlab.com/agrahn/media9,https://gitlab.com/agrahn/media9/issues,2013-01-18,2020-04-15,
+mode,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+morewrites,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,
+msg,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+msvg,media4svg,Alexander Grahn,,https://gitlab.com/agrahn/media4svg,https://gitlab.com/agrahn/media4svg/issues,2020-04-15,2020-04-15,
+muskip,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+namedef,namedef,Phelype Oleinik,https://github.com/PhelypeOleinik/namedef,https://github.com/PhelypeOleinik/namedef.git,https://github.com/PhelypeOleinik/namedef/issues,2020-06-22,2020-06-22,
+nan,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+nicematrix,nicematrix,François Pantigny,,,,2019-12-19,2019-12-19,
+nil,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+ninecolors,ninecolors,Jianrui Lyu,https://github.com/lvjr/ninecolors,https://github.com/lvjr/ninecolors.git,https://github.com/lvjr/ninecolors/issues,2022-04-02,2022-04-02,
+nmc,numerica,Andrew Parsloe,,,,2021-06-30,2021-06-30,
+no,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+notestobib,notes2bib,Joseph Wright,https://github.com/josephwright/notes2bib,https://github.com/josephwright/notes2bib.git,https://github.com/josephwright/notes2bib/issues,2012-11-07,2012-11-07,
+novalue,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+nwejm,nwejm,Denis Bitouzé,https://github.com/dbitouze/nwejm,git@github.com:dbitouze/nwejm.git,,2020-05-13,2020-05-13,
+ocgbase,ocgx2,Alexander Grahn,,https://gitlab.com/agrahn/ocgx2,https://gitlab.com/agrahn/ocgx2/issues,2016-02-26,2020-04-15,
+ocgxii,ocgx2,Alexander Grahn,,https://gitlab.com/agrahn/ocgx2,https://gitlab.com/agrahn/ocgx2/issues,2016-02-26,2020-04-15,
+one,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+or,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28,
+other,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2021-04-23,2021-04-23,
+overleaf,overleaf,Overleaf,https://www.overleaf.com/about,,support+tex-dev@overleaf.com,2020-05-27,2020-05-27,
+para,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2021-03-03,2021-03-03,
+parameter,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+pbs,media9,Alexander Grahn,,https://gitlab.com/agrahn/media9,https://gitlab.com/agrahn/media9/issues,2016-02-26,2020-04-15,
+pdf,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2019-06-02,2019-06-02,
+pdfannot,l3pdfmanagement,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2021-02-23,2021-02-23,
+pdffile,l3pdfmanagement,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2021-02-23,2021-02-23,
+pdfmanagement,l3pdfmanagement,The LaTeX 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,
+pdfmeta,l3pdfmanagement,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2021-02-23,2021-02-23,
+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 LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+pdfxform,l3pdfmanagement,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2021-02-23,2021-02-23,
+peek,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+percent,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+pgf,pgf,The PGF/TikZ Team,https://pgf-tikz.github.io,https://github.com/pgf-tikz/pgf,https://github.com/pgf-tikz/pgf/issues,2020-07-03,2020-07-03,
+pgfmxfp,pgfmath-xfp,Jonathan P. Spratte,https://github.com/Skillmon/ltx_pgfmath-xfp,https://github.com/Skillmon/ltx_pgfmath-xfp,https://github.com/Skillmon/ltx_pgfmath-xfp/issues,2021-05-20,2021-05-20,
+phone,phonenumbers,Keno Wehr,https://ctan.org/pkg/phonenumbers,https://github.com/wehro/phonenumbers,https://github.com/wehro/phonenumbers/issues,2021-08-23,2021-08-23,
+pi,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+piton,piton,François Pantigny,,,,29/09/2022,29/09/2022,
+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,,
+postnotes,postnotes,gusbrs,https://github.com/gusbrs/postnotes,https://github.com/gusbrs/postnotes.git,https://github.com/gusbrs/postnotes/issues,2022-04-23,2022-04-23,
+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 LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+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 LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+property,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2021-01-20,2021-03-03,
+pseudo,pseudo,Magnus Lie Hetland,https://github.com/mlhetland/pseudo.sty,https://github.com/mlhetland/pseudo.sty.git,https://github.com/mlhetland/pseudo.sty/issues,2019-06-24,2019-06-24,
+ptex,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2015-07-28,2015-07-28,
+ptxcd,ptxcd,Marei Peischl,,,,2020-07-27,2020-07-27,Used for specific corporate design templates
+qrbill,qrbill,Marei Peischl,https://github.com/peiTeX/qrbill,https://github.com/peiTeX/qrbill.git,https://github.com/peiTeX/qrbill/issues,2020-06-27,2020-06-27,
+quark,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+rainbow,beamertheme-rainbow,samcarter,https://github.com/samcarter/beamertheme-rainbow,https://github.com/samcarter/beamertheme-rainbow,https://github.com/samcarter/beamertheme-rainbow/issues,2023-07-04,2023-07-04,
+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,
+rawobjects,rawobjects,Paolo De Donato,https://github.com/Loara/lt3rawobjects,https://github.com/Loara/lt3rawobjects/lt3rawobjects.git,https://github.com/Loara/lt3rawobjects/issues,2022-07-19,2022-07-19,
+recursion,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+ref,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2020-12-24,2020-12-24,
+regex,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2018-04-06,
+reverse,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28,
+right,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+rivbook,rivbook,Julien Rivaud,,,,2018-06-13,2018-06-14,
+rivmath,rivmath,Julien Rivaud,,,,2018-06-13,2018-06-13,
+sanuml,sanitize-umlaut,Thomas F. Sturm,https://github.com/T-F-S/sanitize-umlaut,https://github.com/T-F-S/sanitize-umlaut.git,https://github.com/T-F-S/sanitize-umlaut/issues,2022-07-19,2022-07-19,
+scan,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+schulma,schulmathematik,Keno Wehr,https://ctan.org/pkg/schulmathematik,,,2021-08-23,2021-08-23,
+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,
+scripture,scripture,David Purton,https://github.com/dcpurton/scripture,https://github.com/dcpurton/scripture.git,https://github.com/dcpurton/scripture/issues,2022-07-27,2022-07-27,
+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 LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+shipout,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2020-10-01,2021-03-03,
+silly,sillypage,Paulo Cereda,https://github.com/cereda/sillypage,https://github.com/cereda/sillypage.git,https://github.com/cereda/sillypage/issues,2022-02-01,2022-02-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,
+skel,skeldoc,Magnus Lie Hetland,https://github.com/mlhetland/skeldoc.sty,https://github.com/mlhetland/skeldoc.sty.git,https://github.com/mlhetland/skeldoc.sty/issues,2021-01-04,2021-01-04,
+skip,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+slcd,se2thesis,Stephan Lukasczyk,https://github.com/se2p/se2thesis,https://github.com/se2p/se2thesis,https://github.com/se2p/se2thesis/issues,2023-10-18,2023-10-18,
+socket,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2023-10-17,2023-10-17,
+sort,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2017-02-13,
+space,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+starray,starray,Alceu Frigeri,https://github.com/alceu-frigeri/starray,https://github.com/alceu-frigeri/starray,https://github.com/alceu-frigeri/starray/issues,2023-05-15,2023-05-15,
+statistics,statistics,Julien Rivaud,https://gitlab.com/frnchfrgg-latex/statistics,https://gitlab.com/frnchfrgg-latex/statistics.git,https://gitlab.com/frnchfrgg-latex/statistics/issues,2018-06-25,2018-06-25,
+stm,lt3-stm,CV Radhakrishnan,http://www.cvr.cc/,,,2014-02-26,2014-02-26,
+stop,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+str,"l3kernel,l3str-format",The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+stycmd,styledcmd,Paolo De Donato,https://github.com/Loara/styledcmd,https://github.com/Loara/styledcmd.git,https://github.com/Loara/styledcmd/issues,2021-08-30,2021-08-30,
+substances,substances,Clemens Niederberger,https://github.com/cgnieder/substances/,https://github.com/cgnieder/substances.git,https://github.com/cgnieder/substances/issues,2013-03-16,2020-04-14,
+sys,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2015-09-07,2015-09-08,
+tag,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2019-06-02,2019-06-02,
+tasks,tasks,Clemens Niederberger,https://github.com/cgnieder/tasks/,https://github.com/cgnieder/tasks.git,https://github.com/cgnieder/cgnieder/tasks/issues,2013-03-16,2020-04-14,
+tbl,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2023-10-23,2023-10-23,
+tblr,tabularray,Jianrui Lyu,https://github.com/lvjr/tabularray,https://github.com/lvjr/tabularray.git,https://github.com/lvjr/tabularray/issues,2022-04-02,2022-04-02,
+tcobox,tcolorbox,Thomas F. Sturm,https://github.com/T-F-S/tcolorbox,https://github.com/T-F-S/tcolorbox.git,https://github.com/T-F-S/tcolorbox/issues,2020-02-19,2020-02-19,
+template,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2021-01-20,2021-03-03,
+term,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+tex,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+text,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2020-01-15,2020-01-15,
+tikzfill,tikzfill,Thomas F. Sturm,https://github.com/T-F-S/tikzfill,https://github.com/T-F-S/tikzfill.git,https://github.com/T-F-S/tikzfill/issues,2022-07-19,2022-07-19,
+tikzlings,tikzlings,samcarter,https://github.com/samcarter/tikzlings,git@github.com:samcarter/tikzlings.git,https://github.com/samcarter/tikzlings/issues,2023-02-17,2023-02-17,
+tikzsymbols,tikzsymbols,Ben Vitecek,https://github.com/Vidabe/tikzsymbols,https://github.com/Vidabe/tikzsymbols.git,https://github.com/Vidabe/tikzsymbols/issues,2018-09-26,2018-09-26,
+tilde,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+tl,"l3kernel,l3tl-build",The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+tmpa,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+tmpb,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+token,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+true,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2021-04-23,2021-04-23,
+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,
+um,unicode-math,Will Robertson,https://github.com/wspr/unicode-math,https://github.com/wspr/unicode-math.git,https://github.com/wspr/unicode-math/issues,2013-03-16,2013-03-16,
+underscore,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+unravel,unravel,Bruno Le Floch,https://github.com/blefloch/latex-unravel,https://github.com/blefloch/latex-unravel.git,https://github.com/blefloch/latex-unravel/issues,2015-09-22,2015-09-22,
+uptex,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2015-07-28,2015-07-28,
+use,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+utex,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2015-07-28,2015-07-28,
+vbox,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+vcoffin,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28,
+wheelchart,wheelchart,Matthias Floré,,,,2023-12-07,2023-12-07,
+withargs,withargs,Michiel Helvensteijn,,,,2014-02-05,2014-02-05,
+witharrows,witharrows,François Pantigny,,,,2019-12-19,2019-12-19,
+xeCJK,xecjk,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,2013-05-26,2013-05-26,
+xetex,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+xfrac,xfrac,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/xfrac.git,https://github.com/latex3/xfrac/issues,2012-09-27,2024-02-14,
+xmarks,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2021-03-03,2021-03-03,
+xparse,xparse,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+xpatch,"regexpatch,xpatch",Enrico Gregorio,,,,2013-03-16,2013-03-16,
+xpeek,xpeek,Joel C. Salomon,,,,2013-03-16,2013-03-16,
+xpinyin,xpinyin,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,2013-03-16,2013-05-26,
+xsb,xsavebox,Alexander Grahn,,https://gitlab.com/agrahn/xsavebox,https://gitlab.com/agrahn/xsavebox/issues,2016-02-26,2020-04-15,
+xsim,xsim,Clemens Niederberger,https://github.com/cgnieder/xsim/,https://github.com/cgnieder/xsim.git,https://github.com/cgnieder/xsim/issues,2020-04-14,2020-04-14,
+xtemplate,xtemplate,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+yoin,yoin,Tom Hejda,https://github.com/tohecz/yoin,https://github.com/tohecz/yoin.git,https://github.com/tohecz/yoin/issues,2016-02-22,2016-02-22,
+zero,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+zhlipsum,zhlipsum,Xiangdong Zeng,https://github.com/stone-zeng/zhlipsum,https://github.com/stone-zeng/zhlipsum.git,https://github.com/stone-zeng/zhlipsum/issues,2018-06-14,2018-06-14,
+zhnum,zhnumber,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,2013-03-16,2013-05-26,
+zrefcheck,zref-check,gusbrs,https://github.com/gusbrs/zref-check,https://github.com/gusbrs/zref-check,https://github.com/gusbrs/zref-check/issues,2021-08-05,2021-08-05,
+zrefclever,zref-clever,gusbrs,https://github.com/gusbrs/zref-clever,https://github.com/gusbrs/zref-clever,https://github.com/gusbrs/zref-clever/issues,2021-11-29,2021-11-29,
+zrefvario,zref-vario,gusbrs,https://github.com/gusbrs/zref-vario,https://github.com/gusbrs/zref-vario,https://github.com/gusbrs/zref-vario/issues,2022-02-02,2022-02-02,
+zxjt,zxjatype,Takayuki Yato,,,,2013-03-16,2013-03-16,
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3prefixes.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3prefixes.pdf
new file mode 100644
index 00000000000..f3241dfd652
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3prefixes.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3prefixes.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3prefixes.tex
new file mode 100644
index 00000000000..6c7cf24244f
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3prefixes.tex
@@ -0,0 +1,91 @@
+\iffalse meta-comment
+
+File:l3prefixes.tex
+
+Copyright (C) 2019,2021 The LaTeX Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "l3kernel bundle" (The Work in LPPL)
+and all files in that bundle must be distributed together.
+
+The released version of this bundle is available from CTAN.
+
+-----------------------------------------------------------------------
+
+The development version of the bundle can be found at
+
+ https://github.com/latex3/latex3
+
+for those people who are interested.
+
+\fi
+
+\documentclass{article}
+\usepackage{expl3}
+\usepackage{array}
+\usepackage{booktabs}
+\usepackage{longtable}
+\ExplSyntaxOn
+\cs_new_protected:Npn \__prefix_readi:w #1 " #2 " #3 \q_stop
+ {
+ \quark_if_nil:nTF {#2}
+ { \__prefix_readii:w #1 \q_stop }
+ { \__prefix_readi:w #1 {#2} #3 \q_stop }
+ }
+\cs_new_protected:Npn \__prefix_readii:w #1 , #2 , #3 , #4 \q_stop
+ { \__prefix_readiii:nnw {#1} {#3} #2 , \q_stop }
+\cs_new_protected:Npn \__prefix_readiii:nnw #1 #2 #3 , #4 \q_stop
+ {
+ \tl_put_right:Nn \l_tmpb_tl { #1 & #3 & #2 \\ }
+ \tl_if_blank:nF {#4}
+ { \clist_map_inline:nn {#4} { \tl_put_right:Nn \l_tmpb_tl { & ##1 \\ } } }
+ }
+\ior_new:N \l_tmpa_ior
+\ior_open:Nn \l_tmpa_ior { l3prefixes.csv }
+\ior_get:NN \l_tmpa_ior \l_tmpa_tl % Throw away
+\cs_new_protected:Npn \PrintTable
+ {
+ \tl_set:Nn \l_tmpb_tl
+ {
+ \begin { longtable } { @{} *{2}{>{\ttfamily}l} l @{} }
+ \toprule
+ \multicolumn{1}{@{}l}{Prefix}
+ & \multicolumn{1}{l@{}}{Module}
+ & Registrant \\
+ \midrule
+ \endhead
+ \bottomrule
+ \endfoot
+ }
+ \ior_map_inline:Nn \l_tmpa_ior
+ { \__prefix_readi:w ##1 " \q_nil " \q_stop }
+ \tl_put_right:Nn \l_tmpb_tl { \end { longtable } }
+ \tl_replace_all:Nnn \l_tmpb_tl { LaTeX3 } { \LaTeX3 }
+ \tl_use:N \l_tmpb_tl
+ }
+\ExplSyntaxOff
+\begin{document}
+
+This file lists the prefixes which have been registered with the \LaTeX{}
+team for use in \textsf{expl3} code. Programmers are invited to register their
+(public) prefix use by contacting the team at
+\begin{quote}
+ \texttt{modules@latex-project.org}
+\end{quote}
+with the relevant information for the \texttt{.csv} file, i.e.,
+\textit{Prefix, Module or package, Registrant, Homepage, Source repository, Issue tracker}.
+
+
+We recommend that short prefixes are avoided as there are a limited
+number of these very general names available. The
+file \texttt{l3prefixes.csv} contains more details on each module, for
+example any information on source repository or issue tracker.
+
+\PrintTable
+\end{document}
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3styleguide.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3styleguide.pdf
new file mode 100644
index 00000000000..89d4efff148
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3styleguide.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3styleguide.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3styleguide.tex
new file mode 100644
index 00000000000..da169cd8b16
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3styleguide.tex
@@ -0,0 +1,303 @@
+\iffalse meta-comment
+
+File: l3styleguide.tex
+
+Copyright (C) 2011,2012,2014-2024 The LaTeX Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "l3kernel bundle" (The Work in LPPL)
+and all files in that bundle must be distributed together.
+
+The released version of this bundle is available from CTAN.
+
+\fi
+
+\documentclass{l3doc}
+
+
+\title{%
+ The \LaTeX3 kernel: style guide for code authors%
+}
+\author{%
+ The \LaTeX{} Project\thanks
+ {%
+ E-mail:
+ \href{mailto:latex-team@latex-project.org}%
+ {latex-team@latex-project.org}%
+ }%
+}
+\date{Released 2024-04-11}
+
+\begin{document}
+
+\maketitle
+
+\tableofcontents
+
+\section{Introduction}
+
+This document is intended as a style guide for authors of code and
+documentation for the \LaTeX3 kernel. It covers both aspects of coding
+style and the formatting of the sources. The aim of providing these
+guidelines is help ensure consistency of the code and sources from
+different authors. Experience suggests that in the long-term this helps
+with maintenance. There will of course be places where there are
+exceptions to these guidelines: common sense should always be
+applied!
+
+\section{Documentation style}
+
+\LaTeX3 source and documentation should be written using the document
+class \cls{l3doc} in \file{dtx} format. This class provides a number
+of logical mark up elements, which should be used where possible.
+In the main, this is standard \LaTeX{} practice, but there are a
+few points to highlight:
+\begin{itemize}
+ \item
+ Where possible, use \cs{cs} to mark up control sequences
+ rather than using a verbatim environment.
+ \item
+ Arguments which are given in braces should be marked using
+ \cs{Arg} when code-level functions are discussed, but using
+ \cs{marg} for document functions.
+ \item
+ The names \TeX{}, \LaTeX{}, \emph{etc}.\ use the normal logical mark
+ up followed by an empty group (|{}|), with the exception of |\LaTeX3|,
+ where the number should follow directly.
+ \item
+ Where in line verbatim text is used, it should be marked up
+ using the \verb=|...|= construct (\emph{i.e.}~vertical bars delimit
+ the verbatim text).
+ \item In line quotes should be marked up using the \cs{enquote}
+ function.
+ \item
+ Where numbers in the source have a mathematical meaning,
+ they should be included in math mode. Such in-line math mode
+ material should be marked up using |$...$| and \emph{not}
+ |\(...\)|.
+\end{itemize}
+
+Line length in the source files should be under $80$
+characters where possible, as this helps keep everything on the screen
+when editing files. In the \file{dtx} format, documentation lines start
+with a \texttt{\%}, which is usually followed by a space to leave a
+\enquote{comment margin} at the start of each line.
+
+As with code indenting (see later), nested environments and arguments
+should be indented by (at least) two spaces to make the nature of the nesting
+clear. Thus for example a typical arrangement for the \env{function}
+environment might be
+\begin{verbatim*}
+\begin{function}{\seq_gclear:N, \seq_gclear:c}
+ \begin{syntax}
+ \cs{seq_gclear:N} \meta{sequence}
+ \end{syntax}
+ Clears all entries from the \meta{sequence} globally.
+\end{function}
+\end{verbatim*}
+The \enquote{outer} \verb*|% \begin{function}| should have the customary
+space after the |%| character at the start of the line.
+
+In general, a single \env{function} or \env{macro} environment should be
+used for a group of closely-related functions, for example argument
+specification variants. In such cases, a comma-separated list should be
+used, as shown in the preceding example.
+
+\section{Format of the code itself}
+
+The requirement for fewer than $80$ characters per line applies to the code
+itself as well as the surrounding documentation. A number of the general
+style principles for \LaTeX3 code apply: these are described in the following
+paragraph and an example is then given.
+
+With the exception of simple runs of parameter (|{#1}|, |#1#2|,
+\emph{etc.}), everything should be divided up using spaces to make the code
+more readable. In general, these will be single spaces, but in some
+places it makes more sense to align parts of the code to emphasise
+similarity. (Tabs should not be used for introducing white space.)
+
+Each conceptually-separate step in a function should be on a separate
+line, to make the meaning clearer. Hence the \texttt{false} branch
+in the example uses two lines for the two auxiliary function uses.
+
+Within the definition, a two-space indent should be used to show each
+\enquote{level} of code. Thus in the example \cs{tl_if_empty:nTF} is
+indented by two spaces, but the two branches are indented by four
+spaces. Within the \texttt{false} branch, the need for multiple lines
+means that an additional two-space indent should be used to show that
+these lines are all part of the brace group.
+
+The result of these lay-out conventions is code which in general
+looks like the example:
+\begin{verbatim*}
+\cs_new:Npn \module_foo:nn #1#2
+ {
+ \tl_if_empty:nTF {#1}
+ { \module_foo_aux:n { X #2 } }
+ {
+ \module_foo_aux:nn {#1} {#2}
+ \module_foo_aux:n { #1 #2 }
+ }
+ }
+\end{verbatim*}
+
+\section{Code conventions}
+
+All code-level functions should be \enquote{long} if they accept any
+arguments, even if it seems \enquote{very unlikely} that a \cs{par} token
+will be passed. Thus \cs{cs_new_nopar:Npn} and so forth should only be used
+to create interfaces at the document level (where trapping \cs{par} tokens
+may be appropriate) or where comparison to other code known not to be
+\enquote{long} is required (\emph{e.g.}~when working with mixed
+\LaTeXe{}/\pkg{expl3} situations).
+
+The expandability of each function should be well-defined. Functions which
+cannot be fully expanded must be \texttt{protected}. This means that expandable
+functions must themselves only contain expandable material. Functions which
+use any non-expandable material must be defined using \cs{cs_new_protected:Npn}
+or similar.
+
+When using \cs{cs_generate_variant:Nn}, group related variants together
+to make the pattern clearer. A common example is variants of a function
+which has an \texttt{N}-type first argument:
+\begin{verbatim}
+ \cs_generate_variant:Nn \foo:Nn { NV , No }
+ \cs_generate_variant:Nn \foo:Nn { c , cV , co }
+\end{verbatim}
+
+There may be cases where omitting braces from \texttt{o}-type arguments
+is desirable for performance reasons. This should only be done if the
+argument is a single token, thus for example
+\begin{verbatim}
+ \tl_set:No \l_some_tl \l_some_other_tl
+\end{verbatim}
+remains clear and can be used where appropriate.
+
+\section{Private and internal functions}
+
+Private functions (those starting \cs{__}) should not be used between modules.
+The only exception is where a \enquote{family} of modules share some
+\enquote{internal} methods: this happens most obviously in the kernel itself.
+Any internal functions or variables \emph{must} be documented in the same way
+as public ones.
+
+The \pkg{DocStrip} method should be used for internal functions in a module.
+This requires a line
+\begin{quote}
+ \ttfamily
+ \%<@@=\meta{module}>
+\end{quote}
+at the start of the source (\texttt{.dtx}) file, with internal functions
+then written in the form
+\begin{verbatim}
+ \cs_new_protected:Npn \@@_function:nn #1#2
+ ...
+\end{verbatim}
+
+\subsection{Access from other modules}
+
+There may be cases where it is useful to use an internal function from
+a third-party module (this includes cases where you are the author of both
+but they are not part of the same \enquote{family}). In these cases, you should
+\emph{copy} the definition of the internal function to your code: this avoids
+relying on non-documented interfaces. At the same time, it is strongly
+encouraged that you discuss your requirements with the author of the
+code you need to access. The best long-term solution to these cases is for
+new documented interfaces to be added to the parent module.
+
+\subsection{Access to primitives}
+
+As \pkg{expl3} is still a developing system, there are places where direct
+access to engine primitives is required. These are all marked as
+\enquote{do not use} in the code and so require special handling. Where a
+programmer is sure that they need to use a primitive (for example where the
+team have not yet covered access to an area) then a local copy of the
+primitive should be made, for example
+\begin{verbatim}
+ \cs_new_eq:NN \__module_message:w \tex_message:D
+ % ...
+ \cs_new_protected:Npn \__module_fancy_msg:n #1
+ { \__module_message:w { *** #1 *** } }
+\end{verbatim}
+This approach makes it possible for the team and others to find such
+usage (by searching for the \texttt{:D} argument type) but avoids
+multiple uses in general code.
+
+At the same time, the team ask that these use cases are raised on the
+\texttt{LaTeX-L} mailing list. The team are keen to collect use cases for
+areas that have not yet been addressed and to provide new code where the
+required interfaces become clear.
+
+Programmers using primitives should be ready to make updates to their
+code as the team develop additional interfaces.
+
+\section{Auxiliary functions}
+
+In general, the team encourages the use of descriptive names in \LaTeX3 code.
+Thus many helper functions would have names which describe briefly what they do,
+rather than simply indicating that they are auxiliary to some higher-level
+function. However, there are places where one or more \texttt{aux} functions
+are required. Where possible, these should be differentiated by signature
+\begin{verbatim}
+ \cs_new_protected:Npn \@@_function:nn #1#2
+ {
+ ...
+ }
+ \cs_new_protected:Npn \@@_function_aux:nn #1#2
+ {
+ ...
+ }
+ \cs_new_protected:Npn \@@_function_aux:w #1#2 \q_stop
+ {
+ ...
+ }
+\end{verbatim}
+Where more than one auxiliary shares the same signature, the recommended naming
+scheme is \texttt{auxi}, \texttt{auxii} and so on.
+\begin{verbatim}
+ \cs_new_protected:Npn \@@_function_auxi:nn #1#2
+ {
+ ...
+ }
+ \cs_new_protected:Npn \@@_function_auxii:nn #1#2
+ {
+ ...
+ }
+\end{verbatim}
+The use of \texttt{aux_i}, \texttt{aux_ii}, \emph{etc.}\ is discouraged as this
+conflicts with the convention used by \cs{use_i:nn} and related functions.
+
+\section{Functions with `weird' arguments}
+
+When defining commands that do not follow the usual convention of accepting
+arguments as single-tokens or braced-text, the \verb|w| argument specifier
+is used to denote that the function signature cannot fully describe the syntax.
+Two examples from the \LaTeX3 kernel are:
+\begin{quote}
+\verb|\use_none_delimit_by_q_stop:w| $\langle$\,\emph{text}\,$\rangle$ \verb|\q_stop|\\
+\verb|\use_i_delimit_by_q_stop:nw| \char`\{ $\langle$\,\emph{arg}\,$\rangle$\char`}\,$\langle$\,\emph{text}\,$\rangle$ \verb|\q_stop|
+\end{quote}
+More complex definitions are possible if commands are to parse tokens,
+such as the internal kernel command
+\begin{verbatim}
+ \cs_new_protected:Npn \__clist_get:wN #1 , #2 \q_stop #3
+ { \tl_set:Nn #3 {#1} }
+\end{verbatim}
+When the \verb|w| specifier is being used, it is encouraged not to try
+and complicate the rest of the signature too much---for example, it
+would be considered poor style to have a function with a signature like
+\verb|\foo_bar:wnw| unless there were very clear reasons of code clarity.
+A signature such as \verb|:ww| would certainly be discouraged.
+
+Examining the examples above, it can be seen that there are scenarios in
+which it may make logical sense for having a signature such as \verb|:wN| or
+\verb|:nw|, but when in doubt the recommended approach is to simply use
+\verb|:w| as a catch-all.
+
+\end{document}
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3syntax-changes.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3syntax-changes.pdf
new file mode 100644
index 00000000000..7823600916d
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3syntax-changes.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3syntax-changes.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3syntax-changes.tex
new file mode 100644
index 00000000000..85f83bf0e3d
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3syntax-changes.tex
@@ -0,0 +1,190 @@
+\iffalse meta-comment
+
+File: l3syntax-changes.tex
+
+Copyright (C) 2011,2012,2017-2024 The LaTeX Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "l3kernel bundle" (The Work in LPPL)
+and all files in that bundle must be distributed together.
+
+The released version of this bundle is available from CTAN.
+
+\fi
+
+\documentclass{l3doc}
+
+
+\title{%
+ Syntax changes in \LaTeX3 functions%
+}
+\author{%
+ The \LaTeX{} Project\thanks
+ {%
+ E-mail:
+ \href{mailto:latex-team@latex-project.org}%
+ {latex-team@latex-project.org}%
+ }%
+}
+\date{Released 2024-04-11}
+
+\newcommand{\TF}{\textit{(TF)}}
+
+\begin{document}
+
+\maketitle
+
+This file describes functions that were expected to be completely
+stable, but whose syntax has changed in ways that may potentially
+require code relying on them to be changed. This file does not include
+bug-fixes, nor backward-compatible extensions of the syntax,
+nor functions that were completely
+deprecated: the latter are listed in \texttt{l3obsolete.txt}. Only
+changes after August 2011 are listed, with an approximate date.
+
+\section{August 2011}
+
+\begin{itemize}
+ \item \cs{tl_if_single:n\TF} recognized any non-zero number of
+ explicit spaces as \meta{true}, and did not ignore trailing spaces.
+ Now it is \meta{true} for
+ \[
+ \meta{optional spaces}
+ \meta{normal token or brace group}
+ \meta{optional spaces}.
+ \]
+ \item \cs{tl_reverse:n} stripped outer braces and lost unprotected spaces.
+ Now it keeps spaces, leaves unbraced single tokens unbraced, and
+ braced groups braced.
+ \item \cs{tl_trim_spaces:n} only removed one leading and trailing space.
+ Now removes recursively. Also, on the left it used to strip implicit
+ and explicit spaces with any character code. Now it strips only explicit
+ space characters $(32,10)$.
+\end{itemize}
+
+\section{September 2011}
+
+\begin{itemize}
+\item clist functions which receive an \texttt{n}-type comma list argument
+ now trim spaces from each item in the argument.
+\end{itemize}
+
+\section{May 2012}
+
+\begin{itemize}
+ \item The \pkg{l3fp} code has been completely rewritten with a new
+ expandable interface.
+ \item Getting/popping from a comma list or sequence or property list
+ that is empty (or missing the given key) now gives the quark
+ \cs{q_no_value}.
+\end{itemize}
+
+\section{June 2012}
+
+\begin{itemize}
+ \item Access to list functions now indexes from~$1$, not from~$0$.
+ This applies to multiple choices in the \pkg{l3keys} module and
+ the \cs{clist_item:Nn}, \cs{seq_item:Nn} and \cs{tl_item:Nn}
+ functions.
+ \item \cs{tl_trim_spaces:n} now requires a variable number of
+ expansions to fully expand, rather than exactly two. Of course,
+ \texttt{x}-type or \texttt{e}-type expansion still correctly evaluates this function.
+\end{itemize}
+
+\section{July 2012}
+
+\begin{itemize}
+ \item The \cs{tl_if_head_eq_meaning:nN}, \cs{tl_if_head_eq_catcode:nN}
+ and \cs{tl_if_head_eq_charcode:nN} conditionals now never match when
+ their first argument is empty.
+\end{itemize}
+
+\section{August 2012}
+
+\begin{itemize}
+ \item \cs{lua_now:x} is now a standard \texttt{x}-type expansion of
+ \cs{lua_now:n}, which does no expansion. Engine-level expansion is moved
+ to \cs{lua_now:e}, reflecting the fact that this is non-standard in the
+ same way as for example \cs{str_if_eq_x:nn(TF)}.
+\end{itemize}
+
+\section{December 2013}
+
+\begin{itemize}
+ \item In \pkg{l3fp} expressions, the badly named functions |round0|,
+ |round-|, |round+| are now named |trunc|, |floor|, |ceil|.
+\end{itemize}
+
+\section{May 2014}
+
+\begin{itemize}
+ \item Now \cs{int_step_function:nnnN} evaluates its first three
+ arguments (start, step, stop) up front, rather than evaluating them
+ at each step in the loop. The same holds for the related mappings
+ \cs{int_step_inline:nnnn}, \cs{int_step_variable:nnnNn}, and their
+ analogues for \texttt{dim} and \texttt{fp} datatypes.
+\end{itemize}
+
+\section{July 2014}
+
+\begin{itemize}
+ \item In \pkg{l3fp} expressions, juxtaposition is interpreted as
+ multiplication. Now the precedence of juxtaposition is set to be
+ the same as if there was an explicit multiplication
+ sign~\texttt{*}. Previously, juxtaposition would bound more tightly
+ than any other operation.
+\end{itemize}
+
+\section{August 2015}
+
+\begin{itemize}
+ \item The \cs{hbox:n} and related \pkg{l3box} commands now take an
+ \texttt{n}-type argument and provide it braced to the underlying
+ \TeX{} primitive. The functions \cs{hbox:w} and \cs{hbox_end:} in
+ contrast do not read the contents of the box as a macro argument.
+\end{itemize}
+
+\section{2016}
+
+No change.
+
+\section{July 2017}
+
+\begin{itemize}
+ \item Boolean expressions are now evaluated eagerly, namely both
+ operands of logical \texttt{and} (|&&|) and \texttt{or} (\verb"||")
+ are evaluated even when the result of the logical operation is fixed
+ after determining the first operand. For lazy evaluation,
+ \cs{bool_lazy_and_p:nn} and related functions are provided.
+\end{itemize}
+
+\section{November 2017}
+
+\begin{itemize}
+ \item Spaces are now preserved inside keys in \pkg{l3keys}, and
+ trimmed at both ends.
+ \item \cs{cs_generate_variant:Nn} is now stricter: it only allows to
+ change \texttt{N}-type arguments to \texttt{c}, and \texttt{n} to
+ \texttt{o}, \texttt{V}, \texttt{v}, \texttt{f}, \texttt{x}. On the
+ one hand the latter argument types typically give rise to more than
+ one token, not suitable for use by an \texttt{N}-type base function.
+ On the other hand, \texttt{c} variants of \texttt{n} arguments
+ should often be \texttt{v} variants (when the argument is eventually
+ evaluated) or mistakes where the programmer thought the base
+ function was \texttt{N}-type.
+\end{itemize}
+
+\section{February 2020}
+
+\begin{itemize}
+ \item \cs{keyval_parse:NNn} now works by expansion, returning
+ the parsed list inside \cs{exp_not:n}.
+\end{itemize}
+
+\end{document}
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3term-glossary.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/l3term-glossary.pdf
new file mode 100644
index 00000000000..7d03a9143b3
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3term-glossary.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/l3term-glossary.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/l3term-glossary.tex
new file mode 100644
index 00000000000..d1b4b0082c2
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/l3term-glossary.tex
@@ -0,0 +1,180 @@
+\iffalse meta-comment
+
+File: l3term-glossary.tex
+
+Copyright (C) 2018-2024 The LaTeX Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "l3kernel bundle" (The Work in LPPL)
+and all files in that bundle must be distributed together.
+
+The released version of this bundle is available from CTAN.
+
+\fi
+
+\documentclass{l3doc}
+
+
+\title{%
+ Glossary of \TeX{} terms used to describe \LaTeX3 functions%
+}
+\author{%
+ The \LaTeX{} Project\thanks
+ {%
+ E-mail:
+ \href{mailto:latex-team@latex-project.org}%
+ {latex-team@latex-project.org}%
+ }%
+}
+\date{Released 2024-04-11}
+
+\newcommand{\TF}{\textit{(TF)}}
+
+\begin{document}
+
+\maketitle
+
+This file describes aspects of \TeX{} programming that are relevant in a
+\pkg{expl3} context.
+
+\section{Reading a file}
+
+Tokenization.
+
+Treatment of spaces, such as the trap that \verb|\~~a| is equivalent to
+\verb|\~a| in \pkg{expl3} syntax, or that \verb|~| fails to give a space at the
+beginning of a line.
+
+\section{Structure of tokens}
+
+We refer to the documentation of \texttt{l3token} for a complete
+description of all \TeX{} tokens. We distinguish the meaning of the
+token, which controls the expansion of the token and its effect on
+\TeX{}'s state, and its shape, which is used when comparing token lists
+such as for delimited arguments. At any given time two tokens of the
+same shape automatically have the same meaning, but the converse does
+not hold, and the meaning associated with a given shape change when
+doing assignments.
+
+Apart from a few exceptions, a token has one of the following shapes.
+\begin{itemize}
+ \item A control sequence, characterized by the sequence of characters
+ that constitute its name: for instance, \cs{use:n} is a five-letter
+ control sequence.
+ \item An active character token, characterized by its character code
+ (between $0$ and $1114111$ for \LuaTeX{} and \XeTeX{} and less for
+ other engines) and category code~$13$.
+ \item A character token such as |A| or |#|, characterized by its
+ character code and category code (one of $1$, $2$, $3$, $4$, $6$,
+ $7$, $8$, $10$, $11$ or~$12$ whose meaning is described below).
+\end{itemize}
+
+The meaning of a (non-active) character token is fixed by its category
+code (and character code) and cannot be changed. We call these tokens
+\emph{explicit} character tokens. Category codes that a character token
+can have are listed below by giving a sample output of the \TeX{}
+primitive \tn{meaning}, together with their \pkg{expl3} names and most
+common example:
+\begin{itemize}
+ \item[1] begin-group character (|group_begin|, often |{|),
+ \item[2] end-group character (|group_end|, often |}|),
+ \item[3] math shift character (|math_toggle|, often |$|), % $
+ \item[4] alignment tab character (|alignment|, often |&|),
+ \item[6] macro parameter character (|parameter|, often |#|),
+ \item[7] superscript character (|math_superscript|, often |^|),
+ \item[8] subscript character (|math_subscript|, often |_|),
+ \item[10] blank space (|space|, often character code~$32$),
+ \item[11] the letter (|letter|, such as |A|),
+ \item[12] the character (|other|, such as |0|).
+\end{itemize}
+Category code~$13$ (|active|) is discussed below. Input characters can
+also have several other category codes which do not lead to character
+tokens for later processing: $0$~(|escape|), $5$~(|end_line|),
+$9$~(|ignore|), $14$~(|comment|), and $15$~(|invalid|).
+
+The meaning of a control sequence or active character can be identical
+to that of any character token listed above (with any character code),
+and we call such tokens \emph{implicit} character tokens. The meaning
+is otherwise in the following list:
+\begin{itemize}
+ \item a macro, used in \pkg{expl3} for most functions and some variables
+ (|tl|, |fp|, |seq|, \ldots{}),
+ \item a primitive such as \tn{def} or \tn{topmark}, used in \pkg{expl3}
+ for some functions,
+ \item a register such as \tn{count}|123|, used in \pkg{expl3} for the
+ implementation of some variables (|int|, |dim|, \ldots{}),
+ \item a constant integer such as \tn{char}|"56| or
+ \tn{mathchar}|"121|, used when defining a constant using
+ \cs{int_const:Nn},
+ \item a font selection command,
+ \item undefined.
+\end{itemize}
+Macros can be \tn{protected} or not, \tn{long} or not (the opposite of
+what \pkg{expl3} calls |nopar|), and \tn{outer} or not (unused in \pkg{expl3}).
+Their \tn{meaning} takes the form
+\begin{quote}
+ \meta{prefix} |macro:|\meta{argument}|->|\meta{replacement}
+\end{quote}
+where \meta{prefix} is among \tn{protected}\tn{long}\tn{outer},
+\meta{argument} describes parameters that the macro expects, such as
+|#1#2#3|, and \meta{replacement} describes how the parameters are
+manipulated, such as~|\int_eval:n{#2+#1*#3}|. This information can be
+accessed by \cs{cs_prefix_spec:N}, \cs{cs_parameter_spec:N},
+\cs{cs_replacement_spec:N}.
+
+When a macro takes an undelimited argument, explicit space characters
+(with character code $32$ and category code $10$) are ignored. If the
+following token is an explicit character token with category code $1$
+(begin-group) and an arbitrary character code, then \TeX{} scans ahead
+to obtain an equal number of explicit character tokens with category
+code $1$ (begin-group) and $2$ (end-group), and the resulting list of
+tokens (with outer braces removed) becomes the argument. Otherwise, a
+single token is taken as the argument for the macro: we call such single
+tokens \enquote{N-type}, as they are suitable to be used as an argument
+for a function with the signature~\texttt{:N}.
+
+When a macro takes a delimited argument \TeX{} scans ahead until finding
+the delimiter (outside any pairs of begin-group/end-group explicit
+characters), and the resulting list of tokens (with outer braces
+removed) becomes the argument. Note that explicit space characters at
+the start of the argument are \emph{not} ignored in this case (and they
+prevent brace-stripping).
+
+\section{Handling of hash tokens}
+
+\TeX{} uses the hash (octothorpe) character |#| to denote parameters for
+macros: these must be numbered sequentially. To allow handling of nested
+macros, \TeX{} requires that for each nesting level, hash tokens are doubled.
+For example
+\begin{verbatim}
+\cs_new:Npn \mypkg_outer:N #1
+ {
+ \cs_new:Npn \mypkg_inner:N ##1
+ {
+ #1
+ ##1
+ }
+ }
+\end{verbatim}
+would define both |\mypkg_outer:N| and |\mypkg_inner:N| as taking
+exactly one argument. If we then do
+\begin{verbatim}
+\mypkg_outer:N \foo
+\cs_show:N \mypkg_inner:N
+\end{verbatim}
+\TeX{} will report
+\begin{verbatim}
+> \mypkg_inner:N=\long macro:#1->\foo #1.
+\end{verbatim}
+i.e.~the hash is not doubled, but is now the parameter of this macro.
+
+Exactly the same concept applies to anywhere that inline code is nested in
+\pkg{expl3}, for example inline mapping code, key definitions, etc.
+
+\end{document}
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/source3.pdf b/Master/texmf-dist/doc/latex-dev/l3kernel/source3.pdf
new file mode 100644
index 00000000000..a88cd680305
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/source3.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/source3.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/source3.tex
new file mode 100644
index 00000000000..f0aaeb20c87
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/source3.tex
@@ -0,0 +1,106 @@
+\iffalse meta-comment
+
+File: source3.tex
+
+Copyright (C) 1990-2012,2017-2024 The LaTeX Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "l3kernel bundle" (The Work in LPPL)
+and all files in that bundle must be distributed together.
+
+The released version of this bundle is available from CTAN.
+
+-----------------------------------------------------------------------
+
+The development version of the bundle can be found at
+
+ https://github.com/latex3/latex3
+
+for those people who are interested.
+
+\fi
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% This document typesets the LaTeX3 sources as a single document.
+% This produces quite a large file (more than 1670 pages as of Dec 2023).
+%
+% There is also a shorter version (interface3.tex) that only typesets the
+% command % interface descriptions.
+%
+% Do not forget to generate the index (as explained on the terminal output
+% near the end of the run)!
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\documentclass[kernel]{l3doc}
+
+\newif\ifinterface
+\interfacefalse
+
+\listfiles
+
+\begin{document}
+
+\title{The \LaTeX3 Sources}
+\author{%
+ The \LaTeX{} Project\thanks
+ {%
+ E-mail:
+ \href{mailto:latex-team@latex-project.org}
+ {latex-team@latex-project.org}%
+ }%
+}
+\date{Released 2024-04-11}
+
+\pagenumbering{roman}
+\maketitle
+
+%
+% First load all modules and typeset the documentation parts
+%
+
+\input{source3body} % all the individual modules
+
+%
+% Now reload all modules and typeset the implementation parts
+%
+
+\part{Implementation}
+
+\def\maketitle{}
+\let\subsubsection\subsection
+\let\subsection\section
+\let\section\chapter
+
+\EnableImplementation
+\DisableDocumentation
+\DocInputAgain
+
+\clearpage
+\pagestyle{headings}
+
+% Make TeX shut up.
+\hbadness=10000
+\newcount\hbadness
+\hfuzz=\maxdimen
+
+\PrintChanges
+\clearpage
+
+\begingroup
+ \def\endash{--}
+ \catcode`\-\active
+ \def-{\futurelet\temp\indexdash}
+ \def\indexdash{\ifx\temp-\endash\fi}
+
+ \DelayPrintIndex
+\endgroup
+
+\end{document}
diff --git a/Master/texmf-dist/doc/latex-dev/l3kernel/source3body.tex b/Master/texmf-dist/doc/latex-dev/l3kernel/source3body.tex
new file mode 100644
index 00000000000..84712962276
--- /dev/null
+++ b/Master/texmf-dist/doc/latex-dev/l3kernel/source3body.tex
@@ -0,0 +1,620 @@
+\iffalse meta-comment
+
+File: source3body.tex
+
+Copyright (C) 1990-2012,2014-2023 The LaTeX Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "l3kernel bundle" (The Work in LPPL)
+and all files in that bundle must be distributed together.
+
+The released version of this bundle is available from CTAN.
+
+-----------------------------------------------------------------------
+
+The development version of the bundle can be found at
+
+ https://github.com/latex3/latex3
+
+for those people who are interested.
+
+\fi
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% This file is used by
+%
+% source3.tex % documentation including implementation
+%
+% interface3.tex % only interface documentation
+%
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+\begin{abstract}
+
+\setlength\parindent{0pt}
+\setlength\parskip{\baselineskip}
+
+\noindent
+\ifinterface
+This is the reference documentation for the \pkg{expl3}
+programming environment; see the matching \pkg{source3} PDF
+for the typeset sources.
+\else
+This is the typset sources for the \pkg{expl3}
+programming environment; see the matching \pkg{interface3} PDF
+for the API reference manual.
+\fi
+The \pkg{expl3} modules set up a
+naming scheme for \LaTeX{} commands, which allow the \LaTeX{} programmer
+to systematically name functions and variables, and specify the argument
+types of functions.
+
+The \TeX{} and \eTeX{} primitives are all given a new name according to
+these conventions. However, in the main direct use of the primitives is
+not required or encouraged: the \pkg{expl3} modules define an
+independent low-level \LaTeX3 programming language.
+
+The \pkg{expl3} modules are designed to be loaded on top of
+\LaTeXe{}. With an up-to-date \LaTeXe{} kernel, this material is loaded
+as part of the format. The fundamental programming code can also be loaded
+with other \TeX{} formats, subject to restrictions on the full range of
+functionality.
+
+\end{abstract}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Each of the following \DocInput lines includes a file with extension
+% .dtx. Each of these files may be typeset separately. For instance
+% pdflatex l3box.dtx
+% will typeset the source of the LaTeX3 box commands. If you use the
+% Makefile, the index will be generated automatically; e.g.,
+% make doc F=l3box
+%
+% If this file is processed, each of these separate dtx files will be
+% contained as a part of a single document.
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\makeatletter
+% l3doc is based on article, but for these very large documents we need
+% chapters; the code is based on the standard classes but somewhat simplified
+\renewcommand\part{%
+ \clearpage
+ \thispagestyle{plain}%
+ \@tempswafalse
+ \null\vfil
+ \secdef\@part\@spart}
+\newcounter {chapter}
+\numberwithin{section}{chapter}
+\renewcommand \thechapter {\@arabic\c@chapter}
+\renewcommand \thesection {\thechapter.\@arabic\c@section}
+\newcommand*\chaptermark[1]{}
+\setcounter{secnumdepth}{2}
+\newcommand\@chapapp{\chaptername}
+\newcommand\chaptername{Chapter}
+ \def\ps@headings{%
+ \let\@oddfoot\@empty
+ \def\@oddhead{{\slshape\rightmark}\hfil\thepage}%
+ \let\@mkboth\markboth
+ \def\chaptermark##1{%
+ \markright {\MakeUppercase{%
+ \ifnum \c@secnumdepth >\m@ne
+ \@chapapp\ \thechapter. \ %
+ \fi
+ ##1}}}}
+\newcommand\chapter{\clearpage
+ \thispagestyle{plain}%
+ \global\@topnum\z@
+ \@afterindentfalse
+ \secdef\@chapter\@schapter}
+\def\@chapter[#1]#2{\refstepcounter{chapter}%
+ \typeout{\@chapapp\space\thechapter.}%
+ \addcontentsline{toc}{chapter}%
+ {\protect\numberline{\thechapter}#1}%
+ \chaptermark{#1}%
+ \addtocontents{lof}{\protect\addvspace{10\p@}}%
+ \addtocontents{lot}{\protect\addvspace{10\p@}}%
+ \@makechapterhead{#2}%
+ \@afterheading}
+\def\@makechapterhead#1{%
+ \vspace*{50\p@}%
+ {\parindent \z@ \raggedright \normalfont
+ \huge\bfseries \@chapapp\space \thechapter
+ \par\nobreak
+ \vskip 20\p@
+ \interlinepenalty\@M
+ \Huge \bfseries #1\par\nobreak
+ \vskip 40\p@
+ }}
+\newcommand*\l@chapter[2]{%
+ \ifnum \c@tocdepth >\m@ne
+ \addpenalty{-\@highpenalty}%
+ \vskip 1.0em \@plus\p@
+ \setlength\@tempdima{1.5em}%
+ \begingroup
+ \parindent \z@ \rightskip \@pnumwidth
+ \parfillskip -\@pnumwidth
+ \leavevmode \bfseries
+ \advance\leftskip\@tempdima
+ \hskip -\leftskip
+ #1\nobreak\hfil
+ \nobreak\hb@xt@\@pnumwidth{\hss #2%
+ \kern-\p@\kern\p@}\par
+ \penalty\@highpenalty
+ \endgroup
+ \fi}
+\renewcommand*\l@section{\@dottedtocline{1}{1.5em}{2.8em}}
+\renewcommand*\l@subsection{\@dottedtocline{2}{3.8em}{3.2em}}
+\renewcommand*\l@subsubsection{\@dottedtocline{3}{7.0em}{4.1em}}
+\def\partname{Part}
+\def\toclevel@part{-1}
+\def\maketitle{\chapter{\@title}}
+\let\thanks\@gobble
+\let\DelayPrintIndex\PrintIndex
+\let\PrintIndex\@empty
+\providecommand*{\hexnum}[1]{\text{\texttt{\char`\"}#1}}
+\makeatother
+
+\clearpage
+
+{%
+ \def\\{:}% fix "newlines" in the ToC
+ \tableofcontents
+}
+
+\clearpage
+\pagenumbering{arabic}
+
+\part{Introduction}
+
+\chapter{Introduction to \pkg{expl3} and this document}
+
+This document is intended to act as a comprehensive reference manual
+for the \pkg{expl3} language. A general guide to the \LaTeX3
+programming language is found in \href{expl3.pdf}{expl3.pdf}.
+
+\section{Naming functions and variables}
+
+\LaTeX3 does not use \texttt{@} as a \enquote{letter} for defining
+internal macros. Instead, the symbols |_| and \texttt{:}
+are used in internal macro names to provide structure. The name of
+each \emph{function} is divided into logical units using \texttt{_},
+while \texttt{:} separates the \emph{name} of the function from the
+\emph{argument specifier} (\enquote{arg-spec}). This describes the arguments
+expected by the function. In most cases, each argument is represented
+by a single letter. The complete list of arg-spec letters for a function
+is referred to as the \emph{signature} of the function.
+
+Each function name starts with the \emph{module} to which it belongs.
+Thus apart from a small number of very basic functions, all \pkg{expl3}
+function names contain at least one underscore to divide the module
+name from the descriptive name of the function. For example, all
+functions concerned with comma lists are in module \texttt{clist} and
+begin |\clist_|.
+
+Every function must include an argument specifier. For functions which
+take no arguments, this will be blank and the function name will end
+\texttt{:}. Most functions take one or more arguments, and use the
+following argument specifiers:
+\begin{description}
+ \item[\texttt{N} and \texttt{n}] These mean \emph{no manipulation},
+ of a single token for \texttt{N} and of a set of tokens given in
+ braces for \texttt{n}. Both pass the argument through exactly as
+ given. Usually, if you use a single token for an \texttt{n} argument,
+ all will be well.
+ \item[\texttt{c}] This means \emph{csname}, and indicates that the
+ argument will be turned into a csname before being used. So
+ |\foo:c| |{ArgumentOne}| will act in the same way as |\foo:N|
+ |\ArgumentOne|. All macros that appear in the argument are expanded.
+ An internal error will occur if the result of expansion inside
+ a \texttt{c}-type argument is not a series of character tokens.
+ \item[\texttt{V} and \texttt{v}] These mean \emph{value
+ of variable}. The \texttt{V} and \texttt{v} specifiers are used to
+ get the content of a variable without needing to worry about the
+ underlying \TeX{} structure containing the data. A \texttt{V}
+ argument will be a single token (similar to \texttt{N}), for example
+ |\foo:V| |\MyVariable|; on the other hand, using \texttt{v} a
+ csname is constructed first, and then the value is recovered, for
+ example |\foo:v| |{MyVariable}|.
+ \item[\texttt{o}] This means \emph{expansion once}. In general, the
+ \texttt{V} and \texttt{v} specifiers are favoured over \texttt{o}
+ for recovering stored information. However, \texttt{o} is useful
+ for correctly processing information with delimited arguments.
+ \item[\texttt{x}] The \texttt{x} specifier stands for \emph{exhaustive
+ expansion}: every token in the argument is fully expanded until only
+ unexpandable ones remain. The \TeX{} \tn{edef} primitive carries out
+ this type of expansion. Functions which feature an \texttt{x}-type
+ argument are \emph{not} expandable.
+ \item[\texttt{e}] The \texttt{e} specifier is in many respects
+ identical to \texttt{x}, but uses \tn{expanded} primitive.
+ Parameter character (usually~|#|) in the argument need not be doubled.
+ Functions which feature an \texttt{e}-type argument may be
+ expandable.
+ \item[\texttt{f}] The \texttt{f} specifier stands for \emph{full
+ expansion}, and in contrast to \texttt{x} stops at the first
+ non-expandable token (reading the argument from left to right) without
+ trying to expand it. If this token is a \meta{space token}, it is gobbled,
+ and thus won't be part of the resulting argument. For example, when
+ setting a token list variable (a macro used for storage), the sequence
+ \begin{verbatim}
+ \tl_set:Nn \l_mya_tl { A }
+ \tl_set:Nn \l_myb_tl { B }
+ \tl_set:Nf \l_mya_tl { \l_mya_tl \l_myb_tl }
+ \end{verbatim}
+ will leave |\l_mya_tl| with the content |A\l_myb_tl|, as |A| cannot
+ be expanded and so terminates expansion before |\l_myb_tl| is considered.
+ \item[\texttt{T} and \texttt{F}] For logic tests, there are the branch
+ specifiers \texttt{T} (\emph{true}) and \texttt{F} (\emph{false}).
+ Both specifiers treat the input in the same way as \texttt{n} (no
+ change), but make the logic much easier to see.
+ \item[\texttt{p}] The letter \texttt{p} indicates \TeX{}
+ \emph{parameters}. Normally this will be used for delimited
+ functions as \pkg{expl3} provides better methods for creating simple
+ sequential arguments.
+ \item[\texttt{w}] Finally, there is the \texttt{w} specifier for
+ \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} stands for \textbf{Do not use}.
+ All of the \TeX{} primitives are initially \cs[no-index]{let} to a \texttt{D}
+ 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
+ \ifinterface
+ this documentation.
+ \else
+ \href{interface3.pdf}{interface3.pdf}.
+ \fi
+\end{description}
+Notice that the argument specifier describes how the argument is
+processed prior to being passed to the underlying function. For example,
+|\foo:c| will take its argument, convert it to a control sequence and
+pass it to |\foo:N|.
+
+Variables are named in a similar manner to functions, but begin with
+a single letter to define the type of variable:
+\begin{description}
+ \item[\texttt{c}] Constant: global parameters whose value should not
+ be changed.
+ \item[\texttt{g}] Parameters whose value should only be set globally.
+ \item[\texttt{l}] Parameters whose value should only be set locally.
+\end{description}
+Each variable name is then build up in a similar way to that of a
+function, typically starting with the module\footnote{The module names are
+ not used in case of generic scratch registers defined in the data
+ type modules, e.g., the
+ \texttt{int} module contains some scratch variables called \cs{l_tmpa_int},
+ \cs{l_tmpb_int}, and so on. In such a case adding the module name up front
+ to denote the module
+ and in the back to indicate the type, as in
+ \texttt{\string\l_int_tmpa_int} would be very unreadable.} name
+and then a descriptive part.
+Variables end with a short identifier to show the variable type:
+\begin{description}%
+ \item[\texttt{bitset}] a set of bits (a string made up of a series of \texttt{0}
+ and \texttt{1} tokens that are accessed by position).
+ \item[\texttt{clist}] Comma separated list.
+ \item[\texttt{dim}] \enquote{Rigid} lengths.
+ \item[\texttt{fp}] Floating-point values;
+ \item[\texttt{int}] Integer-valued count register.
+ \item[\texttt{muskip}] \enquote{Rubber} lengths for use in
+ mathematics.
+ \item[\texttt{skip}] \enquote{Rubber} lengths.
+ \item[\texttt{str}] String variables: contain character data.
+ \item[\texttt{tl}] Token list variables: placeholder for a token list.
+\end{description}
+Applying \texttt{V}-type or \texttt{v}-type expansion to variables of
+one of the above types is supported, while it is not supported for the
+following variable types:
+\begin{description}
+ \item[\texttt{bool}] Either true or false.
+ \item[\texttt{box}] Box register.
+ \item[\texttt{coffin}] A \enquote{box with handles} --- a higher-level
+ data type for carrying out \texttt{box} alignment operations.
+ \item[\texttt{flag}] Non-negative integer that can be incremented expandably.
+ \item[\texttt{fparray}] Fixed-size array of floating point values.
+ \item[\texttt{intarray}] Fixed-size array of integers.
+ \item[\texttt{ior}/\texttt{iow}] An input or output stream, for
+ reading from or writing to, respectively.
+ \item[\texttt{prop}] Property list: analogue of dictionary or
+ associative arrays in other languages.
+ \item[\texttt{regex}] Regular expression.
+ \item[\texttt{seq}] \enquote{Sequence}: a data type used to implement
+ lists (with access at both ends) and stacks.
+\end{description}
+
+\subsection{Scratch variables}
+
+Modules focussed on variable usage typically provide four scratch variables,
+two local and two global, with names of the form
+\cs[no-index]{\meta{scope}_tmpa_\meta{type}}/\cs[no-index]{\meta{scope}_tmpb_\meta{type}}. These
+are never used by the core code. The nature of \TeX{} grouping means that as
+with any other scratch variable, these should only be set and used with no
+intervening third-party code.
+
+\subsection{Terminological inexactitude}
+
+A word of warning. In this document, and others referring to the \pkg{expl3}
+programming modules, we often refer to \enquote{variables} and
+\enquote{functions} as if they
+were actual constructs from a real programming language. In truth, \TeX{}
+is a macro processor, and functions are simply macros that may or may not take
+arguments and expand to their replacement text. Many of the common variables
+are \emph{also} macros, and if placed into the input stream will simply expand
+to their definition as well~--- a \enquote{function} with no arguments and a
+\enquote{token list variable} are almost the same.\footnote{\TeX{}nically,
+functions with no arguments are \tn{long} while token list variables are not.}
+On the other
+hand, some \enquote{variables} are actually registers that must be
+initialised and their values set and retrieved with specific functions.
+
+The conventions of the \pkg{expl3} code are designed to clearly separate the
+ideas of \enquote{macros that contain data} and
+\enquote{macros that contain code}, and a
+consistent wrapper is applied to all forms of \enquote{data} whether they be
+macros or
+actually registers. This means that sometimes we will use phrases like
+\enquote{the function returns a value}, when actually we just mean
+\enquote{the macro expands to something}. Similarly, the term
+\enquote{execute} might be used in place of \enquote{expand}
+or it might refer to the more specific case of
+\enquote{processing in \TeX's stomach}
+(if you are familiar with the \TeX{}book parlance).
+
+If in doubt, please ask; chances are we've been hasty in writing certain
+definitions and need to be told to tighten up our terminology.
+
+\section{Documentation conventions}
+
+This document is typeset with the experimental \pkg{l3doc} class;
+several conventions are used to help describe the features of the code.
+A number of conventions are used here to make the documentation clearer.
+
+Each group of related functions is given in a box. For a function with
+a \enquote{user} name, this might read:
+\begin{function}[label = ]{\ExplSyntaxOn, \ExplSyntaxOff}
+ \begin{syntax}
+ |\ExplSyntaxOn| \dots{} |\ExplSyntaxOff|
+ \end{syntax}
+ The textual description of how the function works would appear here. The
+ syntax of the function is shown in mono-spaced text to the right of
+ the box. In this example, the function takes no arguments and so the
+ name of the function is simply reprinted.
+\end{function}
+
+For programming functions, which use \texttt{_} and \texttt{:} in their name
+there are a few additional conventions: If two related functions are given
+with identical names but different argument specifiers, these are termed
+\emph{variants} of each other, and the latter functions are printed in grey to
+show this more clearly. They will carry out the same function but will take
+different types of argument:
+\begin{function}[label = ]{\seq_new:N, \seq_new:c}
+ \begin{syntax}
+ |\seq_new:N| \meta{sequence}
+ \end{syntax}
+ When a number of variants are described, the arguments are usually
+ illustrated only for the base function. Here, \meta{sequence} indicates
+ that |\seq_new:N| expects the name of a sequence. From the argument
+ specifier, |\seq_new:c| also expects a sequence name, but as a
+ name rather than as a control sequence. Each argument given in the
+ illustration should be described in the following text.
+\end{function}
+
+\paragraph{Fully expandable functions}
+\hypertarget{expstar}{Some functions are fully expandable},
+which allows them to be used within
+an \texttt{x}-type or \texttt{e}-type argument (in plain \TeX{} terms, inside an \tn{edef} or \tn{expanded}),
+as well as within an \texttt{f}-type argument.
+These fully expandable functions are indicated in the documentation by
+a star:
+\begin{function}[EXP, label = ]{\cs_to_str:N}
+ \begin{syntax}
+ |\cs_to_str:N| \meta{cs}
+ \end{syntax}
+ As with other functions, some text should follow which explains how
+ the function works. Usually, only the star will indicate that the
+ function is expandable. In this case, the function expects a \meta{cs},
+ shorthand for a \meta{control sequence}.
+\end{function}
+
+\paragraph{Restricted expandable functions}
+\hypertarget{rexpstar}{A few functions are fully expandable} but cannot be fully expanded within
+an \texttt{f}-type argument. In this case a hollow star is used to indicate
+this:
+\begin{function}[rEXP, label = ]{\seq_map_function:NN}
+ \begin{syntax}
+ |\seq_map_function:NN| \meta{seq} \meta{function}
+ \end{syntax}
+\end{function}
+
+\paragraph{Conditional functions}
+\hypertarget{explTF}{Conditional (\texttt{if}) functions}
+are normally defined in three variants, with
+\texttt{T}, \texttt{F} and \texttt{TF} argument specifiers. This allows
+them to be used for different \enquote{true}/\enquote{false} branches,
+depending on
+which outcome the conditional is being used to test. To indicate this
+without repetition, this information is given in a shortened form:
+\begin{function}[EXP,TF, label = ]{\sys_if_engine_xetex:}
+ \begin{syntax}
+ |\sys_if_engine_xetex:TF| \Arg{true code} \Arg{false code}
+ \end{syntax}
+ The underlining and italic of \texttt{TF} indicates that three functions
+ are available:
+ \begin{itemize}
+ \item |\sys_if_engine_xetex:T|
+ \item |\sys_if_engine_xetex:F|
+ \item |\sys_if_engine_xetex:TF|
+ \end{itemize}
+ Usually, the illustration
+ will use the \texttt{TF} variant, and so both \meta{true code}
+ and \meta{false code} will be shown. The two variant forms \texttt{T} and
+ \texttt{F} take only \meta{true code} and \meta{false code}, respectively.
+ Here, the star also shows that this function is expandable.
+ With some minor exceptions, \emph{all} conditional functions in the
+ \pkg{expl3} modules should be defined in this way.
+\end{function}
+
+Variables, constants and so on are described in a similar manner:
+\begin{variable}[label = ]{\l_tmpa_tl}
+ A short piece of text will describe the variable: there is no
+ syntax illustration in this case.
+\end{variable}
+
+In some cases, the function is similar to one in \LaTeXe{} or plain \TeX{}.
+In these cases, the text will include an extra \enquote{\textbf{\TeX{}hackers
+note}} section:
+\begin{function}[EXP, label = ]{\token_to_str:N}
+ \begin{syntax}
+ |\token_to_str:N| \meta{token}
+ \end{syntax}
+ The normal description text.
+ \begin{texnote}
+ Detail for the experienced \TeX{} or \LaTeXe\ programmer. In this
+ case, it would point out that this function is the \TeX{} primitive
+ |\string|.
+ \end{texnote}
+\end{function}
+
+\paragraph{Changes to behaviour}
+When new functions are added to \pkg{expl3}, the date of first inclusion is
+given in the documentation. Where the documented behaviour of a function
+changes after it is first introduced, the date of the update will also be
+given. This means that the programmer can be sure that any release of
+\pkg{expl3} after the date given will contain the function of interest with
+expected behaviour as described. Note that changes to code internals, including
+bug fixes, are not recorded in this way \emph{unless} they impact on the
+expected behaviour.
+
+\section{Formal language conventions which apply generally}
+
+As this is a formal reference guide for \LaTeX3 programming, the descriptions
+of functions are intended to be reasonably \enquote{complete}. However, there
+is also a need to avoid repetition. Formal ideas which apply to general
+classes of function are therefore summarised here.
+
+For tests which have a \texttt{TF} argument specification, the test if
+evaluated to give a logically \texttt{TRUE} or \texttt{FALSE} result.
+Depending on this result, either the \meta{true code} or the \meta{false code}
+will be left in the input stream. In the case where the test is expandable,
+and a predicate (|_p|) variant is available, the logical value determined by
+the test is left in the input stream: this will typically be part of a larger
+logical construct.
+
+\section{\TeX{} concepts not supported by \LaTeX3{}}
+
+The \TeX{} concept of an \enquote{\tn{outer}} macro is \emph{not supported}
+at all by \LaTeX3{}. As such, the functions provided here may break when
+used on top of \LaTeXe{} if \tn{outer} tokens are used in the arguments.
+
+\DisableImplementation
+
+\part{Bootstrapping}
+
+\DocInput{l3bootstrap.dtx}
+\DocInput{l3names.dtx}
+\ExplSyntaxOn
+\clist_gput_right:Nn \g_docinput_clist { l3kernel-functions.dtx }
+\ExplSyntaxOff
+
+\part{Programming Flow}
+
+\DocInput{l3basics.dtx}
+\DocInput{l3expan.dtx}
+\DocInput{l3sort.dtx}
+\DocInput{l3tl-analysis.dtx}
+\DocInput{l3regex.dtx}
+\DocInput{l3prg.dtx}
+\DocInput{l3sys.dtx}
+\DocInput{l3msg.dtx}
+\DocInput{l3file.dtx}
+
+\DocInput{l3luatex.dtx}
+
+\DocInput{l3legacy.dtx}
+
+
+\part{Data types}
+
+\DocInput{l3tl.dtx}
+\DocInput{l3tl-build.dtx}
+\DocInput{l3str.dtx}
+\DocInput{l3str-convert.dtx}
+\DocInput{l3quark.dtx}
+\DocInput{l3seq.dtx}
+\DocInput{l3int.dtx}
+\DocInput{l3flag.dtx}
+\DocInput{l3clist.dtx}
+\DocInput{l3token.dtx}
+\DocInput{l3prop.dtx}
+\DocInput{l3skip.dtx}
+\DocInput{l3keys.dtx}
+\DocInput{l3intarray.dtx}
+\DocInput{l3fp.dtx}
+% To get the various submodules of l3fp to appear in the implementation
+% part only, they have to be added to the documentation list after typesetting
+% the 'user' part just for the main module.
+\ExplSyntaxOn
+\clist_gput_right:Nn \g_docinput_clist
+ {
+ l3fp-aux.dtx ,
+ l3fp-traps.dtx ,
+ l3fp-round.dtx ,
+ l3fp-parse.dtx ,
+ l3fp-assign.dtx ,
+ l3fp-logic.dtx ,
+ l3fp-basics.dtx ,
+ l3fp-extended.dtx ,
+ l3fp-expo.dtx ,
+ l3fp-trig.dtx ,
+ l3fp-convert.dtx ,
+ l3fp-random.dtx ,
+ l3fp-types.dtx ,
+ l3fp-symbolic.dtx ,
+ l3fp-functions.dtx
+ }
+\ExplSyntaxOff
+
+\DocInput{l3fparray.dtx}
+\DocInput{l3bitset.dtx}
+\DocInput{l3cctab.dtx}
+
+\part{Text manipulation}
+
+\DocInput{l3unicode.dtx}
+\DocInput{l3text.dtx}
+\ExplSyntaxOn
+\clist_gput_right:Nn \g_docinput_clist
+ {
+ l3text-case.dtx ,
+ l3text-map.dtx ,
+ l3text-purify.dtx
+ }
+\ExplSyntaxOff
+
+\part{Typesetting}
+
+\DocInput{l3box.dtx}
+\DocInput{l3coffins.dtx}
+\DocInput{l3color.dtx}
+\DocInput{l3pdf.dtx}
+
+% implementation part only
+\ExplSyntaxOn
+\clist_gput_right:Nn \g_docinput_clist { l3deprecation.dtx }
+\clist_gput_right:Nn \g_docinput_clist { l3debug.dtx }
+\ExplSyntaxOff
+
+\endinput
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/expl3.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/expl3.dtx
new file mode 100644
index 00000000000..56a62010fae
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/expl3.dtx
@@ -0,0 +1,1798 @@
+% \iffalse meta-comment
+%
+%% File: expl3.dtx
+%
+% Copyright (C) 1990-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver|generic|package|2ekernel>
+%</driver|generic|package|2ekernel>
+\def\ExplFileDate{2024-04-11}%
+%<*driver>
+\documentclass[full]{l3doc}
+\usepackage{graphicx}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \providecommand\acro[1]{\textsc{\MakeLowercase{#1}}}
+% \newenvironment{arg-description}{%
+% \begin{itemize}\def\makelabel##1{\hss\llap{\bfseries##1}}}{\end{itemize}}
+%
+% \title{^^A
+% The \pkg{expl3} package and \LaTeX3 programming^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \begin{abstract}
+%
+% This document gives an introduction to a new set of programming
+% conventions that have been designed to meet the requirements of
+% implementing large scale \TeX{} macro programming projects such as
+% \LaTeX{}. These programming conventions are the base layer of \LaTeX3.
+%
+% The main features of the system described are:
+% \begin{itemize}
+% \item classification of the macros (or, in \LaTeX{} terminology,
+% commands) into \LaTeX{} functions and \LaTeX{} parameters, and also
+% into modules containing related commands;
+% \item a systematic naming scheme based on these classifications;
+% \item a simple mechanism for controlling the expansion of a function's
+% arguments.
+% \end{itemize}
+% This system is being used as the basis for \TeX{} programming within
+% The \LaTeX{} Project. Note that the language is not intended for either
+% document mark-up or style specification. Instead, it is intended that
+% such features will be built on top of the conventions described here.
+%
+% This document is an introduction to the ideas behind the \pkg{expl3}
+% programming interface. For the complete documentation of the programming
+% layer provided by The \LaTeX{} Project, see the accompanying
+% \texttt{interface3} document.
+%
+% \end{abstract}
+%
+% \section{Introduction}
+%
+% The first step to develop a \LaTeX{} kernel beyond \LaTeXe{} is to
+% address how the underlying system is programmed. Rather than the
+% current mix of \LaTeX{} and \TeX{} macros, the \LaTeX3 system provides
+% its own consistent interface to all of the functions needed to
+% control \TeX{}. A key part of this work is to ensure that everything
+% is documented, so that \LaTeX{} programmers and users can work
+% efficiently without needing to be familiar with the internal nature
+% of the kernel or with plain \TeX{}.
+%
+% The \pkg{expl3} bundle provides this new programming interface for
+% \LaTeX{}. To make programming systematic, \LaTeX3 uses some very
+% different conventions to \LaTeXe{} or plain \TeX{}. As a result,
+% programmers starting with \LaTeX3 need to become familiar with
+% the syntax of the new language.
+%
+% The next section shows where this language fits into a complete
+% \TeX{}-based document processing system. We then describe the major
+% features of the syntactic structure of command names, including the
+% argument specification syntax used in function names.
+%
+% The practical ideas behind this argument syntax will be explained,
+% together with the expansion control mechanism and the interface
+% used to define variant forms of functions.
+%
+% As we shall demonstrate, the use of a structured naming scheme and of
+% variant forms for functions greatly improves the readability of the
+% code and hence also its reliability. Moreover, experience has shown
+% that the longer command names which result from the new syntax do not
+% make the process of \emph{writing} code significantly harder.
+%
+% \section{Languages and interfaces}
+%
+% It is possible to identify several distinct languages related to the
+% various interfaces that are needed in a \TeX{}-based document processing
+% system. This section looks at those we consider most important for
+% the \LaTeX3 system.
+%
+% \begin{description}
+% \item[Document mark-up] This comprises those commands (often called
+% tags) that are to embedded in the document (the |.tex| file).
+%
+% It is generally accepted that such mark-up should be essentially
+% \emph{declarative}. It may be traditional \TeX{}-based mark-up such
+% as \LaTeXe{}, as described in~\cite{A-W:LLa94} and~\cite{A-W:GMS94},
+% or a mark-up language defined via \acro{HTML} or \acro{XML}.
+%
+% One problem with more traditional \TeX{} coding conventions (as
+% described in~\cite{A-W:K-TB}) is that the names and syntax of \TeX{}'s
+% primitive formatting commands are ingeniously designed to be
+% \enquote{natural} when used directly by the author as document mark-up
+% or in macros. Ironically, the ubiquity (and widely recognised
+% superiority) of logical mark-up has meant that such explicit
+% formatting commands are almost never needed in documents or in
+% author-defined macros. Thus they are used almost exclusively by
+% \TeX{} programmers to define higher-level commands, and their
+% idiosyncratic syntax is not at all popular with this community.
+% Moreover, many of them have names that could be very useful as
+% document mark-up tags were they not pre-empted as primitives
+% (\emph{e.g.}~\tn{box} or \tn{special}).
+%
+% \item[Designer interface] This relates a (human) typographic
+% designer's specification for a document to a program that
+% \enquote{formats
+% the document}. It should ideally use a declarative language that
+% facilitates expression of the relationship and spacing rules
+% specified for the layout of the various document elements.
+%
+% This language is not embedded in document text and it will be very
+% different in form to the document mark-up language. For \LaTeX{},
+% this level was almost completely missing from \LaTeX{}2.09; \LaTeXe{}
+% made some improvements in this area but it is still the case that
+% implementing a design specification in \LaTeX{} requires far more
+% \enquote{low-level} coding than is acceptable.
+%
+% \item[Programmer interface]
+% This language is the implementation language within which the
+% basic typesetting functionality is implemented, building upon the
+% primitives of \TeX{} (or a successor program). It may also be used
+% to implement the previous two languages \enquote{within} \TeX{}, as in
+% the
+% current \LaTeX{} system.
+%
+% \end{description}
+%
+% The last layer is covered by the conventions described in this
+% document, which describes a system aimed at providing a suitable
+% basis for coding \LaTeX3. Its main distinguishing features are
+% summarised here:
+% \begin{itemize}
+% \item A consistent naming scheme for all commands, including \TeX{}
+% primitives.
+% \item The classification of commands as \LaTeX{} functions or \LaTeX{}
+% parameters, and also their division into modules according to their
+% functionality.
+% \item A simple mechanism for controlling argument expansion.
+% \item Provision of a set of core \LaTeX{} functions that is sufficient
+% for handling programming constructs such as queues, sets, stacks,
+% property lists.
+% \item A \TeX{} programming environment in which, for example, all
+% white space is ignored.
+% \end{itemize}
+%
+% \section{The naming scheme}
+%
+% \LaTeX3 does not use |@| as a \enquote{letter} for defining
+% internal macros. Instead, the symbols |_| and |:|
+% are used in internal macro names to provide structure. In
+% contrast to the plain \TeX{} format and the \LaTeXe{} kernel, these
+% extra letters are used only between parts of a macro name (no
+% strange vowel replacement).
+%
+% While \TeX{} is actually a macro processor, by
+% convention for the \pkg{expl3} programming language we distinguish between
+% \emph{functions} and \emph{variables}. Functions can have arguments and they
+% are either expanded or executed. Variables can be assigned values and they
+% are used in arguments to functions; they are not used directly but are
+% manipulated by functions (including getting and setting functions).
+% Functions and variables with a related functionality (for example accessing
+% counters, or manipulating token lists, \emph{etc.})\ are collected together
+% into a
+% \emph{module}.
+%
+% \subsection{Examples}
+%
+% Before giving the details of the naming scheme, here are a few typical
+% examples to indicate the flavour of the scheme; first some variable
+% names.
+% \begin{quote}
+% \cs{l_tmpa_box} is a local variable (hence the~|l_| prefix)
+% corresponding to a box register.\\
+% \cs{g_tmpa_int} is a global variable (hence the~|g_| prefix)
+% corresponding to an integer register (i.e.~a \TeX{} count
+% register).\\
+% \cs{c_empty_tl} is the constant~(|c_|) token list variable
+% that is always empty.
+% \end{quote}
+%
+% Now here is an example of a typical function name.
+%
+% \cs{seq_push:Nn} is the function which puts the token list specified
+% by its second argument onto the stack specified by its first argument.
+% The different natures of the two arguments are indicated by the~|:Nn|
+% suffix. The first argument must be a single token which \enquote{names}
+% the stack parameter: such single-token arguments are denoted~|N|.
+% The second argument is a normal \TeX{} \enquote{undelimited argument},
+% which
+% may either be a single token or a balanced, brace-delimited token
+% list (which we shall here call a \textit{braced token list}): the~|n|
+% denotes such a \enquote{normal} argument form. The name of the function
+% indicates it belongs to the |seq| module.
+%
+% \subsection{Formal naming syntax}
+%
+% We shall now look in more detail at the syntax of these names. A
+% function name in \LaTeX3 has a name consisting of three parts:
+% \begin{quote}
+% |\|\meta{module}|_|\meta{description}|:|\meta{arg-spec}
+% \end{quote}
+% while a variable has (up to) four distinct parts to its name:
+% \begin{quote}
+% |\|\meta{scope}|_|\meta{module}|_|\meta{description}|_|\meta{type}
+% \end{quote}
+%
+% The syntax of all names contains
+% \begin{quote}
+% \meta{module} and \meta{description}
+% \end{quote}
+% these both give information about the command.
+%
+% A \emph{module} is a collection of closely related functions and
+% variables. Typical module names include~|int| for integer parameters
+% and related functions,~|seq| for sequences and~|box| for boxes.
+%
+% Packages providing new programming functionality will add new modules
+% as needed; the programmer can choose any unused name, consisting
+% of letters only, for a module. In general, the module name and module
+% prefix should be related: for example, the kernel module containing
+% \texttt{box} functions is called \texttt{l3box}. Module names and
+% programmers' contact details are listed in \pkg{l3prefixes.csv}.
+%
+% The \emph{description} gives more detailed information about the
+% function or parameter, and provides a unique name for it. It should
+% consist of letters and, possibly,~|_|~characters. In general, the
+% description should use |_| to divide up \enquote{words} or other easy to
+% follow parts of the name. For example, the \LaTeX3 kernel provides
+% \cs{if_cs_exist:N} which, as might be expected, tests if a command
+% name exists.
+%
+% Where functions for variable manipulation can perform assignments
+% either locally or globally, the latter case is indicated by the inclusion of
+% a |g| in the second part of the function name. Thus \cs{tl_set:Nn} is a local
+% function but \cs{tl_gset:Nn} acts globally. Functions of this type are
+% always documented together, and the scope of action may therefore be
+% inferred from the presence or absence of a |g|. See the next subsection for
+% more detail on variable scope.
+%
+% \subsubsection{Separating private and public material}
+%
+% One of the issues with the \TeX{} language is that it doesn't support
+% name spaces and encapsulation other than by convention. As a result
+% nearly every internal command in the \LaTeXe{} kernel has eventually
+% be used by extension packages as an entry point for modifications or
+% extensions. The consequences of this is that nowadays it is next to
+% impossible to change anything in the \LaTeXe{} kernel (even if it is
+% clearly just an internal command) without breaking something.
+%
+% In \pkg{expl3} we hope to improve this situation drastically by
+% clearly separating public interfaces (that extension packages can use
+% and rely on) and private functions and variables (that should not
+% appear outside of their module). There is (nearly) no way to enforce
+% this without severe computing overhead, so we implement it only
+% through a naming convention, and some support mechanisms. However, we
+% think that this naming convention is easy to understand and to follow,
+% so that we are confident that this will adopted and provides the
+% desired results.
+%
+% Functions created by a module may either be \enquote{public} (documented
+% with a defined interface) or \enquote{private} (to be used only within
+% that module, and thus not formally documented). It is important that
+% only documented interfaces are used; at the same time, it is necessary to
+% show within the name of a function or variable whether it is public
+% or private.
+%
+% To allow clear separation of these two cases, the following convention
+% is used. To denote a private function or a private variable (of the module),
+% two |_| characters are used in front of the module name, e.g.
+% \begin{verbatim}
+% \module_foo:nnn
+% \end{verbatim}
+% is a public function which should be documented while
+% \begin{verbatim}
+% \__module_foo:nnn
+% \end{verbatim}
+% is private to the module, and should \emph{not} be used outside of that
+% module.
+%
+% For variables, to avoid three |_| in a row, the separator for the variable
+% scope and any leading |_| for a private interface in the module part are
+% combined. Thus
+% \begin{verbatim}
+% \l_module_foo_tl
+% \end{verbatim}
+% is a public variable and
+% \begin{verbatim}
+% \l__module_foo_tl
+% \end{verbatim}
+% is private.
+%
+% \subsubsection{Using \texttt{@@} and \pkg{DocStrip} to mark private code}
+%
+% The formal syntax for internal functions allows clear separation of public
+% and private code, but includes redundant information (every internal function
+% or variable includes \texttt{__\meta{module}}). To aid programmers, the
+% \pkg{DocStrip} program introduces the syntax
+% \begin{quote}
+% \ttfamily
+% |%<@@=|\meta{module}|>|
+% \end{quote}
+% which then allows |@@| (and |_@@| in case of variables) to be used as
+% a place holder for \texttt{__\meta{module}} in code. Thus for example
+% \begin{verbatim}
+% %<@@=foo>
+% % \begin{macrocode}
+% \cs_new:Npn \@@_function:n #1
+% ...
+% \tl_new:N \l_@@_my_tl
+% % \end{macrocode}
+% \end{verbatim}
+% is converted by \pkg{DocStrip} to
+% \begin{verbatim}
+% \cs_new:Npn \__foo_function:n #1
+% ...
+% \tl_new:N \l__foo_my_tl
+% \end{verbatim}
+% on extraction. As you can see both |_@@| and |@@| are mapped to
+% \texttt{__\meta{module}}, because we think that this helps to
+% distinguish variables from functions in the source when the |@@|
+% convention is used.
+%
+% \subsubsection{Variables: declaration}
+%
+% In well-formed \pkg{expl3} code, variables should always be declared before
+% assignment is attempted. This is true even for variable types where the
+% underlying \TeX{} implementation will allow direct assignment. This applies
+% both to setting directly (\cs{tl_set:Nn}, etc.) and to setting equal
+% (\cs{tl_set_eq:NN}, etc.).
+%
+% To help programmers to adhere to this approach, the debugging option
+% |check-declarations| may be given
+% \begin{verbatim}
+% \debug_on:n { check-declarations }
+% \end{verbatim}
+% and will issue an error whenever an assignment is made to a non-declared
+% variable. There is a performance implication, so this option should only
+% be used for testing.
+%
+% \subsubsection{Variables: scope and type}
+%
+% The \meta{scope} part of the name describes how the variable can be
+% accessed. Variables are classified as local, global or constant.
+% This \emph{scope} type appears as a code at the beginning of the name;
+% the codes used are:
+% \begin{arg-description}
+% \item[c] constants (global variables whose value should not be
+% changed);
+% \item[g] variables whose value should only be set globally;
+% \item[l] variables whose value should only be set locally.
+% \end{arg-description}
+%
+% Separate functions are provided to assign data to local and global
+% variables; for example, \cs{tl_set:Nn} and \cs{tl_gset:Nn} respectively
+% set the value of a local or global \enquote{token list} variable.
+% Note that it is a poor \TeX{} practice to intermix local and global
+% assignments to a variable; otherwise you risk exhausting the save
+% stack.\footnote{See \emph{The \TeX{}book}, p.\,301, for further
+% information.}
+%
+% The \meta{type} is in the list of available
+% \emph{data-types};\footnote{Of course, if a totally new data type is
+% needed then this will not be the case. However, it is hoped that only
+% the kernel team will need to create new data types.} these include the
+% primitive \TeX{} data-types, such as the various registers, but to
+% these are added data-types built within the \LaTeX{} programming
+% system.
+%
+% The data types in \LaTeX3 are:
+% \begin{description}
+% \item[bitset] a string of bits (\texttt{0} and \texttt{1} tokens) that are
+% accessed by position or by name;
+% \item[bool] either true or false (the \LaTeX3 implementation does
+% not use \tn{iftrue} or \tn{iffalse});
+% \item[box] box register;
+% \item[cctab] category code table;
+% \item[clist] comma separated list;
+% \item[coffin] a \enquote{box with handles} --- a higher-level data
+% type for carrying out |box| alignment operations;
+% \item[dim] \enquote{rigid} lengths;
+% \item[fp] floating-point values;
+% \item[fparray] fixed-size vector of floating-point values;
+% \item[int] integer-valued count register;
+% \item[intarray] fixed-size vector of integer values;
+% \item[ior] an input stream (for reading from a file);
+% \item[iow] an output stream (for writing to a file);
+% \item[muskip] math mode \enquote{rubber} lengths;
+% \item[prop] property list;
+% \item[regex] regular expression;
+% \item[seq] sequence: a data-type used to implement lists (with
+% access at both ends) and stacks;
+% \item[skip] \enquote{rubber} lengths;
+% \item[str] \TeX{} strings: a special case of |tl| in which all
+% characters have category \enquote{other} (catcode~$12$),
+% other than spaces which are category \enquote{space}
+% (catcode~$10$);
+% \item[token] equal to a single arbitrary token;
+% \item[tl] \enquote{token list variables}: placeholders for token lists.
+% \end{description}
+% When the \meta{type} and \meta{module} are identical (as often happens in
+% the more basic modules) the \meta{module} part is often omitted for
+% aesthetic reasons.
+%
+% The name \enquote{token list} may cause confusion, and so some
+% background is useful. \TeX{} works with tokens and lists of tokens,
+% rather than characters. It provides two ways to store these token
+% lists: within macros and as token registers (|toks|). The
+% implementation in \LaTeX3 means that |toks| are not required, and that
+% all operations for storing tokens can use the |tl| variable type.
+%
+% Experienced \TeX{} programmers will notice that some of the variable
+% types listed are native \TeX{} registers whilst others are not. In
+% general, the underlying \TeX{} implementation for a data structure may
+% vary but the \emph{documented interface} will be stable. For example,
+% the |prop| data type was originally implemented as a |toks|, but
+% is currently built on top of the |tl| data structure.
+%
+% \subsubsection{Variables: guidance}
+%
+% Both comma lists and sequences have similar characteristics.
+% They both use special delimiters to mark out one entry from the
+% next, and are both accessible at both ends. In general, it is
+% easier to create comma lists `by hand' as they can be typed
+% in directly. User input often takes the form of a comma separated
+% list and so there are many cases where this is the obvious
+% data type to use. On the other hand, sequences use special internal
+% tokens to separate entries. This means that they can be used to
+% contain material that comma lists cannot (such as items that may
+% themselves contain commas!). In general, comma lists should be
+% preferred for creating fixed lists inside programs and for
+% handling user input where commas will not occur. On the other
+% hand, sequences should be used to store arbitrary lists of
+% data.
+%
+% \pkg{expl3} implements stacks using the sequence data structure.
+% Thus creating stacks involves first creating a sequence, and
+% then using the sequence functions which work in a stack manner
+% (\cs{seq_push:Nn}, \emph{etc}.).
+%
+% Due to the nature of the underlying \TeX{} implementation, it is
+% possible to assign values to token list variables and comma lists
+% without first declaring them. However, this is \emph{not supported
+% behavior}. The \LaTeX3 coding convention is that all variables must
+% be declared before use.
+%
+% The \pkg{expl3} package can be loaded with the \texttt{check-declarations}
+% option to verify that all variables are declared before use. This has
+% a performance implication and is therefore intended for testing during
+% development and not for use in production documents.
+%
+% \subsubsection{Functions: argument specifications}
+%
+% Function names end with an \meta{arg-spec} after a colon. This
+% gives an indication of the types of argument that a function takes,
+% and provides a convenient method of naming similar functions that
+% differ only in their argument forms (see the next section for
+% examples).
+%
+% The \meta{arg-spec} consists of a (possibly empty) list of letters,
+% each denoting one argument of the function. The letter, including
+% its case, conveys information about the type of argument required.
+%
+% All functions have a base form with arguments using one of the
+% following argument specifiers:
+% \begin{arg-description}
+% \item[n] Unexpanded token or braced token list.\\
+% This is a standard \TeX{} undelimited macro argument.
+% \item[N] Single token (unlike~|n|, the argument must \emph{not} be
+% surrounded by braces).\\
+% A typical example of a command taking an~|N|
+% argument is~|\cs_set|, in which the command being defined must be
+% unbraced.
+% \item[p] Primitive \TeX{} parameter specification.\\
+% This can be something simple like~|#1#2#3|, but may use arbitrary
+% delimited argument syntax such as: |#1,#2\q_stop#3|. This is used
+% when defining functions.
+% \item[T,F]
+% These are special cases of~|n| arguments, used for the
+% true and false code in conditional commands.
+% \end{arg-description}
+% There are two other specifiers with more general meanings:
+% \begin{arg-description}
+% \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
+% delimited arguments and the boolean tests needed after certain
+% primitive |\if|\ldots{} commands.
+% \end{arg-description}
+%
+% In case of |n| arguments that consist of a single token the
+% surrounding braces can be omitted in nearly all
+% situations---functions that force the use of braces even for single
+% token arguments are explicitly mentioned. However, programmers are
+% encouraged to always use braces around \texttt{n} arguments, as this
+% makes the relationship between function and argument clearer.
+%
+% Further argument specifiers are available as part of the expansion
+% control system. These are discussed in the next section.
+%
+% \section{Expansion control}
+%
+% Let's take a look at some typical operations one might want to
+% perform. Suppose we maintain a stack of open files and we use the
+% stack |\g_ior_file_name_seq| to keep track of them (\texttt{ior} is
+% the prefix used for the file reading module). The basic operation here
+% is to push a name onto this stack which could be done by the operation
+% \begin{quote}
+% \cs{seq_gpush:Nn} |\g_ior_file_name_seq {#1}|
+% \end{quote}
+% where |#1| is the filename. In other words, this operation would
+% push the file name as is onto the stack.
+%
+% However, we might face a situation where the filename is stored in
+% a variable of some sort, say |\l_ior_curr_file_tl|. In this case we
+% want to retrieve the value of the variable. If we simply use
+% \begin{quote}
+% \cs{seq_gpush:Nn} |\g_ior_file_name_seq| |\l_ior_curr_file_tl|
+% \end{quote}
+% we do not get the value of the variable pushed onto the stack,
+% only the variable name itself. Instead a suitable number of
+% \cs{exp_after:wN} would be necessary (together with extra braces) to
+% change the order of expansion,\footnote{\cs{exp_after:wN} is
+% the \LaTeX3 name for the \TeX{} \tn{expandafter} primitive.} \emph{i.e.}
+% \begin{quote}
+% \cs{exp_after:wN} \\
+% | |\cs{seq_gpush:Nn} \\
+% \cs{exp_after:wN} \\
+% | \g_ior_file_name_seq| \\
+% \cs{exp_after:wN} \\
+% | { \l_ior_curr_file_tl }|
+% \end{quote}
+%
+% The above example is probably the simplest case but already shows
+% how the code changes to something difficult to understand.
+% Furthermore there is an assumption in this: that the storage bin
+% reveals its contents after exactly one expansion. Relying on this
+% means that you cannot do proper checking plus you have to know
+% exactly how a storage bin acts in order to get the correct number
+% of expansions. Therefore \LaTeX3 provides the programmer with a
+% general scheme that keeps the code compact and easy to understand.
+%
+% To denote that some argument to a function needs special treatment one
+% just uses different letters in the arg-spec part of the function to
+% mark the desired behavior. In the above example one would write
+% \begin{quote}
+% \cs{seq_gpush:NV} |\g_ior_file_name_seq \l_ior_curr_file_tl|
+% \end{quote}
+% to achieve the desired effect. Here the |V| (the second argument)
+% is for \enquote{retrieve the value of the variable} before passing it to
+% the base function.
+%
+% The following letters can be used to denote special treatment of
+% arguments before passing it to the base function:
+% \begin{description}
+% \item[c] Character string used as a command name.\\ The argument (a
+% token or braced token list) is \emph{fully expanded}; the result
+% must be a sequence of characters which is then used to construct a
+% command name (\emph{via}~\tn{csname} \ldots \tn{endcsname}). This
+% command name is a single token that is passed to the function as
+% the argument. Hence
+% \begin{quote}
+% \cs{seq_gpush:cV} |{ g_file_name_seq }| \cs{l_tmpa_tl}
+% \end{quote}
+% is equivalent to
+% \begin{quote}
+% \cs{seq_gpush:NV} |\g_file_name_seq| \cs{l_tmpa_tl}.
+% \end{quote}
+% Full expansion means that (a) the entire
+% argument must be expandable and (b) any variables are
+% converted to their content. So the preceding examples are also
+% equivalent to
+% \begin{quote}
+% \cs{tl_new:N} |\g_file_seq_name_tl| \\
+% \cs{tl_gset:Nn} |\g_file_seq_name_tl { g_file_name_seq }| \\
+% \cs{seq_gpush:cV} |{| \cs{tl_use:N} |\g_file_seq_name_tl }| \cs{l_tmpa_tl}.
+% \end{quote}
+% (Token list variables are expandable and we could omit the
+% accessor function \cs{tl_use:N}. Other variable types require the
+% appropriate \cs{\meta{var}_use:N} functions to be used in this
+% context.)
+% \item[V] Value of a variable.\\
+% This means that the contents of the register in question is used
+% as the argument, be it an integer, a length-type register, a token
+% list variable or similar. The value is passed to the function as a
+% braced token list. Can be applied to variables which have a
+% \cs{\meta{var}_use:N} function (other than boxes),
+% and which therefore deliver a single \enquote{value}.
+% \item[v] Value of a register, constructed from a character string
+% used as a command name.\\
+% This is a combination of |c| and |V| which first constructs a
+% control sequence from the argument and then passes the value of
+% the resulting register to the function. Can be applied to
+% variables which have a \cs{\meta{var}_use:N} function (other than
+% boxes), and which therefore deliver a single
+% \enquote{value}.
+% \item[e] Fully-expanded token or braced token list.\\
+% This means that the argument is expanded as in the replacement
+% text of a~\tn{message}, and the expansion is passed to the function as
+% a braced token list.
+% \item[o] One-level-expanded token or braced token list.\\
+% This means that the argument is expanded one level, as by
+% \tn{expandafter}, and the expansion is passed to the function as a
+% braced token list. Note that if the original argument is a braced
+% token list then only the first token in that list is expanded.
+% In general, using \texttt{V} should be preferred to using
+% \texttt{o} for simple variable retrieval.
+% \item[f] Expanding the first token recursively in a braced token
+% list.\\ Almost the same as the |e| type except here the token list
+% is expanded fully until the first unexpandable token is found and
+% the rest is left unchanged. Note that if this function finds a
+% space at the beginning of the argument it gobbles it and does not
+% expand the next token.
+% \item[x] Fully-expanded token or braced token list.\\
+% This expansion is very similar to |e|-type but is not nestable,
+% can only be used to create non-expandable functions, and requires
+% that |#| tokens are doubled in the argument. In almost all cases,
+% |e|-type should be preferred: retained largely for historical
+% reasons, and should where possible be replaced by the |e|-type
+% equivalent.
+% \end{description}
+%
+% \subsection{Simpler means better}
+%
+% Anyone who programs in \TeX{} is frustratingly familiar with the
+% problem of arranging that arguments to functions are suitably expanded
+% before the function is called. To illustrate how expansion control
+% can bring instant relief to this problem we shall consider two
+% examples copied from \texttt{latex.ltx}.
+%
+% \begin{verbatim}
+% \global\expandafter\let
+% \csname\cf@encoding \string#1\expandafter\endcsname
+% \csname ?\string#1\endcsname
+% \end{verbatim}
+% This first piece of code is in essence simply a global \tn{let} whose
+% two arguments firstly have to be constructed before \tn{let} is
+% executed. The |#1| is a control sequence name such as
+% |\textcurrency|. The token to be defined is obtained by
+% concatenating the characters of the current font encoding stored in
+% |\cf@encoding|, which has to be fully expanded, and the name of the
+% symbol. The second token is the same except it uses the default
+% encoding |?|. The result is a mess of interwoven \tn{expandafter}
+% and \tn{csname} beloved of all \TeX{} programmers, and the code is
+% essentially unreadable.
+%
+% Using the conventions and functionality outlined here, the task would
+% be achieved with code such as this:
+% \begin{verbatim}
+% \cs_gset_eq:cc
+% { \cf@encoding \token_to_str:N #1 } { ? \token_to_str:N #1 }
+% \end{verbatim}
+% The command \cs{cs_gset_eq:cc} is a global~\tn{let} that generates
+% command names out of both of its arguments before making the
+% definition. This produces code that is far more readable and more
+% likely to be correct first time. (\cs{token_to_str:N} is the \LaTeX3
+% name for \tn{string}.)
+%
+% Here is the second example.
+% \begin{verbatim}
+% \expandafter
+% \in@
+% \csname sym#3%
+% \expandafter
+% \endcsname
+% \expandafter
+% {%
+% \group@list}%
+% \end{verbatim}
+% This piece of code is part of the definition of another function. It
+% first produces two things: a token list, by expanding |\group@list| once;
+% and a token whose name comes from~`|sym#3|'. Then the function~\cs{in@}
+% is called and this tests if its first argument occurs in the token list
+% of its second argument.
+%
+% Again we can improve enormously on the code. First we shall rename
+% the function~\cs{in@}, which tests if its first argument appears
+% within its second argument, according to our conventions. Such a
+% function takes two normal \enquote{\texttt{n}} arguments and operates
+% on token lists: it might reasonably be named |\tl_test_in:nn|. Thus
+% the variant function we need would be defined with the appropriate
+% argument types and its name would be |\tl_test_in:cV|. Now this code
+% fragment would be simply:
+% \begin{verbatim}
+% \tl_test_in:cV { sym #3 } \group@list
+% \end{verbatim}
+% This code could be improved further by using a sequence |\l_group_seq|
+% rather than the bare token list |\group@list|. Note that, in addition
+% to the lack of \tn{expandafter}, the space after the~|}| is
+% silently ignored since all white space is ignored in this programming
+% environment.
+%
+% \subsection{New functions from old}
+%
+% For many common functions the \LaTeX3 kernel provides variants
+% with a range of argument forms, and similarly it is expected that
+% extension packages providing new functions will make them available in
+% all the commonly needed forms.
+%
+% However, there will be occasions where it is necessary to construct a
+% new such variant form; therefore the expansion module provides a
+% straightforward mechanism for the creation of functions with any
+% required argument type, starting from a function that takes \enquote{normal}
+% \TeX{} undelimited arguments.
+%
+% To illustrate this let us suppose you have a \enquote{base function}
+% |\demo_cmd:Nnn| that takes three normal arguments, and that you need
+% to construct the variant |\demo_cmd:cne|, for which the first argument
+% is used to construct the \emph{name} of a command, whilst the third
+% argument must be fully expanded before being passed to
+% |\demo_cmd:Nnn|.
+% To produce the variant form from the base form, simply use this:
+% \begin{verbatim}
+% \cs_generate_variant:Nn \demo_cmd:Nnn { cne }
+% \end{verbatim}
+% This defines the variant form so that you can then write, for example:
+% \begin{verbatim}
+% \demo_cmd:cne { abc } { pq } { \rst \xyz }
+% \end{verbatim}
+% rather than \ldots\ well, something like this!
+% \begin{verbatim}
+% \def \tempa {{pq}}%
+% \edef \tempb {\rst \xyz}%
+% \expandafter
+% \demo@cmd:nnn
+% \csname abc%
+% \expandafter
+% \expandafter
+% \expandafter
+% \endcsname
+% \expandafter
+% \tempa
+% \expandafter
+% {%
+% \tempb
+% }%
+% \end{verbatim}
+%
+% Another example: you may wish to declare a function
+% |\demo_cmd_b:enene|, a variant of an existing function
+% |\demo_cmd_b:nnnnn|, that fully
+% expands arguments 1,~3 and~5, and produces commands to pass as
+% arguments 2 and~4 using~\tn{csname}.
+% The definition you need is simply
+% \begin{verbatim}
+% \cs_generate_variant:Nn \demo_cmd_b:nnnnn { enene }
+% \end{verbatim}
+%
+% This extension mechanism is written so that if the same new form of
+% some existing command is implemented by two extension packages then the
+% two definitions are identical and thus no conflict occurs.
+%
+% \section{The distribution}
+%
+% The \pkg{expl3} modules are designed to be loaded on top of \LaTeXe{}.
+%
+% \begin{bfseries}
+% The core \pkg{expl3} language is broadly stable, and thus
+% the syntax conventions and functions provided
+% are now ready for wider use. There may still be changes to some
+% functions, but these will be minor when compared to the scope of
+% \pkg{expl3}. A robust mechanism is in place for such deprecations.
+% \end{bfseries}
+%
+% The distribution of \pkg{expl3} is split up into three packages on
+% CTAN: \pkg{l3kernel}, \pkg{l3packages} and \pkg{l3experimental}.
+% The core programming layer provided by \pkg{l3kernel} has been loaded
+% as part of the \LaTeX{} since 2020-02-02. For historical reasons, in
+% older kernel releases
+% \begin{verbatim}
+% \RequirePackage{expl3}
+% \end{verbatim}
+% loads the code distributed as \pkg{l3kernel}. This monolithic
+% package contains all of the modules regarded by the team as stable,
+% and any changes in this code are very limited. This material is
+% therefore suitable for use in third-party packages without concern
+% about changes in support. All of this code is documented in
+% \texttt{interface3.pdf}.
+%
+% The material in \pkg{l3packages} is also stable; this bundle provides
+% user-level commands, some of which have been integrated in the
+% \LaTeX{} kernel.
+%
+% Finally, \pkg{l3experimental} contains modules ready for public use
+% but not yet integrated into \pkg{l3kernel}. These modules have to
+% be loaded explicitly. The team anticipate that all of these modules
+% will move to stable status over time, but they may be more flexible
+% in terms of interface and functionality detail. Feedback on these
+% modules is extremely valuable.
+%
+% \section{Moving from \LaTeXe{} to \pkg{expl3}}
+%
+% To help programmers to use \pkg{expl3} code in existing \LaTeXe{} package,
+% some short notes on making the change are probably desirable.
+% Suggestions for inclusion here are welcome! Some of the following
+% is concerned with code, and some with coding style.
+%
+% \begin{itemize}
+% \item \pkg{expl3} is mainly focused on programming. This means that
+% some areas still require the use of \LaTeXe{} internal macros.
+% For example, you may well need \tn{IfPackageLoadedTF}, as there
+% is currently no native \pkg{expl3} package loading module.
+% \item User level macros should be generated using the mechanism
+% available in the \pkg{ltcmd} module, which is part of the
+% the \LaTeX{} kernel since 2020-10-01.
+% \item At an internal level, most functions should be generated
+% \tn{long} (using \cs{cs_new:Npn}) rather than \enquote{short} (using
+% \cs{cs_new_nopar:Npn}).
+% \item Where possible, declare all variables and functions (using
+% \cs{cs_new:Npn}, \cs{tl_new:N}, etc.) before use.
+% \item Prefer \enquote{higher-level} functions over \enquote{lower-level},
+% where possible. So for example use \cs{cs_if_exist:NTF} and not
+% \cs{if_cs_exist:N}.
+% \item Use space to make code readable. In general, we recommend
+% a layout such as:
+% \begin{verbatim}
+% \cs_new:Npn \foo_bar:Nn #1#2
+% {
+% \cs_if_exist:NTF #1
+% { \__foo_bar:n {#2} }
+% { \__foo_bar:nn {#2} { literal } }
+% }
+% \end{verbatim}
+% where spaces are used around |{| and |}| except for isolated
+% |#1|, |#2|, \emph{etc.}
+% \item Put different code items on separate lines: readability is
+% much more useful than compactness.
+% \item Use long, descriptive names for functions and variables,
+% and for auxiliary functions use the parent function name plus
+% |aux|, |auxi|, |auxii| and so on.
+% \item If in doubt, ask the team via the LaTeX-L list: someone will
+% soon get back to you!
+% \end{itemize}
+%
+% \section{Load-time options for \pkg{expl3}}
+%
+% To support code authors, the \pkg{expl3} package for \LaTeXe{} includes
+% a small number of load-time options. These all work in a key--value
+% sense, recognising the \texttt{true} and \texttt{false} values. Giving
+% the option name alone is equivalent to using the option with the
+% \texttt{true} value.
+%
+% \DescribeOption{check-declarations}
+% All variables used in \pkg{expl3} code should be declared. This is enforced
+% by \TeX{} for variable types based on \TeX{} registers, but not for those
+% which are constructed using macros as the underlying storage system. The
+% \texttt{check-declarations} option enables checking for all variable
+% assignments, issuing an error if any variables are assigned without being
+% initialised. See also \cs{debug_on:n} \texttt{\{check-declarations\}}
+% in \pkg{interface3} for finer control.
+%
+% \DescribeOption{log-functions}
+% The \texttt{log-functions} option is used to enable recording of every new
+% function name in the \texttt{.log} file. This is useful for debugging
+% purposes, as it means that there is a complete list of all functions
+% created by each module loaded (with the exceptions of a very small number
+% required by the bootstrap code). See also \cs{debug_on:n}
+% \texttt{\{log-functions\}} in \pkg{interface3} for finer control.
+%
+% \DescribeOption{backend}
+% Selects the backend to be used for color, graphics and related operations that
+% are backend-dependent. Options available are
+% \begin{itemize}[font = \texttt]
+% \item[dvips] Use the \texttt{dvips} driver.
+% \item[dvipdfmx] Use the \texttt{dvipdfmx} driver.
+% \item[dvisvgm] Use the \texttt{dvisvgm} driver.
+% \item[luatex] Use the direct PDF output mode of \LuaTeX{}
+% \item[pdftex] Use the direct PDF output mode of \pdfTeX{}
+% \item[xetex] Use the \XeTeX{} version of the \texttt{dvipdfmx} driver.
+% \end{itemize}
+% For historical reasons, there is also \texttt{pdfmode} as an equivalent of
+% \texttt{luatex} or \texttt{pdftex}, and \texttt{xdvipdfmx} as
+% an equivalent to \texttt{xetex}, but these are deprecated
+%
+% \DescribeOption{suppress-backend-headers}
+% The \texttt{suppress-backend-headers} option suppresses loading of
+% backend-specific header files; currently this only affects \texttt{dvips}.
+% This option is available to support DVI-based routes that do not
+% support the |header| line used by \texttt{dvips}.
+%
+% The debugging options may also be given using
+% \verb|\keys_set:nn { sys } { ... }|; the \texttt{backend} option can be
+% given in this way \emph{only} if a backend has not already been loaded.
+% This method of setting options is useful where \pkg{expl3} is pre-loaded
+% by the \LaTeXe{} format.
+%
+% \section{Using \pkg{expl3} with formats other than \LaTeXe{}}
+%
+% As well as the \LaTeXe{} package \pkg{expl3}, there is also a
+% \enquote{generic} loader for the code, \texttt{expl3-generic.tex}.
+% This may be loaded using the plain \TeX{} syntax
+% \begin{verbatim}
+% \input expl3-generic %
+% \end{verbatim}
+% This enables the programming layer to work with the other formats.
+% As no options are available loading in this way, the \enquote{native}
+% drivers are automatically used. If this \enquote{generic} loader is
+% used with \LaTeXe{} the code automatically switches to the appropriate
+% package route.
+%
+% After loading the programming layer using the generic interface, the
+% commands \cs{ExplSyntaxOn} and \cs{ExplSyntaxOff} and the code-level
+% functions and variables detailed in \pkg{interface3} are available.
+% Note that other \LaTeXe{} packages \emph{using} \pkg{expl3} are not
+% loadable: package loading is dependent on the \LaTeXe{} package-management
+% mechanism.
+%
+% \section{Getting the version of \pkg{expl3}}
+%
+% \begin{function}{\ExplLoaderFileDate}
+% Once the programming layer is loaded by one of the loaders, you can access
+% its version in the ISO date format \meta{year}-\meta{month}-\meta{day},
+% through \cs{ExplLoaderFileDate}.
+%
+% The current version of \pkg{expl3} is \ExplLoaderFileDate.
+% \end{function}
+%
+% \section{Engine/primitive requirements}
+%
+% To use \pkg{expl3} and the higher level packages provided by the
+% team, the minimal set of primitive requirements is currently described
+% in \href{README.md}{README.md}.
+%
+% Practically, these requirements are met by the engines
+% \begin{itemize}
+% \item \pdfTeX{} v1.40.20 or later.
+% \item \XeTeX{} v0.999991 or later.
+% \item \LuaTeX{} v1.10 or later.
+% \item e-(u)\pTeX{} v3.8.2 or later.
+% \item Prote (2021) or later.
+% \end{itemize}
+%
+% Additional modules beyond the core of \pkg{expl3} may require additional
+% primitives. In particular, third-party authors may significantly
+% extend the primitive coverage requirements.
+%
+% \section{The \LaTeX{} Project}
+%
+% Development of \LaTeX3 is carried out by The \LaTeX{} Project:
+% \url{https://www.latex-project.org/latex3/}.
+%
+% \begin{thebibliography}{1}
+%
+% \bibitem{A-W:K-TB}
+% Donald E Knuth
+% \newblock \emph{The \TeX{}book}.
+% \newblock Addison-Wesley, Reading, Massachusetts, 1984.
+%
+% \bibitem{A-W:GMS94}
+% Goossens, Mittelbach and Samarin.
+% \newblock \emph{ The \LaTeX{} Companion}.
+% \newblock Addison-Wesley, Reading, Massachusetts, 1994.
+%
+% \bibitem{A-W:LLa94}
+% Leslie Lamport.
+% \newblock \emph{\LaTeX{}: A Document Preparation System}.
+% \newblock Addison-Wesley, Reading, Massachusetts, second edition, 1994.
+%
+% \bibitem{tub:MR97-1}
+% Frank Mittelbach and Chris Rowley.
+% \newblock \enquote{The \LaTeX{} Project}.
+% \newblock \emph{TUGboat},
+% Vol.\,18, No.\,3, pp.\,195--198, 1997.
+%
+% \end{thebibliography}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{expl3} implementation}
+%
+% The implementation here covers several things. There are two
+% \enquote{loaders} to define: the parts of the code that are specific to
+% \LaTeXe{} or to non-\LaTeXe{} formats. These have to cover the same
+% concepts as each other but in rather different ways: as a result, much
+% of the code is given in separate blocks. There is also a short piece of
+% code for the start of the \enquote{payload}: this is to ensure that
+% loading is always done in the right way.
+%
+% \subsection{Loader interlock}
+%
+% A short piece of set up to check that the loader and \enquote{payload}
+% versions match.
+%
+% \begin{macro}{\ExplLoaderFileDate}
+% As DocStrip is used to generate \cs{ExplFileDate}
+% for all files from the same source, it has to match. Thus the loaders
+% simply save this information with a new name.
+% \begin{macrocode}
+%<*loader>
+\let\ExplLoaderFileDate\ExplFileDate
+%</loader>
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\c__kernel_expl_date_tl}
+% The \LaTeXe{} loader stores a private copy of \cs{ExplFileDate},
+% which is fixed when the format is build and cannot be changed later.
+% While \cs{ExplFileDate} ensures that the loader (either
+% \texttt{2ekernel} or \texttt{package}) version matches the version
+% of \file{expl3-code.tex}, this one ensures that the version of an
+% eventual |\usepackage{expl3}| matches the version from the format.
+% It seems to be common to have stray format files in the user tree
+% which trigger errors due to mismatched versions (\emph{e.g.}
+% \url{https://github.com/latex3/latex3/issues/781}).
+%
+% This block also goes to the generic loader, as \pkg{expl3} isn't
+% preloaded there, so the kernel date should be equal to the loader
+% date.
+% \begin{macrocode}
+%<*2ekernel|generic>
+\begingroup
+ \catcode`\_=11
+ \expandafter
+ \ifx\csname c__kernel_expl_date_tl\endcsname\relax
+ \global\let\c__kernel_expl_date_tl\ExplFileDate
+ \fi
+\endgroup
+%</2ekernel|generic>
+% \end{macrocode}
+% \end{macro}
+%
+% The interlock test itself is simple: \cs{ExplLoaderFileDate} must be
+% defined and identical to \cs{ExplFileDate}. As this has to work for
+% both \LaTeXe{} and other formats, there is some auto-detection involved.
+% (Done this way avoids having two very similar blocks for \LaTeXe{} and
+% other formats.)
+% \begin{macrocode}
+%<*!loader>
+\begingroup
+ \def\next{\endgroup}%
+ \expandafter\ifx\csname PackageError\endcsname\relax
+ \begingroup
+ \def\next{\endgroup\endgroup}%
+ \def\PackageError#1#2#3%
+ {%
+ \endgroup
+ \errhelp{#3}%
+ \errmessage{#1 Error: #2!}%
+ }%
+ \fi
+ \expandafter\ifx\csname ExplLoaderFileDate\endcsname\relax
+ \def\next
+ {%
+ \PackageError{expl3}{No expl3 loader detected}
+ {%
+ You have attempted to use the expl3 code directly rather than using
+ the correct loader. Loading of expl3 will abort.
+ }%
+ \endgroup
+ \endinput
+ }
+ \else
+ \ifx\ExplLoaderFileDate\ExplFileDate
+ \else
+ \def\next
+ {%
+ \PackageError{expl3}{Mismatched expl3 files detected}
+ {%
+ You have attempted to load expl3 with mismatched files:
+ probably you have one or more files 'locally installed' which
+ are in conflict. Loading of expl3 will abort.
+ }%
+ \endgroup
+ \endinput
+ }%
+ \fi
+\fi
+\next
+%</!loader>
+% \end{macrocode}
+%
+% A reload test for the payload, just in case.
+% \begin{macrocode}
+%<*!loader>
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname ver@expl3-code.tex\endcsname\relax
+ \expandafter\edef\csname ver@expl3-code.tex\endcsname
+ {%
+ \ExplFileDate\space
+ L3 programming layer
+ }%
+\else
+ \expandafter\endinput
+\fi
+%</!loader>
+% \end{macrocode}
+%
+% All good: log the version of the code used (for log completeness). As this
+% is more-or-less \cs{ProvidesPackage} without a separate file and as this also
+% needs to work without \LaTeXe{}, just write the information directly to the
+% log.
+% \begin{macrocode}
+%<*!loader>
+\immediate\write-1 %
+ {%
+ Package: expl3
+ \ExplFileDate\space
+ L3 programming layer (code)%
+ }%
+%</!loader>
+% \end{macrocode}
+%
+% \subsection{\LaTeXe{} loaders}
+%
+% Loading with \LaTeXe{} may be as part of the format (pre-loading)
+% or as a package. We have to allow for both possible paths, and of
+% course the package being loaded on to of the pre-load. That means
+% the code here must be safe against re-loading.
+%
+% \begin{macrocode}
+%<*package&loader|2ekernel>
+% \end{macrocode}
+%
+% Identify the package or add to the format message.
+% \begin{macrocode}
+%<*2ekernel>
+\ifdefined\LaTeXReleaseInfo
+ \LaTeXReleaseInfo\expandafter{\the\LaTeXReleaseInfo
+ \show@release@info{L3 programming layer <\ExplFileDate>}%
+ }%
+\else
+ \everyjob\expandafter{\the\everyjob
+ \message{L3 programming layer <\ExplFileDate>}%
+ }%
+\fi
+%</2ekernel>
+%<*!2ekernel>
+\ProvidesPackage{expl3}
+ [%
+ \ExplFileDate\space
+ L3 programming layer (loader)
+ ]%
+%</!2ekernel>
+% \end{macrocode}
+%
+% \begin{macro}{\ProvidesExplPackage, \ProvidesExplClass, \ProvidesExplFile}
+% For other packages and classes building on this one it is convenient
+% not to need \cs{ExplSyntaxOn} each time. All macros use the same
+% internal one with the proper \LaTeXe{} command.
+% \begin{macrocode}
+\protected\def\ProvidesExplPackage
+ {\@expl@provides@file@@Nnnnnn\ProvidesPackage{Package}}
+\protected\def\ProvidesExplClass
+ {\@expl@provides@file@@Nnnnnn\ProvidesClass{Document Class}}
+\protected\def\ProvidesExplFile
+ {\@expl@provides@file@@Nnnnnn\ProvidesFile{File}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@expl@provides@file@@Nnnnnn}
+% \begin{macro}{\@expl@provides@file@@N}
+% \begin{macro}{\@expl@provides@generic@@wnnw}
+% We need to check the existence of the
+% \cs[no-index]{Provides\meta{thing}}, since we need to load this very
+% early in the \LaTeXe{} kernel. The gymnastics with the version identifier
+% argument allow for the case where it starts with a \texttt{v} and the one
+% where it does not: formally the \texttt{v} is not part of the version,
+% but a lot of people include it.
+% \begin{macrocode}
+\protected\long\def\@expl@provides@file@@Nnnnnn#1#2#3#4#5#6%
+ {%
+ \ifnum0%
+ \ifdefined#11\fi
+ \ifx\relax#1\else1\fi
+ =11
+ \expandafter#1%
+ \else
+ \@expl@provides@generic@@wnnw{#2}%
+ \fi
+ {#3}[{#4 \ifx\relax#5\relax\else\@expl@provides@file@@N#5\space\fi #6}]%
+ \ExplSyntaxOn
+ }
+\long\def\@expl@provides@file@@N#1{v\if#1v\else#1\fi}
+\protected\long\def\@expl@provides@generic@@wnnw#1\fi#2[#3]%
+ {%
+ \immediate\write-1{#1: #2 #3}%
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% Load the business end: this leaves \cs{expl3} syntax on.
+% The test ensures we only load once without needing to know if
+% there was a preloading step.
+% \begin{macrocode}
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname tex\string _let:D\endcsname\relax
+ \expandafter\@firstofone
+\else
+ \expandafter\@gobble
+\fi
+ {\input expl3-code.tex }%
+% \end{macrocode}
+%
+% A check that the bootstrap code did not abort loading: if it did,
+% bail out silently here.
+% \begin{macrocode}
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname tex\string _let:D\endcsname\relax
+ \expandafter\endinput
+\fi
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=expl>
+% \end{macrocode}
+%
+% At this point, if we have \cs{c__kernel_expl_date_tl} defined, just
+% call the \cs{__kernel_dependency_version_check:Nn} auxiliary to check
+% if it matches \cs{ExplLoaderFileDate}. Here the test is performed
+% only if \cs{c__kernel_expl_date_tl} exists because this file can be
+% loaded in a \LaTeXe{} format without \pkg{expl3} preloaded, where that
+% token list doesn't exist.
+%
+% This is all done in the \texttt{package} \pkg{docstrip} guard because
+% it doesn't apply to \pkg{expl3.ltx}.
+% \begin{macrocode}
+%<*package>
+\ifcsname\detokenize{c__kernel_expl_date_tl}\endcsname
+ \expandafter\@firstofone
+\else
+% \end{macrocode}
+% If \cs{c__kernel_expl_date_tl} does \emph{not} exist we may be loading
+% in a format without \pkg{expl3} preloaded or in the earlier (although
+% still compatible) version in which the error mentioned above showed
+% up. If loading as a package, \file{expl3-code.tex} got read and here
+% the \pkg{expl3} syntax is on. Otherwise it was already loaded in a
+% sligtly older kernel, so we fire the incompatibility error message and
+% abort loading.
+% \begin{macrocode}
+ \ifodd\csname\detokenize{l__kernel_expl_bool}\endcsname
+% \end{macrocode}
+% In package mode all files are loaded in one go, so versions will
+% match. We just have to set \cs{c__kernel_expl_date_tl} so that
+% further dependencies don't break:
+% \begin{macrocode}
+ \global\expandafter\let\csname\detokenize
+ {c__kernel_expl_date_tl}\endcsname\ExplLoaderFileDate
+ \expandafter\expandafter
+ \expandafter\@gobble
+ \else
+% \end{macrocode}
+% And reloading in an incompatible version is an error:
+% \begin{macrocode}
+ \expandafter\expandafter
+ \expandafter\@firstofone
+ \fi
+\fi
+ {\csname\detokenize{__kernel_dependency_version_check:Nn}\endcsname
+ \ExplLoaderFileDate{expl3.sty}}%
+%</package>
+% \end{macrocode}
+%
+% Here we can also detect whether we're reloading. This code goes into
+% \texttt{expl3.ltx} and \texttt{expl3.sty}, the former loaded into the
+% \LaTeXe{} format. When this code is loaded for the first time, the
+% \cs{g_@@_reload_bool} boolean doesn't exist (\cs{else} branch of the
+% \cs{ifcsname} below), so we create it. If the \cs{ifcsname} is true,
+% then we do \cs{ExplSyntaxOn} (because when reloading,
+% \texttt{expl3-code.tex} isn't read again), and set
+% \cs{g_@@_reload_bool} to true.
+% \begin{macrocode}
+\ifcsname\detokenize{g_@@_reload_bool}\endcsname
+ \ExplSyntaxOn
+ \bool_gset_true:N \g_@@_reload_bool
+\else
+ \bool_new:N \g_@@_reload_bool
+\fi
+% \end{macrocode}
+%
+% \begin{variable}{\c_@@_def_ext_tl}
+% Needed by \LaTeXe{}, and avoiding a re-load issue. Variables are
+% best checked explicitly, rather than with \cs{g_@@_reload_bool}
+% because some appear only in one of the code files, so
+% \cs{g_@@_reload_bool} doesn't necessarily mean that the variable
+% is already declared.
+% \begin{macrocode}
+\tl_if_exist:NF \c_@@_def_ext_tl
+ { \tl_const:Nn \c_@@_def_ext_tl { def } }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}
+% {\__kernel_sys_configuration_load:n,\__kernel_sys_configuration_load_std:n}
+% To load configurations, we have the following cases
+% \begin{itemize}
+% \item \pkg{expl3} is pre-loaded: by the time any configuration loads,
+% we have the full file loading stack, and only need the standard
+% version of the code here.
+% \item The package is loaded with pre-loading: we again use the standard
+% version, but we don't test just yet.
+% \item The package is used without pre-loaded code: we need to manually
+% manage \pkg{expl3} syntax.
+% \end{itemize}
+% \begin{macrocode}
+\cs_gset_protected:Npn \__kernel_sys_configuration_load:n #1
+%<*!2ekernel>
+ {
+ \ExplSyntaxOff
+ \cs_undefine:c { ver@ #1 .def }
+ \@onefilewithoptions {#1} [ ] [ ]
+ \c_@@_def_ext_tl
+ \ExplSyntaxOn
+ }
+\cs_gset_protected:Npn \__kernel_sys_configuration_load_std:n #1
+%</!2ekernel>
+ {
+ \cs_undefine:c { ver@ #1 .def }
+ \@onefilewithoptions {#1} [ ] [ ]
+ \c_@@_def_ext_tl
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_options_clist}
+% \begin{macrocode}
+%<*!2ekernel>
+\clist_if_exist:NF \l_@@_options_clist
+ { \clist_new:N \l_@@_options_clist }
+\DeclareOption*
+ { \clist_put_right:NV \l_@@_options_clist \CurrentOption }
+\ProcessOptions \relax
+%</!2ekernel>
+% \end{macrocode}
+% \end{variable}
+%
+% Pretty standard setting creation.
+% \begin{macrocode}
+\keys_define:nn { sys }
+ {
+ backend .choices:nn =
+ { dvipdfmx , dvips , dvisvgm , luatex , pdftex , pdfmode , xdvipdfmx , xetex }
+ { \sys_load_backend:n {#1} } ,
+ check-declarations .choice: ,
+ check-declarations / true .code:n =
+ { \debug_on:n { check-declarations } } ,
+ check-declarations / false .code:n =
+ {
+ \__kernel_if_debug:TF
+ { \debug_off:n { check-declarations } }
+ { }
+ } ,
+ check-declarations .default:n = true ,
+ driver .meta:n = { backend = #1 } ,
+ enable-debug .code:n = {} , % A stub
+ log-functions .choice: ,
+ log-functions / true .code:n =
+ { \debug_on:n { log-functions } } ,
+ log-functions / false .code:n =
+ {
+ \__kernel_if_debug:TF
+ { \debug_off:n { log-functions } }
+ { }
+ } ,
+ log-functions .default:n = true ,
+ suppress-backend-headers .bool_gset_inverse:N
+ = \g__kernel_backend_header_bool ,
+ suppress-backend-headers .initial:n = false ,
+ undo-recent-deprecations .code:n = {} % A stub
+ }
+% \end{macrocode}
+%
+% \begin{macro}{\@expl@sys@load@backend@@@@}
+% A backend has to be in place by the start of the document: this has
+% to be before global options are checked for use.
+%
+% The \cs[no-index]{@expl@...@@@@} macros defined in this package are
+% interfaces for \LaTeXe{}. There are currently (this will change
+% with the next release of \LaTeXe{}) two possible cases, at this
+% point of the code: either \cs{@expl@sys@load@backend@@@@} (and the
+% others) already exist because they were defined in
+% \texttt{ltexpl.ltx} (in \texttt{2ekernel} mode) or in
+% \texttt{expl3.ltx} (in \texttt{package} mode).
+%
+% In \texttt{2ekernel} mode, if they exist we are using a future
+% (2020-10-01) release of \LaTeXe{} and we don't need (and can't)
+% patch \LaTeXe's internals because these commands are already there.
+% Though if they don't exist in \texttt{2ekernel} mode, we're using
+% an older version of the kernel, so we \emph{must} patch.
+%
+% In \texttt{package} mode, if these commands exist, then we are using
+% a version of \LaTeXe{} with \pkg{expl3} preloaded (any version)
+% and in any case patching is already done or the macros are in the
+% format itself, so nothing to do.
+% But if in \texttt{package} mode these macros don't exist, we have an
+% even older version of \LaTeXe{} which doesn't even have
+% \pkg{expl3} preloaded, so patching is necessary.
+%
+% All this means that in both \texttt{2ekernel} and \texttt{package}
+% mode we have to check whether \cs{@expl@sys@load@backend@@@@}
+% exists, and patch some \LaTeXe{} internals if it doesn't.
+%
+% In newer \LaTeXe{}, these macros have an empty definition in
+% \texttt{ltexpl.dtx} in case something wrong happens while loading
+% this file (\texttt{expl3.ltx}), so they can safely be used in the
+% \LaTeXe{} kernel.
+%
+% \cs{@expl@sys@load@backend@@@@} is inserted right at the beginning
+% of \cs{document}, but after closing the group started by \cs{begin}.
+% When using \cs{tl_put_left:Nn} to patch the backend loading in
+% \cs{document}, we need to make sure that it happens at group level
+% zero, thus the strange |\endgroup...\begingroup| thing.
+%
+% This chunk of code should only be executed when loading
+% \texttt{expl3.sty} in a \LaTeXe{} without \pkg{expl3} preloaded, so
+% we check if \cs{@expl@sys@load@backend@@@@} exists.
+% \begin{macrocode}
+\cs_if_exist:NF \@expl@sys@load@backend@@@@
+ {
+ \tl_put_left:Nn \document
+ {
+ \endgroup
+ \@expl@sys@load@backend@@@@
+ \begingroup
+ }
+ }
+% \end{macrocode}
+%
+% Now we define it anyhow.
+% \begin{macrocode}
+\cs_gset_protected:Npn \@expl@sys@load@backend@@@@
+ {
+ \str_if_exist:NF \c_sys_backend_str
+ { \sys_load_backend:n { } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% Process package options.
+% \begin{macrocode}
+%<*!2ekernel>
+\keys_set:nV { sys } \l_@@_options_clist
+\str_if_exist:NF \c_sys_backend_str
+ { \sys_load_backend:n { } }
+%</!2ekernel>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*!2ekernel>
+\bool_if:NT \g_@@_reload_bool
+ {
+ \cs_gset_eq:NN \__kernel_sys_configuration_load:n
+ \__kernel_sys_configuration_load_std:n
+ \ExplSyntaxOff
+ \file_input_stop:
+ }
+%</!2ekernel>
+% \end{macrocode}
+%
+% Load the dynamic part of the code, either now or during the next run.
+% \begin{macrocode}
+\cs_if_free:cTF { ver@expl3.sty }
+ {
+ \tex_everyjob:D \exp_after:wN
+ {
+ \tex_the:D \tex_everyjob:D
+ \__kernel_sys_everyjob:
+ }
+ }
+ { \__kernel_sys_everyjob: }
+% \end{macrocode}
+%
+% \begin{variable}{\s_@@_stop}
+% Internal scan marks. This code has to be reload-safe, so this one
+% has to be guarded with \cs{if_cs_exist:N} (\cs{cs_if_exist:NF}) wouldn't
+% do because it returns false for control sequences equal to \cs{scan_stop:}.
+% \begin{macrocode}
+\reverse_if:N \if_cs_exist:N \s_@@_stop
+ \scan_new:N \s_@@_stop
+\fi:
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@pushfilename, \@popfilename}
+% \begin{macro}{\@@_status_pop:w}
+% \begin{macro}{\@expl@push@filename@@@@}
+% \begin{macro}{\@expl@push@filename@aux@@@@}
+% \begin{macro}{\@expl@pop@filename@@@@}
+% The idea here is to use \LaTeXe{}'s \tn{@pushfilename} and
+% \tn{@popfilename} to track the current syntax status. This can be
+% achieved by saving the current status flag at each push to a stack,
+% then recovering it at the pop stage and checking if the code
+% environment should still be active.
+%
+% Here the code follows the same patching logic than above for
+% \cs{@expl@sys@load@backend@@@@}.
+% \begin{macrocode}
+\cs_if_exist:NF \@expl@push@filename@@@@
+ {
+ \tl_put_left:Nn \@pushfilename { \@expl@push@filename@@@@ }
+ \tl_put_right:Nn \@pushfilename { \@expl@push@filename@aux@@@@ }
+ }
+\cs_gset_protected:Npn \@expl@push@filename@@@@
+ {
+ \exp_args:Ne \__kernel_file_input_push:n
+ {
+ \tl_to_str:N \@currname .
+ \tl_to_str:N \@currext
+ }
+ \tl_put_left:Ne \l_@@_status_stack_tl
+ {
+ \bool_if:NTF \l__kernel_expl_bool
+ { 1 }
+ { 0 }
+ }
+ \ExplSyntaxOff
+ }
+% \end{macrocode}
+% This bit of trickery is needed to grab the name of the file being loaded
+% so we can record it.
+% \begin{macrocode}
+\cs_gset_protected:Npn \@expl@push@filename@aux@@@@ #1#2#3
+ {
+ \str_gset:Nn \g_file_curr_name_str {#3}
+ #1 #2 {#3}
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_if_exist:NF \@expl@pop@filename@@@@
+ {
+ \tl_put_right:Nn \@popfilename
+ { \@expl@pop@filename@@@@ }
+ }
+\cs_gset_protected:Npn \@expl@pop@filename@@@@
+ {
+ \__kernel_file_input_pop:
+ \tl_if_empty:NTF \l_@@_status_stack_tl
+ { \ExplSyntaxOff }
+ { \exp_after:wN \@@_status_pop:w \l_@@_status_stack_tl \s_@@_stop }
+ }
+% \end{macrocode}
+% The pop auxiliary function removes the first item from the stack,
+% saves the rest of the stack and then does the test. The flag here
+% is not a proper \texttt{bool}, so a low-level test is used.
+% \begin{macrocode}
+\cs_gset_protected:Npn \@@_status_pop:w #1#2 \s_@@_stop
+ {
+ \tl_set:Nn \l_@@_status_stack_tl {#2}
+ \int_if_odd:nTF {#1}
+ { \ExplSyntaxOn }
+ { \ExplSyntaxOff }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_status_stack_tl}
+% As \pkg{expl3} itself cannot be loaded with the code environment
+% already active, at the end of the package \cs{ExplSyntaxOff} can
+% safely be called.
+% \begin{macrocode}
+\tl_if_exist:NF \l_@@_status_stack_tl
+ {
+ \tl_new:N \l_@@_status_stack_tl
+ \tl_set:Nn \l_@@_status_stack_tl { 0 }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% Tidy up configuration loading, as promised.
+% \begin{macrocode}
+%<*!2ekernel>
+\cs_gset_eq:NN \__kernel_sys_configuration_load:n
+ \__kernel_sys_configuration_load_std:n
+%</!2ekernel>
+% \end{macrocode}
+%
+% For pre-loading, we have to manually disable the syntax.
+% \begin{macrocode}
+%<*2ekernel>
+\ExplSyntaxOff
+%</2ekernel>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package&loader|2ekernel>
+% \end{macrocode}
+%
+% \subsection{Generic loader}
+%
+% \begin{macrocode}
+%<*generic>
+% \end{macrocode}
+%
+% The generic loader starts with a test to ensure that the current format is
+% not \LaTeXe{}!
+% \begin{macrocode}
+\begingroup
+ \def\tempa{LaTeX2e}%
+ \def\next{}%
+ \ifx\fmtname\tempa
+ \def\next
+ {%
+ \PackageInfo{expl3}{Switching from generic to LaTeX2e loader}%
+% \end{macrocode}
+% The \cs{relax} stops \cs{RequirePackage} from scanning for a date
+% argument. Putting \tn{endinput} \emph{after} loading the package is
+% crucial, as otherwise \tn{endinput} would close the file
+% \file{expl3.sty} at the end of its first line: indeed, as long as
+% \file{expl3.sty} is open it is impossible to close the file
+% \file{expl3-generic.tex}.
+% \begin{macrocode}
+ \RequirePackage{expl3}\relax \endinput
+ }%
+ \fi
+\expandafter\endgroup
+\next
+% \end{macrocode}
+%
+% Reload check and identify the package:
+% no \LaTeXe{} mechanism so this is all pretty basic.
+% \begin{macrocode}
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname ver@expl3-generic.tex\endcsname\relax
+\else
+ \immediate\write-1
+ {%
+ Package expl3 Info: The package is already loaded.%
+ }%
+ \expandafter\endinput
+\fi
+\immediate\write-1
+ {%
+ Package: expl3
+ \ExplFileDate\space
+ L3 programming layer (loader)%
+ }%
+\expandafter\edef\csname ver@expl3-generic.tex\endcsname
+ {\ExplFileDate\space L3 programming layer}%
+% \end{macrocode}
+%
+% \begin{macrocode}
+\ifnum\currentgrouplevel=0 %
+\else
+ \errhelp{expl3 cannot be loaded inside a group:
+ reading the code has been aborted.}%
+ \errmessage{expl3 Error: Loading attempted inside a group!}%
+ \expandafter\endinput
+\fi
+% \end{macrocode}
+%
+% \begin{variable}[int]{\l@expl@tidy@tl}
+% Save the category code of |@| and then set it to \enquote{letter}.
+% \begin{macrocode}
+\expandafter\edef\csname l@expl@tidy@tl\endcsname
+ {%
+ \catcode64=\the\catcode64\relax
+ \let\expandafter\noexpand\csname l@expl@tidy@tl\endcsname
+ \noexpand\undefined
+ }%
+\catcode64=11 %
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\AtBeginDocument}
+% \begin{macro}[int]{\expl@AtBeginDocument}
+% There are a few uses of \cs{AtBeginDocument} in the package code: the
+% easiest way around that is to simply do the code \enquote{now}. As
+% bundles such as \pkg{miniltx} may have defined \cs{AtBeginDocument}
+% any existing definition is saved for restoration after the payload.
+% \begin{macrocode}
+\let\expl@AtBeginDocument\AtBeginDocument
+\def\AtBeginDocument#1{#1}%
+\expandafter\def\expandafter\l@expl@tidy@tl\expandafter
+ {%
+ \l@expl@tidy@tl
+ \let\AtBeginDocument\expl@AtBeginDocument
+ \let\expl@AtBeginDocument\undefined
+ }%
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% Load the business end: this leaves \cs{expl3} syntax on.
+% \begin{macrocode}
+\input expl3-code.tex %
+% \end{macrocode}
+%
+% A check that the bootstrap code did not abort loading: if it did,
+% bail out silently here.
+% \begin{macrocode}
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname tex\string _let:D\endcsname\relax
+ \expandafter\endinput
+\fi
+% \end{macrocode}
+%
+% \begin{macro}{\__kernel_sys_configuration_load:n}
+% Very basic.
+% \begin{macrocode}
+\cs_gset_protected:Npn \__kernel_sys_configuration_load:n #1
+ {
+ \group_begin:
+ \cs_set_protected:Npn \ProvidesExplFile
+ {
+ \char_set_catcode_space:n { `\ }
+ \ProvidesExplFileAux
+ }
+ \cs_set_protected:Npn \ProvidesExplFileAux ##1##2##3##4
+ {
+ \group_end:
+ \iow_log:e { File:~##1~##2~v##3~##4 }
+ }
+ \tex_input:D #1 .def \scan_stop:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\g__kernel_backend_header_bool}
+% Load the dynamic code and standard back-end.
+% \begin{macrocode}
+\__kernel_sys_everyjob:
+\bool_new:N \g__kernel_backend_header_bool
+\bool_gset_true:N \g__kernel_backend_header_bool
+\sys_load_backend:n { }
+% \end{macrocode}
+% \end{macro}
+%
+% For the generic loader, a few final steps to take. Turn of \cs{expl3}
+% syntax and tidy up the small number of temporary changes.
+% \begin{macrocode}
+\ExplSyntaxOff
+\l@expl@tidy@tl
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</generic>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3.ins b/Master/texmf-dist/source/latex-dev/l3kernel/l3.ins
new file mode 100644
index 00000000000..bf7a75381d6
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3.ins
@@ -0,0 +1,182 @@
+\iffalse meta-comment
+
+File l3.ins
+
+Copyright (C) 2011,2012,2014-2024 The LaTeX Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "l3kernel bundle" (The Work in LPPL)
+and all files in that bundle must be distributed together.
+
+-----------------------------------------------------------------------
+
+The development version of the bundle can be found at
+
+ https://github.com/latex3/latex3
+
+for those people who are interested.
+
+-----------------------------------------------------------------------
+
+Any modification of this file should ensure that the copyright and
+license information is placed in the derived files.
+
+\fi
+
+\let\jobname\relax
+\input docstrip %
+\askforoverwritefalse
+
+\preamble
+
+Copyright (C) 1990-2024 The LaTeX Project
+
+It may be distributed and/or modified under the conditions of
+the LaTeX Project Public License (LPPL), either version 1.3c of
+this license or (at your option) any later version. The latest
+version of this license is in the file:
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "l3kernel bundle" (The Work in LPPL)
+and all files in that bundle must be distributed together.
+
+\endpreamble
+% stop docstrip adding \endinput
+\postamble
+\endpostamble
+
+\keepsilent
+
+\generate
+ {
+ \file{expl3-code.tex}
+ {
+ \from{expl3.dtx} {package}
+ \from{l3bootstrap.dtx} {package}
+ \from{l3names.dtx} {package,tex}
+ \from{l3basics.dtx} {package}
+ \from{l3expan.dtx} {package}
+ \from{l3quark.dtx} {package}
+ \from{l3tl.dtx} {package}
+ \from{l3tl-build.dtx} {package}
+ \from{l3str.dtx} {package}
+ \from{l3seq.dtx} {package}
+ \from{l3int.dtx} {package}
+ \from{l3flag.dtx} {package}
+ \from{l3prg.dtx} {package}
+ \from{l3sys.dtx} {package,tex}
+ \from{l3clist.dtx} {package}
+ \from{l3token.dtx} {package,tex}
+ \from{l3prop.dtx} {package}
+ \from{l3msg.dtx} {package}
+ \from{l3file.dtx} {package}
+ \from{l3skip.dtx} {package}
+ \from{l3keys.dtx} {package}
+ \from{l3intarray.dtx} {package,tex}
+ \from{l3fp.dtx} {package}
+ \from{l3fp-aux.dtx} {package}
+ \from{l3fp-traps.dtx} {package}
+ \from{l3fp-round.dtx} {package}
+ \from{l3fp-parse.dtx} {package}
+ \from{l3fp-assign.dtx} {package}
+ \from{l3fp-logic.dtx} {package}
+ \from{l3fp-basics.dtx} {package}
+ \from{l3fp-extended.dtx}{package}
+ \from{l3fp-expo.dtx} {package}
+ \from{l3fp-trig.dtx} {package}
+ \from{l3fp-convert.dtx} {package}
+ \from{l3fp-random.dtx} {package}
+ \from{l3fp-types.dtx} {package}
+ \from{l3fp-symbolic.dtx}{package}
+ \from{l3fp-functions.dtx}{package}
+ \from{l3fparray.dtx} {package}
+ \from{l3bitset.dtx} {package}
+ \from{l3cctab.dtx} {package}
+ \from{l3sort.dtx} {package}
+ \from{l3str-convert.dtx}{package}
+ \from{l3tl-analysis.dtx}{package}
+ \from{l3regex.dtx} {package}
+ \from{l3box.dtx} {package}
+ \from{l3color.dtx} {package}
+ \from{l3pdf.dtx} {package,tex}
+ \from{l3coffins.dtx} {package}
+ \from{l3luatex.dtx} {package,tex}
+ \from{l3unicode.dtx} {package}
+ \from{l3text.dtx} {package}
+ \from{l3text-case.dtx} {package}
+ \from{l3text-map.dtx} {package}
+ \from{l3text-purify.dtx}{package}
+ \from{l3legacy.dtx} {package}
+ \from{l3deprecation.dtx}{package}
+ }
+ }
+
+\generate{\file{expl3.sty} {\from{expl3.dtx} {package,loader}}}
+\generate{\file{expl3.ltx} {\from{expl3.dtx} {2ekernel,loader}}}
+\generate{\file{expl3-generic.tex}{\from{expl3.dtx} {generic,loader}}}
+
+\generate{\file{l3doc.cls} {\from{l3doc.dtx} {class}}}
+% not distributed:
+%\generate{\file{l3doc.ist} {\from{l3doc.dtx} {docist}}}
+
+% ISO-8859 encodings.
+\generate{%
+ \file{l3str-enc-iso88591.def} {\from{l3str-convert.dtx}{iso88591}}%
+ \file{l3str-enc-iso88592.def} {\from{l3str-convert.dtx}{iso88592}}%
+ \file{l3str-enc-iso88593.def} {\from{l3str-convert.dtx}{iso88593}}%
+ \file{l3str-enc-iso88594.def} {\from{l3str-convert.dtx}{iso88594}}%
+ \file{l3str-enc-iso88595.def} {\from{l3str-convert.dtx}{iso88595}}%
+ \file{l3str-enc-iso88596.def} {\from{l3str-convert.dtx}{iso88596}}%
+ \file{l3str-enc-iso88597.def} {\from{l3str-convert.dtx}{iso88597}}%
+ \file{l3str-enc-iso88598.def} {\from{l3str-convert.dtx}{iso88598}}%
+ \file{l3str-enc-iso88599.def} {\from{l3str-convert.dtx}{iso88599}}%
+ \file{l3str-enc-iso885910.def} {\from{l3str-convert.dtx}{iso885910}}%
+ \file{l3str-enc-iso885911.def} {\from{l3str-convert.dtx}{iso885911}}%
+ \file{l3str-enc-iso885913.def} {\from{l3str-convert.dtx}{iso885913}}%
+ \file{l3str-enc-iso885914.def} {\from{l3str-convert.dtx}{iso885914}}%
+ \file{l3str-enc-iso885915.def} {\from{l3str-convert.dtx}{iso885915}}%
+ \file{l3str-enc-iso885916.def} {\from{l3str-convert.dtx}{iso885916}}%
+}
+
+\generate{\file{l3debug.def} {\from{l3debug.dtx} {package}}}
+
+\generate{\file{l3docstrip.tex} {\from{l3docstrip.dtx} {program}}}
+
+\generate{\file{l3names.def} {\from{l3names.dtx} {names}}}
+
+% Lua code
+
+\def\MetaPrefix{--}
+\preamble
+
+Copyright (C) 1990-2024 The LaTeX Project
+
+It may be distributed and/or modified under the conditions of
+the LaTeX Project Public License (LPPL), either version 1.3c of
+this license or (at your option) any later version. The latest
+version of this license is in the file:
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "l3kernel bundle" (The Work in LPPL)
+and all files in that bundle must be distributed together.
+
+\endpreamble
+\nopostamble
+\generate{\file{expl3.lua}{
+ \from{l3luatex.dtx}{package,lua}
+ \from{l3names.dtx}{package,lua}
+ \from{l3sys.dtx}{package,lua}
+ \from{l3token.dtx}{package,lua}
+ \from{l3intarray.dtx}{package,lua}
+ \from{l3pdf.dtx}{package,lua}
+}}
+
+\endbatchfile
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3basics.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3basics.dtx
new file mode 100644
index 00000000000..eabf678caa6
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3basics.dtx
@@ -0,0 +1,3507 @@
+% \iffalse meta-comment
+%
+%% File: l3basics.dtx
+%
+% Copyright (C) 1990-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3basics} module\\ Basic definitions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% As the name suggests, this module holds some basic definitions which
+% are needed by most or all other modules in this set.
+%
+% Here we describe those functions that are used all over the place.
+% By that, we mean functions dealing with the construction and testing of
+% control sequences. Furthermore the basic parts of conditional
+% processing are covered; conditional processing dealing with specific
+% data types is described in the modules specific for the respective
+% data types.
+%
+% \section{No operation functions}
+%
+% \begin{function}[EXP]{\prg_do_nothing:}
+% \begin{syntax}
+% \cs{prg_do_nothing:}
+% \end{syntax}
+% An expandable function which does nothing at all: leaves nothing
+% in the input stream after a single expansion.
+% \end{function}
+%
+% \begin{function}{\scan_stop:}
+% \begin{syntax}
+% \cs{scan_stop:}
+% \end{syntax}
+% A non-expandable function which does nothing. Does not vanish on
+% expansion but produces no typeset output.
+% \end{function}
+%
+% \section{Grouping material}
+%
+% \begin{function}{\group_begin:, \group_end:}
+% \begin{syntax}
+% \cs{group_begin:}
+% \cs{group_end:}
+% \end{syntax}
+% These functions begin and end a group for definition purposes.
+% Assignments are local to groups unless carried out in a global
+% manner. (A small number of exceptions to this rule will be noted
+% as necessary elsewhere in this document.) Each \cs{group_begin:}
+% must be matched by a \cs{group_end:}, although this does not have
+% to occur within the same function. Indeed, it is often necessary
+% to start a group within one function and finish it within another,
+% for example when seeking to use non-standard category codes.
+% \begin{texnote}
+% These are the \TeX{} primitives \tn{begingroup} and \tn{endgroup}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}{\group_insert_after:N}
+% \begin{syntax}
+% \cs{group_insert_after:N} \meta{token}
+% \end{syntax}
+% Adds \meta{token} to the list of \meta{tokens} to be inserted
+% when the current group level ends. The list of \meta{tokens} to be
+% inserted is empty at the beginning of a group: multiple
+% applications of \cs{group_insert_after:N} may be used to build
+% the inserted list one \meta{token} at a time. The current group
+% level may be closed by a \cs{group_end:} function or by a token
+% with category code $2$ (close-group), namely a ^^A{
+% |}| if standard category codes apply.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{aftergroup}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2021-05-11]{\group_show_list:, \group_log_list:}
+% \begin{syntax}
+% \cs{group_show_list:}
+% \cs{group_log_list:}
+% \end{syntax}
+% Display (to the terminal or log file) a list of the groups that are
+% currently opened. This is intended for tracking down problems.
+% \begin{texnote}
+% This is a wrapper around the \eTeX{} primitive \tn{showgroups}.
+% \end{texnote}
+% \end{function}
+%
+% \section{Control sequences and functions}
+%
+% As \TeX{} is a macro language, creating new functions means
+% creating macros. At point of use, a function is replaced by
+% the replacement text (\enquote{code}) in which each parameter
+% in the code (|#1|, |#2|, \emph{etc.}) is replaced the appropriate
+% arguments absorbed by the function. In the following, \meta{code}
+% is therefore used as a shorthand for \enquote{replacement text}.
+%
+% Functions which are not \enquote{protected} are fully expanded
+% inside an \texttt{e}-type or \texttt{x}-type expansion.
+% In contrast, \enquote{protected} functions are not expanded within
+% \texttt{e} and \texttt{x} expansions.
+%
+% \subsection{Defining functions}
+%
+% Functions can be created with no requirement that they are declared
+% first (in contrast to variables, which must always be declared).
+% Declaring a function before setting up the code means that the name
+% chosen is checked and an error raised if it is already in use.
+% The name of a function can be checked at the point of definition using
+% the \cs[no-index]{cs_new\ldots} functions: this is recommended for all
+% functions which are defined for the first time.
+%
+% There are three ways to define new functions.
+% All classes define a function to expand to the substitution text.
+% Within the substitution text the actual parameters are substituted
+% for the formal parameters (|#1|, |#2|, \ldots).
+% \begin{description}
+% \item[\texttt{new}]
+% Create a new function with the \texttt{new} scope,
+% such as \cs{cs_new:Npn}. The definition is global and results in
+% an error if it is already defined.
+% \item[\texttt{set}]
+% Create a new function with the \texttt{set} scope,
+% such as \cs{cs_set:Npn}. The definition is restricted to the current
+% \TeX{} group and does not result in an error if the function is already
+% defined.
+% \item[\texttt{gset}]
+% Create a new function with the \texttt{gset} scope,
+% such as \cs{cs_gset:Npn}. The definition is global and
+% does not result in an error if the function is already defined.
+% \end{description}
+%
+% Within each set of scope there are different ways to define a function.
+% The differences depend on restrictions on the actual parameters and
+% the expandability of the resulting function.
+% \begin{description}
+% \item[\texttt{nopar}]
+% Create a new function with the \texttt{nopar} restriction,
+% such as \cs{cs_set_nopar:Npn}. The parameter may not contain
+% \cs{par} tokens.
+% \item[\texttt{protected}]
+% Create a new function with the \texttt{protected} restriction,
+% such as \cs{cs_set_protected:Npn}. The parameter may contain
+% \cs{par} tokens but the function will not expand within an
+% \texttt{e}-type or \texttt{x}-type expansion.
+% \end{description}
+%
+% Finally, the functions in
+% Subsections~\ref{sec:l3basics:defining-new-function-1}~and
+% \ref{sec:l3basics:defining-new-function-2} are primarily meant to define
+% \emph{base functions} only. Base functions can only have the following
+% argument specifiers:
+% \begin{description}
+% \item[|N| and |n|] No manipulation.
+% \item[|T| and |F|] Functionally equivalent to |n| (you are actually
+% encouraged to use the family of |\prg_new_conditional:| functions
+% described in Section~\ref{sec:l3prg:new-conditional-functions}).
+% \item[|p| and |w|] These are special cases.
+% \end{description}
+%
+% The |\cs_new:| functions below (and friends) do not stop you from using
+% other argument specifiers in your function names, but they do not handle
+% expansion for you. You should define the base function and then use
+% \cs{cs_generate_variant:Nn} to generate custom variants as described in
+% Section~\ref{sec:l3expan:variants-method}.
+%
+% \subsection{Defining new functions using parameter text}
+% \label{sec:l3basics:defining-new-function-1}
+%
+% \begin{function}
+% {
+% \cs_new:Npn, \cs_new:cpn,
+% \cs_new:Npe, \cs_new:cpe,
+% \cs_new:Npx, \cs_new:cpx
+% }
+% \begin{syntax}
+% \cs{cs_new:Npn} \meta{function} \meta{parameters} \Arg{code}
+% \end{syntax}
+% Creates \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the \meta{parameters} (|#1|, |#2|,
+% \emph{etc.}) will be replaced by those absorbed by the function.
+% The definition is global and an error results if the
+% \meta{function} is already defined.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_new_nopar:Npn, \cs_new_nopar:cpn,
+% \cs_new_nopar:Npe, \cs_new_nopar:cpe,
+% \cs_new_nopar:Npx, \cs_new_nopar:cpx
+% }
+% \begin{syntax}
+% \cs{cs_new_nopar:Npn} \meta{function} \meta{parameters} \Arg{code}
+% \end{syntax}
+% Creates \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the \meta{parameters} (|#1|, |#2|,
+% \emph{etc.}) will be replaced by those absorbed by the function.
+% When the \meta{function} is used the \meta{parameters} absorbed
+% cannot contain \cs{par} tokens. The definition is global and
+% an error results if the \meta{function} is already defined.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_new_protected:Npn, \cs_new_protected:cpn,
+% \cs_new_protected:Npe, \cs_new_protected:cpe,
+% \cs_new_protected:Npx, \cs_new_protected:cpx
+% }
+% \begin{syntax}
+% \cs{cs_new_protected:Npn} \meta{function} \meta{parameters} \Arg{code}
+% \end{syntax}
+% Creates \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the \meta{parameters} (|#1|, |#2|,
+% \emph{etc.}) will be replaced by those absorbed by the function.
+% The \meta{function} will not expand within an \texttt{e}-type or
+% or \texttt{x}-type
+% argument. The definition is global and an error results if the
+% \meta{function} is already defined.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_new_protected_nopar:Npn, \cs_new_protected_nopar:cpn ,
+% \cs_new_protected_nopar:Npe, \cs_new_protected_nopar:cpe ,
+% \cs_new_protected_nopar:Npx, \cs_new_protected_nopar:cpx
+% }
+% \begin{syntax}
+% \cs{cs_new_protected_nopar:Npn} \meta{function} \meta{parameters} \Arg{code}
+% \end{syntax}
+% Creates \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the \meta{parameters} (|#1|, |#2|,
+% \emph{etc.}) will be replaced by those absorbed by the function.
+% When the \meta{function} is used the \meta{parameters} absorbed
+% cannot contain \cs{par} tokens. The \meta{function} will not
+% expand within an \texttt{e}-type or \texttt{x}-type argument. The definition is global
+% and an error results if the \meta{function} is already defined.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_set:Npn, \cs_set:cpn,
+% \cs_set:Npe, \cs_set:cpe,
+% \cs_set:Npx, \cs_set:cpx
+% }
+% \begin{syntax}
+% \cs{cs_set:Npn} \meta{function} \meta{parameters} \Arg{code}
+% \end{syntax}
+% Sets \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the \meta{parameters} (|#1|, |#2|,
+% \emph{etc.}) will be replaced by those absorbed by the function.
+% The assignment of a meaning to the \meta{function} is restricted to
+% the current \TeX{} group level.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_set_nopar:Npn, \cs_set_nopar:cpn,
+% \cs_set_nopar:Npe, \cs_set_nopar:cpe,
+% \cs_set_nopar:Npx, \cs_set_nopar:cpx
+% }
+% \begin{syntax}
+% \cs{cs_set_nopar:Npn} \meta{function} \meta{parameters} \Arg{code}
+% \end{syntax}
+% Sets \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the \meta{parameters} (|#1|, |#2|,
+% \emph{etc.}) will be replaced by those absorbed by the function.
+% When the \meta{function} is used the \meta{parameters} absorbed
+% cannot contain \cs{par} tokens. The assignment of a meaning
+% to the \meta{function} is restricted to the current \TeX{} group
+% level.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_set_protected:Npn, \cs_set_protected:cpn,
+% \cs_set_protected:Npe, \cs_set_protected:cpe,
+% \cs_set_protected:Npx, \cs_set_protected:cpx
+% }
+% \begin{syntax}
+% \cs{cs_set_protected:Npn} \meta{function} \meta{parameters} \Arg{code}
+% \end{syntax}
+% Sets \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the \meta{parameters} (|#1|, |#2|,
+% \emph{etc.}) will be replaced by those absorbed by the function.
+% The assignment of a meaning to the \meta{function} is restricted to
+% the current \TeX{} group level. The \meta{function} will
+% not expand within an \texttt{e}-type or \texttt{x}-type argument.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_set_protected_nopar:Npn, \cs_set_protected_nopar:cpn ,
+% \cs_set_protected_nopar:Npe, \cs_set_protected_nopar:cpe ,
+% \cs_set_protected_nopar:Npx, \cs_set_protected_nopar:cpx ,
+% }
+% \begin{syntax}
+% \cs{cs_set_protected_nopar:Npn} \meta{function} \meta{parameters} \Arg{code}
+% \end{syntax}
+% Sets \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the \meta{parameters} (|#1|, |#2|,
+% \emph{etc.}) will be replaced by those absorbed by the function.
+% When the \meta{function} is used the \meta{parameters} absorbed
+% cannot contain \cs{par} tokens. The assignment of a meaning
+% to the \meta{function} is restricted to the current \TeX{} group
+% level. The \meta{function} will not expand within an
+% \texttt{e}-type or \texttt{x}-type argument.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_gset:Npn, \cs_gset:cpn,
+% \cs_gset:Npe, \cs_gset:cpe,
+% \cs_gset:Npx, \cs_gset:cpx
+% }
+% \begin{syntax}
+% \cs{cs_gset:Npn} \meta{function} \meta{parameters} \Arg{code}
+% \end{syntax}
+% Globally sets \meta{function} to expand to \meta{code} as replacement
+% text. Within the \meta{code}, the \meta{parameters} (|#1|, |#2|,
+% \emph{etc.}) will be replaced by those absorbed by the function.
+% The assignment of a meaning to the \meta{function} is \emph{not}
+% restricted to the current \TeX{} group level: the assignment is
+% global.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_gset_nopar:Npn, \cs_gset_nopar:cpn,
+% \cs_gset_nopar:Npe, \cs_gset_nopar:cpe,
+% \cs_gset_nopar:Npx, \cs_gset_nopar:cpx
+% }
+% \begin{syntax}
+% \cs{cs_gset_nopar:Npn} \meta{function} \meta{parameters} \Arg{code}
+% \end{syntax}
+% Globally sets \meta{function} to expand to \meta{code} as replacement
+% text. Within the \meta{code}, the \meta{parameters} (|#1|, |#2|,
+% \emph{etc.}) will be replaced by those absorbed by the function.
+% When the \meta{function} is used the \meta{parameters} absorbed
+% cannot contain \cs{par} tokens. The assignment of a meaning to the
+% \meta{function} is \emph{not} restricted to the current \TeX{}
+% group level: the assignment is global.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_gset_protected:Npn, \cs_gset_protected:cpn,
+% \cs_gset_protected:Npe, \cs_gset_protected:cpe,
+% \cs_gset_protected:Npx, \cs_gset_protected:cpx
+% }
+% \begin{syntax}
+% \cs{cs_gset_protected:Npn} \meta{function} \meta{parameters} \Arg{code}
+% \end{syntax}
+% Globally sets \meta{function} to expand to \meta{code} as replacement
+% text. Within the \meta{code}, the \meta{parameters} (|#1|, |#2|,
+% \emph{etc.}) will be replaced by those absorbed by the function.
+% The assignment of a meaning to the \meta{function} is \emph{not}
+% restricted to the current \TeX{} group level: the assignment is
+% global. The \meta{function} will not expand within an
+% \texttt{e}-type or \texttt{x}-type argument.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_gset_protected_nopar:Npn, \cs_gset_protected_nopar:cpn,
+% \cs_gset_protected_nopar:Npe, \cs_gset_protected_nopar:cpe,
+% \cs_gset_protected_nopar:Npx, \cs_gset_protected_nopar:cpx
+% }
+% \begin{syntax}
+% \cs{cs_gset_protected_nopar:Npn} \meta{function} \meta{parameters} \Arg{code}
+% \end{syntax}
+% Globally sets \meta{function} to expand to \meta{code} as replacement
+% text. Within the \meta{code}, the \meta{parameters} (|#1|, |#2|,
+% \emph{etc.}) will be replaced by those absorbed by the function.
+% When the \meta{function} is used the \meta{parameters} absorbed
+% cannot contain \cs{par} tokens. The assignment of a meaning to the
+% \meta{function} is \emph{not} restricted to the current \TeX{}
+% group level: the assignment is global. The \meta{function} will
+% not expand within an \texttt{e}-type or \texttt{x}-type argument.
+% \end{function}
+%
+% \subsection{Defining new functions using the signature}
+% \label{sec:l3basics:defining-new-function-2}
+%
+% \begin{function}
+% {
+% \cs_new:Nn, \cs_new:cn,
+% \cs_new:Ne, \cs_new:ce
+% }
+% \begin{syntax}
+% \cs{cs_new:Nn} \meta{function} \Arg{code}
+% \end{syntax}
+% Creates \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the number of \meta{parameters} is detected
+% automatically from the function signature. These \meta{parameters}
+% (|#1|, |#2|, \emph{etc.}) will be replaced by those absorbed by the
+% function. The definition is global and
+% an error results if the \meta{function} is already defined.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_new_nopar:Nn, \cs_new_nopar:cn,
+% \cs_new_nopar:Ne, \cs_new_nopar:ce
+% }
+% \begin{syntax}
+% \cs{cs_new_nopar:Nn} \meta{function} \Arg{code}
+% \end{syntax}
+% Creates \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the number of \meta{parameters} is detected
+% automatically from the function signature. These \meta{parameters}
+% (|#1|, |#2|, \emph{etc.}) will be replaced by those absorbed by the
+% function. When the \meta{function} is used the \meta{parameters}
+% absorbed cannot contain \cs{par} tokens. The definition is global and
+% an error results if the \meta{function} is already defined.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_new_protected:Nn, \cs_new_protected:cn,
+% \cs_new_protected:Ne, \cs_new_protected:ce
+% }
+% \begin{syntax}
+% \cs{cs_new_protected:Nn} \meta{function} \Arg{code}
+% \end{syntax}
+% Creates \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the number of \meta{parameters} is detected
+% automatically from the function signature. These \meta{parameters}
+% (|#1|, |#2|, \emph{etc.}) will be replaced by those absorbed by the
+% function. The \meta{function} will not expand within an \texttt{e}-type
+% or \texttt{x}-type argument. The definition is global and
+% an error results if the \meta{function} is already defined.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_new_protected_nopar:Nn, \cs_new_protected_nopar:cn,
+% \cs_new_protected_nopar:Ne, \cs_new_protected_nopar:ce
+% }
+% \begin{syntax}
+% \cs{cs_new_protected_nopar:Nn} \meta{function} \Arg{code}
+% \end{syntax}
+% Creates \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the number of \meta{parameters} is detected
+% automatically from the function signature. These \meta{parameters}
+% (|#1|, |#2|, \emph{etc.}) will be replaced by those absorbed by the
+% function. When the \meta{function} is used the \meta{parameters}
+% absorbed cannot contain \cs{par} tokens. The \meta{function} will not
+% expand within an \texttt{e}-type or \texttt{x}-type argument. The definition is global and
+% an error results if the \meta{function} is already defined.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_set:Nn, \cs_set:cn,
+% \cs_set:Ne, \cs_set:ce
+% }
+% \begin{syntax}
+% \cs{cs_set:Nn} \meta{function} \Arg{code}
+% \end{syntax}
+% Sets \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the number of \meta{parameters} is detected
+% automatically from the function signature. These \meta{parameters}
+% (|#1|, |#2|, \emph{etc.}) will be replaced by those absorbed by the
+% function.
+% The assignment of a meaning to the \meta{function} is restricted to
+% the current \TeX{} group level.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_set_nopar:Nn, \cs_set_nopar:cn,
+% \cs_set_nopar:Ne, \cs_set_nopar:ce
+% }
+% \begin{syntax}
+% \cs{cs_set_nopar:Nn} \meta{function} \Arg{code}
+% \end{syntax}
+% Sets \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the number of \meta{parameters} is detected
+% automatically from the function signature. These \meta{parameters}
+% (|#1|, |#2|, \emph{etc.}) will be replaced by those absorbed by the
+% function. When the \meta{function} is used the \meta{parameters}
+% absorbed cannot contain \cs{par} tokens.
+% The assignment of a meaning to the \meta{function} is restricted to
+% the current \TeX{} group level.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_set_protected:Nn, \cs_set_protected:cn,
+% \cs_set_protected:Ne, \cs_set_protected:ce
+% }
+% \begin{syntax}
+% \cs{cs_set_protected:Nn} \meta{function} \Arg{code}
+% \end{syntax}
+% Sets \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the number of \meta{parameters} is detected
+% automatically from the function signature. These \meta{parameters}
+% (|#1|, |#2|, \emph{etc.}) will be replaced by those absorbed by the
+% function. The \meta{function} will not expand within an \texttt{e}-type
+% or \texttt{x}-type argument.
+% The assignment of a meaning to the \meta{function} is restricted to
+% the current \TeX{} group level.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_set_protected_nopar:Nn, \cs_set_protected_nopar:cn,
+% \cs_set_protected_nopar:Ne, \cs_set_protected_nopar:ce
+% }
+% \begin{syntax}
+% \cs{cs_set_protected_nopar:Nn} \meta{function} \Arg{code}
+% \end{syntax}
+% Sets \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the number of \meta{parameters} is detected
+% automatically from the function signature. These \meta{parameters}
+% (|#1|, |#2|, \emph{etc.}) will be replaced by those absorbed by the
+% function. When the \meta{function} is used the \meta{parameters}
+% absorbed cannot contain \cs{par} tokens. The \meta{function} will not
+% expand within an \texttt{e}-type or \texttt{x}-type argument.
+% The assignment of a meaning to the \meta{function} is restricted to
+% the current \TeX{} group level.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_gset:Nn, \cs_gset:cn,
+% \cs_gset:Ne, \cs_gset:ce
+% }
+% \begin{syntax}
+% \cs{cs_gset:Nn} \meta{function} \Arg{code}
+% \end{syntax}
+% Sets \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the number of \meta{parameters} is detected
+% automatically from the function signature. These \meta{parameters}
+% (|#1|, |#2|, \emph{etc.}) will be replaced by those absorbed by the
+% function.
+% The assignment of a meaning to the \meta{function} is global.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_gset_nopar:Nn, \cs_gset_nopar:cn,
+% \cs_gset_nopar:Ne, \cs_gset_nopar:ce
+% }
+% \begin{syntax}
+% \cs{cs_gset_nopar:Nn} \meta{function} \Arg{code}
+% \end{syntax}
+% Sets \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the number of \meta{parameters} is detected
+% automatically from the function signature. These \meta{parameters}
+% (|#1|, |#2|, \emph{etc.}) will be replaced by those absorbed by the
+% function. When the \meta{function} is used the \meta{parameters}
+% absorbed cannot contain \cs{par} tokens.
+% The assignment of a meaning to the \meta{function} is global.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_gset_protected:Nn, \cs_gset_protected:cn,
+% \cs_gset_protected:Ne, \cs_gset_protected:ce
+% }
+% \begin{syntax}
+% \cs{cs_gset_protected:Nn} \meta{function} \Arg{code}
+% \end{syntax}
+% Sets \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the number of \meta{parameters} is detected
+% automatically from the function signature. These \meta{parameters}
+% (|#1|, |#2|, \emph{etc.}) will be replaced by those absorbed by the
+% function. The \meta{function} will not expand within an \texttt{e}-type
+% or \texttt{x}-type argument.
+% The assignment of a meaning to the \meta{function} is global.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_gset_protected_nopar:Nn, \cs_gset_protected_nopar:cn,
+% \cs_gset_protected_nopar:Ne, \cs_gset_protected_nopar:ce
+% }
+% \begin{syntax}
+% \cs{cs_gset_protected_nopar:Nn} \meta{function} \Arg{code}
+% \end{syntax}
+% Sets \meta{function} to expand to \meta{code} as replacement text.
+% Within the \meta{code}, the number of \meta{parameters} is detected
+% automatically from the function signature. These \meta{parameters}
+% (|#1|, |#2|, \emph{etc.}) will be replaced by those absorbed by the
+% function. When the \meta{function} is used the \meta{parameters}
+% absorbed cannot contain \cs{par} tokens. The \meta{function} will not
+% expand within an \texttt{e}-type or \texttt{x}-type argument.
+% The assignment of a meaning to the \meta{function} is global.
+% \end{function}
+%
+% \begin{function}[updated = 2012-01-14]
+% {
+% \cs_generate_from_arg_count:NNnn,
+% \cs_generate_from_arg_count:NNno,
+% \cs_generate_from_arg_count:cNnn,
+% \cs_generate_from_arg_count:Ncnn
+% }
+% \begin{syntax}
+% \cs{cs_generate_from_arg_count:NNnn} \meta{function} \meta{creator} \Arg{number} \Arg{code}
+% \end{syntax}
+% Uses the \meta{creator} function (which should have signature
+% |Npn|, for example \cs{cs_new:Npn}) to define a \meta{function}
+% which takes \meta{number} arguments and has \meta{code} as
+% replacement text. The \meta{number} of arguments is an integer expression,
+% evaluated as detailed for \cs{int_eval:n}.
+% \end{function}
+%
+% \subsection{Copying control sequences}
+%
+% Control sequences (not just functions as defined above) can
+% be set to have the same meaning using the functions described
+% here. Making two control sequences equivalent means that the
+% second control sequence is a \emph{copy} of the first (rather than
+% a pointer to it). Thus the old and new control sequence are not
+% tied together: changes to one are not reflected in the other.
+%
+% In the following text \enquote{cs} is used as an abbreviation for
+% \enquote{control sequence}.
+%
+% \begin{function}
+% {\cs_new_eq:NN, \cs_new_eq:Nc, \cs_new_eq:cN, \cs_new_eq:cc}
+% \begin{syntax}
+% \cs{cs_new_eq:NN} \meta{cs_1} \meta{cs_2}
+% \cs{cs_new_eq:NN} \meta{cs_1} \meta{token}
+% \end{syntax}
+% Globally creates \meta{control sequence_1} and sets it to have the same
+% meaning as \meta{control sequence_2} or <token>.
+% The second control sequence may
+% subsequently be altered without affecting the copy.
+% \end{function}
+%
+% \begin{function}
+% {\cs_set_eq:NN, \cs_set_eq:Nc, \cs_set_eq:cN, \cs_set_eq:cc}
+% \begin{syntax}
+% \cs{cs_set_eq:NN} \meta{cs_1} \meta{cs_2}
+% \cs{cs_set_eq:NN} \meta{cs_1} \meta{token}
+% \end{syntax}
+% Sets \meta{control sequence_1} to have the same meaning as
+% \meta{control sequence_2} (or <token>).
+% The second control sequence may subsequently be
+% altered without affecting the copy. The assignment of a meaning
+% to the \meta{control sequence_1} is restricted to the current
+% \TeX{} group level.
+% \end{function}
+%
+% \begin{function}
+% {\cs_gset_eq:NN, \cs_gset_eq:Nc, \cs_gset_eq:cN, \cs_gset_eq:cc}
+% \begin{syntax}
+% \cs{cs_gset_eq:NN} \meta{cs_1} \meta{cs_2}
+% \cs{cs_gset_eq:NN} \meta{cs_1} \meta{token}
+% \end{syntax}
+% Globally sets \meta{control sequence_1} to have the same meaning as
+% \meta{control sequence_2} (or <token>).
+% The second control sequence may subsequently be
+% altered without affecting the copy. The assignment of a meaning to
+% the \meta{control sequence_1} is \emph{not} restricted to the current
+% \TeX{} group level: the assignment is global.
+% \end{function}
+%
+% \subsection{Deleting control sequences}
+%
+% There are occasions where control sequences need to be deleted.
+% This is handled in a very simple manner.
+%
+% \begin{function}[updated = 2011-09-15]{\cs_undefine:N, \cs_undefine:c}
+% \begin{syntax}
+% \cs{cs_undefine:N} \meta{control sequence}
+% \end{syntax}
+% Sets \meta{control sequence} to be globally undefined.
+% \end{function}
+%
+% \subsection{Showing control sequences}
+%
+% \begin{function}[EXP, updated = 2011-12-22]{\cs_meaning:N, \cs_meaning:c}
+% \begin{syntax}
+% \cs{cs_meaning:N} \meta{control sequence}
+% \end{syntax}
+% This function expands to the \emph{meaning} of the \meta{control sequence}
+% control sequence. For a macro, this includes the \meta{replacement text}.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{meaning}.
+% For tokens that are not control sequences, it is more logical to
+% use \cs{token_to_meaning:N}.
+% The \texttt{c} variant correctly reports undefined arguments.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[updated = 2017-02-14]{\cs_show:N, \cs_show:c}
+% \begin{syntax}
+% \cs{cs_show:N} \meta{control sequence}
+% \end{syntax}
+% Displays the definition of the \meta{control sequence} on the
+% terminal.
+% \begin{texnote}
+% This is similar to the \TeX{} primitive \tn{show}, wrapped to a
+% fixed number of characters per line.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2017-02-14]{\cs_log:N, \cs_log:c}
+% \begin{syntax}
+% \cs{cs_log:N} \meta{control sequence}
+% \end{syntax}
+% Writes the definition of the \meta{control sequence} in the log
+% file. See also \cs{cs_show:N} which displays the result in the
+% terminal.
+% \end{function}
+%
+% \subsection{Converting to and from control sequences}
+%
+% \begin{function}[EXP]{\use:c}
+% \begin{syntax}
+% \cs{use:c} \Arg{control sequence name}
+% \end{syntax}
+% Expands the \meta{control sequence name} until only characters
+% remain, and then converts this into a control sequence. This process
+% requires two expansions. As in other \texttt{c}-type arguments the
+% \meta{control sequence name} must, when fully expanded, consist of
+% character tokens, typically a mixture of category code $10$ (space),
+% $11$ (letter) and $12$ (other).
+% \end{function}
+%
+% As an example of the \cs{use:c} function, both
+% \begin{verbatim}
+% \use:c { a b c }
+% \end{verbatim}
+% and
+% \begin{verbatim}
+% \tl_new:N \l_my_tl
+% \tl_set:Nn \l_my_tl { a b c }
+% \use:c { \tl_use:N \l_my_tl }
+% \end{verbatim}
+% would be equivalent to
+% \begin{verbatim}
+% \abc
+% \end{verbatim}
+% after two expansions of \cs{use:c}.
+%
+% \begin{function}[noTF, EXP, added = 2012-11-10]
+% {\cs_if_exist_use:N, \cs_if_exist_use:c}
+% \begin{syntax}
+% \cs{cs_if_exist_use:N} \meta{control sequence}
+% \cs{cs_if_exist_use:NTF} \meta{control sequence} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{control sequence} is currently defined
+% according to the conditional \cs{cs_if_exist:NTF}
+% (whether as a function or another control sequence type), and if it
+% is inserts the \meta{control sequence} into the input stream followed
+% by the \meta{true code}. Otherwise the \meta{false code} is used.
+% \end{function}
+%
+% \begin{function}[EXP]{\cs:w, \cs_end:}
+% \begin{syntax}
+% \cs{cs:w} \meta{control sequence name} \cs{cs_end:}
+% \end{syntax}
+% Converts the given \meta{control sequence name} into a single
+% control sequence token. This process requires one expansion.
+% The content for \meta{control sequence name} may be literal
+% material or from other expandable functions. The
+% \meta{control sequence name} must, when fully expanded, consist
+% of character tokens which are not active: typically
+% of category code $10$ (space), $11$ (letter)
+% or $12$ (other), or a mixture of these.
+% \begin{texnote}
+% These are the \TeX{} primitives \tn{csname} and \tn{endcsname}.
+% \end{texnote}
+% \end{function}
+%
+% As an example of the \cs{cs:w} and \cs{cs_end:} functions, both
+% \begin{verbatim}
+% \cs:w a b c \cs_end:
+% \end{verbatim}
+% and
+% \begin{verbatim}
+% \tl_new:N \l_my_tl
+% \tl_set:Nn \l_my_tl { a b c }
+% \cs:w \tl_use:N \l_my_tl \cs_end:
+% \end{verbatim}
+% would be equivalent to
+% \begin{verbatim}
+% \abc
+% \end{verbatim}
+% after one expansion of \cs{cs:w}.
+%
+% \begin{function}[EXP]{\cs_to_str:N}
+% \begin{syntax}
+% \cs{cs_to_str:N} \meta{control sequence}
+% \end{syntax}
+% Converts the given \meta{control sequence} into a series of
+% characters with category code $12$ (other), except spaces,
+% of category code $10$. The result does \emph{not} include
+% the current escape token, contrarily to \cs{token_to_str:N}.
+% Full expansion of this function requires exactly $2$ expansion
+% steps, and so an \texttt{e}-type or \texttt{x}-type expansion, or two
+% \texttt{o}-type expansions are required to
+% convert the \meta{control sequence} to a sequence of characters
+% in the input stream. In most cases, an \texttt{f}-expansion
+% is correct as well, but this loses a space at the start
+% of the result.
+% \end{function}
+%
+% \section{Analysing control sequences}
+%
+% \begin{function}[EXP, added = 2018-04-06]{\cs_split_function:N}
+% \begin{syntax}
+% \cs{cs_split_function:N} \meta{function}
+% \end{syntax}
+% Splits the \meta{function} into the \meta{name} (\emph{i.e.}~the part
+% before the colon) and the \meta{signature} (\emph{i.e.}~after the colon).
+% This information is then placed in the input stream
+% in three parts: the \meta{name}, the
+% \meta{signature} and a logic token indicating if a colon was found
+% (to differentiate variables from function names). The \meta{name}
+% does not include the escape character, and both the \meta{name} and
+% \meta{signature} are made up of tokens with category code $12$
+% (other).
+% \end{function}
+%
+% The next three functions decompose \TeX{} macros into their
+% constituent parts: if the \meta{token} passed is not a macro then no
+% decomposition can occur. In the latter case, all three functions leave
+% \cs{scan_stop:} in the input stream.
+%
+% \begin{function}[EXP, added = 2019-02-27]{\cs_prefix_spec:N}
+% \begin{syntax}
+% \cs{cs_prefix_spec:N} \meta{token}
+% \end{syntax}
+% If the \meta{token} is a macro, this function leaves the applicable
+% \TeX{} prefixes in input stream as a string of tokens of category
+% code $12$ (with spaces having category code $10$). Thus for example
+% \begin{verbatim}
+% \cs_set:Npn \next:nn #1#2 { x #1~y #2 }
+% \cs_prefix_spec:N \next:nn
+% \end{verbatim}
+% leaves |\long| in the input stream. If the \meta{token} is
+% not a macro then \cs{scan_stop:} is left in the input stream.
+% \begin{texnote}
+% The prefix can be empty, |\long|, |\protected| or
+% |\protected\long| with backslash replaced by the current escape
+% character.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2022-06-24]{\cs_parameter_spec:N}
+% \begin{syntax}
+% \cs{cs_parameter_spec:N} \meta{token}
+% \end{syntax}
+% If the \meta{token} is a macro, this function leaves the primitive
+% \TeX{} parameter specification in input stream as a string of
+% character tokens of category code $12$ (with spaces having category
+% code $10$). Thus for example
+% \begin{verbatim}
+% \cs_set:Npn \next:nn #1#2 { x #1 y #2 }
+% \cs_parameter_spec:N \next:nn
+% \end{verbatim}
+% leaves |#1#2| in the input stream. If the \meta{token} is
+% not a macro then \cs{scan_stop:} is left in the input stream.
+% \begin{texnote}
+% If the parameter specification contains the string |->|, then the
+% function produces incorrect results.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2019-02-27]{\cs_replacement_spec:N, \cs_replacement_spec:c}
+% \begin{syntax}
+% \cs{cs_replacement_spec:N} \meta{token}
+% \end{syntax}
+% If the \meta{token} is a macro, this function leaves the replacement
+% text in input stream as a string of character tokens of category
+% code $12$ (with spaces having category code $10$). Thus for example
+% \begin{verbatim}
+% \cs_set:Npn \next:nn #1#2 { x #1~y #2 }
+% \cs_replacement_spec:N \next:nn
+% \end{verbatim}
+% leaves \verb*|x#1 y#2| in the input stream. If the \meta{token} is
+% not a macro then \cs{scan_stop:} is left in the input stream.
+% \begin{texnote}
+% If the parameter specification contains the string |->|, then the
+% function produces incorrect results.
+% \end{texnote}
+% \end{function}
+%
+% \section{Using or removing tokens and arguments}
+%
+% Tokens in the input can be read and used or read and discarded.
+% If one or more tokens are wrapped in braces then when absorbing them
+% the outer set is removed. At the same time, the category code
+% of each token is set when the token is read by a function (if it
+% is read more than once, the category code is determined by
+% the situation in force when first function absorbs the token).
+%
+% \begin{function}[EXP]{\use:n, \use:nn, \use:nnn, \use:nnnn}
+% \begin{syntax}
+% \cs{use:n} \Arg{group_1}
+% \cs{use:nn} \Arg{group_1} \Arg{group_2}
+% \cs{use:nnn} \Arg{group_1} \Arg{group_2} \Arg{group_3}
+% \cs{use:nnnn} \Arg{group_1} \Arg{group_2} \Arg{group_3} \Arg{group_4}
+% \end{syntax}
+% As illustrated, these functions absorb between one and four
+% arguments, as indicated by the argument specifier. The braces
+% surrounding each argument are removed and the remaining tokens are
+% left in the input stream. The category code of these tokens is
+% also fixed by this process (if it has not already been by some
+% other absorption). All of these functions require only a single
+% expansion to operate, so that one expansion of
+% \begin{verbatim}
+% \use:nn { abc } { { def } }
+% \end{verbatim}
+% results in the input stream containing
+% \begin{verbatim}
+% abc { def }
+% \end{verbatim}
+% \emph{i.e.} only the outer braces are removed.
+% \begin{texnote}
+% The \cs{use:n} function is equivalent to \LaTeXe{}'s \tn{@firstofone}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]
+% {
+% \use_i:nn, \use_ii:nn ,
+% \use_i:nnn , \use_ii:nnn , \use_iii:nnn ,
+% \use_i:nnnn, \use_ii:nnnn, \use_iii:nnnn, \use_iv:nnnn ,
+% \use_i:nnnnn, \use_ii:nnnnn, \use_iii:nnnnn, \use_iv:nnnnn ,
+% \use_v:nnnnn ,
+% \use_i:nnnnnn, \use_ii:nnnnnn, \use_iii:nnnnnn, \use_iv:nnnnnn ,
+% \use_v:nnnnnn , \use_vi:nnnnnn ,
+% \use_i:nnnnnnn, \use_ii:nnnnnnn, \use_iii:nnnnnnn, \use_iv:nnnnnnn ,
+% \use_v:nnnnnnn , \use_vi:nnnnnnn , \use_vii:nnnnnnn ,
+% \use_i:nnnnnnnn, \use_ii:nnnnnnnn, \use_iii:nnnnnnnn, \use_iv:nnnnnnnn ,
+% \use_v:nnnnnnnn , \use_vi:nnnnnnnn , \use_vii:nnnnnnnn , \use_viii:nnnnnnnn ,
+% \use_i:nnnnnnnnn, \use_ii:nnnnnnnnn, \use_iii:nnnnnnnnn, \use_iv:nnnnnnnnn ,
+% \use_v:nnnnnnnnn , \use_vi:nnnnnnnnn , \use_vii:nnnnnnnnn , \use_viii:nnnnnnnnn ,
+% \use_ix:nnnnnnnnn
+% }
+% \begin{syntax}
+% \cs{use_i:nn} \Arg{arg_1} \Arg{arg_2}
+% \cs{use_i:nnn} \Arg{arg_1} \Arg{arg_2} \Arg{arg_3}
+% \cs{use_i:nnnn} \Arg{arg_1} \Arg{arg_2} \Arg{arg_3} \Arg{arg_4}
+% \cs{use_i:nnnnn} \Arg{arg_1} \Arg{arg_2} \Arg{arg_3} \Arg{arg_4} \Arg{arg_5}
+% \cs{use_i:nnnnnn} \Arg{arg_1} \Arg{arg_2} \Arg{arg_3} \Arg{arg_4} \Arg{arg_5} \Arg{arg_6}
+% \cs{use_i:nnnnnnn} \Arg{arg_1} \Arg{arg_2} \Arg{arg_3} \Arg{arg_4} \Arg{arg_5} \Arg{arg_6} \Arg{arg_7}
+% \cs{use_i:nnnnnnnn} \Arg{arg_1} \Arg{arg_2} \Arg{arg_3} \Arg{arg_4} \Arg{arg_5} \Arg{arg_6} \Arg{arg_7} \Arg{arg_8}
+% \cs{use_i:nnnnnnnnn} \Arg{arg_1} \Arg{arg_2} \Arg{arg_3} \Arg{arg_4} \Arg{arg_5} \Arg{arg_6} \Arg{arg_7} \Arg{arg_8} \Arg{arg_9}
+% \end{syntax}
+% These functions absorb a number ($n$) arguments from the input stream.
+% They then discard all arguments other than that indicated by the roman
+% numeral, which is left in the input stream. For example, \cs{use_i:nn}
+% discards the second argument, and leaves the content of the first
+% argument in the input stream.
+% The category code
+% of these tokens is also fixed (if it has not already been by
+% some other absorption). A single expansion is needed for the
+% functions to take effect.
+% \end{function}
+%
+% \begin{function}[EXP]{\use_i_ii:nnn}
+% \begin{syntax}
+% \cs{use_i_ii:nnn} \Arg{arg_1} \Arg{arg_2} \Arg{arg_3}
+% \end{syntax}
+% This function absorbs three arguments and leaves the content of the
+% first and second in the input stream. The category code of
+% these tokens is also fixed (if it has not already been by
+% some other absorption). A single expansion is needed for the
+% function to take effect. An example:
+% \begin{verbatim}
+% \use_i_ii:nnn { abc } { { def } } { ghi }
+% \end{verbatim}
+% results in the input stream containing
+% \begin{verbatim}
+% abc { def }
+% \end{verbatim}
+% \emph{i.e.} the outer braces are removed and the third group
+% is removed.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2019-06-02]{\use_ii_i:nn}
+% \begin{syntax}
+% \cs{use_ii_i:nn} \Arg{arg_1} \Arg{arg_2}
+% \end{syntax}
+% This function absorbs two arguments and leaves the content of the
+% second and first in the input stream. The category code of
+% these tokens is also fixed (if it has not already been by
+% some other absorption). A single expansion is needed for the
+% function to take effect.
+% \end{function}
+%
+% \begin{function}[EXP]
+% {
+% \use_none:n ,
+% \use_none:nn ,
+% \use_none:nnn ,
+% \use_none:nnnn ,
+% \use_none:nnnnn ,
+% \use_none:nnnnnn ,
+% \use_none:nnnnnnn ,
+% \use_none:nnnnnnnn ,
+% \use_none:nnnnnnnnn
+% }
+% \begin{syntax}
+% \cs{use_none:n} \Arg{group_1}
+% \end{syntax}
+% These functions absorb between one and nine groups from the
+% input stream, leaving nothing on the resulting input stream.
+% These functions work after a single expansion. One or more of the
+% \texttt{n} arguments may be an unbraced single token
+% (\emph{i.e.}~an \texttt{N} argument).
+% \begin{texnote}
+% These are equivalent to \LaTeXe{}'s \tn{@gobble}, \tn{@gobbletwo},
+% \emph{etc.}
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-06-18, updated = 2023-07-05]{\use:e}
+% \begin{syntax}
+% \cs{use:e} \Arg{expandable tokens}
+% \end{syntax}
+% Fully expands the \meta{token list} in an \texttt{e}-type manner,
+% in which parameter character (usually~|#|) need not be doubled, \emph{and}
+% the function remains fully expandable.
+% \begin{texnote}
+% \cs{use:e} is a wrapper around the primitive \tn{expanded}.
+% It requires two expansions to complete its action.
+% \end{texnote}
+% \end{function}
+%
+% \subsection{Selecting tokens from delimited arguments}
+%
+% A different kind of function for selecting tokens from the token
+% stream are those that use delimited arguments.
+%
+% \begin{function}[EXP]
+% {
+% \use_none_delimit_by_q_nil:w,
+% \use_none_delimit_by_q_stop:w,
+% \use_none_delimit_by_q_recursion_stop:w
+% }
+% \begin{syntax}
+% \cs{use_none_delimit_by_q_nil:w} \meta{balanced text} \cs{q_nil}
+% \cs{use_none_delimit_by_q_stop:w} \meta{balanced text} \cs{q_stop}
+% \cs{use_none_delimit_by_q_recursion_stop:w} \meta{balanced text} \cs{q_recursion_stop}
+% \end{syntax}
+% Absorb the \meta{balanced text} from the input stream delimited by
+% the marker given in the function name, leaving nothing in the
+% input stream.
+% \end{function}
+%
+% \begin{function}[EXP]
+% {
+% \use_i_delimit_by_q_nil:nw,
+% \use_i_delimit_by_q_stop:nw,
+% \use_i_delimit_by_q_recursion_stop:nw
+% }
+% \begin{syntax}
+% \cs{use_i_delimit_by_q_nil:nw} \Arg{inserted tokens} \meta{balanced text} \cs{q_nil}
+% \cs{use_i_delimit_by_q_stop:nw} \Arg{inserted tokens} \meta{balanced text} \cs{q_stop}
+% \cs{use_i_delimit_by_q_recursion_stop:nw} \Arg{inserted tokens} \meta{balanced text} \cs{q_recursion_stop}
+% \end{syntax}
+% Absorb the \meta{balanced text} from the input stream delimited by
+% the marker given in the function name, leaving \meta{inserted tokens}
+% in the input stream for further processing.
+% \end{function}
+%
+% \section{Predicates and conditionals}
+%
+% \LaTeX3 has three concepts for conditional flow processing:
+% \begin{description}
+% \item[Branching conditionals]
+% Functions that carry out a test and then execute, depending on its
+% result, either the code supplied as the \meta{true code} or the
+% \meta{false code}.
+% These arguments are denoted with |T| and |F|, respectively. An
+% example would be
+% \begin{quote}
+% |\cs_if_free:cTF {abc}| \Arg{true code} \Arg{false code}
+% \end{quote}
+% a function that turns the first argument into a control sequence
+% (since it's marked as |c|) then checks whether this control sequence
+% is still free and then depending on the result carries out the code in
+% the second argument (true case) or in the third argument (false
+% case).
+%
+% These type of functions are known as \enquote{conditionals};
+% whenever a |TF| function is defined it is usually accompanied by
+% |T| and |F| functions as well. These are provided for convenience when
+% the branch only needs to go a single way. Package writers are free to
+% choose which types to define but the kernel definitions always
+% provide all three versions.
+%
+% Important to note is that these branching conditionals with \meta{true
+% code} and/or \meta{false code} are always defined in a way that the
+% code of the chosen alternative can operate on following tokens in
+% the input stream.
+%
+% These conditional functions may or may not be fully expandable, but if
+% they are expandable they are accompanied by a \enquote{predicate}
+% for the same test as described below.
+%
+% \item[Predicates]
+% \enquote{Predicates} are functions that return a special type of
+% boolean value which can be tested by the boolean expression parser.
+% All functions of this type
+% are expandable and have names that end with |_p| in the
+% description part. For example,
+% \begin{quote}
+% \cs{cs_if_free_p:N}
+% \end{quote}
+% would be a predicate function for the same type of test as the
+% conditional described above. It would return \enquote{true} if its
+% argument (a single token denoted by |N|) is still free for definition.
+% It would be used in constructions like
+% \begin{quote}
+% |\bool_if:nTF {| \\
+% \verb" \cs_if_free_p:N \l_tmpz_tl || \cs_if_free_p:N \g_tmpz_tl " \\
+% |}|
+% \Arg{true code} \Arg{false code}
+% \end{quote}
+%
+% For each predicate defined, a \enquote{branching conditional}
+% also exists that behaves like a conditional described above.
+%
+% \item[Primitive conditionals]
+% There is a third variety of conditional, which is the original
+% concept used in plain \TeX{} and \LaTeXe{}. Their use is discouraged
+% in \pkg{expl3} (although still used in low-level definitions)
+% because they are more fragile and in many cases require more
+% expansion control (hence more code) than the two types of
+% conditionals described above.
+% \end{description}
+%
+% \subsection{Tests on control sequences}
+%
+% \begin{function}[EXP,pTF]{\cs_if_eq:NN}
+% \begin{syntax}
+% \cs{cs_if_eq_p:NN} \meta{cs_1} \meta{cs_2}
+% \cs{cs_if_eq:NNTF} \meta{cs_1} \meta{cs_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Compares the definition of two \meta{control sequences} and
+% is logically \texttt{true} if they are the same, \emph{i.e.}~if they have exactly
+% the same definition when examined with \cs{cs_show:N}.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\cs_if_exist:N, \cs_if_exist:c}
+% \begin{syntax}
+% \cs{cs_if_exist_p:N} \meta{control sequence}
+% \cs{cs_if_exist:NTF} \meta{control sequence} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{control sequence} is currently defined
+% (whether as a function or another control sequence type). Any
+% definition of \meta{control sequence} other than \tn{relax}
+% evaluates as \texttt{true}.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\cs_if_free:N, \cs_if_free:c}
+% \begin{syntax}
+% \cs{cs_if_free_p:N} \meta{control sequence}
+% \cs{cs_if_free:NTF} \meta{control sequence} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{control sequence} is currently free to
+% be defined. This test is \texttt{false} if the
+% \meta{control sequence} currently exists (as defined by
+% \cs{cs_if_exist:NTF}).
+% \end{function}
+%
+% \subsection{Primitive conditionals}
+%
+% The \eTeX{} engine itself provides many different conditionals. Some
+% expand whatever comes after them and others don't. Hence the names
+% for these underlying functions often contains a |:w| part but
+% higher level functions are often available. See for instance
+% \cs{int_compare_p:nNn} which is a wrapper for \cs{if_int_compare:w}.
+%
+% Certain conditionals deal with specific data types like boxes and
+% fonts and are described there. The ones described below are either
+% the universal conditionals or deal with control sequences. We
+% prefix primitive conditionals with |\if_|, except for \cs{if:w}.
+%
+% \begin{function}[EXP]
+% {\if_true:, \if_false:, \else:, \fi:, \reverse_if:N}
+% \begin{syntax}
+% "\if_true:" <true code> "\else:" <false code> "\fi:" \\
+% "\if_false:" <true code> "\else:" <false code> "\fi:" \\
+% "\reverse_if:N" <primitive conditional>
+% \end{syntax}
+% "\if_true:" always executes <true code>, while "\if_false:" always
+% executes <false code>. "\reverse_if:N" reverses any two-way primitive
+% conditional. "\else:" and "\fi:" delimit the branches of the
+% conditional. The function "\or:" is documented in \pkg{l3int} and
+% used in case switches.
+% \begin{texnote}
+% \cs{if_true:} and \cs{if_false:} are equivalent to their corresponding
+% \TeX{} primitive conditionals \tn{iftrue} and \tn{iffalse};
+% \cs{else:} and \cs{fi:} are the \TeX{} primitives \tn{else} and \tn{fi};
+% \cs{reverse_if:N} is the \eTeX{} primitive \tn{unless}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\if_meaning:w}
+% \begin{syntax}
+% "\if_meaning:w" <arg_1> <arg_2> <true code> "\else:" <false code> "\fi:"
+% \end{syntax}
+% "\if_meaning:w" executes <true code> when <arg_1> and <arg_2> are the same,
+% otherwise it executes <false code>.
+% <arg_1> and <arg_2> could be functions, variables, tokens; in all cases the
+% \emph{unexpanded} definitions are compared.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{ifx}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\if:w, \if_charcode:w, \if_catcode:w}
+% \begin{syntax}
+% "\if:w" <token(s)> <true code> "\else:" <false code> "\fi:" \\
+% "\if_catcode:w" <token(s)> <true code> "\else:" <false code> "\fi:"
+% \end{syntax}
+% "\if_charcode:w" is an alternative name for "\if:w".
+% These conditionals expand \meta{token(s)} until two
+% unexpandable tokens \meta{token_1} and \meta{token_2} are found;
+% any further tokens up to the next unbalanced "\else:" are the true branch,
+% ending with \meta{true code}. It is executed if the condition is fulfilled,
+% otherwise \meta{false code} is executed.
+% You can omit "\else:" when just in front of "\fi:" and
+% you can nest "\if...\else:...\fi:" constructs inside the true branch or the
+% \meta{false code}.
+% With "\exp_not:N", you can prevent the expansion of a token.
+%
+% "\if_catcode:w"
+% tests if \meta{token_1} and \meta{token_2} have the same category code whereas
+% "\if:w" and \cs{if_charcode:w} test if they have the same character code.
+% \begin{texnote}
+% \cs{if:w} and \cs{if_charcode:w} are both the \TeX{} primitive \tn{if}.
+% \cs{if_catcode:w} is the \TeX{} primitive \tn{ifcat}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\if_cs_exist:N, \if_cs_exist:w}
+% \begin{syntax}
+% "\if_cs_exist:N" <cs> <true code> "\else:" <false code> "\fi:" \\
+% "\if_cs_exist:w" <tokens> "\cs_end:" <true code> "\else:" <false code> "\fi:"
+% \end{syntax}
+% Check if <cs> appears in the hash table or if the control sequence
+% that can be formed from <tokens> appears in the hash table. The
+% latter function does not turn the control sequence in question into
+% "\scan_stop:"! This can be useful when dealing with control
+% sequences which cannot be entered as a single token.
+% \begin{texnote}
+% These are the \TeX{} primitives \tn{ifdefined} and \tn{ifcsname}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]
+% {
+% \if_mode_horizontal:, \if_mode_vertical:,
+% \if_mode_math:, \if_mode_inner:
+% }
+% \begin{syntax}
+% "\if_mode_horizontal:" <true code> "\else:" <false code> "\fi:"
+% \end{syntax}
+% Execute <true code> if currently in horizontal mode, otherwise
+% execute <false code>. Similar for the other functions.
+% \begin{texnote}
+% These are the \TeX{} primitives \tn{ifhmode}, \tn{ifvmode}, \tn{ifmmode},
+% and~\tn{ifinner}.
+% \end{texnote}
+% \end{function}
+%
+% \section{Starting a paragraph}
+%
+% \begin{function}[added = 2017-07-04]{\mode_leave_vertical:}
+% \begin{syntax}
+% \cs{mode_leave_vertical:}
+% \end{syntax}
+% Ensures that \TeX{} is not in vertical (inter-paragraph) mode. In
+% horizontal or math mode this command has no effect, in vertical mode it
+% switches to horizontal mode, and inserts a box of width
+% \tn{parindent}, followed by the \tn{everypar} token list.
+% \begin{texnote}
+% This results in the contents of the \tn{everypar} token register being
+% inserted, after \cs{mode_leave_vertical:} is complete. Notice that in
+% contrast to the \LaTeXe{} \tn{leavevmode} approach, no box is used
+% by the method implemented here.
+% \end{texnote}
+% \end{function}
+%
+% \section{Debugging support}
+%
+% \begin{function}[added = 2017-07-16, updated = 2023-05-23]{\debug_on:n, \debug_off:n}
+% \begin{syntax}
+% \cs{debug_on:n} |{| \meta{comma-separated list} |}|
+% \cs{debug_off:n} |{| \meta{comma-separated list} |}|
+% \end{syntax}
+% Turn on and off within a group various debugging code, some of which
+% is also available as \pkg{expl3} load-time options. The items that
+% can be used in the \meta{list} are
+% \begin{itemize}
+% \item \texttt{check-declarations} that checks all \pkg{expl3}
+% variables used were previously declared and that local/global
+% variables (based on their name or on their first assignment) are
+% only locally/globally assigned;
+% \item \texttt{check-expressions} that checks integer, dimension,
+% skip, and muskip expressions are not terminated prematurely;
+% \item \texttt{deprecation} that makes deprecated commands produce errors;
+% \item \texttt{log-functions} that logs function definitions and
+% variable declarations;
+% \item \texttt{all} that does all of the above.
+% \end{itemize}
+% Providing these as switches rather than options allows testing code
+% even if it relies on other packages: load all other packages, call
+% \cs{debug_on:n}, and load the code that one is interested in
+% testing.
+% \end{function}
+%
+% \begin{function}[added = 2017-11-28]{\debug_suspend:, \debug_resume:}
+% \begin{syntax}
+% \cs{debug_suspend:} \ldots{} \cs{debug_resume:}
+% \end{syntax}
+% Suppress (locally) errors and logging from \texttt{debug} commands,
+% except for the \texttt{deprecation} errors. These pairs
+% of commands can be nested. This can be used around pieces of code
+% that are known to fail checks, if such failures should be ignored.
+% See for instance \pkg{l3cctab} and \pkg{l3coffins}.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3basics} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \subsection{Renaming some \TeX{} primitives (again)}
+%
+% Having given all the \TeX{} primitives a consistent name, we need to
+% give sensible names to the ones we actually want to use.
+% These will be defined as needed in the appropriate modules, but we
+% do a few now, just to get started.\footnote{This renaming gets expensive
+% in terms of csname usage, an alternative scheme would be to just use
+% the \cs[no-index]{tex_\ldots:D} name in the cases where no good alternative exists.}
+%
+% \begin{macro}[EXP]
+% {
+% \if_true:, \if_false:, \or:, \else:, \fi:, \reverse_if:N,
+% \if:w, \if_charcode:w, \if_catcode:w, \if_meaning:w
+% }
+% Then some conditionals.
+% \begin{macrocode}
+\tex_global:D \tex_let:D \if_true: \tex_iftrue:D
+\tex_global:D \tex_let:D \if_false: \tex_iffalse:D
+\tex_global:D \tex_let:D \or: \tex_or:D
+\tex_global:D \tex_let:D \else: \tex_else:D
+\tex_global:D \tex_let:D \fi: \tex_fi:D
+\tex_global:D \tex_let:D \reverse_if:N \tex_unless:D
+\tex_global:D \tex_let:D \if:w \tex_if:D
+\tex_global:D \tex_let:D \if_charcode:w \tex_if:D
+\tex_global:D \tex_let:D \if_catcode:w \tex_ifcat:D
+\tex_global:D \tex_let:D \if_meaning:w \tex_ifx:D
+\tex_global:D \tex_let:D \if_bool:N \tex_ifodd:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \if_mode_math:,
+% \if_mode_horizontal:,
+% \if_mode_vertical:,
+% \if_mode_inner:
+% }
+% \TeX{} lets us detect some if its modes.
+% \begin{macrocode}
+\tex_global:D \tex_let:D \if_mode_math: \tex_ifmmode:D
+\tex_global:D \tex_let:D \if_mode_horizontal: \tex_ifhmode:D
+\tex_global:D \tex_let:D \if_mode_vertical: \tex_ifvmode:D
+\tex_global:D \tex_let:D \if_mode_inner: \tex_ifinner:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\if_cs_exist:N, \if_cs_exist:w, \cs:w, \cs_end:}
+% Building csnames and testing if control sequences exist.
+% \begin{macrocode}
+\tex_global:D \tex_let:D \if_cs_exist:N \tex_ifdefined:D
+\tex_global:D \tex_let:D \if_cs_exist:w \tex_ifcsname:D
+\tex_global:D \tex_let:D \cs:w \tex_csname:D
+\tex_global:D \tex_let:D \cs_end: \tex_endcsname:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\exp_after:wN, \exp_not:N, \exp_not:n}
+% The five |\exp_| functions are used in the \pkg{l3expan} module
+% where they are described.
+% \begin{macrocode}
+\tex_global:D \tex_let:D \exp_after:wN \tex_expandafter:D
+\tex_global:D \tex_let:D \exp_not:N \tex_noexpand:D
+\tex_global:D \tex_let:D \exp_not:n \tex_unexpanded:D
+\tex_global:D \tex_let:D \exp:w \tex_romannumeral:D
+\tex_global:D \tex_chardef:D \exp_end: = 0 ~
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\token_to_meaning:N, \cs_meaning:N}
+% Examining a control sequence or token.
+% \begin{macrocode}
+\tex_global:D \tex_let:D \token_to_meaning:N \tex_meaning:D
+\tex_global:D \tex_let:D \cs_meaning:N \tex_meaning:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\tl_to_str:n, \token_to_str:N, \__kernel_tl_to_str:w}
+% Making strings.
+% \begin{macrocode}
+\tex_global:D \tex_let:D \tl_to_str:n \tex_detokenize:D
+\tex_global:D \tex_let:D \token_to_str:N \tex_string:D
+\tex_global:D \tex_let:D \__kernel_tl_to_str:w \tex_detokenize:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\scan_stop:, \group_begin:, \group_end:}
+% The next three are basic functions for which there also exist
+% versions that are safe inside alignments. These safe versions are
+% defined in the \pkg{l3prg} module.
+% \begin{macrocode}
+\tex_global:D \tex_let:D \scan_stop: \tex_relax:D
+\tex_global:D \tex_let:D \group_begin: \tex_begingroup:D
+\tex_global:D \tex_let:D \group_end: \tex_endgroup:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%<@@=int>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\if_int_compare:w, \@@_to_roman:w}
+% For integers.
+% \begin{macrocode}
+\tex_global:D \tex_let:D \if_int_compare:w \tex_ifnum:D
+\tex_global:D \tex_let:D \@@_to_roman:w \tex_romannumeral:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\group_insert_after:N}
+% Adding material after the end of a group.
+% \begin{macrocode}
+\tex_global:D \tex_let:D \group_insert_after:N \tex_aftergroup:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\exp_args:Nc, \exp_args:cc}
+% Discussed in \pkg{l3expan}, but needed much earlier.
+% \begin{macrocode}
+\tex_long:D \tex_gdef:D \exp_args:Nc #1#2
+ { \exp_after:wN #1 \cs:w #2 \cs_end: }
+\tex_long:D \tex_gdef:D \exp_args:cc #1#2
+ { \cs:w #1 \exp_after:wN \cs_end: \cs:w #2 \cs_end: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, documented-as=\token_to_meaning:N]
+% {\token_to_meaning:c, \token_to_str:c, \cs_meaning:c}
+% A small number of variants defined by hand.
+% Some of the necessary functions
+% (\cs{use_i:nn}, \cs{use_ii:nn}, and \cs{exp_args:NNc}) are not
+% defined at that point yet, but will be defined before those variants
+% are used. The \cs{cs_meaning:c} command must check for an undefined
+% control sequence to avoid defining it mistakenly.
+% \begin{macrocode}
+\tex_gdef:D \token_to_str:c { \exp_args:Nc \token_to_str:N }
+\tex_long:D \tex_gdef:D \cs_meaning:c #1
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ { \exp_args:Nc \cs_meaning:N {#1} }
+ { \tl_to_str:n {undefined} }
+ }
+\tex_global:D \tex_let:D \token_to_meaning:c = \cs_meaning:c
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Defining some constants}
+%
+% \begin{variable}{\c_zero_int}
+% We need the constant \cs{c_zero_int}
+% which is used by some functions in current module. The
+% rest are defined in the \pkg{l3int} module -- at least for the
+% ones that can be defined with \cs{tex_chardef:D} or
+% \cs{tex_mathchardef:D}. For other constants the \pkg{l3int} module is
+% required but it can't be used until the allocation has been set
+% up properly!
+% \begin{macrocode}
+\tex_global:D \tex_chardef:D \c_zero_int = 0 ~
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_max_register_int}
+% This is here as this particular integer is needed in modules
+% loaded before \pkg{l3int}, and is documented in \pkg{l3int}.
+% \LuaTeX{} and those which contain parts of the Omega extensions have
+% more registers available than \eTeX{}.
+% \begin{macrocode}
+\tex_ifdefined:D \tex_luatexversion:D
+ \tex_global:D \tex_chardef:D \c_max_register_int = 65 535 ~
+\tex_else:D
+ \tex_ifdefined:D \tex_omathchardef:D
+ \tex_global:D \tex_omathchardef:D \c_max_register_int = 65535 ~
+ \tex_else:D
+ \tex_global:D \tex_mathchardef:D \c_max_register_int = 32767 ~
+ \tex_fi:D
+\tex_fi:D
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Defining functions}
+%
+% We start by providing functions for the typical definition
+% functions. First the global ones.
+%
+% \begin{macro}
+% {
+% \cs_gset_nopar:Npn , \cs_gset_nopar:Npe , \cs_gset_nopar:Npx ,
+% \cs_gset:Npn , \cs_gset:Npe , \cs_gset:Npx ,
+% \cs_gset_protected_nopar:Npn , \cs_gset_protected_nopar:Npe , \cs_gset_protected_nopar:Npx ,
+% \cs_gset_protected:Npn , \cs_gset_protected:Npe , \cs_gset_protected:Npx
+% }
+% All assignment functions in \LaTeX3 should be naturally protected;
+% after all, the \TeX{} primitives for assignments are and it can be
+% a cause of problems if others aren't.
+% \begin{macrocode}
+\tex_global:D \tex_let:D \cs_gset_nopar:Npn \tex_gdef:D
+\tex_global:D \tex_let:D \cs_gset_nopar:Npe \tex_xdef:D
+\tex_global:D \tex_let:D \cs_gset_nopar:Npx \tex_xdef:D
+\tex_protected:D \tex_long:D \tex_gdef:D \cs_gset:Npn
+ { \tex_long:D \tex_gdef:D }
+\tex_protected:D \tex_long:D \tex_gdef:D \cs_gset:Npe
+ { \tex_long:D \tex_xdef:D }
+\tex_global:D \tex_let:D \cs_gset:Npx \cs_gset:Npe
+\tex_protected:D \tex_long:D \tex_gdef:D \cs_gset_protected_nopar:Npn
+ { \tex_protected:D \tex_gdef:D }
+\tex_protected:D \tex_long:D \tex_gdef:D \cs_gset_protected_nopar:Npe
+ { \tex_protected:D \tex_xdef:D }
+\tex_global:D \tex_let:D \cs_gset_protected_nopar:Npx \cs_gset_protected_nopar:Npe
+\tex_protected:D \tex_long:D \tex_gdef:D \cs_gset_protected:Npn
+ { \tex_protected:D \tex_long:D \tex_gdef:D }
+\tex_protected:D \tex_long:D \tex_gdef:D \cs_gset_protected:Npe
+ { \tex_protected:D \tex_long:D \tex_xdef:D }
+\tex_global:D \tex_let:D \cs_gset_protected:Npx \cs_gset_protected:Npe
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \cs_set_nopar:Npn , \cs_set_nopar:Npe , \cs_set_nopar:Npx ,
+% \cs_set:Npn , \cs_set:Npe , \cs_set:Npx ,
+% \cs_set_protected_nopar:Npn , \cs_set_protected_nopar:Npe , \cs_set_protected_nopar:Npx ,
+% \cs_set_protected:Npn , \cs_set_protected:Npe , \cs_set_protected:Npx
+% }
+% Local versions of the above functions.
+% \begin{macrocode}
+\tex_global:D \tex_let:D \cs_set_nopar:Npn \tex_def:D
+\tex_global:D \tex_let:D \cs_set_nopar:Npe \tex_edef:D
+\tex_global:D \tex_let:D \cs_set_nopar:Npx \tex_edef:D
+\cs_gset_protected:Npn \cs_set:Npn
+ { \tex_long:D \tex_def:D }
+\cs_gset_protected:Npn \cs_set:Npe
+ { \tex_long:D \tex_edef:D }
+\tex_global:D \tex_let:D \cs_set:Npx \cs_set:Npe
+\cs_gset_protected:Npn \cs_set_protected_nopar:Npn
+ { \tex_protected:D \tex_def:D }
+\cs_gset_protected:Npn \cs_set_protected_nopar:Npe
+ { \tex_protected:D \tex_edef:D }
+\tex_global:D \tex_let:D \cs_set_protected_nopar:Npx \cs_set_protected_nopar:Npe
+\cs_gset_protected:Npn \cs_set_protected:Npn
+ { \tex_protected:D \tex_long:D \tex_def:D }
+\cs_gset_protected:Npn \cs_set_protected:Npe
+ { \tex_protected:D \tex_long:D \tex_edef:D }
+\tex_global:D \tex_let:D \cs_set_protected:Npx \cs_set_protected:Npe
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Selecting tokens}
+%
+% \begin{macrocode}
+%<@@=exp>
+% \end{macrocode}
+%
+% \begin{variable}{\l_@@_internal_tl}
+% Scratch token list variable for \pkg{l3expan}, used by \cs{use:x},
+% used in defining conditionals. We don't use |tl| methods because
+% \pkg{l3basics} is loaded earlier.
+% \begin{macrocode}
+\cs_gset_nopar:Npn \l_@@_internal_tl { }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\use:c}
+% This macro grabs its argument and returns a csname from it.
+% \begin{macrocode}
+\cs_gset:Npn \use:c #1 { \cs:w #1 \cs_end: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[deprecated]{\use:x}
+% Fully expands its argument and passes it to the input stream. Uses
+% the reserved \cs{l_@@_internal_tl} which we've set up above.
+% \begin{macrocode}
+\cs_gset_protected:Npn \use:x #1
+ {
+ \cs_set_nopar:Npx \l_@@_internal_tl {#1}
+ \l_@@_internal_tl
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%<@@=use>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\use:e}
+% \begin{macrocode}
+\cs_gset:Npn \use:e #1 { \tex_expanded:D {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%<@@=exp>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\use:n, \use:nn, \use:nnn, \use:nnnn}
+% These macros grab their arguments and return them back to the input
+% (with outer braces removed).
+% \begin{macrocode}
+\cs_gset:Npn \use:n #1 {#1}
+\cs_gset:Npn \use:nn #1#2 {#1#2}
+\cs_gset:Npn \use:nnn #1#2#3 {#1#2#3}
+\cs_gset:Npn \use:nnnn #1#2#3#4 {#1#2#3#4}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\use_i:nn, \use_ii:nn}
+% The equivalent to \LaTeXe{}'s \tn{@firstoftwo} and \tn{@secondoftwo}.
+% \begin{macrocode}
+\cs_gset:Npn \use_i:nn #1#2 {#1}
+\cs_gset:Npn \use_ii:nn #1#2 {#2}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \use_i:nnn , \use_ii:nnn , \use_iii:nnn ,
+% \use_i:nnnn, \use_ii:nnnn, \use_iii:nnnn, \use_iv:nnnn ,
+% \use_i:nnnnn, \use_ii:nnnnn, \use_iii:nnnnn, \use_iv:nnnnn ,
+% \use_v:nnnnn ,
+% \use_i:nnnnnn, \use_ii:nnnnnn, \use_iii:nnnnnn, \use_iv:nnnnnn ,
+% \use_v:nnnnnn , \use_vi:nnnnnn ,
+% \use_i:nnnnnnn, \use_ii:nnnnnnn, \use_iii:nnnnnnn, \use_iv:nnnnnnn ,
+% \use_v:nnnnnnn , \use_vi:nnnnnnn , \use_vii:nnnnnnn ,
+% \use_i:nnnnnnnn, \use_ii:nnnnnnnn, \use_iii:nnnnnnnn, \use_iv:nnnnnnnn ,
+% \use_v:nnnnnnnn , \use_vi:nnnnnnnn , \use_vii:nnnnnnnn , \use_viii:nnnnnnnn ,
+% \use_i:nnnnnnnnn, \use_ii:nnnnnnnnn, \use_iii:nnnnnnnnn, \use_iv:nnnnnnnnn ,
+% \use_v:nnnnnnnnn , \use_vi:nnnnnnnnn , \use_vii:nnnnnnnnn , \use_viii:nnnnnnnnn ,
+% \use_ix:nnnnnnnnn
+% }
+% We also need something for picking up arguments from a longer list.
+% \begin{macrocode}
+\cs_gset:Npn \use_i:nnn #1#2#3 {#1}
+\cs_gset:Npn \use_ii:nnn #1#2#3 {#2}
+\cs_gset:Npn \use_iii:nnn #1#2#3 {#3}
+\cs_gset:Npn \use_i:nnnn #1#2#3#4 {#1}
+\cs_gset:Npn \use_ii:nnnn #1#2#3#4 {#2}
+\cs_gset:Npn \use_iii:nnnn #1#2#3#4 {#3}
+\cs_gset:Npn \use_iv:nnnn #1#2#3#4 {#4}
+\cs_gset:Npn \use_i:nnnnn #1#2#3#4#5 {#1}
+\cs_gset:Npn \use_ii:nnnnn #1#2#3#4#5 {#2}
+\cs_gset:Npn \use_iii:nnnnn #1#2#3#4#5 {#3}
+\cs_gset:Npn \use_iv:nnnnn #1#2#3#4#5 {#4}
+\cs_gset:Npn \use_v:nnnnn #1#2#3#4#5 {#5}
+\cs_gset:Npn \use_i:nnnnnn #1#2#3#4#5#6 {#1}
+\cs_gset:Npn \use_ii:nnnnnn #1#2#3#4#5#6 {#2}
+\cs_gset:Npn \use_iii:nnnnnn #1#2#3#4#5#6 {#3}
+\cs_gset:Npn \use_iv:nnnnnn #1#2#3#4#5#6 {#4}
+\cs_gset:Npn \use_v:nnnnnn #1#2#3#4#5#6 {#5}
+\cs_gset:Npn \use_vi:nnnnnn #1#2#3#4#5#6 {#6}
+\cs_gset:Npn \use_i:nnnnnnn #1#2#3#4#5#6#7 {#1}
+\cs_gset:Npn \use_ii:nnnnnnn #1#2#3#4#5#6#7 {#2}
+\cs_gset:Npn \use_iii:nnnnnnn #1#2#3#4#5#6#7 {#3}
+\cs_gset:Npn \use_iv:nnnnnnn #1#2#3#4#5#6#7 {#4}
+\cs_gset:Npn \use_v:nnnnnnn #1#2#3#4#5#6#7 {#5}
+\cs_gset:Npn \use_vi:nnnnnnn #1#2#3#4#5#6#7 {#6}
+\cs_gset:Npn \use_vii:nnnnnnn #1#2#3#4#5#6#7 {#7}
+\cs_gset:Npn \use_i:nnnnnnnn #1#2#3#4#5#6#7#8 {#1}
+\cs_gset:Npn \use_ii:nnnnnnnn #1#2#3#4#5#6#7#8 {#2}
+\cs_gset:Npn \use_iii:nnnnnnnn #1#2#3#4#5#6#7#8 {#3}
+\cs_gset:Npn \use_iv:nnnnnnnn #1#2#3#4#5#6#7#8 {#4}
+\cs_gset:Npn \use_v:nnnnnnnn #1#2#3#4#5#6#7#8 {#5}
+\cs_gset:Npn \use_vi:nnnnnnnn #1#2#3#4#5#6#7#8 {#6}
+\cs_gset:Npn \use_vii:nnnnnnnn #1#2#3#4#5#6#7#8 {#7}
+\cs_gset:Npn \use_viii:nnnnnnnn #1#2#3#4#5#6#7#8 {#8}
+\cs_gset:Npn \use_i:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#1}
+\cs_gset:Npn \use_ii:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#2}
+\cs_gset:Npn \use_iii:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#3}
+\cs_gset:Npn \use_iv:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#4}
+\cs_gset:Npn \use_v:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#5}
+\cs_gset:Npn \use_vi:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#6}
+\cs_gset:Npn \use_vii:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#7}
+\cs_gset:Npn \use_viii:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#8}
+\cs_gset:Npn \use_ix:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#9}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\use_i_ii:nnn}
+% \begin{macrocode}
+\cs_gset:Npn \use_i_ii:nnn #1#2#3 {#1#2}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\use_ii_i:nn}
+% \begin{macrocode}
+\cs_gset:Npn \use_ii_i:nn #1#2 { #2 #1 }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}[EXP]
+% {
+% \use_none_delimit_by_q_nil:w ,
+% \use_none_delimit_by_q_stop:w ,
+% \use_none_delimit_by_q_recursion_stop:w
+% }
+% Functions that gobble everything until they see either \cs{q_nil},
+% \cs{q_stop}, or \cs{q_recursion_stop}, respectively.
+% \begin{macrocode}
+\cs_gset:Npn \use_none_delimit_by_q_nil:w #1 \q_nil { }
+\cs_gset:Npn \use_none_delimit_by_q_stop:w #1 \q_stop { }
+\cs_gset:Npn \use_none_delimit_by_q_recursion_stop:w #1 \q_recursion_stop { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \use_i_delimit_by_q_nil:nw ,
+% \use_i_delimit_by_q_stop:nw ,
+% \use_i_delimit_by_q_recursion_stop:nw
+% }
+% Same as above but execute first argument after gobbling. Very useful
+% when you need to skip the rest of a mapping sequence but want an
+% easy way to control what should be expanded next.
+% \begin{macrocode}
+\cs_gset:Npn \use_i_delimit_by_q_nil:nw #1#2 \q_nil {#1}
+\cs_gset:Npn \use_i_delimit_by_q_stop:nw #1#2 \q_stop {#1}
+\cs_gset:Npn \use_i_delimit_by_q_recursion_stop:nw
+ #1#2 \q_recursion_stop {#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Gobbling tokens from input}
+%
+% \begin{macro}[EXP]
+% {
+% \use_none:n,
+% \use_none:nn,
+% \use_none:nnn,
+% \use_none:nnnn,
+% \use_none:nnnnn,
+% \use_none:nnnnnn,
+% \use_none:nnnnnnn,
+% \use_none:nnnnnnnn,
+% \use_none:nnnnnnnnn
+% }
+% To gobble tokens from the input we use a standard naming convention:
+% the number of tokens gobbled is given by the number of |n|'s
+% following the |:| in the name. Although we could define functions to
+% remove ten arguments or more using separate calls of
+% \cs{use_none:nnnnn}, this is very non-intuitive to the programmer
+% who will assume that expanding such a function once takes care
+% of gobbling all the tokens in one go.
+% \begin{macrocode}
+\cs_gset:Npn \use_none:n #1 { }
+\cs_gset:Npn \use_none:nn #1#2 { }
+\cs_gset:Npn \use_none:nnn #1#2#3 { }
+\cs_gset:Npn \use_none:nnnn #1#2#3#4 { }
+\cs_gset:Npn \use_none:nnnnn #1#2#3#4#5 { }
+\cs_gset:Npn \use_none:nnnnnn #1#2#3#4#5#6 { }
+\cs_gset:Npn \use_none:nnnnnnn #1#2#3#4#5#6#7 { }
+\cs_gset:Npn \use_none:nnnnnnnn #1#2#3#4#5#6#7#8 { }
+\cs_gset:Npn \use_none:nnnnnnnnn #1#2#3#4#5#6#7#8#9 { }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Debugging and patching later definitions}
+%
+% \begin{macrocode}
+%<@@=debug>
+% \end{macrocode}
+%
+% \begin{macro}{\__kernel_if_debug:TF}
+% A more meaningful test of whether debugging is enabled than messing
+% up with guards. We can also more easily change the logic in one
+% place then. This is needed primarily for deprecations.
+% \begin{macrocode}
+\cs_gset_protected:Npn \__kernel_if_debug:TF #1#2 {#2}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\debug_on:n, \debug_off:n}
+% Stubs.
+% \begin{macrocode}
+\cs_gset_protected:Npn \debug_on:n #1
+ {
+ \sys_load_debug:
+ \cs_if_exist:NT \@@_all_on:
+ { \debug_on:n {#1} }
+ }
+\cs_gset_protected:Npn \debug_off:n #1
+ {
+ \sys_load_debug:
+ \cs_if_exist:NT \@@_all_on:
+ { \debug_off:n {#1} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\debug_suspend:, \debug_resume:}
+% \begin{macrocode}
+\cs_gset_protected:Npn \debug_suspend: { }
+\cs_gset_protected:Npn \debug_resume: { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__kernel_deprecation_code:nn}
+% \begin{variable}{\g_@@_deprecation_on_tl, \g_@@_deprecation_off_tl}
+% Make deprecated commands throw errors if the user requests it.
+% This relies on two token lists, filled up in \pkg{l3deprecation}.
+% \begin{macrocode}
+\cs_gset_nopar:Npn \g_@@_deprecation_on_tl { }
+\cs_gset_nopar:Npn \g_@@_deprecation_off_tl { }
+\cs_gset_protected:Npn \__kernel_deprecation_code:nn #1#2
+ {
+ \tl_gput_right:Nn \g_@@_deprecation_on_tl {#1}
+ \tl_gput_right:Nn \g_@@_deprecation_off_tl {#2}
+ }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \subsection{Conditional processing and definitions}
+%
+% \begin{macrocode}
+%<@@=prg>
+% \end{macrocode}
+%
+% Underneath any predicate function (|_p|) or other conditional forms
+% (|TF|, etc.) is a built-in logic saying that it after all of the
+% testing and processing must return the \meta{state} this leaves
+% \TeX{} in. Therefore, a simple user interface could be something like
+% \begin{verbatim}
+% \if_meaning:w #1#2
+% \prg_return_true:
+% \else:
+% \if_meaning:w #1#3
+% \prg_return_true:
+% \else:
+% \prg_return_false:
+% \fi:
+% \fi:
+% \end{verbatim}
+% Usually, a \TeX{} programmer would have to insert a number of
+% \cs{exp_after:wN}s to ensure the state value is returned at exactly
+% the point where the last conditional is finished. However, that
+% obscures the code and forces the \TeX{} programmer to prove that
+% he/she knows the $2^{n}-1$ table. We therefore provide the simpler
+% interface.
+%
+% \begin{macro}[EXP]{\prg_return_true:, \prg_return_false:}
+% The idea here is that \cs{exp:w} expands fully any
+% \cs{else:} and \cs{fi:} that are waiting to be discarded,
+% before reaching the \cs{exp_end:} which leaves an empty expansion.
+% The code can then leave either the first or second argument in the
+% input stream. This means that all of the branching code has to contain
+% at least two tokens: see how the logical tests are actually implemented
+% to see this.
+% \begin{macrocode}
+\cs_gset:Npn \prg_return_true:
+ { \exp_after:wN \use_i:nn \exp:w }
+\cs_gset:Npn \prg_return_false:
+ { \exp_after:wN \use_ii:nn \exp:w}
+% \end{macrocode}
+% An extended state space could be implemented by including a more
+% elaborate function in place of \cs{use_i:nn}/\cs{use_ii:nn}. Provided
+% two arguments are absorbed then the code would work.
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_use_none_delimit_by_q_recursion_stop:w}
+% Private version of \cs{use_none_delimit_by_q_recursion_stop:w}.
+% \begin{macrocode}
+\cs_gset:Npn \@@_use_none_delimit_by_q_recursion_stop:w
+ #1 \q_@@_recursion_stop { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \prg_set_conditional:Npnn ,
+% \prg_gset_conditional:Npnn ,
+% \prg_new_conditional:Npnn ,
+% \prg_set_protected_conditional:Npnn ,
+% \prg_gset_protected_conditional:Npnn,
+% \prg_new_protected_conditional:Npnn
+% }
+% \begin{macro}{\@@_generate_conditional_parm:NNNpnn}
+% The user functions for the types using parameter text from the
+% programmer. The various functions only differ by which function is
+% used for the assignment. For those |Npnn| type functions, we must
+% grab the parameter text, reading everything up to a left brace
+% before continuing. Then split the base function into name and
+% signature, and feed \Arg{name} \Arg{signature} \meta{boolean}
+% \Arg{set~or~new} \Arg{maybe~protected} \Arg{parameters} |{TF,...}|
+% \Arg{code} to the auxiliary function responsible for defining all
+% conditionals.
+% Note that |e| stands for expandable and |p| for protected.
+% \begin{macrocode}
+\cs_gset_protected:Npn \prg_set_conditional:Npnn
+ { \@@_generate_conditional_parm:NNNpnn \cs_set:Npn e }
+\cs_gset_protected:Npn \prg_gset_conditional:Npnn
+ { \@@_generate_conditional_parm:NNNpnn \cs_gset:Npn e }
+\cs_gset_protected:Npn \prg_new_conditional:Npnn
+ { \@@_generate_conditional_parm:NNNpnn \cs_new:Npn e }
+\cs_gset_protected:Npn \prg_set_protected_conditional:Npnn
+ { \@@_generate_conditional_parm:NNNpnn \cs_set_protected:Npn p }
+\cs_gset_protected:Npn \prg_gset_protected_conditional:Npnn
+ { \@@_generate_conditional_parm:NNNpnn \cs_gset_protected:Npn p }
+\cs_gset_protected:Npn \prg_new_protected_conditional:Npnn
+ { \@@_generate_conditional_parm:NNNpnn \cs_new_protected:Npn p }
+\cs_gset_protected:Npn \@@_generate_conditional_parm:NNNpnn #1#2#3#4#
+ {
+ \use:e
+ {
+ \@@_generate_conditional:nnNNNnnn
+ \cs_split_function:N #3
+ }
+ #1 #2 {#4}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \prg_set_conditional:Nnn ,
+% \prg_gset_conditional:Nnn ,
+% \prg_new_conditional:Nnn ,
+% \prg_set_protected_conditional:Nnn ,
+% \prg_gset_protected_conditional:Nnn,
+% \prg_new_protected_conditional:Nnn
+% }
+% \begin{macro}
+% {
+% \@@_generate_conditional_count:NNNnn ,
+% \@@_generate_conditional_count:nnNNNnn
+% }
+% The user functions for the types automatically inserting the correct
+% parameter text based on the signature. The various functions only
+% differ by which function is used for the assignment. Split the base
+% function into name and signature. The second auxiliary generates
+% the parameter text from the number of letters in the signature.
+% Then feed \Arg{name} \Arg{signature} \meta{boolean} \Arg{set~or~new}
+% \Arg{maybe~protected} \Arg{parameters} |{TF,...}| \Arg{code} to the
+% auxiliary function responsible for defining all conditionals. If
+% the \meta{signature} has more than $9$ letters, the definition is
+% aborted since \TeX{} macros have at most $9$ arguments. The
+% erroneous case where the function name contains no colon is captured
+% later.
+% \begin{macrocode}
+\cs_gset_protected:Npn \prg_set_conditional:Nnn
+ { \@@_generate_conditional_count:NNNnn \cs_set:Npn e }
+\cs_gset_protected:Npn \prg_gset_conditional:Nnn
+ { \@@_generate_conditional_count:NNNnn \cs_set:Npn e }
+\cs_gset_protected:Npn \prg_new_conditional:Nnn
+ { \@@_generate_conditional_count:NNNnn \cs_new:Npn e }
+\cs_gset_protected:Npn \prg_set_protected_conditional:Nnn
+ { \@@_generate_conditional_count:NNNnn \cs_set_protected:Npn p }
+\cs_gset_protected:Npn \prg_gset_protected_conditional:Nnn
+ { \@@_generate_conditional_count:NNNnn \cs_gset_protected:Npn p }
+\cs_gset_protected:Npn \prg_new_protected_conditional:Nnn
+ { \@@_generate_conditional_count:NNNnn \cs_new_protected:Npn p }
+\cs_gset_protected:Npn \@@_generate_conditional_count:NNNnn #1#2#3
+ {
+ \use:e
+ {
+ \@@_generate_conditional_count:nnNNNnn
+ \cs_split_function:N #3
+ }
+ #1 #2
+ }
+\cs_gset_protected:Npn \@@_generate_conditional_count:nnNNNnn #1#2#3#4#5
+ {
+ \__kernel_cs_parm_from_arg_count:nnF
+ { \@@_generate_conditional:nnNNNnnn {#1} {#2} #3 #4 #5 }
+ { \tl_count:n {#2} }
+ {
+ \msg_error:nnee { kernel } { bad-number-of-arguments }
+ { \token_to_str:c { #1 : #2 } }
+ { \tl_count:n {#2} }
+ \use_none:nn
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_generate_conditional:nnNNNnnn,
+% \@@_generate_conditional:NNnnnnNw,
+% \@@_generate_conditional_test:w,
+% \@@_generate_conditional_fast:nw,
+% }
+% The workhorse here is going through a list of desired forms, \emph{i.e.},
+% |p|, |TF|, |T| and |F|. The first three arguments come from splitting up
+% the base form of the conditional, which gives the name, signature
+% and a boolean to signal whether or not there was a colon in the
+% name. In the absence of a colon, we throw an error and don't define
+% any conditional. The fourth and fifth arguments build up the
+% defining function. The sixth is the parameters to use (possibly
+% empty), the seventh is the list of forms to define, the eighth is the
+% replacement text which we will augment when defining the forms.
+% The use of \cs{tl_to_str:n} makes the later loop more robust.
+%
+% A large number of our low-level conditionals look like \meta{code}
+% \cs{prg_return_true:} \cs{else:} \cs{prg_return_false:} \cs{fi:} so
+% we optimize this special case by calling
+% \cs{@@_generate_conditional_fast:nw} \Arg{code}. This passes
+% \cs{use_i:nn} instead of \cs{use_i_ii:nnn} to functions such as
+% \cs{@@_generate_p_form:wNNnnnnN}.
+% \begin{macrocode}
+\cs_gset_protected:Npn \@@_generate_conditional:nnNNNnnn #1#2#3#4#5#6#7#8
+ {
+ \if_meaning:w \c_false_bool #3
+ \msg_error:nne { kernel } { missing-colon }
+ { \token_to_str:c {#1} }
+ \exp_after:wN \use_none:nn
+ \fi:
+ \use:e
+ {
+ \exp_not:N \@@_generate_conditional:NNnnnnNw
+ \exp_not:n { #4 #5 {#1} {#2} {#6} }
+ \@@_generate_conditional_test:w
+ #8 \s_@@_mark
+ \@@_generate_conditional_fast:nw
+ \prg_return_true: \else: \prg_return_false: \fi: \s_@@_mark
+ \use_none:n
+ \exp_not:n { {#8} \use_i_ii:nnn }
+ \tl_to_str:n {#7}
+ \exp_not:n { , \q_@@_recursion_tail , \q_@@_recursion_stop }
+ }
+ }
+\cs_gset:Npn \@@_generate_conditional_test:w
+ #1 \prg_return_true: \else: \prg_return_false: \fi: \s_@@_mark #2
+ { #2 {#1} }
+\cs_gset:Npn \@@_generate_conditional_fast:nw #1#2 \exp_not:n #3
+ { \exp_not:n { {#1} \use_i:nn } }
+% \end{macrocode}
+% Looping through the list of desired forms. First are six arguments
+% and seventh is the form. Use the form to call the
+% correct type. If the form does not exist, the \cs{use:c}
+% construction results in \tn{relax}, and the error message is
+% displayed (unless the form is empty, to allow for |{T, , F}|),
+% then \cs{use_none:nnnnnnnn} cleans up. Otherwise, the
+% error message is removed by the variant form.
+% \begin{macrocode}
+\cs_gset_protected:Npn \@@_generate_conditional:NNnnnnNw #1#2#3#4#5#6#7#8 ,
+ {
+ \if_meaning:w \q_@@_recursion_tail #8
+ \exp_after:wN \@@_use_none_delimit_by_q_recursion_stop:w
+ \fi:
+ \use:c { @@_generate_ #8 _form:wNNnnnnN }
+ \tl_if_empty:nF {#8}
+ {
+ \msg_error:nnee
+ { kernel } { conditional-form-unknown }
+ {#8} { \token_to_str:c { #3 : #4 } }
+ }
+ \use_none:nnnnnnnn
+ \s_@@_stop
+ #1 #2 {#3} {#4} {#5} {#6} #7
+ \@@_generate_conditional:NNnnnnNw #1 #2 {#3} {#4} {#5} {#6} #7
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_generate_p_form:wNNnnnnN,
+% \@@_generate_TF_form:wNNnnnnN,
+% \@@_generate_T_form:wNNnnnnN,
+% \@@_generate_F_form:wNNnnnnN
+% }
+% \begin{macro}[EXP]{\@@_p_true:w, \@@_T_true:w, \@@_F_true:w, \@@_TF_true:w}
+% How to generate the various forms. Those functions take the
+% following arguments: 1: junk, 2: \cs{cs_set:Npn} or similar, 3: |p|
+% (for protected conditionals) or |e|, 4: function name, 5: signature,
+% 6: parameter text, 7: replacement (possibly trimmed by
+% \cs{@@_generate_conditional_fast:nw}), 8: \cs{use_i_ii:nnn} or
+% \cs{use_i:nn} (for \enquote{fast} conditionals). Remember that the
+% logic-returning functions expect two arguments to be present after
+% \cs{exp_end:}: notice the construction of the different variants
+% relies on this, and that the |TF| and |F| variants will be slightly
+% faster than the |T| version. The |p| form is only valid for
+% expandable tests, we check for that by making sure that the second
+% argument is empty. For \enquote{fast} conditionals, |#7| has an
+% extra \cs[no-index]{if_\ldots{}}. To optimize a bit further we
+% don't use \cs{exp_after:wN} \cs{use_ii:nnn} and similar but instead use
+% \cs{@@_TF_true:w} and similar to swap out the macro after \cs{fi:}. It would
+% be a tiny bit faster if we directly grabbed the |T| and |F| arguments there,
+% but if those are actually missing, the recovery from the runaway argument
+% would not insert \cs{fi:} back, messing up nesting of conditionals.
+% \begin{macrocode}
+\cs_gset_protected:Npn \@@_generate_p_form:wNNnnnnN
+ #1 \s_@@_stop #2#3#4#5#6#7#8
+ {
+ \if_meaning:w e #3
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ {
+ #8
+ { \exp_args:Nc #2 { #4 _p: #5 } #6 }
+ { { #7 \exp_end: \c_true_bool \c_false_bool } }
+ { #7 \@@_p_true:w \fi: \c_false_bool }
+ }
+ {
+ \msg_error:nne { kernel } { protected-predicate }
+ { \token_to_str:c { #4 _p: #5 } }
+ }
+ }
+\cs_gset_protected:Npn \@@_generate_T_form:wNNnnnnN
+ #1 \s_@@_stop #2#3#4#5#6#7#8
+ {
+ #8
+ { \exp_args:Nc #2 { #4 : #5 T } #6 }
+ { { #7 \exp_end: \use:n \use_none:n } }
+ { #7 \@@_T_true:w \fi: \use_none:n }
+ }
+\cs_gset_protected:Npn \@@_generate_F_form:wNNnnnnN
+ #1 \s_@@_stop #2#3#4#5#6#7#8
+ {
+ #8
+ { \exp_args:Nc #2 { #4 : #5 F } #6 }
+ { { #7 \exp_end: { } } }
+ { #7 \@@_F_true:w \fi: \use:n }
+ }
+\cs_gset_protected:Npn \@@_generate_TF_form:wNNnnnnN
+ #1 \s_@@_stop #2#3#4#5#6#7#8
+ {
+ #8
+ { \exp_args:Nc #2 { #4 : #5 TF } #6 }
+ { { #7 \exp_end: } }
+ { #7 \@@_TF_true:w \fi: \use_ii:nn }
+ }
+\cs_gset:Npn \@@_p_true:w \fi: \c_false_bool { \fi: \c_true_bool }
+\cs_gset:Npn \@@_T_true:w \fi: \use_none:n { \fi: \use:n }
+\cs_gset:Npn \@@_F_true:w \fi: \use:n { \fi: \use_none:n }
+\cs_gset:Npn \@@_TF_true:w \fi: \use_ii:nn { \fi: \use_i:nn }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \prg_set_eq_conditional:NNn ,
+% \prg_gset_eq_conditional:NNn,
+% \prg_new_eq_conditional:NNn
+% }
+% \begin{macro}{\@@_set_eq_conditional:NNNn}
+% The setting-equal functions. Split both functions and feed
+% \Arg{name_1} \Arg{signature_1} \meta{boolean_1}
+% \Arg{name_2} \Arg{signature_2} \meta{boolean_2}
+% \meta{copying~function} \meta{conditions} |,| \cs{q_@@_recursion_tail}
+% |,| \cs{q_@@_recursion_stop}
+% to a first auxiliary.
+% \begin{macrocode}
+\cs_gset_protected:Npn \prg_set_eq_conditional:NNn
+ { \@@_set_eq_conditional:NNNn \cs_set_eq:cc }
+\cs_gset_protected:Npn \prg_gset_eq_conditional:NNn
+ { \@@_set_eq_conditional:NNNn \cs_gset_eq:cc }
+\cs_gset_protected:Npn \prg_new_eq_conditional:NNn
+ { \@@_set_eq_conditional:NNNn \cs_new_eq:cc }
+\cs_gset_protected:Npn \@@_set_eq_conditional:NNNn #1#2#3#4
+ {
+ \use:e
+ {
+ \exp_not:N \@@_set_eq_conditional:nnNnnNNw
+ \cs_split_function:N #2
+ \cs_split_function:N #3
+ \exp_not:N #1
+ \tl_to_str:n {#4}
+ \exp_not:n { , \q_@@_recursion_tail , \q_@@_recursion_stop }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_set_eq_conditional:nnNnnNNw ,
+% \@@_set_eq_conditional_loop:nnnnNw
+% }
+% \begin{macro}[EXP]
+% {
+% \@@_set_eq_conditional_p_form:nnn ,
+% \@@_set_eq_conditional_TF_form:nnn ,
+% \@@_set_eq_conditional_T_form:nnn ,
+% \@@_set_eq_conditional_F_form:nnn ,
+% }
+% Split the function to be defined, and setup a manual clist loop over
+% argument |#6| of the first auxiliary. The second auxiliary receives
+% twice three arguments coming from splitting the function to be
+% defined and the function to copy. Make sure that both functions
+% contained a colon, otherwise we don't know how to build
+% conditionals, hence abort. Call the looping macro, with arguments
+% \Arg{name_1} \Arg{signature_1} \Arg{name_2} \Arg{signature_2}
+% \meta{copying~function} and followed by the comma list. At each
+% step in the loop, make sure that the conditional form we copy is
+% defined, and copy it, otherwise abort.
+% \begin{macrocode}
+\cs_gset_protected:Npn \@@_set_eq_conditional:nnNnnNNw #1#2#3#4#5#6
+ {
+ \if_meaning:w \c_false_bool #3
+ \msg_error:nne { kernel } { missing-colon }
+ { \token_to_str:c {#1} }
+ \exp_after:wN \@@_use_none_delimit_by_q_recursion_stop:w
+ \fi:
+ \if_meaning:w \c_false_bool #6
+ \msg_error:nne { kernel } { missing-colon }
+ { \token_to_str:c {#4} }
+ \exp_after:wN \@@_use_none_delimit_by_q_recursion_stop:w
+ \fi:
+ \@@_set_eq_conditional_loop:nnnnNw {#1} {#2} {#4} {#5}
+ }
+\cs_gset_protected:Npn \@@_set_eq_conditional_loop:nnnnNw #1#2#3#4#5#6 ,
+ {
+ \if_meaning:w \q_@@_recursion_tail #6
+ \exp_after:wN \@@_use_none_delimit_by_q_recursion_stop:w
+ \fi:
+ \use:c { @@_set_eq_conditional_ #6 _form:wNnnnn }
+ \tl_if_empty:nF {#6}
+ {
+ \msg_error:nnee
+ { kernel } { conditional-form-unknown }
+ {#6} { \token_to_str:c { #1 : #2 } }
+ }
+ \use_none:nnnnnn
+ \s_@@_stop
+ #5 {#1} {#2} {#3} {#4}
+ \@@_set_eq_conditional_loop:nnnnNw {#1} {#2} {#3} {#4} #5
+ }
+\cs_gset:Npn \@@_set_eq_conditional_p_form:wNnnnn #1 \s_@@_stop #2#3#4#5#6
+ { #2 { #3 _p : #4 } { #5 _p : #6 } }
+\cs_gset:Npn \@@_set_eq_conditional_TF_form:wNnnnn #1 \s_@@_stop #2#3#4#5#6
+ { #2 { #3 : #4 TF } { #5 : #6 TF } }
+\cs_gset:Npn \@@_set_eq_conditional_T_form:wNnnnn #1 \s_@@_stop #2#3#4#5#6
+ { #2 { #3 : #4 T } { #5 : #6 T } }
+\cs_gset:Npn \@@_set_eq_conditional_F_form:wNnnnn #1 \s_@@_stop #2#3#4#5#6
+ { #2 { #3 : #4 F } { #5 : #6 F } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% All that is left is to define the canonical boolean true and false.
+% I think Michael originated the idea of expandable boolean tests. At
+% first these were supposed to expand into either \texttt{TT} or
+% \texttt{TF} to be tested using \cs{if:w} but this was later changed to
+% |00| and |01|, so they could be used in logical
+% operations. Later again they were changed to being numerical
+% constants with values of $1$ for true and $0$ for false. We need
+% this from the get-go.
+%
+% \begin{variable}{\c_true_bool, \c_false_bool}
+% Here are the canonical boolean values.
+% \begin{macrocode}
+\tex_global:D \tex_chardef:D \c_true_bool = 1 ~
+\tex_global:D \tex_chardef:D \c_false_bool = 0 ~
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Dissecting a control sequence}
+%
+% \begin{macrocode}
+%<@@=cs>
+% \end{macrocode}
+%
+% \begin{function}[EXP]{\@@_count_signature:N}
+% \begin{syntax}
+% \cs{@@_count_signature:N} \meta{function}
+% \end{syntax}
+% Splits the \meta{function} into the \meta{name} (\emph{i.e.}~the part
+% before the colon) and the \meta{signature} (\emph{i.e.}~after the colon).
+% The \meta{number} of tokens in the \meta{signature} is then left in
+% the input stream. If there was no \meta{signature} then the result is
+% the marker value $-1$.
+% \end{function}
+%
+% \begin{function}{\@@_tmp:w}
+% Function used for various short-term usages, for instance defining
+% functions whose definition involves tokens which are hard to insert
+% normally (spaces, characters with category other).
+% \end{function}
+%
+% \begin{macro}[EXP]{\cs_to_str:N}
+% \begin{macro}[EXP]{\@@_to_str:N, \@@_to_str:w}
+% This converts a control sequence into the character string of its
+% name, removing the leading escape character. This turns out to be
+% a non-trivial matter as there a different cases:
+% \begin{itemize}
+% \item The usual case of a printable escape character;
+% \item the case of a non-printable escape characters, e.g., when
+% the value of the \tn{escapechar} is negative;
+% \item when the escape character is a space.
+% \end{itemize}
+% One approach to solve this is to test how many tokens result from
+% |\token_to_str:N \a|. If there are two tokens, then the escape
+% character is printable, while if it is non-printable then only
+% one is present.
+%
+% However, there is an additional complication: the control
+% sequence itself may start with a space. Clearly that should \emph{not} be
+% lost in the process of converting to a string. So the approach adopted is
+% a little more intricate still. When the escape character is printable,
+% \verb*|\token_to_str:N \ | yields the escape character itself and a space.
+% The character codes are different, thus the \cs{if:w} test is false,
+% and \TeX{} reads \cs{@@_to_str:N} after turning the following
+% control sequence into a string; this auxiliary removes the escape
+% character, and stops the expansion of the initial \cs{tex_romannumeral:D}.
+% The second case is that the escape character is not printable. Then
+% the \cs{if:w} test is unfinished after reading a the space from
+% \verb*|\token_to_str:N \ |, and the auxiliary \cs{@@_to_str:w}
+% is expanded, feeding |-| as a second character for the test;
+% the test is false, and \TeX{} skips to \cs{fi:}, then performs
+% \cs{token_to_str:N}, and stops the \cs{tex_romannumeral:D} with \cs{c_zero_int}.
+% The last case is that the escape character is itself a space. In this
+% case, the \cs{if:w} test is true, and the auxiliary \cs{@@_to_str:w}
+% comes into play, inserting |-\int_value:w|, which expands \cs{c_zero_int}
+% to the character |0|. The initial \cs{tex_romannumeral:D} then sees
+% |0|, which is not a terminated number, followed by the escape character,
+% a space, which is removed, terminating the expansion of
+% \cs{tex_romannumeral:D}.
+% In all three cases, \cs{cs_to_str:N} takes two expansion steps
+% to be fully expanded.
+% \begin{macrocode}
+\cs_gset:Npn \cs_to_str:N
+ {
+% \end{macrocode}
+% We implement the expansion scheme using \cs{tex_romannumeral:D}
+% terminating it with \cs{c_zero_int} rather than using \cs{exp:w} and
+% \cs{exp_end:} as we normally do. The reason is that the code
+% heavily depends on terminating the expansion with \cs{c_zero_int} so
+% we make this dependency explicit.
+% \begin{macrocode}
+ \tex_romannumeral:D
+ \if:w \token_to_str:N \ \@@_to_str:w \fi:
+ \exp_after:wN \@@_to_str:N \token_to_str:N
+ }
+\cs_gset:Npn \@@_to_str:N #1 { \c_zero_int }
+\cs_gset:Npn \@@_to_str:w #1 \@@_to_str:N
+ { - \int_value:w \fi: \exp_after:wN \c_zero_int }
+% \end{macrocode}
+% If speed is a concern we could use \tn{csstring} in \LuaTeX{}. For
+% the empty csname that primitive gives an empty result while the
+% current \cs{cs_to_str:N} gives incorrect results in all engines
+% (this is impossible to fix without huge performance hit).
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\cs_split_function:N}
+% \begin{macro}[EXP]
+% {\@@_split_function_auxi:w, \@@_split_function_auxii:w}
+% This function takes a function name and splits it into name with
+% the escape char removed and argument specification. In addition to
+% this, a third argument, a boolean \meta{true} or \meta{false} is
+% returned with \meta{true} for when there is a colon in the function
+% and \meta{false} if there is not.
+%
+% First ensure that we actually get a properly evaluated string by
+% expanding \cs{cs_to_str:N} twice. If the function contained a
+% colon, the auxiliary takes as |#1| the function name, delimited by
+% the first colon, then the signature |#2|, delimited by \cs{s_@@_mark},
+% then \cs{c_true_bool} as |#3|, and |#4| cleans up until \cs{s_@@_stop}.
+% Otherwise, the |#1| contains the function name and \cs{s_@@_mark}
+% \cs{c_true_bool}, |#2| is empty, |#3| is \cs{c_false_bool}, and |#4|
+% cleans up. The second
+% auxiliary trims the trailing \cs{s_@@_mark} from the function name if
+% present (that is, if the original function had no colon).
+% \begin{macrocode}
+\cs_gset_protected:Npn \@@_tmp:w #1
+ {
+ \cs_gset:Npn \cs_split_function:N ##1
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \@@_split_function_auxi:w
+ \cs_to_str:N ##1 \s_@@_mark \c_true_bool
+ #1 \s_@@_mark \c_false_bool \s_@@_stop
+ }
+ \cs_gset:Npn \@@_split_function_auxi:w
+ ##1 #1 ##2 \s_@@_mark ##3##4 \s_@@_stop
+ { \@@_split_function_auxii:w ##1 \s_@@_mark \s_@@_stop {##2} ##3 }
+ \cs_gset:Npn \@@_split_function_auxii:w ##1 \s_@@_mark ##2 \s_@@_stop
+ { {##1} }
+ }
+\exp_after:wN \@@_tmp:w \token_to_str:N :
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Exist or free}
+%
+% A control sequence is said to \emph{exist} (to be used) if has an entry in
+% the hash table and its meaning is different from the primitive
+% \tn{relax} token. A control sequence is said to be \emph{free}
+% (to be defined) if it does not already exist.
+%
+% \begin{macro}[pTF, EXP]{\cs_if_exist:N, \cs_if_exist:c}
+% \begin{macro}{\@@_if_exist_c_aux:,\@@_if_exist_c_aux:w}
+% Two versions for checking existence. For the |N| form we firstly
+% check for \cs{scan_stop:} and then if it is in the hash
+% table. There is no problem when inputting something like \cs{else:}
+% or \cs{fi:} as \TeX{} will only ever skip input in case the token
+% tested against is \cs{scan_stop:}.
+%
+% In both the |N| and |c| form we use the way \cs{prg_set_conditional:Npnn}
+% optimizes the conditionals to negate the tests using \cs{else:} (the
+% \cs{else:} in the top level functions will be removed by the optimization,
+% and this usage of \cs{else:} will be fine).
+% \begin{macrocode}
+\prg_gset_conditional:Npnn \cs_if_exist:N #1 { p , T , F , TF }
+ {
+ \if_meaning:w #1 \scan_stop:
+ \use_i:nnnn
+ \else:
+ \fi:
+ \if_cs_exist:N #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% For the |c| form we firstly check if it is in the hash table and
+% then for \cs{scan_stop:} so that we do not add it to the hash table
+% unless it was already there. Here we have to be careful as the text
+% to be skipped if the first test is false may contain tokens that
+% disturb the scanner. Therefore, we ensure that the second test is
+% performed after the first one has concluded completely.
+% \begin{macrocode}
+\cs_if_exist:NTF \tex_lastnamedcs:D
+ {
+ \prg_gset_conditional:Npnn \cs_if_exist:c #1 { p , T , F , TF }
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \@@_if_exist_c_aux:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ \cs_gset:Npn \@@_if_exist_c_aux:
+ { \fi: \exp_after:wN \if_meaning:w \tex_lastnamedcs:D \scan_stop: \else: }
+ }
+ {
+ \prg_gset_conditional:Npnn \cs_if_exist:c #1 { p , T , F , TF }
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \@@_if_exist_c_aux:w
+ \fi:
+ \use_none:n {#1}
+ \if_false:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ \cs_gset:Npn \@@_if_exist_c_aux:w \fi: \use_none:n #1 \if_false:
+ { \fi: \exp_after:wN \if_meaning:w \cs:w #1 \cs_end: \scan_stop: \else: }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF, EXP]{\cs_if_free:N, \cs_if_free:c}
+% The logical reversal of the above.
+% \begin{macrocode}
+\prg_gset_conditional:Npnn \cs_if_free:N #1 { p , T , F , TF }
+ {
+ \if_cs_exist:N #1
+ \else:
+ \use_none:nnnn
+ \fi:
+ \if_meaning:w #1 \scan_stop:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_if_exist:NTF \tex_lastnamedcs:D
+ {
+ \prg_gset_conditional:Npnn \cs_if_free:c #1 { p , T , F , TF }
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \@@_if_free_c_aux:w
+ \fi:
+ \if_true:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ \cs_gset:Npn \@@_if_free_c_aux:w \fi: \if_true:
+ { \fi: \exp_after:wN \if_meaning:w \tex_lastnamedcs:D \scan_stop: }
+ }
+ {
+ \prg_gset_conditional:Npnn \cs_if_free:c #1 { p , T , F , TF }
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \@@_if_free_c_aux:w
+ \fi:
+ \use_none:n {#1}
+ \if_true:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ \cs_gset:Npn \@@_if_free_c_aux:w \fi: \use_none:n #1 \if_true:
+ { \fi: \exp_after:wN \if_meaning:w \cs:w #1 \cs_end: \scan_stop: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP,noTF,added=2011-10-10]
+% {\cs_if_exist_use:N, \cs_if_exist_use:c}
+% \begin{macro}{\@@_if_exist_use_aux:w, \@@_if_exist_use_aux:Nnn}
+% The \cs[index=cs_if_exist_use:N]{cs_if_exist_use:\ldots{}}
+% functions cannot be implemented
+% as conditionals because the true branch must leave both the control
+% sequence itself and the true code in the input stream.
+% For the \texttt{c} variants, we are careful not to put the control
+% sequence in the hash table if it does not exist.
+% If available we use the \tn{lastnamedcs} primitive.
+% \begin{macrocode}
+\cs_gset:Npn \cs_if_exist_use:NTF #1#2
+ { \cs_if_exist:NTF #1 { #1 #2 } }
+\cs_gset:Npn \cs_if_exist_use:NF #1
+ { \cs_if_exist:NTF #1 #1 }
+\cs_gset:Npn \cs_if_exist_use:NT #1 #2
+ { \cs_if_exist:NT #1 { #1 #2 } }
+\cs_gset:Npn \cs_if_exist_use:N #1
+ { \cs_if_exist:NT #1 #1 }
+\cs_if_exist:NTF \tex_lastnamedcs:D
+ {
+ \cs_gset:Npn \cs_if_exist_use:cTF #1
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \@@_if_exist_use_aux:w
+ \fi:
+ \use_ii:nn
+ }
+ \cs_gset:Npn \@@_if_exist_use_aux:w \fi: \use_ii:nn
+ { \fi: \exp_after:wN \@@_if_exist_use_aux:Nnn \tex_lastnamedcs:D }
+ }
+ {
+ \cs_gset:Npn \cs_if_exist_use:cTF #1
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \@@_if_exist_use_aux:w
+ \fi:
+ \use_iii:nnn {#1}
+ }
+ \cs_gset:Npn \@@_if_exist_use_aux:w \fi: \use_iii:nnn #1
+ { \fi: \exp_after:wN \@@_if_exist_use_aux:Nnn \cs:w #1 \cs_end: }
+ }
+\cs_gset:Npn \@@_if_exist_use_aux:Nnn #1#2
+ {
+ \if_meaning:w #1 \scan_stop:
+ \exp_after:wN \use_iii:nnn
+ \fi:
+ \use_i:nn { #1 #2 }
+ }
+\cs_gset:Npn \cs_if_exist_use:cF #1
+ { \cs_if_exist_use:cTF {#1} {} }
+\cs_gset:Npn \cs_if_exist_use:cT #1#2
+ { \cs_if_exist_use:cTF {#1} {#2} {} }
+\cs_gset:Npn \cs_if_exist_use:c #1
+ { \cs_if_exist_use:cTF {#1} {} {} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Preliminaries for new functions}
+%
+% We provide two kinds of functions that can be used to define
+% control sequences. On the one hand we have functions that check
+% if their argument doesn't already exist, they are called
+% |\..._new|. The second type of defining functions doesn't check
+% if the argument is already defined.
+%
+% Before we can define them, we need some auxiliary macros that allow
+% us to generate error messages. The next few definitions here are
+% only temporary, they will be redefined later on.
+%
+% \begin{macro}[documented-as = \msg_error:nnnn]
+% {\msg_error:nnee, \msg_error:nne, \msg_error:nn}
+% If an internal error occurs before \LaTeX3 has loaded \pkg{l3msg} then
+% the code should issue a usable if terse error message and halt. This
+% can only happen if a coding error is made by the team, so this is
+% a reasonable response. Setting the \tn{newlinechar} is needed, to
+% turn |^^J| into a proper line break in plain \TeX{}.
+% \begin{macrocode}
+\cs_gset_protected:Npn \msg_error:nnee #1#2#3#4
+ {
+ \tex_newlinechar:D = `\^^J \scan_stop:
+ \tex_errmessage:D
+ {
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!~! ^^J
+ Argh,~internal~LaTeX3~error! ^^J ^^J
+ Module ~ #1 , ~ message~name~"#2": ^^J
+ Arguments~'#3'~and~'#4' ^^J ^^J
+ This~is~one~for~The~LaTeX3~Project:~bailing~out
+ }
+ \tex_end:D
+ }
+\cs_gset_protected:Npn \msg_error:nne #1#2#3
+ { \msg_error:nnee {#1} {#2} {#3} { } }
+\cs_gset_protected:Npn \msg_error:nn #1#2
+ { \msg_error:nnee {#1} {#2} { } { } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\msg_line_context:}
+% Another one from \pkg{l3msg} which will be altered later.
+% \begin{macrocode}
+\cs_gset:Npn \msg_line_context:
+ { on~line~ \tex_the:D \tex_inputlineno:D }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[documented-as=\iow_log:n]{\iow_log:e, \iow_term:e}
+% We define a routine to write only to the log file. And a
+% similar one for writing to both the log file and the terminal.
+% These will be redefined later by \pkg{l3file}.
+% \begin{macrocode}
+\cs_gset_protected:Npn \iow_log:e
+ { \tex_immediate:D \tex_write:D -1 }
+\cs_gset_protected:Npn \iow_term:e
+ { \tex_immediate:D \tex_write:D 16 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__kernel_chk_if_free_cs:N, \__kernel_chk_if_free_cs:c}
+% This command is called by \cs{cs_new_nopar:Npn} and \cs{cs_new_eq:NN}
+% \emph{etc.}\
+% to make sure that the argument sequence is not already in use. If
+% it is, an error is signalled. It checks if \meta{csname} is
+% undefined or \cs{scan_stop:}. Otherwise an error message is
+% issued. We have to make sure we don't put the argument into the
+% conditional processing since it may be an |\if...| type function!
+% \begin{macrocode}
+\cs_gset_protected:Npn \__kernel_chk_if_free_cs:N #1
+ {
+ \cs_if_free:NF #1
+ {
+ \msg_error:nnee { kernel } { command-already-defined }
+ { \token_to_str:N #1 } { \token_to_meaning:N #1 }
+ }
+ }
+\cs_gset_protected:Npn \__kernel_chk_if_free_cs:c
+ { \exp_args:Nc \__kernel_chk_if_free_cs:N }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Defining new functions}
+%
+% \begin{macrocode}
+%<@@=cs>
+% \end{macrocode}
+%
+% \begin{macro}
+% {
+% \cs_new_nopar:Npn , \cs_new_nopar:Npe , \cs_new_nopar:Npx ,
+% \cs_new:Npn , \cs_new:Npe , \cs_new:Npx ,
+% \cs_new_protected_nopar:Npn , \cs_new_protected_nopar:Npe , \cs_new_protected_nopar:Npx ,
+% \cs_new_protected:Npn , \cs_new_protected:Npe , \cs_new_protected:Npx
+% }
+% \begin{macro}{\@@_tmp:w}
+% Function which check that the control sequence is free before
+% defining it.
+% \begin{macrocode}
+\cs_set:Npn \@@_tmp:w #1#2
+ {
+ \cs_gset_protected:Npn #1 ##1
+ {
+ \__kernel_chk_if_free_cs:N ##1
+ #2 ##1
+ }
+ }
+\@@_tmp:w \cs_new_nopar:Npn \cs_gset_nopar:Npn
+\@@_tmp:w \cs_new_nopar:Npe \cs_gset_nopar:Npe
+\@@_tmp:w \cs_new_nopar:Npx \cs_gset_nopar:Npx
+\@@_tmp:w \cs_new:Npn \cs_gset:Npn
+\@@_tmp:w \cs_new:Npe \cs_gset:Npe
+\@@_tmp:w \cs_new:Npx \cs_gset:Npx
+\@@_tmp:w \cs_new_protected_nopar:Npn \cs_gset_protected_nopar:Npn
+\@@_tmp:w \cs_new_protected_nopar:Npe \cs_gset_protected_nopar:Npe
+\@@_tmp:w \cs_new_protected_nopar:Npx \cs_gset_protected_nopar:Npx
+\@@_tmp:w \cs_new_protected:Npn \cs_gset_protected:Npn
+\@@_tmp:w \cs_new_protected:Npe \cs_gset_protected:Npe
+\@@_tmp:w \cs_new_protected:Npx \cs_gset_protected:Npx
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[documented-as=\cs_set_nopar:Npn]
+% {
+% \cs_set_nopar:cpn , \cs_set_nopar:cpe , \cs_set_nopar:cpx ,
+% \cs_gset_nopar:cpn , \cs_gset_nopar:cpe , \cs_gset_nopar:cpx ,
+% \cs_new_nopar:cpn , \cs_new_nopar:cpe , \cs_new_nopar:cpx
+% }
+% Like \cs{cs_set_nopar:Npn} and \cs{cs_new_nopar:Npn}, except that the
+% first argument consists of the sequence of characters that should
+% be used to form the name of the desired control sequence (the |c|
+% stands for csname argument, see the expansion module). Global
+% versions are also provided.
+%
+% \cs{cs_set_nopar:cpn}\meta{string}\meta{rep-text} turns \meta{string}
+% into a csname and then assigns \meta{rep-text} to it by using
+% \cs{cs_set_nopar:Npn}. This means that there might be a parameter
+% string between the two arguments.
+% \begin{macrocode}
+\cs_set:Npn \@@_tmp:w #1#2
+ { \cs_new_protected_nopar:Npn #1 { \exp_args:Nc #2 } }
+\@@_tmp:w \cs_set_nopar:cpn \cs_set_nopar:Npn
+\@@_tmp:w \cs_set_nopar:cpe \cs_set_nopar:Npe
+\@@_tmp:w \cs_set_nopar:cpx \cs_set_nopar:Npx
+\@@_tmp:w \cs_gset_nopar:cpn \cs_gset_nopar:Npn
+\@@_tmp:w \cs_gset_nopar:cpe \cs_gset_nopar:Npe
+\@@_tmp:w \cs_gset_nopar:cpx \cs_gset_nopar:Npx
+\@@_tmp:w \cs_new_nopar:cpn \cs_new_nopar:Npn
+\@@_tmp:w \cs_new_nopar:cpe \cs_new_nopar:Npe
+\@@_tmp:w \cs_new_nopar:cpx \cs_new_nopar:Npx
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[documented-as=\cs_set:Npn]
+% {
+% \cs_set:cpn , \cs_set:cpe , \cs_set:cpx ,
+% \cs_gset:cpn , \cs_gset:cpe , \cs_gset:cpx ,
+% \cs_new:cpn , \cs_new:cpe , \cs_new:cpx
+% }
+% Variants of the \cs{cs_set:Npn} versions which make a csname out
+% of the first arguments. We may also do this globally.
+% \begin{macrocode}
+\@@_tmp:w \cs_set:cpn \cs_set:Npn
+\@@_tmp:w \cs_set:cpe \cs_set:Npe
+\@@_tmp:w \cs_set:cpx \cs_set:Npx
+\@@_tmp:w \cs_gset:cpn \cs_gset:Npn
+\@@_tmp:w \cs_gset:cpe \cs_gset:Npe
+\@@_tmp:w \cs_gset:cpx \cs_gset:Npx
+\@@_tmp:w \cs_new:cpn \cs_new:Npn
+\@@_tmp:w \cs_new:cpe \cs_new:Npe
+\@@_tmp:w \cs_new:cpx \cs_new:Npx
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[documented-as=\cs_set_protected_nopar:Npn]
+% {
+% \cs_set_protected_nopar:cpn , \cs_set_protected_nopar:cpe , \cs_set_protected_nopar:cpx ,
+% \cs_gset_protected_nopar:cpn , \cs_gset_protected_nopar:cpe , \cs_gset_protected_nopar:cpx ,
+% \cs_new_protected_nopar:cpn , \cs_new_protected_nopar:cpe , \cs_new_protected_nopar:cpx
+% }
+% Variants of the \cs{cs_set_protected_nopar:Npn} versions which make
+% a csname out of the first arguments. We may also do this globally.
+% \begin{macrocode}
+\@@_tmp:w \cs_set_protected_nopar:cpn \cs_set_protected_nopar:Npn
+\@@_tmp:w \cs_set_protected_nopar:cpe \cs_set_protected_nopar:Npe
+\@@_tmp:w \cs_set_protected_nopar:cpx \cs_set_protected_nopar:Npx
+\@@_tmp:w \cs_gset_protected_nopar:cpn \cs_gset_protected_nopar:Npn
+\@@_tmp:w \cs_gset_protected_nopar:cpe \cs_gset_protected_nopar:Npe
+\@@_tmp:w \cs_gset_protected_nopar:cpx \cs_gset_protected_nopar:Npx
+\@@_tmp:w \cs_new_protected_nopar:cpn \cs_new_protected_nopar:Npn
+\@@_tmp:w \cs_new_protected_nopar:cpe \cs_new_protected_nopar:Npe
+\@@_tmp:w \cs_new_protected_nopar:cpx \cs_new_protected_nopar:Npx
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[documented-as=\cs_set_protected:Npn]
+% {
+% \cs_set_protected:cpn , \cs_set_protected:cpe , \cs_set_protected:cpx ,
+% \cs_gset_protected:cpn , \cs_gset_protected:cpe , \cs_gset_protected:cpx ,
+% \cs_new_protected:cpn , \cs_new_protected:cpe , \cs_new_protected:cpx
+% }
+% Variants of the \cs{cs_set_protected:Npn} versions which make a csname
+% out of the first arguments. We may also do this globally.
+% \begin{macrocode}
+\@@_tmp:w \cs_set_protected:cpn \cs_set_protected:Npn
+\@@_tmp:w \cs_set_protected:cpe \cs_set_protected:Npe
+\@@_tmp:w \cs_set_protected:cpx \cs_set_protected:Npx
+\@@_tmp:w \cs_gset_protected:cpn \cs_gset_protected:Npn
+\@@_tmp:w \cs_gset_protected:cpe \cs_gset_protected:Npe
+\@@_tmp:w \cs_gset_protected:cpx \cs_gset_protected:Npx
+\@@_tmp:w \cs_new_protected:cpn \cs_new_protected:Npn
+\@@_tmp:w \cs_new_protected:cpe \cs_new_protected:Npe
+\@@_tmp:w \cs_new_protected:cpx \cs_new_protected:Npx
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Copying definitions}
+%
+% \begin{macro}
+% {
+% \cs_set_eq:NN , \cs_set_eq:cN , \cs_set_eq:Nc , \cs_set_eq:cc ,
+% \cs_gset_eq:NN , \cs_gset_eq:cN , \cs_gset_eq:Nc , \cs_gset_eq:cc ,
+% \cs_new_eq:NN , \cs_new_eq:cN , \cs_new_eq:Nc , \cs_new_eq:cc
+% }
+% These macros allow us to copy the definition of a control sequence
+% to another control sequence.
+%
+% The |=| sign allows us to define funny char tokens like |=| itself
+% or \verb*| | with this function. For the definition of
+% |\c_space_char{~}| to work we need the |~| after the |=|.
+%
+% \cs{cs_set_eq:NN} is long to avoid problems with a literal argument
+% of \cs{par}. While \cs{cs_new_eq:NN} will probably never be correct
+% with a first argument of \cs{par}, define it long in order to throw
+% an \enquote{already defined} error rather than
+% \enquote{runaway argument}.
+% \begin{macrocode}
+\cs_new_protected:Npn \cs_set_eq:NN #1 { \tex_let:D #1 =~ }
+\cs_new_protected:Npn \cs_set_eq:cN { \exp_args:Nc \cs_set_eq:NN }
+\cs_new_protected:Npn \cs_set_eq:Nc { \exp_args:NNc \cs_set_eq:NN }
+\cs_new_protected:Npn \cs_set_eq:cc { \exp_args:Ncc \cs_set_eq:NN }
+\cs_new_protected:Npn \cs_gset_eq:NN { \tex_global:D \cs_set_eq:NN }
+\cs_new_protected:Npn \cs_gset_eq:Nc { \exp_args:NNc \cs_gset_eq:NN }
+\cs_new_protected:Npn \cs_gset_eq:cN { \exp_args:Nc \cs_gset_eq:NN }
+\cs_new_protected:Npn \cs_gset_eq:cc { \exp_args:Ncc \cs_gset_eq:NN }
+\cs_new_protected:Npn \cs_new_eq:NN #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \tex_global:D \cs_set_eq:NN #1
+ }
+\cs_new_protected:Npn \cs_new_eq:cN { \exp_args:Nc \cs_new_eq:NN }
+\cs_new_protected:Npn \cs_new_eq:Nc { \exp_args:NNc \cs_new_eq:NN }
+\cs_new_protected:Npn \cs_new_eq:cc { \exp_args:Ncc \cs_new_eq:NN }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Undefining functions}
+%
+% \begin{macro}{\cs_undefine:N, \cs_undefine:c}
+% The following function is used to free the main memory from the
+% definition of some function that isn't in use any longer.
+% The \texttt{c} variant is careful not to add the control sequence
+% to the hash table if it isn't there yet, and it also avoids nesting
+% \TeX{} conditionals in case |#1| is unbalanced in this matter.
+% We optimize the case where the command exists by reducing as much as
+% possible the tokens in the conditional.
+% \begin{macrocode}
+\cs_new_protected:Npn \cs_undefine:N #1
+ { \cs_gset_eq:NN #1 \tex_undefined:D }
+\cs_new_protected:Npn \cs_undefine:c #1
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \else:
+ \use_i:nnnn
+ \fi:
+ \exp_args:Nc \cs_undefine:N {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Generating parameter text from argument count}
+%
+% \begin{macrocode}
+%<@@=cs>
+% \end{macrocode}
+%
+% \begin{macro}{\__kernel_cs_parm_from_arg_count:nnF}
+% \begin{macro}{\@@_parm_from_arg_count_test:nnF}
+% \LaTeX3 provides shorthands to define control sequences and
+% conditionals with a simple parameter text, derived directly from the
+% signature, or more generally from knowing the number of arguments,
+% between~$0$ and~$9$. This function expands to its first argument,
+% untouched, followed by a brace group containing the parameter text
+% |{#|$1$\ldots{}|#|$n$|}|, where $n$ is the result of evaluating the
+% second argument (as described in \cs{int_eval:n}). If the second
+% argument gives a result outside the range $[0,9]$, the third
+% argument is returned instead, normally an error message. Some of
+% the functions use here are not defined yet, but will be defined
+% before this function is called.
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_cs_parm_from_arg_count:nnF #1#2
+ {
+ \exp_args:Ne \@@_parm_from_arg_count_test:nnF
+ {
+ \exp_after:wN \exp_not:n
+ \if_case:w \int_eval:n {#2}
+ { }
+ \or: { ##1 }
+ \or: { ##1##2 }
+ \or: { ##1##2##3 }
+ \or: { ##1##2##3##4 }
+ \or: { ##1##2##3##4##5 }
+ \or: { ##1##2##3##4##5##6 }
+ \or: { ##1##2##3##4##5##6##7 }
+ \or: { ##1##2##3##4##5##6##7##8 }
+ \or: { ##1##2##3##4##5##6##7##8##9 }
+ \else: { \c_false_bool }
+ \fi:
+ }
+ {#1}
+ }
+\cs_new_protected:Npn \@@_parm_from_arg_count_test:nnF #1#2
+ {
+ \if_meaning:w \c_false_bool #1
+ \exp_after:wN \use_ii:nn
+ \else:
+ \exp_after:wN \use_i:nn
+ \fi:
+ { #2 {#1} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Defining functions from a given number of arguments}
+%
+% \begin{macrocode}
+%<@@=cs>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\@@_count_signature:N, \@@_count_signature:c}
+% \begin{macro}[EXP]{\@@_count_signature:n}
+% \begin{macro}[EXP]{\@@_count_signature:nnN}
+% Counting the number of tokens in the signature, \emph{i.e.}, the
+% number of arguments the function should take. Since this is not
+% used in any time-critical function, we simply use \cs{tl_count:n} if
+% there is a signature, otherwise $-1$ arguments to signal an error.
+% We need a variant form right away.
+% \begin{macrocode}
+\cs_new:Npn \@@_count_signature:N #1
+ { \exp_args:Nf \@@_count_signature:n { \cs_split_function:N #1 } }
+\cs_new:Npn \@@_count_signature:n #1
+ { \int_eval:n { \@@_count_signature:nnN #1 } }
+\cs_new:Npn \@@_count_signature:nnN #1#2#3
+ {
+ \if_meaning:w \c_true_bool #3
+ \tl_count:n {#2}
+ \else:
+ -1
+ \fi:
+ }
+\cs_new:Npn \@@_count_signature:c
+ { \exp_args:Nc \@@_count_signature:N }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \cs_generate_from_arg_count:NNnn,
+% \cs_generate_from_arg_count:cNnn,
+% \cs_generate_from_arg_count:Ncnn
+% }
+% We provide a constructor function for defining functions with a
+% given number of arguments. For this we need to choose the correct
+% parameter text and then use that when defining. Since \TeX{}
+% supports from zero to nine arguments, we use a simple switch to
+% choose the correct parameter text, ensuring the result is returned
+% after finishing the conditional. If it is not between zero and
+% nine, we throw an error.
+%
+% 1: function to define, 2: with what to define it, 3: the number of
+% args it requires and 4: the replacement text
+% \begin{macrocode}
+\cs_new_protected:Npn \cs_generate_from_arg_count:NNnn #1#2#3#4
+ {
+ \__kernel_cs_parm_from_arg_count:nnF { \use:nnn #2 #1 } {#3}
+ {
+ \msg_error:nnee { kernel } { bad-number-of-arguments }
+ { \token_to_str:N #1 } { \int_eval:n {#3} }
+ \use_none:n
+ }
+ {#4}
+ }
+% \end{macrocode}
+% A variant form we need right away, plus one which is used elsewhere but
+% which is most logically created here.
+% \begin{macrocode}
+\cs_new_protected:Npn \cs_generate_from_arg_count:cNnn
+ { \exp_args:Nc \cs_generate_from_arg_count:NNnn }
+\cs_new_protected:Npn \cs_generate_from_arg_count:Ncnn
+ { \exp_args:NNc \cs_generate_from_arg_count:NNnn }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Using the signature to define functions}
+%
+% \begin{macrocode}
+%<@@=cs>
+% \end{macrocode}
+%
+% We can now combine some of the tools we have to provide a simple
+% interface for defining functions, where the number of arguments is
+% read from the signature. For instance,
+% |\cs_set:Nn \foo_bar:nn {#1,#2}|.
+%
+% \begin{macro}
+% {
+% \cs_set:Nn , \cs_set:Ne , \cs_set:Nx ,
+% \cs_set_nopar:Nn , \cs_set_nopar:Ne , \cs_set_nopar:Nx ,
+% \cs_set_protected:Nn , \cs_set_protected:Ne , \cs_set_protected:Nx ,
+% \cs_set_protected_nopar:Nn , \cs_set_protected_nopar:Ne , \cs_set_protected_nopar:Nx ,
+% \cs_gset:Nn , \cs_gset:Ne , \cs_gset:Nx ,
+% \cs_gset_nopar:Nn , \cs_gset_nopar:Ne , \cs_gset_nopar:Nx ,
+% \cs_gset_protected:Nn , \cs_gset_protected:Ne , \cs_gset_protected:Nx ,
+% \cs_gset_protected_nopar:Nn , \cs_gset_protected_nopar:Ne , \cs_gset_protected_nopar:Nx ,
+% \cs_new:Nn , \cs_new:Ne , \cs_new:Nx ,
+% \cs_new_nopar:Nn , \cs_new_nopar:Ne , \cs_new_nopar:Nx ,
+% \cs_new_protected:Nn , \cs_new_protected:Ne , \cs_new_protected:Nx ,
+% \cs_new_protected_nopar:Nn , \cs_new_protected_nopar:Ne , \cs_new_protected_nopar:Nx ,
+% }
+% We want to define \cs{cs_set:Nn} as
+% \begin{verbatim}
+% \cs_set_protected:Npn \cs_set:Nn #1#2
+% {
+% \cs_generate_from_arg_count:NNnn #1 \cs_set:Npn
+% { \@@_count_signature:N #1 } {#2}
+% }
+% \end{verbatim}
+% In short, to define \cs{cs_set:Nn} we need just use \cs{cs_set:Npn},
+% everything else is the same for each variant. Therefore, we can
+% make it simpler by temporarily defining a function to do this for
+% us.
+% \begin{macrocode}
+\cs_set:Npn \@@_tmp:w #1#2#3
+ {
+ \cs_new_protected:cpx { cs_ #1 : #2 }
+ {
+ \exp_not:N \@@_generate_from_signature:NNn
+ \exp_after:wN \exp_not:N \cs:w cs_ #1 : #3 \cs_end:
+ }
+ }
+\cs_new_protected:Npn \@@_generate_from_signature:NNn #1#2
+ {
+ \use:e
+ {
+ \@@_generate_from_signature:nnNNNn
+ \cs_split_function:N #2
+ }
+ #1 #2
+ }
+\cs_new_protected:Npn \@@_generate_from_signature:nnNNNn #1#2#3#4#5#6
+ {
+ \bool_if:NTF #3
+ {
+ \cs_set_nopar:Npx \@@_tmp:w
+ { \tl_map_function:nN {#2} \@@_generate_from_signature:n }
+ \tl_if_empty:oF \@@_tmp:w
+ {
+ \msg_error:nneee { kernel } { non-base-function }
+ { \token_to_str:N #5 } {#2} { \@@_tmp:w }
+ }
+ \cs_generate_from_arg_count:NNnn
+ #5 #4 { \tl_count:n {#2} } {#6}
+ }
+ {
+ \msg_error:nne { kernel } { missing-colon }
+ { \token_to_str:N #5 }
+ }
+ }
+\cs_new:Npn \@@_generate_from_signature:n #1
+ {
+ \if:w n #1 \else: \if:w N #1 \else:
+ \if:w T #1 \else: \if:w F #1 \else: #1 \fi: \fi: \fi: \fi:
+ }
+% \end{macrocode}
+% Then we define the 24 variants beginning with |N|.
+% \begin{macrocode}
+\@@_tmp:w { set } { Nn } { Npn }
+\@@_tmp:w { set } { Ne } { Npe }
+\@@_tmp:w { set } { Nx } { Npx }
+\@@_tmp:w { set_nopar } { Nn } { Npn }
+\@@_tmp:w { set_nopar } { Ne } { Npe }
+\@@_tmp:w { set_nopar } { Nx } { Npx }
+\@@_tmp:w { set_protected } { Nn } { Npn }
+\@@_tmp:w { set_protected } { Ne } { Npe }
+\@@_tmp:w { set_protected } { Nx } { Npx }
+\@@_tmp:w { set_protected_nopar } { Nn } { Npn }
+\@@_tmp:w { set_protected_nopar } { Ne } { Npe }
+\@@_tmp:w { set_protected_nopar } { Nx } { Npx }
+\@@_tmp:w { gset } { Nn } { Npn }
+\@@_tmp:w { gset } { Ne } { Npe }
+\@@_tmp:w { gset } { Nx } { Npx }
+\@@_tmp:w { gset_nopar } { Nn } { Npn }
+\@@_tmp:w { gset_nopar } { Ne } { Npe }
+\@@_tmp:w { gset_nopar } { Nx } { Npx }
+\@@_tmp:w { gset_protected } { Nn } { Npn }
+\@@_tmp:w { gset_protected } { Ne } { Npe }
+\@@_tmp:w { gset_protected } { Nx } { Npx }
+\@@_tmp:w { gset_protected_nopar } { Nn } { Npn }
+\@@_tmp:w { gset_protected_nopar } { Ne } { Npe }
+\@@_tmp:w { gset_protected_nopar } { Nx } { Npx }
+\@@_tmp:w { new } { Nn } { Npn }
+\@@_tmp:w { new } { Ne } { Npe }
+\@@_tmp:w { new } { Nx } { Npx }
+\@@_tmp:w { new_nopar } { Nn } { Npn }
+\@@_tmp:w { new_nopar } { Ne } { Npe }
+\@@_tmp:w { new_nopar } { Nx } { Npx }
+\@@_tmp:w { new_protected } { Nn } { Npn }
+\@@_tmp:w { new_protected } { Ne } { Npe }
+\@@_tmp:w { new_protected } { Nx } { Npx }
+\@@_tmp:w { new_protected_nopar } { Nn } { Npn }
+\@@_tmp:w { new_protected_nopar } { Ne } { Npe }
+\@@_tmp:w { new_protected_nopar } { Nx } { Npx }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[documented-as=\cs_set:Nn]
+% {
+% \cs_set:cn , \cs_set:ce , \cs_set:cx ,
+% \cs_set_nopar:cn , \cs_set_nopar:ce , \cs_set_nopar:cx ,
+% \cs_set_protected:cn , \cs_set_protected:ce , \cs_set_protected:cx ,
+% \cs_set_protected_nopar:cn , \cs_set_protected_nopar:ce , \cs_set_protected_nopar:cx ,
+% \cs_gset:cn , \cs_gset:ce , \cs_gset:cx ,
+% \cs_gset_nopar:cn , \cs_gset_nopar:ce , \cs_gset_nopar:cx ,
+% \cs_gset_protected:cn , \cs_gset_protected:ce , \cs_gset_protected:cx ,
+% \cs_gset_protected_nopar:cn , \cs_gset_protected_nopar:ce , \cs_gset_protected_nopar:cx ,
+% \cs_new:cn , \cs_new:ce , \cs_new:cx ,
+% \cs_new_nopar:cn , \cs_new_nopar:ce , \cs_new_nopar:cx ,
+% \cs_new_protected:cn , \cs_new_protected:ce , \cs_new_protected:cx ,
+% \cs_new_protected_nopar:cn , \cs_new_protected_nopar:ce , \cs_new_protected_nopar:cx ,
+% }
+% The 24 |c| variants simply use \cs{exp_args:Nc}.
+% \begin{macrocode}
+\cs_set:Npn \@@_tmp:w #1#2
+ {
+ \cs_new_protected:cpx { cs_ #1 : c #2 }
+ {
+ \exp_not:N \exp_args:Nc
+ \exp_after:wN \exp_not:N \cs:w cs_ #1 : N #2 \cs_end:
+ }
+ }
+\@@_tmp:w { set } { n }
+\@@_tmp:w { set } { e }
+\@@_tmp:w { set } { x }
+\@@_tmp:w { set_nopar } { n }
+\@@_tmp:w { set_nopar } { e }
+\@@_tmp:w { set_nopar } { x }
+\@@_tmp:w { set_protected } { n }
+\@@_tmp:w { set_protected } { e }
+\@@_tmp:w { set_protected } { x }
+\@@_tmp:w { set_protected_nopar } { n }
+\@@_tmp:w { set_protected_nopar } { e }
+\@@_tmp:w { set_protected_nopar } { x }
+\@@_tmp:w { gset } { n }
+\@@_tmp:w { gset } { e }
+\@@_tmp:w { gset } { x }
+\@@_tmp:w { gset_nopar } { n }
+\@@_tmp:w { gset_nopar } { e }
+\@@_tmp:w { gset_nopar } { x }
+\@@_tmp:w { gset_protected } { n }
+\@@_tmp:w { gset_protected } { e }
+\@@_tmp:w { gset_protected } { x }
+\@@_tmp:w { gset_protected_nopar } { n }
+\@@_tmp:w { gset_protected_nopar } { e }
+\@@_tmp:w { gset_protected_nopar } { x }
+\@@_tmp:w { new } { n }
+\@@_tmp:w { new } { e }
+\@@_tmp:w { new } { x }
+\@@_tmp:w { new_nopar } { n }
+\@@_tmp:w { new_nopar } { e }
+\@@_tmp:w { new_nopar } { x }
+\@@_tmp:w { new_protected } { n }
+\@@_tmp:w { new_protected } { e }
+\@@_tmp:w { new_protected } { x }
+\@@_tmp:w { new_protected_nopar } { n }
+\@@_tmp:w { new_protected_nopar } { e }
+\@@_tmp:w { new_protected_nopar } { x }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Checking control sequence equality}
+%
+% \begin{macro}[pTF, EXP]
+% {\cs_if_eq:NN, \cs_if_eq:cN, \cs_if_eq:Nc, \cs_if_eq:cc}
+% Check if two control sequences are identical.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \cs_if_eq:NN #1#2 { p , T , F , TF }
+ {
+ \if_meaning:w #1#2
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\cs_new:Npn \cs_if_eq_p:cN { \exp_args:Nc \cs_if_eq_p:NN }
+\cs_new:Npn \cs_if_eq:cNTF { \exp_args:Nc \cs_if_eq:NNTF }
+\cs_new:Npn \cs_if_eq:cNT { \exp_args:Nc \cs_if_eq:NNT }
+\cs_new:Npn \cs_if_eq:cNF { \exp_args:Nc \cs_if_eq:NNF }
+\cs_new:Npn \cs_if_eq_p:Nc { \exp_args:NNc \cs_if_eq_p:NN }
+\cs_new:Npn \cs_if_eq:NcTF { \exp_args:NNc \cs_if_eq:NNTF }
+\cs_new:Npn \cs_if_eq:NcT { \exp_args:NNc \cs_if_eq:NNT }
+\cs_new:Npn \cs_if_eq:NcF { \exp_args:NNc \cs_if_eq:NNF }
+\cs_new:Npn \cs_if_eq_p:cc { \exp_args:Ncc \cs_if_eq_p:NN }
+\cs_new:Npn \cs_if_eq:ccTF { \exp_args:Ncc \cs_if_eq:NNTF }
+\cs_new:Npn \cs_if_eq:ccT { \exp_args:Ncc \cs_if_eq:NNT }
+\cs_new:Npn \cs_if_eq:ccF { \exp_args:Ncc \cs_if_eq:NNF }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Diagnostic functions}
+%
+% \begin{macrocode}
+%<@@=kernel>
+% \end{macrocode}
+%
+% \begin{macro}{\@@_chk_defined:NT}
+% Error if the variable |#1| is not defined.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_chk_defined:NT #1#2
+ {
+ \cs_if_exist:NTF #1
+ {#2}
+ {
+ \msg_error:nne { kernel } { variable-not-defined }
+ { \token_to_str:N #1 }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_register_show:N, \@@_register_show:c, \@@_register_log:N, \@@_register_log:c}
+% \begin{macro}{\@@_register_show_aux:NN, \@@_register_show_aux:nNN}
+% Simply using the \tn{showthe} primitive does not allow for
+% line-wrapping, so instead use \cs{tl_show:n} and \cs{tl_log:n} (defined
+% in \pkg{l3tl} and that performs line-wrapping). This displays
+% |>~|\meta{variable}|=|\meta{value}. We expand the value before-hand
+% as otherwise some integers (such as \tn{currentgrouplevel} or
+% \tn{currentgrouptype}) altered by the line-wrapping code would show
+% wrong values.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_register_show:N
+ { \@@_register_show_aux:NN \tl_show:n }
+\cs_new_protected:Npn \@@_register_show:c
+ { \exp_args:Nc \@@_register_show:N }
+\cs_new_protected:Npn \@@_register_log:N
+ { \@@_register_show_aux:NN \tl_log:n }
+\cs_new_protected:Npn \@@_register_log:c
+ { \exp_args:Nc \@@_register_log:N }
+\cs_new_protected:Npn \@@_register_show_aux:NN #1#2
+ {
+ \@@_chk_defined:NT #2
+ {
+ \exp_args:No \@@_register_show_aux:nNN
+ { \tex_the:D #2 } #2 #1
+ }
+ }
+\cs_new_protected:Npn \@@_register_show_aux:nNN #1#2#3
+ { \exp_args:No #3 { \token_to_str:N #2 = #1 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\cs_show:N, \cs_show:c, \cs_log:N, \cs_log:c, \@@_show:NN}
+% Some control sequences have a very long name or meaning. Thus,
+% simply using \TeX{}'s primitive \tn{show} could lead to overlong
+% lines. The output of this primitive is mimicked to some extent,
+% then the re-built string is given to \cs{tl_show:n} or \cs{tl_log:n} for
+% line-wrapping. We must expand the meaning before passing it to the
+% wrapping code as otherwise we would wrongly see the definitions that
+% are in place there. To get correct escape characters, set the
+% \tn{escapechar} in a group; this also localizes the assignment
+% performed by \texttt{e}-expansion. The \cs{cs_show:c} and \cs{cs_log:c} commands
+% convert their argument to a control sequence within a group to avoid
+% showing \tn{relax} for undefined control sequences.
+% \begin{macrocode}
+\cs_new_protected:Npn \cs_show:N { \@@_show:NN \tl_show:n }
+\cs_new_protected:Npn \cs_show:c
+ { \group_begin: \exp_args:NNc \group_end: \cs_show:N }
+\cs_new_protected:Npn \cs_log:N { \@@_show:NN \tl_log:n }
+\cs_new_protected:Npn \cs_log:c
+ { \group_begin: \exp_args:NNc \group_end: \cs_log:N }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \group_begin:
+ \int_set:Nn \tex_escapechar:D { `\\ }
+ \exp_args:NNe
+ \group_end:
+ #1 { \token_to_str:N #2 = \cs_meaning:N #2 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\group_show_list:, \group_log_list:, \@@_group_show:NN}
+% Wrapper around \tn{showgroups}. Getting \TeX{} to write to the log
+% without interruption the run is done by altering the interaction
+% mode.
+% \begin{macrocode}
+\cs_new_protected:Npn \group_show_list:
+ { \@@_group_show:NN \use_none:n 1 }
+\cs_new_protected:Npn \group_log_list:
+ { \@@_group_show:NN \int_gzero:N 0 }
+\cs_new_protected:Npn \@@_group_show:NN #1#2
+ {
+ \use:e
+ {
+ #1 \tex_interactionmode:D
+ \int_set:Nn \tex_tracingonline:D {#2}
+ \int_set:Nn \tex_errorcontextlines:D { -1 }
+ \exp_not:N \exp_after:wN \scan_stop:
+ \tex_showgroups:D
+ \int_gset:Nn \tex_interactionmode:D
+ { \int_use:N \tex_interactionmode:D }
+ \int_set:Nn \tex_tracingonline:D
+ { \int_use:N \tex_tracingonline:D }
+ \int_set:Nn \tex_errorcontextlines:D
+ { \int_use:N \tex_errorcontextlines:D }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Decomposing a macro definition}
+%
+% \begin{macro}{\cs_prefix_spec:N}
+% \begin{macro}{\cs_parameter_spec:N}
+% \begin{macro}{\cs_replacement_spec:N}
+% \begin{macro}{\@@_prefix_arg_replacement:wN}
+% We sometimes want to test if a control sequence can be expanded to
+% reveal a hidden value. However, we cannot just expand the macro
+% blindly as it may have arguments and none might be
+% present. Therefore we define these functions to pick either the
+% prefix(es), the parameter specification, or the replacement text from
+% a macro. All of this information is returned as characters with
+% catcode~$12$. If the token in question isn't a macro, the token
+% \cs{scan_stop:} is returned instead.
+% \begin{macrocode}
+\use:e
+ {
+ \exp_not:n { \cs_new:Npn \@@_prefix_arg_replacement:wN #1 }
+ \tl_to_str:n { macro : } \exp_not:n { #2 -> #3 \s_@@_stop #4 }
+ }
+ { #4 {#1} {#2} {#3} }
+\cs_new:Npn \cs_prefix_spec:N #1
+ {
+ \token_if_macro:NTF #1
+ {
+ \exp_after:wN \@@_prefix_arg_replacement:wN
+ \token_to_meaning:N #1 \s_@@_stop \use_i:nnn
+ }
+ { \scan_stop: }
+ }
+\cs_new:Npn \cs_parameter_spec:N #1
+ {
+ \token_if_macro:NTF #1
+ {
+ \exp_after:wN \@@_prefix_arg_replacement:wN
+ \token_to_meaning:N #1 \s_@@_stop \use_ii:nnn
+ }
+ { \scan_stop: }
+ }
+\cs_new:Npn \cs_replacement_spec:N #1
+ {
+ \token_if_macro:NTF #1
+ {
+ \exp_after:wN \@@_prefix_arg_replacement:wN
+ \token_to_meaning:N #1 \s_@@_stop \use_iii:nnn
+ }
+ { \scan_stop: }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Doing nothing functions}
+%
+% \begin{macro}[EXP]{\prg_do_nothing:}
+% This does not fit anywhere else!
+% \begin{macrocode}
+\cs_new:Npn \prg_do_nothing: { }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Breaking out of mapping functions}
+%
+% \begin{macrocode}
+%<@@=prg>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\prg_break_point:Nn, \prg_map_break:Nn}
+% In inline mappings, the nesting level must be reset
+% at the end of the mapping, even when the user decides
+% to break out. This is done by putting the code that
+% must be performed as an argument of \cs{@@_break_point:Nn}.
+% The breaking functions are then defined to jump to
+% that point and perform the argument of \cs{@@_break_point:Nn},
+% before the user's code (if any). There is a check that we close the
+% correct loop, otherwise we continue breaking.
+% \begin{macrocode}
+\cs_new_eq:NN \prg_break_point:Nn \use_ii:nn
+\cs_new:Npn \prg_map_break:Nn #1#2#3 \prg_break_point:Nn #4#5
+ {
+ #5
+ \if_meaning:w #1 #4
+ \exp_after:wN \use_iii:nnn
+ \fi:
+ \prg_map_break:Nn #1 {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\prg_break_point:}
+% \begin{macro}{\prg_break:, \prg_break:n}
+% Very simple analogues of \cs{prg_break_point:Nn} and
+% \cs{prg_map_break:Nn}, for use in fast short-term recursions which
+% are not mappings, do not need to support nesting, and in which
+% nothing has to be done at the end of the loop.
+% \begin{macrocode}
+\cs_new_eq:NN \prg_break_point: \prg_do_nothing:
+\cs_new:Npn \prg_break: #1 \prg_break_point: { }
+\cs_new:Npn \prg_break:n #1#2 \prg_break_point: {#1}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Starting a paragraph}
+%
+% \begin{macro}{\mode_leave_vertical:}
+% The approach here is different to that used by \LaTeXe{} or plain \TeX{},
+% which unbox a void box to force horizontal mode. That inserts the
+% \tn{everypar} tokens \emph{before} the re-inserted unboxing tokens. The
+% approach here uses a protected macro, equivalent to the \tn{quitvmode}
+% primitive. In vertical mode, the \tn{indent} primitive is inserted:
+% this will switch to horizontal mode and insert \tn{everypar} tokens and
+% nothing else. Unlike the \LaTeXe{} version, the availability of \eTeX{}
+% means using a mode test can be done at for example the start of an
+% \tn{halign}.
+% \begin{macrocode}
+\cs_new_protected:Npn \mode_leave_vertical:
+ {
+ \if_mode_vertical:
+ \exp_after:wN \tex_indent:D
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3bitset.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3bitset.dtx
new file mode 100644
index 00000000000..b0bd996d261
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3bitset.dtx
@@ -0,0 +1,629 @@
+% \iffalse meta-comment
+%
+%% File: l3bitset.dtx
+%
+% Copyright (C) 2020-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% http://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+% \title{^^A
+% The \pkg{l3bitset} module\\ Bitsets^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% This module defines and implements the data type \texttt{bitset}, a vector of
+% bits. The size of the vector may grow dynamically.
+% Individual bits can be set and unset by names pointing to an index position.
+% The names |1|, |2|, |3|, \ldots\ are predeclared and point to the
+% index positions $1$, $2$, $3$,\ldots. More names can be added and existing names can
+% be changed.
+% The index is like all other indices in \pkg{expl3} modules \emph{1-based}.
+% A \texttt{bitset} can be output as binary number or---as needed e.g. in a
+% PDF dictionary---as decimal (arabic) number.
+% Currently only a small subset of the functions provided by the \pkg{bitset}
+% package are implemented here, mainly the functions needed to use bitsets in
+% PDF dictionaries.
+%
+% The bitset is stored as a string (but one shouldn't rely on the internal
+% representation) and so the vector size is theoretically
+% unlimited, only restricted by \TeX-memory. But the functions to set and clear
+% bits use integer functions for the index so bitsets can't be longer
+% than $2^{31} - 1$.
+% The export function
+% \cs{bitset_to_arabic:N} can use functions from the \texttt{int} module only if
+% the largest index used for this bitset is smaller than $32$, for longer
+% bitsets \texttt{fp} is used and this is slower.
+%
+% \section{Creating bitsets}
+%
+% \begin{function}[added = 2023-11-15]
+% {\bitset_new:N,\bitset_new:c,\bitset_new:Nn, \bitset_new:cn}
+% \begin{syntax}
+% \cs{bitset_new:N} \meta{bitset var} \\
+% \cs{bitset_new:Nn} \meta{bitset var}
+% ~~\{
+% ~~~~\meta{name_1} |=| \meta{index_1} |,|
+% ~~~~\meta{name_2} |=| \meta{index_2} |,| \ldots{}
+% ~~\}
+% \end{syntax}
+% Creates a new \meta{bitset var} or raises an error if the name is already taken.
+% The declaration is global. The \meta{bitset var} is initially $0$.
+%
+% Bitsets are implemented as string variables consisting of
+% \texttt{1}'s and \texttt{0}'s.
+% The rightmost number is the index position $1$, so
+% the string variable can be viewed directly as the binary number.
+% But one shouldn't rely on the internal representation, but use the
+% dedicated \cs{bitset_to_bin:N} instead to get the binary number.
+%
+% The name--index pairs given in the second
+% argument of \cs{bitset_new:Nn} declares names for some indices,
+% which can be used to set and unset bits.
+% The names |1|, |2|, |3|, \ldots\ are predeclared and point to the
+% index positions $1$, $2$, $3$, \ldots.
+%
+% \meta{index\ldots} should be a positive number or an
+% \meta{integer expression} which evaluates to a positive number.
+% The expression is evaluated when the index is used, not at declaration time.
+% The names \meta{name\ldots}
+% should be unique. Using a number as name, e.g.~|10=1|, is allowed, it
+% then overwrites the predeclared name |10|,
+% but the index position $10$ can then only be reached if some other
+% name for it exists, e.g. |ten=10|.
+% It is not necessary to give every index
+% a name, and an index can have more than one name. The named index
+% can be extended or changed with the next function.
+% \end{function}
+%
+% \begin{function}[added = 2023-11-15]
+% {\bitset_addto_named_index:Nn}
+% \begin{syntax}
+% \cs{bitset_addto_named_index:Nn} \meta{bitset var}
+% ~~\{
+% ~~~~\meta{name_1} |=| \meta{index_1} |,|
+% ~~~~\meta{name_2} |=| \meta{index_2} |,| \ldots{}
+% ~~\}
+% \end{syntax}
+% This extends or changes the name--index pairs for \meta{bitset var}
+% globally as described for \cs{bitset_new:Nn}.
+% \end{function}
+%
+% For example after these settings
+% \begin{verbatim}
+% \bitset_new:Nn \l_pdfannot_F_bitset
+% {
+% Invisible = 1,
+% Hidden = 2,
+% Print = 3,
+% NoZoom = 4,
+% NoRotate = 5,
+% NoView = 6,
+% ReadOnly = 7,
+% Locked = 8,
+% ToggleNoView = 9,
+% LockedContents = 10
+% }
+% \bitset_addto_named_index:Nn \l_pdfannot_F_bitset
+% {
+% print = 3
+% }
+% \end{verbatim}
+% it is possible to set bit $3$ by using any of these alternatives:
+% \begin{verbatim}
+% \bitset_set_true:Nn \l_pdfannot_F_bitset {Print}
+% \bitset_set_true:Nn \l_pdfannot_F_bitset {print}
+% \bitset_set_true:Nn \l_pdfannot_F_bitset {3}
+% \end{verbatim}
+%
+% \begin{function}[EXP, pTF,added = 2023-11-15]
+% {\bitset_if_exist:N, \bitset_if_exist:c}
+% \begin{syntax}
+% \cs{bitset_if_exist_p:N} \meta{bitset var}
+% \cs{bitset_if_exist:NTF} \meta{bitset var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{bitset var} exist.
+% \end{function}
+%
+% \section{Setting and unsetting bits}
+%
+% \begin{function}[added = 2023-11-15]
+% {
+% \bitset_set_true:Nn, \bitset_set_true:cn,
+% \bitset_gset_true:Nn, \bitset_gset_true:cn
+% }
+% \begin{syntax}
+% \cs{bitset_set_true:Nn} \meta{bitset var} \Arg{name}
+% \end{syntax}
+% This sets the bit of the index position represented by \Arg{name} to $1$.
+% \meta{name} should be either one of the predeclared names
+% |1|, |2|, |3|, \ldots, or one of the names added manually.
+% Index position are 1-based.
+% If needed the length of the bit vector is enlarged.
+% \end{function}
+%
+% \begin{function}[added = 2023-11-15]
+% {
+% \bitset_set_false:Nn, \bitset_set_false:cn,
+% \bitset_gset_false:Nn, \bitset_gset_false:cn
+% }
+% \begin{syntax}
+% \cs{bitset_set_false:Nn} \meta{bitset var} \Arg{name}
+% \end{syntax}
+% This unsets the bit of the index position represented by \Arg{name} (sets
+% it to $0$).
+% \meta{name} should be either one of the predeclared names
+% |1|, |2|, |3|, \ldots, or one of the names added manually.
+% The index is $1$-based. If the index position is larger
+% than the current length of the bit vector
+% nothing happens. If the leading (left most) bit is unset,
+% zeros are not trimmed but stay in the bit vector and are still shown
+% by \cs{bitset_show:N}.
+% \end{function}
+%
+% \begin{function}[added = 2023-11-15]
+% {\bitset_clear:N,\bitset_clear:c,\bitset_gclear:N,\bitset_gclear:c}
+% \begin{syntax}
+% \cs{bitset_clear:N} \meta{bitset var}
+% \end{syntax}
+% This resets the bitset to the initial state. The declared names are not changed.
+% \end{function}
+%
+% \section{Using bitsets}
+%
+% \begin{function}[EXP,added = 2023-11-15]
+% {\bitset_item:Nn, \bitset_item:cn}
+% \begin{syntax}
+% \cs{bitset_item:Nn} \meta{bitset var} \Arg{name}
+% \end{syntax}
+% \cs{bitset_item:Nn} outputs \texttt{1} if the bit with
+% the index number represented by \meta{name} is set and \texttt{0} otherwise.
+% \meta{name} is either one of the predeclared names
+% |1|, |2|, |3|, \ldots, or one of the names added manually.
+% \end{function}
+%
+% \begin{function}[EXP,added = 2023-11-15]
+% {\bitset_to_bin:N, \bitset_to_bin:c}
+% \begin{syntax}
+% \cs{bitset_to_bin:N} \meta{bitset var}
+% \end{syntax}
+% This leaves the current value of the bitset expressed as
+% a binary (string) number in the input stream.
+% If no bit has been set yet, the output is zero.
+% \end{function}
+%
+% \begin{function}[EXP,added = 2023-11-15]
+% {\bitset_to_arabic:N, \bitset_to_arabic:c}
+% \begin{syntax}
+% \cs{bitset_to_arabic:N} \meta{bitset var}
+% \end{syntax}
+% This leaves the current value of the bitset expressed as
+% a decimal number in the input stream. If no bit has been set yet,
+% the output is zero. The function uses \cs{int_from_bin:n} if the largest
+% index that have been set or unset is smaller than $32$, and a slower
+% implementation based on \cs{fp_eval:n} otherwise.
+% \end{function}
+%
+% \begin{function}[added = 2023-11-15]
+% {\bitset_show:N, \bitset_show:c}
+% \begin{syntax}
+% \cs{bitset_show:N} \meta{bitset var}
+% \end{syntax}
+% Displays the binary and decimal values of the \meta{bitset var}
+% on the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2023-11-15]
+% {\bitset_log:N, \bitset_log:c}
+% \begin{syntax}
+% \cs{bitset_log:N} \meta{bitset var}
+% \end{syntax}
+% Writes the binary and decimal values of the \meta{bitset var}
+% in the log file.
+% \end{function}
+%
+% \begin{function}[added = 2023-11-15]
+% {
+% \bitset_show_named_index:N, \bitset_show_named_index:c
+% }
+% \begin{syntax}
+% \cs{bitset_show_named_index:N} \meta{bitset var}
+% \end{syntax}
+% Displays declared name--index pairs of the \meta{bitset var}
+% on the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2023-12-11]
+% {
+% \bitset_log_named_index:N, \bitset_log_named_index:c
+% }
+% \begin{syntax}
+% \cs{bitset_log_named_index:N} \meta{bitset var}
+% \end{syntax}
+% Writes declared name--index pairs of the \meta{bitset var}
+% in the log file.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3bitset} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=bitset>
+% \end{macrocode}
+%
+% Transitional support.
+% \begin{macrocode}
+\cs_if_exist:NT \@expl@finalise@setup@@@@
+ {
+ \tl_gput_right:Nn \@expl@finalise@setup@@@@
+ { \declare@file@substitution { l3bitset.sty } { null.tex } }
+ }
+% \end{macrocode}
+%
+% A bitset is a string variable.
+% \begin{macro}{\bitset_new:N, \bitset_new:c}
+% \begin{macro}{\bitset_new:Nn, \bitset_new:cn}
+% \begin{macrocode}
+\cs_new_protected:Npn \bitset_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_eq:NN #1 \c_zero_str
+ \prop_new:c { g__bitset_ \cs_to_str:N #1 _name_prop }
+ }
+\cs_new_protected:Npn \bitset_new:Nn #1 #2
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_eq:NN #1 \c_zero_str
+ \prop_new:c { g__bitset_ \cs_to_str:N #1 _name_prop }
+ \prop_gset_from_keyval:cn
+ { g__bitset_ \cs_to_str:N #1 _name_prop }
+ {#2}
+ }
+\cs_generate_variant:Nn \bitset_new:N { c }
+\cs_generate_variant:Nn \bitset_new:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\bitset_addto_named_index:Nn}
+% \begin{macrocode}
+\cs_new_protected:Npn \bitset_addto_named_index:Nn #1#2
+ {
+ \prop_gput_from_keyval:cn
+ { g_@@_ \cs_to_str:N #1 _name_prop } { #2 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[EXP,pTF]
+% {
+% \bitset_if_exist:N, \bitset_if_exist:c
+% }
+% Existence tests.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn
+ \bitset_if_exist:N \str_if_exist:N { p , T , F , TF }
+\prg_new_eq_conditional:NNn
+ \bitset_if_exist:c \str_if_exist:c { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_set_true:Nn, \@@_gset_true:Nn, \@@_set_false:Nn, \@@_gset_false:Nn}
+% \begin{macro}{\@@_set:NNnN}
+% The internal command uses only numbers (integer expressions) for the
+% position.
+% A bit is set by either extending the string or by splitting it and
+% then inserting an $1$. It is not checked if the value was already $1$.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_set_true:Nn #1#2
+ { \@@_set:NNnN \str_set:Ne #1 {#2} 1 }
+\cs_new_protected:Npn \@@_gset_true:Nn #1#2
+ { \@@_set:NNnN \str_gset:Ne #1 {#2} 1 }
+\cs_new_protected:Npn \@@_set_false:Nn #1#2
+ { \@@_set:NNnN \str_set:Ne #1 {#2} 0 }
+\cs_new_protected:Npn \@@_gset_false:Nn #1#2
+ { \@@_set:NNnN \str_gset:Ne #1 {#2} 0 }
+\cs_new_protected:Npn \@@_set:NNnN #1#2#3#4
+ {
+ \int_compare:nNnT {#3} > { 0 }
+ {
+ \int_compare:nNnTF { \str_count:N #2 } < {#3}
+ {
+ #1 #2
+ {
+ #4
+ \prg_replicate:nn { #3 - \str_count:N #2 - 1 } { 0 }
+ #2
+ }
+ }
+ {
+ #1 #2
+ {
+ \str_range:Nnn #2 { 1 } { -1 - (#3) }
+ #4
+ \str_range:Nnn #2 { 1 - (#3) } { -1 }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_internal_int}
+% \begin{macrocode}
+\int_new:N \l_@@_internal_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[TF]{\@@_test_digits:n}
+% \begin{macro}{\@@_test_digits_end:n}
+% \begin{macro}{\@@_test_digits:w}
+% \url{https://chat.stackexchange.com/transcript/message/56878159#56878159}
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \@@_test_digits:n #1 { TF }
+ {
+ \tex_afterassignment:D \@@_test_digits:w
+ \l_@@_internal_int = 0 \tl_trim_spaces_apply:nN {#1} \tl_to_str:n
+ \@@_test_digits_end:
+ \use_i:nnn \if_false:
+ \@@_test_digits_end:
+ \if_int_compare:w \c_zero_int < \l_@@_internal_int
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new_eq:NN \@@_test_digits_end: \exp_stop_f:
+\cs_new_protected:Npn \@@_test_digits:w #1 \@@_test_digits_end: { }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \bitset_set_true:Nn, \bitset_set_true:cn,
+% \bitset_gset_true:Nn, \bitset_gset_true:cn,
+% \bitset_set_false:Nn, \bitset_set_false:cn,
+% \bitset_gset_false:Nn, \bitset_gset_false:cn
+% }
+% \begin{macro}{\@@_set_aux:NNn}
+% The user commands must first translate the argument to an index number.
+% \begin{macrocode}
+\cs_new_protected:Npn \bitset_set_true:Nn #1#2
+ { \@@_set:NNn \@@_set_true:Nn #1 {#2} }
+\cs_new_protected:Npn \bitset_gset_true:Nn #1#2
+ { \@@_set:NNn \@@_gset_true:Nn #1 {#2} }
+\cs_new_protected:Npn \bitset_set_false:Nn #1#2
+ { \@@_set:NNn \@@_set_false:Nn #1 {#2} }
+\cs_new_protected:Npn \bitset_gset_false:Nn #1#2
+ { \@@_set:NNn \@@_gset_false:Nn #1 {#2} }
+\cs_new_protected:Npn \@@_set:NNn #1#2#3
+ {
+ \prop_if_in:cnTF { g_@@_ \cs_to_str:N #2 _name_prop } {#3}
+ {
+ #1 #2
+ {
+ \prop_item:cn { g_@@_ \cs_to_str:N #2 _name_prop } {#3}
+ }
+ }
+ {
+ \@@_test_digits:nTF {#3}
+ {
+ #1 #2 {#3}
+ \prop_gput:cnn { g_@@_ \cs_to_str:N #2 _name_prop } {#3} {#3}
+ }
+ {
+ \msg_warning:nnee { bitset } { unknown-name }
+ { \token_to_str:N #2 }
+ { \tl_to_str:n {#3} }
+ }
+ }
+ }
+\cs_generate_variant:Nn \bitset_set_true:Nn { c }
+\cs_generate_variant:Nn \bitset_gset_true:Nn { c }
+\cs_generate_variant:Nn \bitset_set_false:Nn { c }
+\cs_generate_variant:Nn \bitset_gset_false:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \bitset_clear:N, \bitset_clear:c,
+% \bitset_gclear:N, \bitset_gclear:c
+% }
+% \begin{macrocode}
+\cs_new_protected:Npn \bitset_clear:N #1
+ {
+ \str_set_eq:NN #1 \c_zero_str
+ }
+\cs_new_protected:Npn \bitset_gclear:N #1
+ {
+ \str_gset_eq:NN #1 \c_zero_str
+ }
+\cs_generate_variant:Nn \bitset_clear:N { c }
+\cs_generate_variant:Nn \bitset_gclear:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \bitset_to_arabic:N, \bitset_to_arabic:c,
+% \bitset_to_bin:N, \bitset_to_bin:c,
+% }
+% \begin{macro}[EXP]{\@@_to_int:nN}
+% The naming of the commands follow the names in the \texttt{int} module.
+% \cs{bitset_to_arabic:N} uses \cs{int_from_bin:n} if the string is shorter
+% than $32$ and the slower \cs{fp_eval:n} for larger bitsets.
+% \begin{macrocode}
+\cs_new:Npn \bitset_to_arabic:N #1
+ {
+ \int_compare:nNnTF { \str_count:N #1 } < { 32 }
+ { \exp_args:No \int_from_bin:n {#1} }
+ {
+ \exp_after:wN \@@_to_int:nN \exp_after:wN 0
+ #1 \q_recursion_tail \q_recursion_stop
+ }
+ }
+\cs_new:Npn \@@_to_int:nN #1#2
+ {
+ \quark_if_recursion_tail_stop_do:Nn #2 {#1}
+ \exp_args:Nf \@@_to_int:nN { \fp_eval:n { #1 * 2 + #2 } }
+ }
+\cs_new:Npn \bitset_to_bin:N #1
+ {
+ #1
+ }
+\cs_generate_variant:Nn \bitset_to_arabic:N { c }
+\cs_generate_variant:Nn \bitset_to_bin:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \bitset_item:Nn, \bitset_item:cn
+% }
+% All bits that have been set at anytime have an entry in the prop,
+% so we can take everything else as $0$.
+% \begin{macrocode}
+\cs_new:Npn \bitset_item:Nn #1#2
+ {
+ \prop_if_in:cnTF { g_@@_ \cs_to_str:N #1 _name_prop } {#2}
+ {
+ \int_eval:n
+ {
+ \str_item:Nn #1
+ { 0 - ( \prop_item:cn { g_@@_ \cs_to_str:N #1 _name_prop } {#2} ) }
+ +0
+ }
+ }
+ {
+ 0
+ }
+ }
+\cs_generate_variant:Nn \bitset_item:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \bitset_show:N, \bitset_show:c,
+% \bitset_log:N, \bitset_log:c
+% }
+% \begin{macrocode}
+\cs_new_protected:Npn \bitset_show:N { \@@_show:NN \msg_show:nneeee }
+\cs_generate_variant:Nn \bitset_show:N { c }
+\cs_new_protected:Npn \bitset_log:N { \@@_show:NN \msg_log:nneeee }
+\cs_generate_variant:Nn \bitset_log:N { c }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ #1 { bitset } { show }
+ { \token_to_str:N #2 }
+ { \bitset_to_bin:N #2 }
+ { \bitset_to_arabic:N #2 }
+ { }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \bitset_show_named_index:N, \bitset_show_named_index:c,
+% \bitset_log_named_index:N, \bitset_log_named_index:c
+% }
+% \begin{macrocode}
+\cs_new_protected:Npn \bitset_show_named_index:N
+ { \@@_show_named_index:NN \msg_show:nneeee }
+\cs_generate_variant:Nn \bitset_show_named_index:N { c }
+\cs_new_protected:Npn \bitset_log_named_index:N
+ { \@@_show_named_index:NN \msg_log:nneeee }
+\cs_generate_variant:Nn \bitset_log_named_index:N { c }
+\cs_new_protected:Npn \@@_show_named_index:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ #1 { bitset } { show-names }
+ { \token_to_str:N #2 }
+ { \prop_map_function:cN { g_@@_ \cs_to_str:N #2 _name_prop } \msg_show_item:nn }
+ { } { }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Messages}
+% \begin{macrocode}
+\msg_new:nnn { bitset } { show }
+ {
+ The~bitset~#1~has~the~representation: \\
+ >~binary:~#2 \\
+ >~arabic:~#3 .
+ }
+\msg_new:nnn { bitset } { show-names }
+ {
+ The~bitset~#1~
+ \tl_if_empty:nTF {#2}
+ { knows~no~names~yet \\>~ . }
+ { knows~the~name/index~pairs~(without~outer~braces): #2 . }
+ }
+\msg_new:nnn { bitset } { unknown-name }
+ { The~name~'#2'~is~unknown~for~bitset~\tl_to_str:n {#1} }
+\prop_gput:Nnn \g_msg_module_name_prop { bitset } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { bitset } { }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3bootstrap.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3bootstrap.dtx
new file mode 100644
index 00000000000..2c51bf3bb04
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3bootstrap.dtx
@@ -0,0 +1,357 @@
+% \iffalse meta-comment
+%
+%% File: l3bootstrap.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3bootstrap} module\\ Bootstrap code^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{Using the \LaTeX3 modules}
+%
+% The modules documented in ^^A
+% \ifinterface
+% this file (and \file{source3} for documented sources) ^^A
+% \else
+% \file{interface3} (and this file) ^^A
+% \fi
+% are designed to be used on top of \LaTeXe{} and are already pre-loaded
+% since \LaTeXe{} 2020-02-02.
+% To support older formats, the |\usepackage{expl3}| or |\RequirePackage{expl3}|
+% instructions are still available to load them all as one.
+%
+% As the modules use a coding syntax different from standard
+% \LaTeXe{} it provides a few functions for setting it up.
+%
+% \begin{function}[updated = 2011-08-13]{\ExplSyntaxOn, \ExplSyntaxOff}
+% \begin{syntax}
+% \cs{ExplSyntaxOn} \meta{code} \cs{ExplSyntaxOff}
+% \end{syntax}
+% The \cs{ExplSyntaxOn} function switches to a category code
+% regime in which spaces and new lines are ignored, and in which the colon (|:|)
+% and underscore (|_|) are treated as \enquote{letters}, thus allowing
+% access to the names of code functions and variables. Within this
+% environment, |~| is used to input a space. The \cs{ExplSyntaxOff}
+% reverts to the document category code regime.
+% \begin{texnote}
+% Spaces introduced by~|~| behave much in the same way as normal
+% space characters in the standard category code regime: they are
+% ignored after a control word or at the start of a line, and
+% multiple consecutive~|~| are equivalent to a single one. However,
+% |~|~is \emph{not} ignored at the end of a line.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[updated = 2017-03-19, updated = 2023-08-03]
+% {\ProvidesExplPackage, \ProvidesExplClass, \ProvidesExplFile}
+% \begin{syntax}
+% \cs{ProvidesExplPackage} \Arg{package} \Arg{date} \Arg{version} \Arg{description}
+% \end{syntax}
+% These functions act broadly in the same way as the corresponding
+% \LaTeXe{} kernel
+% functions \tn{ProvidesPackage}, \tn{ProvidesClass} and
+% \tn{ProvidesFile}. However, they also implicitly switch
+% \cs{ExplSyntaxOn} for the remainder of the code with the file. At the
+% end of the file, \cs{ExplSyntaxOff} will be called to reverse this.
+% (This is the same concept as \LaTeXe{} provides in turning on
+% \tn{makeatletter} within package and class code.) The \meta{date} should
+% be given in the format \meta{year}/\meta{month}/\meta{day} or in the ISO
+% date format \meta{year}-\meta{month}-\meta{day}. If the
+% \meta{version} is given then a leading \texttt{v} is optional: if given
+% as a \enquote{pure} version string, a \texttt{v} will be prepended.
+% \end{function}
+%
+% \begin{function}[updated = 2012-06-04]{\GetIdInfo}
+% \begin{syntax}
+% \cs{GetIdInfo} |$Id:| \meta{SVN info field} |$| \Arg{description}
+% \end{syntax}
+% Extracts all information from a SVN field. Spaces are not
+% ignored in these fields. The information pieces are stored in
+% separate control sequences with \cs{ExplFileName} for the part of the
+% file name leading up to the period, \cs{ExplFileDate} for date,
+% \cs{ExplFileVersion} for version and \cs{ExplFileDescription} for the
+% description.
+% \end{function}
+%
+% To summarize: Every single package using this syntax should identify
+% itself using one of the above methods. Special care is taken so that
+% every package or class file loaded with \tn{RequirePackage} or similar
+% are loaded with usual \LaTeXe{} category codes and the \LaTeX3 category code
+% scheme is reloaded when needed afterwards. See implementation for
+% details. If you use the \cs{GetIdInfo} command you can use the
+% information when loading a package with
+% \begin{verbatim}
+% \ProvidesExplPackage{\ExplFileName}
+% {\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}
+% \end{verbatim}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3bootstrap} implementation}
+%
+% \begin{macrocode}
+%<*package>
+%<@@=kernel>
+% \end{macrocode}
+%
+% \subsection{The \tn{pdfstrcmp} primitive in \XeTeX{}}
+%
+% Only \pdfTeX{} has a primitive called \tn{pdfstrcmp}. The \XeTeX{}
+% version is just \tn{strcmp}, so there is some shuffling to do. As
+% this is still a real primitive, using the \pdfTeX{} name is \enquote{safe}.
+% \begin{macrocode}
+\begingroup\expandafter\expandafter\expandafter\endgroup
+ \expandafter\ifx\csname pdfstrcmp\endcsname\relax
+ \let\pdfstrcmp\strcmp
+\fi
+% \end{macrocode}
+%
+% \subsection{Loading support \Lua{} code}
+%
+% When \LuaTeX{} is used there are various pieces of \Lua{} code which need to
+% be loaded. The code itself is defined in \pkg{l3luatex} and is extracted into
+% a separate file. Thus here the task is to load the \Lua{} code both now and
+% (if required) at the start of each job.
+% \begin{macrocode}
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname directlua\endcsname\relax
+\else
+ \ifnum\luatexversion<110 %
+ \else
+% \end{macrocode}
+% For \LuaTeX{} we make sure the basic support is loaded:
+% this is only necessary in plain.
+% \begin{macrocode}
+ \begingroup\expandafter\expandafter\expandafter\endgroup
+ \expandafter\ifx\csname newcatcodetable\endcsname\relax
+ \input{ltluatex}%
+ \fi
+ \begingroup\expandafter\expandafter\expandafter\endgroup
+ \expandafter\ifx\csname newluabytecode\endcsname\relax
+ \else
+ \newluabytecode\@expl@luadata@bytecode
+ \fi
+ \directlua{require("expl3")}%
+% \end{macrocode}
+% As the user might be making a custom format, no assumption is made about
+% matching package mode with only loading the \Lua{} code once. Instead, a
+% query to \Lua{} reveals what mode is in operation.
+% \begin{macrocode}
+ \ifnum 0%
+ \directlua{
+ if status.ini_version then
+ tex.write("1")
+ end
+ }>0 %
+ \everyjob\expandafter{%
+ \the\expandafter\everyjob
+ \csname\detokenize{lua_now:n}\endcsname{require("expl3")}%
+ }%
+ \fi
+ \fi
+\fi
+% \end{macrocode}
+%
+% \subsection{Engine requirements}
+%
+% The code currently requires \eTeX{}, the set of \enquote{\pdfTeX{}
+% extensions} \emph{including} \tn{expanded}, and for Unicode engines the
+% ability to generate arbitrary character tokens by expansion. That is covered
+% by all supported engines since \TeX{} Live 2019, which we therefore use
+% as a baseline for engine and \LaTeX{} format support.
+% For \LuaTeX, we require at least Lua 5.3 and the |token.set_lua| function.
+% This is available at least since \LuaTeX{} 1.10, which again is the one
+% in \TeX{} Live 2019. (u)p\TeX{} only gained \tn{ifincsname} for \TeX{}
+% Live 2020, but at present that primitive is unused in \pkg{expl3} so for
+% the present it's not tested. If and when that changes, we will need to
+% revisit the code here.
+% \begin{macrocode}
+\begingroup
+ \def\next{\endgroup}%
+ \def\ShortText{Required primitives not found}%
+ \def\LongText%
+ {%
+ The L3 programming layer requires the e-TeX primitives and the
+ \LineBreak 'pdfTeX utilities' as described in the README file.
+ \LineBreak
+ These are available in the engines\LineBreak
+ - pdfTeX v1.40.20\LineBreak
+ - XeTeX v0.999991\LineBreak
+ - LuaTeX v1.10\LineBreak
+ - e-(u)pTeX v3.8.2\LineBreak
+ - Prote (2021)\LineBreak
+ or later.\LineBreak
+ \LineBreak
+ }%
+ \ifnum0%
+ \expandafter\ifx\csname luatexversion\endcsname\relax
+ \expandafter\ifx\csname expanded\endcsname\relax\else 1\fi
+ \else
+ \ifnum\luatexversion<110 \else 1\fi
+ \fi
+ =0 %
+ \newlinechar`\^^J %
+ \def\LineBreak{\noexpand\MessageBreak}%
+ \expandafter\ifx\csname PackageError\endcsname\relax
+ \def\LineBreak{^^J}%
+ \begingroup
+ \lccode`\~=`\ \lccode`\}=`\ %
+ \lccode`\T=`\T\lccode`\H=`\H%
+ \catcode`\ =11 %
+\lowercase{\endgroup\def\PackageError#1#2#3{%
+\begingroup\errorcontextlines-1\immediate\write0{}\errhelp{#3}\def%
+\ {#1 Error: #2.^^J^^J
+Type H <return> for immediate help}\def~{\errmessage{%
+\ }}~\endgroup}}%
+ \fi
+ \edef\next
+ {%
+ \noexpand\PackageError{expl3}{\ShortText}
+ {\LongText Loading of expl3 will abort!}%
+ \endgroup
+ \noexpand\endinput
+ }%
+ \fi
+\next
+% \end{macrocode}
+%
+% \subsection{The \LaTeX3 code environment}
+%
+% The code environment is now set up.
+%
+% \begin{macro}{\ExplSyntaxOff}
+% Before changing any category codes, in package mode we need to save
+% the situation before loading. Note the set up here means that once applied
+% \cs{ExplSyntaxOff} becomes a \enquote{do nothing} command until
+% \cs{ExplSyntaxOn} is used.
+% \begin{macrocode}
+\protected\edef\ExplSyntaxOff
+ {%
+ \protected\def\noexpand\ExplSyntaxOff{}%
+ \catcode 9 = \the\catcode 9\relax
+ \catcode 32 = \the\catcode 32\relax
+ \catcode 34 = \the\catcode 34\relax
+ \catcode 58 = \the\catcode 58\relax
+ \catcode 94 = \the\catcode 94\relax
+ \catcode 95 = \the\catcode 95\relax
+ \catcode 124 = \the\catcode 124\relax
+ \catcode 126 = \the\catcode 126\relax
+ \endlinechar = \the\endlinechar\relax
+ \chardef\csname\detokenize{l_@@_expl_bool}\endcsname = 0\relax
+ }%
+% \end{macrocode}
+% \end{macro}
+%
+% The code environment is now set up.
+% \begin{macrocode}
+\catcode 9 = 9\relax
+\catcode 32 = 9\relax
+\catcode 34 = 12\relax
+\catcode 58 = 11\relax
+\catcode 94 = 7\relax
+\catcode 95 = 11\relax
+\catcode 124 = 12\relax
+\catcode 126 = 10\relax
+\endlinechar = 32\relax
+% \end{macrocode}
+%
+% \begin{variable}{\l_@@_expl_bool}
+% The status for code syntax: this is on at present.
+% \begin{macrocode}
+\global\chardef\l_@@_expl_bool = 1\relax
+% \end{macrocode}
+%\end{variable}
+%
+% \begin{macro}{\ExplSyntaxOn}
+% The idea here is that multiple \cs{ExplSyntaxOn} calls are not
+% going to mess up category codes, and that multiple calls to
+% \cs{ExplSyntaxOff} are also not wasting time. Applying
+% \cs{ExplSyntaxOn} alters the definition of \cs{ExplSyntaxOff}
+% and so in package mode this function should not be used until after
+% the end of the loading process!
+% \begin{macrocode}
+\protected \def \ExplSyntaxOn
+ {
+ \bool_if:NF \l_@@_expl_bool
+ {
+ \cs_set_protected:Npe \ExplSyntaxOff
+ {
+ \char_set_catcode:nn { 9 } { \char_value_catcode:n { 9 } }
+ \char_set_catcode:nn { 32 } { \char_value_catcode:n { 32 } }
+ \char_set_catcode:nn { 34 } { \char_value_catcode:n { 34 } }
+ \char_set_catcode:nn { 58 } { \char_value_catcode:n { 58 } }
+ \char_set_catcode:nn { 94 } { \char_value_catcode:n { 94 } }
+ \char_set_catcode:nn { 95 } { \char_value_catcode:n { 95 } }
+ \char_set_catcode:nn { 124 } { \char_value_catcode:n { 124 } }
+ \char_set_catcode:nn { 126 } { \char_value_catcode:n { 126 } }
+ \tex_endlinechar:D =
+ \tex_the:D \tex_endlinechar:D \scan_stop:
+ \bool_set_false:N \l_@@_expl_bool
+ \cs_set_protected:Npn \ExplSyntaxOff { }
+ }
+ }
+ \char_set_catcode_ignore:n { 9 } % tab
+ \char_set_catcode_ignore:n { 32 } % space
+ \char_set_catcode_other:n { 34 } % double quote
+ \char_set_catcode_letter:n { 58 } % colon
+ \char_set_catcode_math_superscript:n { 94 } % circumflex
+ \char_set_catcode_letter:n { 95 } % underscore
+ \char_set_catcode_other:n { 124 } % pipe
+ \char_set_catcode_space:n { 126 } % tilde
+ \tex_endlinechar:D = 32 \scan_stop:
+ \bool_set_true:N \l_@@_expl_bool
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3box.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3box.dtx
new file mode 100644
index 00000000000..2d4902950fb
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3box.dtx
@@ -0,0 +1,2530 @@
+% \iffalse meta-comment
+%
+%% File: l3box.dtx
+%
+% Copyright (C) 2005-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3box} module\\ Boxes^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% Box variables contain typeset material that can be inserted on the
+% page or in other boxes. Their contents cannot be converted back to
+% lists of tokens.
+% There are three kinds of box operations: horizontal mode denoted
+% with prefix |\hbox_|, vertical mode with prefix |\vbox_|, and the
+% generic operations working in both modes with prefix |\box_|.
+% For instance, a new box variable containing the words \enquote{Hello,
+% world!} (in a horizontal box) can be obtained by the following code.
+% \begin{verbatim}
+% \box_new:N \l_hello_box
+% \hbox_set:Nn \l_hello_box { Hello, ~ world! }
+% \end{verbatim}
+% The argument is typeset inside a \TeX{} group so that any variables
+% assigned during the construction of this box restores its value
+% afterwards.
+%
+% Box variables from \pkg{l3box} are compatible with those of \LaTeXe{}
+% and plain \TeX{} and can be used interchangeably. The \pkg{l3box}
+% commands to construct boxes, such as \cs{hbox:n} or \cs{hbox_set:Nn},
+% are \enquote{color-safe}, meaning that
+% \begin{verbatim}
+% \hbox:n { \color_select:n { blue } Hello, } ~ world!
+% \end{verbatim}
+% will result in \enquote{Hello,} taking the color blue, but
+% \enquote{world!} remaining with the prevailing color outside the box.
+%
+% \section{Creating and initialising boxes}
+%
+% \begin{function}{\box_new:N, \box_new:c}
+% \begin{syntax}
+% \cs{box_new:N} \meta{box}
+% \end{syntax}
+% Creates a new \meta{box} or raises an error if the name is
+% already taken. The declaration is global. The \meta{box} is
+% initially void.
+% \end{function}
+%
+% \begin{function}{\box_clear:N, \box_clear:c, \box_gclear:N, \box_gclear:c}
+% \begin{syntax}
+% \cs{box_clear:N} \meta{box}
+% \end{syntax}
+% Clears the content of the \meta{box} by setting the box equal to
+% \cs{c_empty_box}.
+% \end{function}
+%
+% \begin{function}
+% {\box_clear_new:N, \box_clear_new:c, \box_gclear_new:N, \box_gclear_new:c}
+% \begin{syntax}
+% \cs{box_clear_new:N} \meta{box}
+% \end{syntax}
+% Ensures that the \meta{box} exists globally by applying
+% \cs{box_new:N} if necessary, then applies
+% \cs[index=box_clear:N]{box_(g)clear:N} to leave
+% the \meta{box} empty.
+% \end{function}
+%
+% \begin{function}
+% {
+% \box_set_eq:NN, \box_set_eq:cN, \box_set_eq:Nc, \box_set_eq:cc,
+% \box_gset_eq:NN, \box_gset_eq:cN, \box_gset_eq:Nc, \box_gset_eq:cc
+% }
+% \begin{syntax}
+% \cs{box_set_eq:NN} \meta{box_1} \meta{box_2}
+% \end{syntax}
+% Sets the content of \meta{box_1} equal to that of \meta{box_2}.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added=2012-03-03]
+% {\box_if_exist:N, \box_if_exist:c}
+% \begin{syntax}
+% \cs{box_if_exist_p:N} \meta{box}
+% \cs{box_if_exist:NTF} \meta{box} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{box} is currently defined. This does not
+% check that the \meta{box} really is a box.
+% \end{function}
+%
+% \section{Using boxes}
+%
+% \begin{function}{\box_use:N, \box_use:c}
+% \begin{syntax}
+% \cs{box_use:N} \meta{box}
+% \end{syntax}
+% Inserts the current content of the \meta{box} onto the current
+% list for typesetting. An error is raised if the variable does
+% not exist or if it is invalid.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{copy}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}{\box_move_right:nn, \box_move_left:nn}
+% \begin{syntax}
+% \cs{box_move_right:nn} \Arg{dim expr} \Arg{box function}
+% \end{syntax}
+% This function operates in vertical mode, and inserts the
+% material specified by the \meta{box function}
+% such that its reference point is displaced horizontally by the given
+% \meta{dim expr} from the reference point for typesetting, to the right
+% or left as appropriate. The \meta{box function} should be
+% a box operation such as \cs{box_use:N} |\<box>| or a \enquote{raw}
+% box specification such as \cs{vbox:n} |{ xyz }|.
+% \end{function}
+%
+% \begin{function}{\box_move_up:nn, \box_move_down:nn}
+% \begin{syntax}
+% \cs{box_move_up:nn} \Arg{dim expr} \Arg{box function}
+% \end{syntax}
+% This function operates in horizontal mode, and inserts the
+% material specified by the \meta{box function}
+% such that its reference point is displaced vertically by the given
+% \meta{dim expr} from the reference point for typesetting, up
+% or down as appropriate. The \meta{box function} should be
+% a box operation such as \cs{box_use:N} |\<box>| or a \enquote{raw}
+% box specification such as \cs{vbox:n} |{ xyz }|.
+% \end{function}
+%
+% \section{Measuring and setting box dimensions}
+%
+% \begin{function}{\box_dp:N, \box_dp:c}
+% \begin{syntax}
+% \cs{box_dp:N} \meta{box}
+% \end{syntax}
+% Calculates the depth (below the baseline) of the \meta{box}
+% in a form suitable for use in a \meta{dim expr}.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{dp}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}{\box_ht:N, \box_ht:c}
+% \begin{syntax}
+% \cs{box_ht:N} \meta{box}
+% \end{syntax}
+% Calculates the height (above the baseline) of the \meta{box}
+% in a form suitable for use in a \meta{dim expr}.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{ht}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}{\box_wd:N, \box_wd:c}
+% \begin{syntax}
+% \cs{box_wd:N} \meta{box}
+% \end{syntax}
+% Calculates the width of the \meta{box} in a form
+% suitable for use in a \meta{dim expr}.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{wd}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2021-05-05]{\box_ht_plus_dp:N, \box_ht_plus_dp:c}
+% \begin{syntax}
+% \cs{box_ht_plus_dp:N} \meta{box}
+% \end{syntax}
+% Calculates the total vertical size (height plus depth) of the \meta{box}
+% in a form suitable for use in a \meta{dim expr}.
+% \end{function}
+%
+% \begin{function}[updated = 2019-01-22]
+% {
+% \box_set_dp:Nn, \box_set_dp:cn,
+% \box_gset_dp:Nn, \box_gset_dp:cn
+% }
+% \begin{syntax}
+% \cs{box_set_dp:Nn} \meta{box} \Arg{dim expr}
+% \end{syntax}
+% Set the depth (below the baseline) of the \meta{box} to the value of
+% the \Arg{dim expr}.
+% \end{function}
+%
+% \begin{function}[updated = 2019-01-22]
+% {
+% \box_set_ht:Nn, \box_set_ht:cn,
+% \box_gset_ht:Nn, \box_gset_ht:cn
+% }
+% \begin{syntax}
+% \cs{box_set_ht:Nn} \meta{box} \Arg{dim expr}
+% \end{syntax}
+% Set the height (above the baseline) of the \meta{box} to the value of
+% the \Arg{dim expr}.
+% \end{function}
+%
+% \begin{function}[updated = 2019-01-22]
+% {
+% \box_set_wd:Nn, \box_set_wd:cn,
+% \box_gset_wd:Nn, \box_gset_wd:cn
+% }
+% \begin{syntax}
+% \cs{box_set_wd:Nn} \meta{box} \Arg{dim expr}
+% \end{syntax}
+% Set the width of the \meta{box} to the value of the
+% \Arg{dim expr}.
+% \end{function}
+%
+% \section{Box conditionals}
+%
+% \begin{function}[EXP,pTF]{\box_if_empty:N, \box_if_empty:c}
+% \begin{syntax}
+% \cs{box_if_empty_p:N} \meta{box}
+% \cs{box_if_empty:NTF} \meta{box} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{box} is a empty (equal to \cs{c_empty_box}).
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\box_if_horizontal:N, \box_if_horizontal:c}
+% \begin{syntax}
+% \cs{box_if_horizontal_p:N} \meta{box}
+% \cs{box_if_horizontal:NTF} \meta{box} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{box} is a horizontal box.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\box_if_vertical:N, \box_if_vertical:c}
+% \begin{syntax}
+% \cs{box_if_vertical_p:N} \meta{box}
+% \cs{box_if_vertical:NTF} \meta{box} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{box} is a vertical box.
+% \end{function}
+%
+% \section{The last box inserted}
+%
+% \begin{function}
+% {
+% \box_set_to_last:N, \box_set_to_last:c,
+% \box_gset_to_last:N, \box_gset_to_last:c
+% }
+% \begin{syntax}
+% \cs{box_set_to_last:N} \meta{box}
+% \end{syntax}
+% Sets the \meta{box} equal to the last item (box) added to the current
+% partial list, removing the item from the list at the same time. When
+% applied to the main vertical list, the \meta{box} is always void as
+% it is not possible to recover the last added item.
+% \end{function}
+%
+% \section{Constant boxes}
+%
+% \begin{variable}[updated = 2012-11-04]{\c_empty_box}
+% This is a permanently empty box, which is neither set as horizontal
+% nor vertical.
+% \begin{texnote}
+% At the \TeX{} level this is a void box.
+% \end{texnote}
+% \end{variable}
+%
+% \section{Scratch boxes}
+%
+% \begin{variable}[updated = 2012-11-04]{\l_tmpa_box, \l_tmpb_box}
+% Scratch boxes for local assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \begin{variable}{\g_tmpa_box, \g_tmpb_box}
+% Scratch boxes for global assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \section{Viewing box contents}
+%
+% \begin{function}[updated = 2012-05-11]{\box_show:N, \box_show:c}
+% \begin{syntax}
+% \cs{box_show:N} \meta{box}
+% \end{syntax}
+% Shows full details of the content of the \meta{box} in the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2012-05-11]{\box_show:Nnn, \box_show:cnn}
+% \begin{syntax}
+% \cs{box_show:Nnn} \meta{box} \Arg{int expr_1} \Arg{int expr_2}
+% \end{syntax}
+% Display the contents of \meta{box} in the terminal, showing the first
+% \meta{int expr_1} items of the box, and descending into \meta{int expr_2}
+% group levels.
+% \end{function}
+%
+% \begin{function}[added = 2012-05-11]{\box_log:N, \box_log:c}
+% \begin{syntax}
+% \cs{box_log:N} \meta{box}
+% \end{syntax}
+% Writes full details of the content of the \meta{box} to the log.
+% \end{function}
+%
+% \begin{function}[added = 2012-05-11]{\box_log:Nnn, \box_log:cnn}
+% \begin{syntax}
+% \cs{box_log:Nnn} \meta{box} \Arg{int expr_1} \Arg{int expr_2}
+% \end{syntax}
+% Writes the contents of \meta{box} to the log, showing the first
+% \meta{int expr_1} items of the box, and descending into \meta{int expr_2}
+% group levels.
+% \end{function}
+%
+% \section{Boxes and color}
+%
+% All \LaTeX{}3 boxes are \enquote{color safe}: a color set inside the box
+% stops applying after the end of the box has occurred.
+%
+% \section{Horizontal mode boxes}
+%
+% \begin{function}[updated = 2017-04-05]{\hbox:n}
+% \begin{syntax}
+% \cs{hbox:n} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} into a horizontal box of natural
+% width and then includes this box in the current list for typesetting.
+% \end{function}
+%
+% \begin{function}[updated = 2017-04-05]{\hbox_to_wd:nn}
+% \begin{syntax}
+% \cs{hbox_to_wd:nn} \Arg{dim expr} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} into a horizontal box of width
+% \meta{dim expr} and then includes this box in the current list for
+% typesetting.
+% \end{function}
+%
+% \begin{function}[updated = 2017-04-05]{\hbox_to_zero:n}
+% \begin{syntax}
+% \cs{hbox_to_zero:n} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} into a horizontal box of zero width
+% and then includes this box in the current list for typesetting.
+% \end{function}
+%
+% \begin{function}[updated = 2017-04-05]
+% {\hbox_set:Nn, \hbox_set:cn, \hbox_gset:Nn, \hbox_gset:cn}
+% \begin{syntax}
+% \cs{hbox_set:Nn} \meta{box} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} at natural width and then stores the
+% result inside the \meta{box}.
+% \end{function}
+%
+% \begin{function}[updated = 2017-04-05]
+% {
+% \hbox_set_to_wd:Nnn, \hbox_set_to_wd:cnn,
+% \hbox_gset_to_wd:Nnn, \hbox_gset_to_wd:cnn
+% }
+% \begin{syntax}
+% \cs{hbox_set_to_wd:Nnn} \meta{box} \Arg{dim expr} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} to the width given by the \meta{dim expr}
+% and then stores the result inside the \meta{box}.
+% \end{function}
+%
+% \begin{function}[added = 2020-08-25]{\hbox_overlap_center:n}
+% \begin{syntax}
+% \cs{hbox_overlap_center:n} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} into a horizontal box of zero width
+% such that material protrudes equally to both sides of the insertion point.
+% \end{function}
+%
+% \begin{function}[updated = 2017-04-05]{\hbox_overlap_right:n}
+% \begin{syntax}
+% \cs{hbox_overlap_right:n} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} into a horizontal box of zero width
+% such that material protrudes to the right of the insertion point.
+% \end{function}
+%
+% \begin{function}[updated = 2017-04-05]{\hbox_overlap_left:n}
+% \begin{syntax}
+% \cs{hbox_overlap_left:n} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} into a horizontal box of zero width
+% such that material protrudes to the left of the insertion point.
+% \end{function}
+%
+% \begin{function}[updated = 2017-04-05]
+% {
+% \hbox_set:Nw, \hbox_set:cw,
+% \hbox_set_end:,
+% \hbox_gset:Nw, \hbox_gset:cw,
+% \hbox_gset_end:
+% }
+% \begin{syntax}
+% \cs{hbox_set:Nw} \meta{box} \meta{contents} \cs{hbox_set_end:}
+% \end{syntax}
+% Typesets the \meta{contents} at natural width and then stores the
+% result inside the \meta{box}. In contrast
+% to \cs{hbox_set:Nn} this function does not absorb the argument
+% when finding the \meta{content}, and so can be used in circumstances
+% where the \meta{content} may not be a simple argument.
+% \end{function}
+%
+% \begin{function}[added = 2017-06-08]
+% {
+% \hbox_set_to_wd:Nnw, \hbox_set_to_wd:cnw,
+% \hbox_gset_to_wd:Nnw, \hbox_gset_to_wd:cnw
+% }
+% \begin{syntax}
+% \cs{hbox_set_to_wd:Nnw} \meta{box} \Arg{dim expr} \meta{contents} \cs{hbox_set_end:}
+% \end{syntax}
+% Typesets the \meta{contents} to the width given by the \meta{dim expr}
+% and then stores the result inside the \meta{box}. In contrast
+% to \cs{hbox_set_to_wd:Nnn} this function does not absorb the argument
+% when finding the \meta{content}, and so can be used in circumstances
+% where the \meta{content} may not be a simple argument
+% \end{function}
+%
+% \begin{function}{\hbox_unpack:N, \hbox_unpack:c}
+% \begin{syntax}
+% \cs{hbox_unpack:N} \meta{box}
+% \end{syntax}
+% Unpacks the content of the horizontal \meta{box}, retaining any stretching
+% or shrinking applied when the \meta{box} was set.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{unhcopy}.
+% \end{texnote}
+% \end{function}
+%
+% \section{Vertical mode boxes}
+%
+% Vertical boxes inherit their baseline from their contents. The
+% standard case is that the baseline of the box is at the same position
+% as that of the last item added to the box. This means that the box
+% has no depth unless the last item added to it had depth. As a
+% result most vertical boxes have a large height value and small or
+% zero depth. The exception are |_top| boxes, where the reference point
+% is that of the first item added. These tend to have a large depth and
+% small height, although the latter is typically non-zero.
+%
+% \begin{function}[updated = 2017-04-05]{\vbox:n}
+% \begin{syntax}
+% \cs{vbox:n} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} into a vertical box of natural height
+% and includes this box in the current list for typesetting.
+% \end{function}
+%
+% \begin{function}[updated = 2017-04-05]{\vbox_top:n}
+% \begin{syntax}
+% \cs{vbox_top:n} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} into a vertical box of natural height
+% and includes this box in the current list for typesetting. The
+% baseline of the box is equal to that of the \emph{first}
+% item added to the box.
+% \end{function}
+%
+% \begin{function}[updated = 2017-04-05]{\vbox_to_ht:nn}
+% \begin{syntax}
+% \cs{vbox_to_ht:nn} \Arg{dim expr} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} into a vertical box of height
+% \meta{dim expr} and then includes this box in the current list for
+% typesetting.
+% \end{function}
+%
+% \begin{function}[updated = 2017-04-05]{\vbox_to_zero:n}
+% \begin{syntax}
+% \cs{vbox_to_zero:n} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} into a vertical box of zero height
+% and then includes this box in the current list for typesetting.
+% \end{function}
+%
+% \begin{function}[updated = 2017-04-05]
+% {\vbox_set:Nn, \vbox_set:cn, \vbox_gset:Nn, \vbox_gset:cn}
+% \begin{syntax}
+% \cs{vbox_set:Nn} \meta{box} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} at natural height and then stores the
+% result inside the \meta{box}.
+% \end{function}
+%
+% \begin{function}[updated = 2017-04-05]
+% {\vbox_set_top:Nn, \vbox_set_top:cn, \vbox_gset_top:Nn, \vbox_gset_top:cn}
+% \begin{syntax}
+% \cs{vbox_set_top:Nn} \meta{box} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} at natural height and then stores the
+% result inside the \meta{box}. The baseline of the box is equal
+% to that of the \emph{first} item added to the box.
+% \end{function}
+%
+% \begin{function}[updated = 2017-04-05]
+% {
+% \vbox_set_to_ht:Nnn, \vbox_set_to_ht:cnn,
+% \vbox_gset_to_ht:Nnn, \vbox_gset_to_ht:cnn
+% }
+% \begin{syntax}
+% \cs{vbox_set_to_ht:Nnn} \meta{box} \Arg{dim expr} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} to the height given by the
+% \meta{dim expr} and then stores the result inside the \meta{box}.
+% \end{function}
+%
+% \begin{function}[updated = 2017-04-05]
+% {
+% \vbox_set:Nw, \vbox_set:cw,
+% \vbox_set_end:,
+% \vbox_gset:Nw, \vbox_gset:cw,
+% \vbox_gset_end:
+% }
+% \begin{syntax}
+% \cs{vbox_set:Nw} \meta{box} \meta{contents} \cs{vbox_set_end:}
+% \end{syntax}
+% Typesets the \meta{contents} at natural height and then stores the
+% result inside the \meta{box}. In contrast
+% to \cs{vbox_set:Nn} this function does not absorb the argument
+% when finding the \meta{content}, and so can be used in circumstances
+% where the \meta{content} may not be a simple argument.
+% \end{function}
+%
+% \begin{function}[added = 2017-06-08]
+% {
+% \vbox_set_to_ht:Nnw, \vbox_set_to_ht:cnw,
+% \vbox_gset_to_ht:Nnw, \vbox_gset_to_ht:cnw
+% }
+% \begin{syntax}
+% \cs{vbox_set_to_ht:Nnw} \meta{box} \Arg{dim expr} \meta{contents} \cs{vbox_set_end:}
+% \end{syntax}
+% Typesets the \meta{contents} to the height given by the \meta{dim expr}
+% and then stores the result inside the \meta{box}. In contrast
+% to \cs{vbox_set_to_ht:Nnn} this function does not absorb the argument
+% when finding the \meta{content}, and so can be used in circumstances
+% where the \meta{content} may not be a simple argument
+% \end{function}
+%
+%
+% \begin{function}[updated = 2018-12-29]
+% {
+% \vbox_set_split_to_ht:NNn, \vbox_set_split_to_ht:cNn,
+% \vbox_set_split_to_ht:Ncn, \vbox_set_split_to_ht:ccn,
+% \vbox_gset_split_to_ht:NNn, \vbox_gset_split_to_ht:cNn,
+% \vbox_gset_split_to_ht:Ncn, \vbox_gset_split_to_ht:ccn
+% }
+% \begin{syntax}
+% \cs{vbox_set_split_to_ht:NNn} \meta{box_1} \meta{box_2} \Arg{dim expr}
+% \end{syntax}
+% Sets \meta{box_1} to contain material to the height given by the
+% \meta{dim expr} by removing content from the top of \meta{box_2}
+% (which must be a vertical box).
+% \end{function}
+%
+% \begin{function}{\vbox_unpack:N, \vbox_unpack:c}
+% \begin{syntax}
+% \cs{vbox_unpack:N} \meta{box}
+% \end{syntax}
+% Unpacks the content of the vertical \meta{box}, retaining any stretching
+% or shrinking applied when the \meta{box} was set.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{unvcopy}.
+% \end{texnote}
+% \end{function}
+%
+% \section{Using boxes efficiently}
+%
+% The functions above for using box contents work in exactly the same
+% way as for any other \pkg{expl3} variable. However, for efficiency
+% reasons, it is also useful to have functions which \emph{drop} box
+% contents on use. When a box is dropped, the box becomes empty at the group
+% level \emph{where the box was originally set} rather than necessarily
+% \emph{at the current group level}. For example, with
+% \begin{verbatim}
+% \hbox_set:Nn \l_tmpa_box { A }
+% \group_begin:
+% \hbox_set:Nn \l_tmpa_box { B }
+% \group_begin:
+% \box_use_drop:N \l_tmpa_box
+% \group_end:
+% \box_show:N \l_tmpa_box
+% \group_end:
+% \box_show:N \l_tmpa_box
+% \end{verbatim}
+% the first use of \cs{box_show:N} will show an entirely cleared (void) box, and the
+% second will show the letter |A| in the box.
+%
+% These functions should be preferred when the content of the box is no
+% longer required after use. Note that due to the unusual scoping behaviour of
+% \texttt{drop} functions they may be applied to both local and global boxes:
+% the latter will naturally be set and thus cleared at a global level.
+%
+% \begin{function}{\box_use_drop:N, \box_use_drop:c}
+% \begin{syntax}
+% \cs{box_use_drop:N} \meta{box}
+% \end{syntax}
+% Inserts the current content of the \meta{box} onto the current
+% list for typesetting then drops the box content. An error is raised if the
+% variable does not exist or if it is invalid. This function may be applied to
+% local or global boxes.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{box}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2019-01-17]
+% {
+% \box_set_eq_drop:NN, \box_set_eq_drop:cN,
+% \box_set_eq_drop:Nc, \box_set_eq_drop:cc
+% }
+% \begin{syntax}
+% \cs{box_set_eq_drop:NN} \meta{box_1} \meta{box_2}
+% \end{syntax}
+% Sets the content of \meta{box_1} equal to that of \meta{box_2}, then
+% drops \meta{box_2}.
+% \end{function}
+%
+% \begin{function}[added = 2019-01-17]
+% {
+% \box_gset_eq_drop:NN, \box_gset_eq_drop:cN,
+% \box_gset_eq_drop:Nc, \box_gset_eq_drop:cc
+% }
+% \begin{syntax}
+% \cs{box_gset_eq_drop:NN} \meta{box_1} \meta{box_2}
+% \end{syntax}
+% Sets the content of \meta{box_1} globally equal to that of \meta{box_2},
+% then drops \meta{box_2}.
+% \end{function}
+%
+% \begin{function}[added = 2019-01-17]
+% {\hbox_unpack_drop:N, \hbox_unpack_drop:c}
+% \begin{syntax}
+% \cs{hbox_unpack_drop:N} \meta{box}
+% \end{syntax}
+% Unpacks the content of the horizontal \meta{box}, retaining any stretching
+% or shrinking applied when the \meta{box} was set. The original \meta{box}
+% is then dropped.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{unhbox}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2019-01-17]
+% {\vbox_unpack_drop:N, \vbox_unpack_drop:c}
+% \begin{syntax}
+% \cs{vbox_unpack_drop:N} \meta{box}
+% \end{syntax}
+% Unpacks the content of the vertical \meta{box}, retaining any stretching
+% or shrinking applied when the \meta{box} was set. The original \meta{box}
+% is then dropped.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{unvbox}.
+% \end{texnote}
+% \end{function}
+%
+% \section{Affine transformations}
+%
+% Affine transformations are changes which (informally) preserve straight
+% lines. Simple translations are affine transformations, but are better handled
+% in \TeX{} by doing the translation first, then inserting an unmodified box.
+% On the other hand, rotation and resizing of boxed material can best be
+% handled by modifying boxes. These transformations are described here.
+%
+% \begin{function}[added = 2017-04-04, updated = 2019-01-22]
+% {
+% \box_autosize_to_wd_and_ht:Nnn, \box_autosize_to_wd_and_ht:cnn,
+% \box_gautosize_to_wd_and_ht:Nnn, \box_gautosize_to_wd_and_ht:cnn,
+% }
+% \begin{syntax}
+% \cs{box_autosize_to_wd_and_ht:Nnn} \meta{box} \Arg{x-size} \Arg{y-size}
+% \end{syntax}
+% Resizes the \meta{box} to fit within the given \meta{x-size} (horizontally)
+% and \meta{y-size} (vertically); both of the sizes are dimension
+% expressions. The \meta{y-size} is the height only: it does not include any
+% depth. The updated \meta{box} is an |hbox|, irrespective of the nature
+% of the \meta{box} before the resizing is applied. The final size of the
+% \meta{box} is the smaller of \Arg{x-size} and \Arg{y-size},
+% \emph{i.e.}~the result fits within the dimensions specified. Negative
+% sizes cause the material in the \meta{box} to be reversed in direction,
+% but the reference point of the \meta{box} is unchanged. Thus a negative
+% \meta{y-size} results in the \meta{box} having a depth dependent on the
+% height of the original and \emph{vice versa}.
+% \end{function}
+%
+% \begin{function}[added = 2017-04-04, updated = 2019-01-22]
+% {
+% \box_autosize_to_wd_and_ht_plus_dp:Nnn,
+% \box_autosize_to_wd_and_ht_plus_dp:cnn,
+% \box_gautosize_to_wd_and_ht_plus_dp:Nnn,
+% \box_gautosize_to_wd_and_ht_plus_dp:cnn
+% }
+% \begin{syntax}
+% \cs{box_autosize_to_wd_and_ht_plus_dp:Nnn} \meta{box} \Arg{x-size} \Arg{y-size}
+% \end{syntax}
+% Resizes the \meta{box} to fit within the given \meta{x-size} (horizontally)
+% and \meta{y-size} (vertically); both of the sizes are dimension
+% expressions. The \meta{y-size} is the total vertical size (height plus
+% depth). The updated \meta{box} is an |hbox|, irrespective of the nature
+% of the \meta{box} before the resizing is applied. The final size of the
+% \meta{box} is the smaller of \Arg{x-size} and \Arg{y-size},
+% \emph{i.e.}~the result fits within the dimensions specified. Negative
+% sizes cause the material in the \meta{box} to be reversed in direction,
+% but the reference point of the \meta{box} is unchanged. Thus a negative
+% \meta{y-size} results in the \meta{box} having a depth dependent on the
+% height of the original and \emph{vice versa}.
+% \end{function}
+%
+% \begin{function}[updated = 2019-01-22]
+% {
+% \box_resize_to_ht:Nn, \box_resize_to_ht:cn,
+% \box_gresize_to_ht:Nn, \box_gresize_to_ht:cn
+% }
+% \begin{syntax}
+% \cs{box_resize_to_ht:Nn} \meta{box} \Arg{y-size}
+% \end{syntax}
+% Resizes the \meta{box} to \meta{y-size} (vertically), scaling the horizontal
+% size by the same amount; \meta{y-size} is a dimension expression. The
+% \meta{y-size} is the height only: it does not include any depth. The updated
+% \meta{box} is an |hbox|, irrespective of the nature of the \meta{box}
+% before the resizing is applied. A negative \meta{y-size} causes the
+% material in the \meta{box} to be reversed in direction, but the reference
+% point of the \meta{box} is unchanged. Thus a negative \meta{y-size}
+% results in the \meta{box} having a depth dependent on the height of the
+% original and \emph{vice versa}.
+% \end{function}
+%
+% \begin{function}[updated = 2019-01-22]
+% {
+% \box_resize_to_ht_plus_dp:Nn, \box_resize_to_ht_plus_dp:cn,
+% \box_gresize_to_ht_plus_dp:Nn, \box_gresize_to_ht_plus_dp:cn,
+% }
+% \begin{syntax}
+% \cs{box_resize_to_ht_plus_dp:Nn} \meta{box} \Arg{y-size}
+% \end{syntax}
+% Resizes the \meta{box} to \meta{y-size} (vertically), scaling the horizontal
+% size by the same amount; \meta{y-size} is a dimension expression. The
+% \meta{y-size} is the total vertical size (height plus depth). The updated
+% \meta{box} is an |hbox|, irrespective of the nature of the \meta{box}
+% before the resizing is applied. A negative \meta{y-size} causes
+% the material in the \meta{box} to be reversed in direction, but the
+% reference point of the \meta{box} is unchanged. Thus a negative
+% \meta{y-size} results in the \meta{box} having a depth dependent on the
+% height of the original and \emph{vice versa}.
+% \end{function}
+%
+% \begin{function}[updated = 2019-01-22]
+% {
+% \box_resize_to_wd:Nn, \box_resize_to_wd:cn,
+% \box_gresize_to_wd:Nn, \box_gresize_to_wd:cn
+% }
+% \begin{syntax}
+% \cs{box_resize_to_wd:Nn} \meta{box} \Arg{x-size}
+% \end{syntax}
+% Resizes the \meta{box} to \meta{x-size} (horizontally), scaling the vertical
+% size by the same amount; \meta{x-size} is a dimension expression. The updated
+% \meta{box} is an |hbox|, irrespective of the nature of the \meta{box}
+% before the resizing is applied. A negative \meta{x-size} causes the
+% material in the \meta{box} to be reversed in direction, but the reference
+% point of the \meta{box} is unchanged. Thus a negative \meta{x-size}
+% results in the \meta{box} having a depth dependent on the height of the
+% original and \emph{vice versa}.
+% \end{function}
+%
+% \begin{function}[added = 2014-07-03, updated = 2019-01-22]
+% {
+% \box_resize_to_wd_and_ht:Nnn, \box_resize_to_wd_and_ht:cnn,
+% \box_gresize_to_wd_and_ht:Nnn, \box_gresize_to_wd_and_ht:cnn
+% }
+% \begin{syntax}
+% \cs{box_resize_to_wd_and_ht:Nnn} \meta{box} \Arg{x-size} \Arg{y-size}
+% \end{syntax}
+% Resizes the \meta{box} to \meta{x-size} (horizontally) and \meta{y-size}
+% (vertically): both of the sizes are dimension expressions. The
+% \meta{y-size} is the height only and does not include any depth. The updated
+% \meta{box} is an |hbox|, irrespective of the nature of the \meta{box}
+% before the resizing is applied. Negative sizes cause the material in
+% the \meta{box} to be reversed in direction, but the reference point of the
+% \meta{box} is unchanged. Thus a negative \meta{y-size} results in
+% the \meta{box} having a depth dependent on the height of the original and
+% \emph{vice versa}.
+% \end{function}
+%
+% \begin{function}[added = 2017-04-06, updated = 2019-01-22]
+% {
+% \box_resize_to_wd_and_ht_plus_dp:Nnn,
+% \box_resize_to_wd_and_ht_plus_dp:cnn,
+% \box_gresize_to_wd_and_ht_plus_dp:Nnn,
+% \box_gresize_to_wd_and_ht_plus_dp:cnn
+% }
+% \begin{syntax}
+% \cs{box_resize_to_wd_and_ht_plus_dp:Nnn} \meta{box} \Arg{x-size} \Arg{y-size}
+% \end{syntax}
+% Resizes the \meta{box} to \meta{x-size} (horizontally) and \meta{y-size}
+% (vertically): both of the sizes are dimension expressions. The
+% \meta{y-size} is the total vertical size (height plus depth). The updated
+% \meta{box} is an |hbox|, irrespective of the nature of the \meta{box}
+% before the resizing is applied. Negative sizes cause the material in
+% the \meta{box} to be reversed in direction, but the reference point of the
+% \meta{box} is unchanged. Thus a negative \meta{y-size} results in
+% the \meta{box} having a depth dependent on the height of the original and
+% \emph{vice versa}.
+% \end{function}
+%
+% \begin{function}[updated = 2019-01-22]
+% {
+% \box_rotate:Nn, \box_rotate:cn,
+% \box_grotate:Nn, \box_grotate:cn
+% }
+% \begin{syntax}
+% \cs{box_rotate:Nn} \meta{box} \Arg{angle}
+% \end{syntax}
+% Rotates the \meta{box} by \meta{angle} (a \meta{fp expr} in degrees)
+% anti-clockwise about
+% its reference point. The reference point of the updated box is moved
+% horizontally such that it is at the left side of the smallest rectangle
+% enclosing the rotated material. The updated \meta{box} is an |hbox|,
+% irrespective of the nature of the \meta{box} before the rotation is applied.
+% \end{function}
+%
+% \begin{function}[updated = 2019-01-22]
+% {
+% \box_scale:Nnn, \box_scale:cnn,
+% \box_gscale:Nnn, \box_gscale:cnn
+% }
+% \begin{syntax}
+% \cs{box_scale:Nnn} \meta{box} \Arg{x-scale} \Arg{y-scale}
+% \end{syntax}
+% Scales the \meta{box} by factors \meta{x-scale} and \meta{y-scale} in
+% the horizontal and vertical directions, respectively (both scales are
+% \meta{fp expr}). The updated \meta{box} is an |hbox|, irrespective
+% of the nature of the \meta{box} before the scaling is applied. Negative
+% scalings cause the material in the \meta{box} to be reversed in
+% direction, but the reference point of the \meta{box} is unchanged.
+% Thus a negative \meta{y-scale} results in the \meta{box} having a depth
+% dependent on the height of the original and \emph{vice versa}.
+% \end{function}
+%
+% \section{Viewing part of a box}
+%
+% \begin{function}[updated = 2023-04-14]
+% {
+% \box_set_clipped:N, \box_set_clipped:c,
+% \box_gset_clipped:N, \box_gset_clipped:c
+% }
+% \begin{syntax}
+% \cs{box_set_clipped:N} \meta{box}
+% \end{syntax}
+% Clips the \meta{box} in the output so that only material inside the
+% bounding box is displayed in the output. The updated \meta{box} is an
+% hbox, irrespective of the nature of the \meta{box} before the clipping is
+% applied. Additional box levels are also generated by this operation.
+%
+% \begin{texnote}
+% Clipping is implemented by the driver, and as such the full content of
+% the box is placed in the output file. Thus clipping does not remove
+% any information from the raw output, and hidden material can therefore
+% be viewed by direct examination of the file.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2019-01-23]
+% {
+% \box_set_trim:Nnnnn, \box_set_trim:cnnnn,
+% \box_gset_trim:Nnnnn, \box_gset_trim:cnnnn
+% }
+% \begin{syntax}
+% \cs{box_set_trim:Nnnnn} \meta{box} \Arg{left} \Arg{bottom} \Arg{right} \Arg{top}
+% \end{syntax}
+% Adjusts the bounding box of the \meta{box} \meta{left} is removed from
+% the left-hand edge of the bounding box, \meta{right} from the right-hand
+% edge and so fourth. All adjustments are \meta{dim exprs}.
+% Material outside of the bounding box is still displayed in the output
+% unless \cs{box_set_clipped:N} is subsequently applied.
+% The updated \meta{box} is an
+% hbox, irrespective of the nature of the \meta{box} before the trim
+% operation is applied.
+% Additional box levels are also generated by this operation.
+% The behavior of the operation where the trims requested is
+% greater than the size of the box is undefined.
+% \end{function}
+%
+% \begin{function}[added = 2019-01-23]
+% {
+% \box_set_viewport:Nnnnn, \box_set_viewport:cnnnn,
+% \box_gset_viewport:Nnnnn, \box_gset_viewport:cnnnn
+% }
+% \begin{syntax}
+% \cs{box_set_viewport:Nnnnn} \meta{box} \Arg{llx} \Arg{lly} \Arg{urx} \Arg{ury}
+% \end{syntax}
+% Adjusts the bounding box of the \meta{box} such that it has lower-left
+% co-ordinates (\meta{llx}, \meta{lly}) and upper-right co-ordinates
+% (\meta{urx}, \meta{ury}). All four co-ordinate positions are
+% \meta{dim exprs}. Material outside of the bounding box is
+% still displayed in the output unless \cs{box_set_clipped:N} is
+% subsequently applied.
+% The updated \meta{box} is an
+% hbox, irrespective of the nature of the \meta{box} before the viewport
+% operation is applied.
+% Additional box levels are also generated by this operation.
+% \end{function}
+%
+% \section{Primitive box conditionals}
+%
+% \begin{function}[EXP]{\if_hbox:N}
+% \begin{syntax}
+% \cs{if_hbox:N} \meta{box}
+% ~~\meta{true code}
+% \cs{else:}
+% ~~\meta{false code}
+% \cs{fi:}
+% \end{syntax}
+% Tests is \meta{box} is a horizontal box.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{ifhbox}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\if_vbox:N}
+% \begin{syntax}
+% \cs{if_vbox:N} \meta{box}
+% ~~\meta{true code}
+% \cs{else:}
+% ~~\meta{false code}
+% \cs{fi:}
+% \end{syntax}
+% Tests is \meta{box} is a vertical box.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{ifvbox}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\if_box_empty:N}
+% \begin{syntax}
+% \cs{if_box_empty:N} \meta{box}
+% ~~\meta{true code}
+% \cs{else:}
+% ~~\meta{false code}
+% \cs{fi:}
+% \end{syntax}
+% Tests is \meta{box} is an empty (void) box.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{ifvoid}.
+% \end{texnote}
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3box} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=box>
+% \end{macrocode}
+%
+% \subsection{Support code}
+%
+% \begin{macro}{\@@_dim_eval:w}
+% \begin{macro}{\@@_dim_eval:n}
+% Evaluating a dimension expression expandably. The only
+% difference with \cs{dim_eval:n} is the lack of \cs{dim_use:N}, to
+% produce an internal dimension rather than expand it into characters.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_dim_eval:w \tex_dimexpr:D
+\cs_new:Npn \@@_dim_eval:n #1
+ { \@@_dim_eval:w #1 \scan_stop: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\__kernel_kern:n}
+% We need kerns in a few places. At present, we don't have a module for
+% this concept, so it goes in at first use: here. The idea is to avoid
+% repeated use of the bare primitive.
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_kern:n #1
+ { \tex_kern:D \@@_dim_eval:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Creating and initialising boxes}
+%
+% \TestFiles{m3box001.lvt}
+%
+% \begin{macro}{\box_new:N, \box_new:c}
+% Defining a new \meta{box} register: remember that box $255$ is not
+% generally available.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs:w newbox \cs_end: #1
+ }
+\cs_generate_variant:Nn \box_new:N { c }
+% \end{macrocode}
+%
+% \begin{macro}{\box_clear:N, \box_clear:c}
+% \begin{macro}{\box_gclear:N, \box_gclear:c}
+% \testfile*
+% Clear a \meta{box} register.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_clear:N #1
+ { \box_set_eq:NN #1 \c_empty_box }
+\cs_new_protected:Npn \box_gclear:N #1
+ { \box_gset_eq:NN #1 \c_empty_box }
+\cs_generate_variant:Nn \box_clear:N { c }
+\cs_generate_variant:Nn \box_gclear:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\box_clear_new:N, \box_clear_new:c}
+% \begin{macro}{\box_gclear_new:N, \box_gclear_new:c}
+% \testfile*
+% Clear or new.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_clear_new:N #1
+ { \box_if_exist:NTF #1 { \box_clear:N #1 } { \box_new:N #1 } }
+\cs_new_protected:Npn \box_gclear_new:N #1
+ { \box_if_exist:NTF #1 { \box_gclear:N #1 } { \box_new:N #1 } }
+\cs_generate_variant:Nn \box_clear_new:N { c }
+\cs_generate_variant:Nn \box_gclear_new:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\box_set_eq:NN, \box_set_eq:cN, \box_set_eq:Nc, \box_set_eq:cc}
+% \testfile*
+% \begin{macro}
+% {\box_gset_eq:NN, \box_gset_eq:cN, \box_gset_eq:Nc, \box_gset_eq:cc}
+% \testfile*
+% Assigning the contents of a box to be another box.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_set_eq:NN #1#2
+ { \tex_setbox:D #1 \tex_copy:D #2 }
+\cs_new_protected:Npn \box_gset_eq:NN #1#2
+ { \tex_global:D \tex_setbox:D #1 \tex_copy:D #2 }
+\cs_generate_variant:Nn \box_set_eq:NN { c , Nc , cc }
+\cs_generate_variant:Nn \box_gset_eq:NN { c , Nc , cc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \box_set_eq_drop:NN, \box_set_eq_drop:cN,
+% \box_set_eq_drop:Nc, \box_set_eq_drop:cc
+% }
+% \begin{macro}
+% {
+% \box_gset_eq_drop:NN, \box_gset_eq_drop:cN,
+% \box_gset_eq_drop:Nc, \box_gset_eq_drop:cc
+% }
+% Assigning the contents of a box to be another box, then drops the
+% original box.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_set_eq_drop:NN #1#2
+ { \tex_setbox:D #1 \tex_box:D #2 }
+\cs_new_protected:Npn \box_gset_eq_drop:NN #1#2
+ { \tex_global:D \tex_setbox:D #1 \tex_box:D #2 }
+\cs_generate_variant:Nn \box_set_eq_drop:NN { c , Nc , cc }
+\cs_generate_variant:Nn \box_gset_eq_drop:NN { c , Nc , cc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\box_if_exist:N, \box_if_exist:c}
+% Copies of the \texttt{cs} functions defined in \pkg{l3basics}.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \box_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \box_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Measuring and setting box dimensions}
+%
+% \begin{macro}{\box_ht:N, \box_ht:c}
+% \begin{macro}{\box_dp:N, \box_dp:c}
+% \begin{macro}{\box_wd:N, \box_wd:c}
+% \testfile*
+% Accessing the height, depth, and width of a \meta{box} register.
+% \begin{macrocode}
+\cs_new_eq:NN \box_ht:N \tex_ht:D
+\cs_new_eq:NN \box_dp:N \tex_dp:D
+\cs_new_eq:NN \box_wd:N \tex_wd:D
+\cs_generate_variant:Nn \box_ht:N { c }
+\cs_generate_variant:Nn \box_dp:N { c }
+\cs_generate_variant:Nn \box_wd:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\box_ht_plus_dp:N}
+% The \cs{box_ht:N} and \cs{box_dp:N} primitives do not expand but
+% rather are suitable for use after \tn{the} or inside dimension
+% expressions. Here we obtain the same behaviour by using
+% \cs{@@_dim_eval:n} (basically \tn{dimexpr}) rather than
+% \cs{dim_eval:n} (basically \tn{the} \tn{dimexpr}).
+% \begin{macrocode}
+\cs_new_protected:Npn \box_ht_plus_dp:N #1
+ { \@@_dim_eval:n { \box_ht:N #1 + \box_dp:N #1 } }
+\cs_generate_variant:Nn \box_ht_plus_dp:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\box_set_ht:Nn, \box_set_ht:cn, \box_gset_ht:Nn, \box_gset_ht:cn}
+% \begin{macro}{\box_set_dp:Nn, \box_set_dp:cn, \box_gset_dp:Nn, \box_gset_dp:cn}
+% \begin{macro}{\box_set_wd:Nn, \box_set_wd:cn, \box_gset_wd:Nn, \box_gset_wd:cn}
+% Setting the size whilst respecting local scope requires copying;
+% the same issue does not come up when working globally.
+% When debugging, the dimension expression |#2| is surrounded by
+% parentheses to catch early termination.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_set_dp:Nn #1#2
+ {
+ \tex_setbox:D #1 = \tex_copy:D #1
+ \box_dp:N #1 \@@_dim_eval:n {#2}
+ }
+\cs_generate_variant:Nn \box_set_dp:Nn { c }
+\cs_new_protected:Npn \box_gset_dp:Nn #1#2
+ { \box_dp:N #1 \@@_dim_eval:n {#2} }
+\cs_generate_variant:Nn \box_gset_dp:Nn { c }
+\cs_new_protected:Npn \box_set_ht:Nn #1#2
+ {
+ \tex_setbox:D #1 = \tex_copy:D #1
+ \box_ht:N #1 \@@_dim_eval:n {#2}
+ }
+\cs_generate_variant:Nn \box_set_ht:Nn { c }
+\cs_new_protected:Npn \box_gset_ht:Nn #1#2
+ { \box_ht:N #1 \@@_dim_eval:n {#2} }
+\cs_generate_variant:Nn \box_gset_ht:Nn { c }
+\cs_new_protected:Npn \box_set_wd:Nn #1#2
+ {
+ \tex_setbox:D #1 = \tex_copy:D #1
+ \box_wd:N #1 \@@_dim_eval:n {#2}
+ }
+\cs_generate_variant:Nn \box_set_wd:Nn { c }
+\cs_new_protected:Npn \box_gset_wd:Nn #1#2
+ { \box_wd:N #1 \@@_dim_eval:n {#2} }
+\cs_generate_variant:Nn \box_gset_wd:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Using boxes}
+%
+% \begin{macro}{\box_use_drop:N, \box_use_drop:c}
+% \begin{macro}{\box_use:N, \box_use:c}
+% Using a \meta{box}. These are just \TeX{} primitives with meaningful
+% names.
+% \begin{macrocode}
+\cs_new_eq:NN \box_use_drop:N \tex_box:D
+\cs_new_eq:NN \box_use:N \tex_copy:D
+\cs_generate_variant:Nn \box_use_drop:N { c }
+\cs_generate_variant:Nn \box_use:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\box_move_left:nn, \box_move_right:nn}
+% \begin{macro}{\box_move_up:nn, \box_move_down:nn}
+% \testfile*
+% Move box material in different directions.
+% When debugging, the dimension expression |#1| is surrounded by
+% parentheses to catch early termination.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_move_left:nn #1#2
+ { \tex_moveleft:D \@@_dim_eval:n {#1} #2 }
+\cs_new_protected:Npn \box_move_right:nn #1#2
+ { \tex_moveright:D \@@_dim_eval:n {#1} #2 }
+\cs_new_protected:Npn \box_move_up:nn #1#2
+ { \tex_raise:D \@@_dim_eval:n {#1} #2 }
+\cs_new_protected:Npn \box_move_down:nn #1#2
+ { \tex_lower:D \@@_dim_eval:n {#1} #2 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Box conditionals}
+%
+% \begin{macro}{\if_hbox:N}
+% \begin{macro}{\if_vbox:N}
+% \begin{macro}{\if_box_empty:N}
+% \testfile*
+% The primitives for testing if a \meta{box} is empty/void or which
+% type of box it is.
+% \begin{macrocode}
+\cs_new_eq:NN \if_hbox:N \tex_ifhbox:D
+\cs_new_eq:NN \if_vbox:N \tex_ifvbox:D
+\cs_new_eq:NN \if_box_empty:N \tex_ifvoid:D
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\box_if_horizontal:N, \box_if_horizontal:c}
+% \testfile*
+% \begin{macro}[pTF]{\box_if_vertical:N, \box_if_vertical:c}
+% \testfile*
+% \begin{macrocode}
+\prg_new_conditional:Npnn \box_if_horizontal:N #1 { p , T , F , TF }
+ { \if_hbox:N #1 \prg_return_true: \else: \prg_return_false: \fi: }
+\prg_new_conditional:Npnn \box_if_vertical:N #1 { p , T , F , TF }
+ { \if_vbox:N #1 \prg_return_true: \else: \prg_return_false: \fi: }
+\prg_generate_conditional_variant:Nnn \box_if_horizontal:N
+ { c } { p , T , F , TF }
+\prg_generate_conditional_variant:Nnn \box_if_vertical:N
+ { c } { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\box_if_empty:N, \box_if_empty:c}
+% \testfile*
+% Testing if a \meta{box} is empty/void.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \box_if_empty:N #1 { p , T , F , TF }
+ { \if_box_empty:N #1 \prg_return_true: \else: \prg_return_false: \fi: }
+\prg_generate_conditional_variant:Nnn \box_if_empty:N
+ { c } { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{The last box inserted}
+%
+% \begin{macro}{\box_set_to_last:N, \box_set_to_last:c}
+% \begin{macro}{\box_gset_to_last:N, \box_gset_to_last:c}
+% \testfile*
+% Set a box to the previous box.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_set_to_last:N #1
+ { \tex_setbox:D #1 \tex_lastbox:D }
+\cs_new_protected:Npn \box_gset_to_last:N #1
+ { \tex_global:D \tex_setbox:D #1 \tex_lastbox:D }
+\cs_generate_variant:Nn \box_set_to_last:N { c }
+\cs_generate_variant:Nn \box_gset_to_last:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Constant boxes}
+%
+% \begin{variable}{\c_empty_box}
+% A box we never use.
+% \begin{macrocode}
+\box_new:N \c_empty_box
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Scratch boxes}
+%
+% \begin{variable}{\l_tmpa_box, \l_tmpb_box, \g_tmpa_box, \g_tmpb_box}
+% Scratch boxes.
+% \begin{macrocode}
+\box_new:N \l_tmpa_box
+\box_new:N \l_tmpb_box
+\box_new:N \g_tmpa_box
+\box_new:N \g_tmpb_box
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Viewing box contents}
+%
+% \TeX{}'s \tn{showbox} is not really that helpful in many cases, and
+% it is also inconsistent with other \LaTeX3{} \texttt{show} functions as it
+% does not actually shows material in the terminal. So we provide a richer
+% set of functionality.
+%
+% \begin{macro}{\box_show:N, \box_show:c}
+% \begin{macro}{\box_show:Nnn, \box_show:cnn}
+% Essentially a wrapper around the internal function, but evaluating
+% the breadth and depth arguments now outside the group.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_show:N #1
+ { \box_show:Nnn #1 \c_max_int \c_max_int }
+\cs_generate_variant:Nn \box_show:N { c }
+\cs_new_protected:Npn \box_show:Nnn #1#2#3
+ { \@@_show:NNff 1 #1 { \int_eval:n {#2} } { \int_eval:n {#3} } }
+\cs_generate_variant:Nn \box_show:Nnn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\box_log:N, \box_log:c}
+% \begin{macro}{\box_log:Nnn, \box_log:cnn}
+% \begin{macro}{\@@_log:nNnn}
+% Getting \TeX{} to write to the log without interruption the run is done by
+% altering the interaction mode. For that, the \eTeX{} extensions are needed.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_log:N #1
+ { \box_log:Nnn #1 \c_max_int \c_max_int }
+\cs_generate_variant:Nn \box_log:N { c }
+\cs_new_protected:Npn \box_log:Nnn
+ { \exp_args:No \@@_log:nNnn { \tex_the:D \tex_interactionmode:D } }
+\cs_new_protected:Npn \@@_log:nNnn #1#2#3#4
+ {
+ \int_gset:Nn \tex_interactionmode:D { 0 }
+ \@@_show:NNff 0 #2 { \int_eval:n {#3} } { \int_eval:n {#4} }
+ \int_gset:Nn \tex_interactionmode:D {#1}
+ }
+\cs_generate_variant:Nn \box_log:Nnn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_show:NNnn, \@@_show:NNff}
+% The internal auxiliary to actually do the output uses a group to deal
+% with breadth and depth values. The \cs{use:n} here gives better output
+% appearance. Setting \tn{tracingonline} and \tn{errorcontextlines} is
+% used to control what appears in the terminal.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_show:NNnn #1#2#3#4
+ {
+ \box_if_exist:NTF #2
+ {
+ \group_begin:
+ \int_set:Nn \tex_showboxbreadth:D {#3}
+ \int_set:Nn \tex_showboxdepth:D {#4}
+ \int_set:Nn \tex_tracingonline:D {#1}
+ \int_set:Nn \tex_errorcontextlines:D { -1 }
+ \tex_showbox:D \use:n {#2}
+ \group_end:
+ }
+ {
+ \msg_error:nne { kernel } { variable-not-defined }
+ { \token_to_str:N #2 }
+ }
+ }
+\cs_generate_variant:Nn \@@_show:NNnn { NNff }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Horizontal mode boxes}
+%
+% \begin{macro}{\hbox:n}
+% \testfile{m3box002.lvt}
+% Put a horizontal box directly into the input stream.
+% \begin{macrocode}
+\cs_new_protected:Npn \hbox:n #1
+ { \tex_hbox:D \scan_stop: { \color_group_begin: #1 \color_group_end: } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\hbox_set:Nn, \hbox_set:cn}
+% \begin{macro}{\hbox_gset:Nn, \hbox_gset:cn}
+% \testfile*
+% \begin{macrocode}
+\cs_new_protected:Npn \hbox_set:Nn #1#2
+ {
+ \tex_setbox:D #1 \tex_hbox:D
+ { \color_group_begin: #2 \color_group_end: }
+ }
+\cs_new_protected:Npn \hbox_gset:Nn #1#2
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_hbox:D
+ { \color_group_begin: #2 \color_group_end: }
+ }
+\cs_generate_variant:Nn \hbox_set:Nn { c }
+\cs_generate_variant:Nn \hbox_gset:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\hbox_set_to_wd:Nnn, \hbox_set_to_wd:cnn}
+% \begin{macro}{\hbox_gset_to_wd:Nnn, \hbox_gset_to_wd:cnn}
+% \testfile*
+% Storing material in a horizontal box with a specified width.
+% Again, put the dimension expression in parentheses when debugging.
+% \begin{macrocode}
+\cs_new_protected:Npn \hbox_set_to_wd:Nnn #1#2#3
+ {
+ \tex_setbox:D #1 \tex_hbox:D to \@@_dim_eval:n {#2}
+ { \color_group_begin: #3 \color_group_end: }
+ }
+\cs_new_protected:Npn \hbox_gset_to_wd:Nnn #1#2#3
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_hbox:D to \@@_dim_eval:n {#2}
+ { \color_group_begin: #3 \color_group_end: }
+ }
+\cs_generate_variant:Nn \hbox_set_to_wd:Nnn { c }
+\cs_generate_variant:Nn \hbox_gset_to_wd:Nnn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\hbox_set:Nw, \hbox_set:cw}
+% \begin{macro}{\hbox_gset:Nw, \hbox_gset:cw}
+% \begin{macro}{\hbox_set_end:, \hbox_gset_end:}
+% \testfile*
+% Storing material in a horizontal box. This type is useful in
+% environment definitions.
+% \begin{macrocode}
+\cs_new_protected:Npn \hbox_set:Nw #1
+ {
+ \tex_setbox:D #1 \tex_hbox:D
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\cs_new_protected:Npn \hbox_gset:Nw #1
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_hbox:D
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\cs_generate_variant:Nn \hbox_set:Nw { c }
+\cs_generate_variant:Nn \hbox_gset:Nw { c }
+\cs_new_protected:Npn \hbox_set_end:
+ {
+ \color_group_end:
+ \c_group_end_token
+ }
+\cs_new_eq:NN \hbox_gset_end: \hbox_set_end:
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\hbox_set_to_wd:Nnw, \hbox_set_to_wd:cnw}
+% \begin{macro}{\hbox_gset_to_wd:Nnw, \hbox_gset_to_wd:cnw}
+% Combining the above ideas.
+% \begin{macrocode}
+\cs_new_protected:Npn \hbox_set_to_wd:Nnw #1#2
+ {
+ \tex_setbox:D #1 \tex_hbox:D to \@@_dim_eval:n {#2}
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\cs_new_protected:Npn \hbox_gset_to_wd:Nnw #1#2
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_hbox:D to \@@_dim_eval:n {#2}
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\cs_generate_variant:Nn \hbox_set_to_wd:Nnw { c }
+\cs_generate_variant:Nn \hbox_gset_to_wd:Nnw { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\hbox_to_wd:nn}
+% \begin{macro}{\hbox_to_zero:n}
+% \testfile*
+% Put a horizontal box directly into the input stream.
+% \begin{macrocode}
+\cs_new_protected:Npn \hbox_to_wd:nn #1#2
+ {
+ \tex_hbox:D to \@@_dim_eval:n {#1}
+ { \color_group_begin: #2 \color_group_end: }
+ }
+\cs_new_protected:Npn \hbox_to_zero:n #1
+ {
+ \tex_hbox:D to \c_zero_dim
+ { \color_group_begin: #1 \color_group_end: }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\hbox_overlap_center:n, \hbox_overlap_left:n, \hbox_overlap_right:n}
+% Put a zero-sized box with the contents pushed against one side (which
+% makes it stick out on the other) directly into the input stream.
+% \begin{macrocode}
+\cs_new_protected:Npn \hbox_overlap_center:n #1
+ { \hbox_to_zero:n { \tex_hss:D #1 \tex_hss:D } }
+\cs_new_protected:Npn \hbox_overlap_left:n #1
+ { \hbox_to_zero:n { \tex_hss:D #1 } }
+\cs_new_protected:Npn \hbox_overlap_right:n #1
+ { \hbox_to_zero:n { #1 \tex_hss:D } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\hbox_unpack:N, \hbox_unpack:c}
+% \begin{macro}{\hbox_unpack_drop:N, \hbox_unpack_drop:c}
+% \testfile*
+% Unpacking a box and if requested also clear it.
+% \begin{macrocode}
+\cs_new_eq:NN \hbox_unpack:N \tex_unhcopy:D
+\cs_new_eq:NN \hbox_unpack_drop:N \tex_unhbox:D
+\cs_generate_variant:Nn \hbox_unpack:N { c }
+\cs_generate_variant:Nn \hbox_unpack_drop:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Vertical mode boxes}
+%
+% \TeX{} ends these boxes directly with the internal \emph{end_graf}
+% routine. This means that there is no \cs{par} at the end of vertical
+% boxes unless we insert one. Thus all vertical boxes include a \cs{par}
+% just before closing the color group.
+%
+% \begin{macro}{\vbox:n}
+% \TestFiles{m3box003.lvt}
+% \begin{macro}{\vbox_top:n}
+% \TestFiles{m3box003.lvt}
+% Put a vertical box directly into the input stream.
+% \begin{macrocode}
+\cs_new_protected:Npn \vbox:n #1
+ { \tex_vbox:D { \color_group_begin: #1 \par \color_group_end: } }
+\cs_new_protected:Npn \vbox_top:n #1
+ { \tex_vtop:D { \color_group_begin: #1 \par \color_group_end: } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\vbox_to_ht:nn, \vbox_to_zero:n}
+% \begin{macro}{\vbox_to_ht:nn, \vbox_to_zero:n}
+% \testfile*
+% Put a vertical box directly into the input stream.
+% \begin{macrocode}
+\cs_new_protected:Npn \vbox_to_ht:nn #1#2
+ {
+ \tex_vbox:D to \@@_dim_eval:n {#1}
+ { \color_group_begin: #2 \par \color_group_end: }
+ }
+\cs_new_protected:Npn \vbox_to_zero:n #1
+ {
+ \tex_vbox:D to \c_zero_dim
+ { \color_group_begin: #1 \par \color_group_end: }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\vbox_set:Nn, \vbox_set:cn}
+% \begin{macro}{\vbox_gset:Nn, \vbox_gset:cn}
+% \testfile*
+% Storing material in a vertical box with a natural height.
+% \begin{macrocode}
+\cs_new_protected:Npn \vbox_set:Nn #1#2
+ {
+ \tex_setbox:D #1 \tex_vbox:D
+ { \color_group_begin: #2 \par \color_group_end: }
+ }
+\cs_new_protected:Npn \vbox_gset:Nn #1#2
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_vbox:D
+ { \color_group_begin: #2 \par \color_group_end: }
+ }
+\cs_generate_variant:Nn \vbox_set:Nn { c }
+\cs_generate_variant:Nn \vbox_gset:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\vbox_set_top:Nn, \vbox_set_top:cn}
+% \begin{macro}{\vbox_gset_top:Nn, \vbox_gset_top:cn}
+% \testfile*
+% Storing material in a vertical box with a natural height and reference
+% point at the baseline of the first object in the box.
+% \begin{macrocode}
+\cs_new_protected:Npn \vbox_set_top:Nn #1#2
+ {
+ \tex_setbox:D #1 \tex_vtop:D
+ { \color_group_begin: #2 \par \color_group_end: }
+ }
+\cs_new_protected:Npn \vbox_gset_top:Nn #1#2
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_vtop:D
+ { \color_group_begin: #2 \par \color_group_end: }
+ }
+\cs_generate_variant:Nn \vbox_set_top:Nn { c }
+\cs_generate_variant:Nn \vbox_gset_top:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\vbox_set_to_ht:Nnn, \vbox_set_to_ht:cnn}
+% \begin{macro}{\vbox_gset_to_ht:Nnn, \vbox_gset_to_ht:cnn}
+% \testfile*
+% Storing material in a vertical box with a specified height.
+% \begin{macrocode}
+\cs_new_protected:Npn \vbox_set_to_ht:Nnn #1#2#3
+ {
+ \tex_setbox:D #1 \tex_vbox:D to \@@_dim_eval:n {#2}
+ { \color_group_begin: #3 \par \color_group_end: }
+ }
+\cs_new_protected:Npn \vbox_gset_to_ht:Nnn #1#2#3
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_vbox:D to \@@_dim_eval:n {#2}
+ { \color_group_begin: #3 \par \color_group_end: }
+ }
+\cs_generate_variant:Nn \vbox_set_to_ht:Nnn { c }
+\cs_generate_variant:Nn \vbox_gset_to_ht:Nnn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\vbox_set:Nw, \vbox_set:cw}
+% \begin{macro}{\vbox_gset:Nw, \vbox_gset:cw}
+% \begin{macro}{\vbox_set_end:, \vbox_gset_end:}
+% \testfile*
+% Storing material in a vertical box. This type is useful in
+% environment definitions.
+% \begin{macrocode}
+\cs_new_protected:Npn \vbox_set:Nw #1
+ {
+ \tex_setbox:D #1 \tex_vbox:D
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\cs_new_protected:Npn \vbox_gset:Nw #1
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_vbox:D
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\cs_generate_variant:Nn \vbox_set:Nw { c }
+\cs_generate_variant:Nn \vbox_gset:Nw { c }
+\cs_new_protected:Npn \vbox_set_end:
+ {
+ \par
+ \color_group_end:
+ \c_group_end_token
+ }
+\cs_new_eq:NN \vbox_gset_end: \vbox_set_end:
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\vbox_set_to_ht:Nnw, \vbox_set_to_ht:cnw}
+% \begin{macro}{\vbox_gset_to_ht:Nnw, \vbox_gset_to_ht:cnw}
+% A combination of the above ideas.
+% \begin{macrocode}
+\cs_new_protected:Npn \vbox_set_to_ht:Nnw #1#2
+ {
+ \tex_setbox:D #1 \tex_vbox:D to \@@_dim_eval:n {#2}
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\cs_new_protected:Npn \vbox_gset_to_ht:Nnw #1#2
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_vbox:D to \@@_dim_eval:n {#2}
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\cs_generate_variant:Nn \vbox_set_to_ht:Nnw { c }
+\cs_generate_variant:Nn \vbox_gset_to_ht:Nnw { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\vbox_unpack:N, \vbox_unpack:c}
+% \begin{macro}{\vbox_unpack_drop:N, \vbox_unpack_drop:c}
+% \testfile*
+% Unpacking a box and if requested also clear it.
+% \begin{macrocode}
+\cs_new_eq:NN \vbox_unpack:N \tex_unvcopy:D
+\cs_new_eq:NN \vbox_unpack_drop:N \tex_unvbox:D
+\cs_generate_variant:Nn \vbox_unpack:N { c }
+\cs_generate_variant:Nn \vbox_unpack_drop:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \vbox_set_split_to_ht:NNn, \vbox_set_split_to_ht:cNn,
+% \vbox_set_split_to_ht:Ncn, \vbox_set_split_to_ht:ccn,
+% \vbox_gset_split_to_ht:NNn, \vbox_gset_split_to_ht:cNn,
+% \vbox_gset_split_to_ht:Ncn, \vbox_gset_split_to_ht:ccn,
+% }
+% \testfile*
+% Splitting a vertical box in two.
+% \begin{macrocode}
+\cs_new_protected:Npn \vbox_set_split_to_ht:NNn #1#2#3
+ { \tex_setbox:D #1 \tex_vsplit:D #2 to \@@_dim_eval:n {#3} }
+\cs_generate_variant:Nn \vbox_set_split_to_ht:NNn { c , Nc , cc }
+\cs_new_protected:Npn \vbox_gset_split_to_ht:NNn #1#2#3
+ {
+ \tex_global:D \tex_setbox:D #1
+ \tex_vsplit:D #2 to \@@_dim_eval:n {#3}
+ }
+\cs_generate_variant:Nn \vbox_gset_split_to_ht:NNn { c , Nc , cc }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Affine transformations}
+%
+% \begin{variable}{\l_@@_angle_fp}
+% When rotating boxes, the angle itself may be needed by the
+% engine-dependent code. This is done using the \pkg{fp} module so
+% that the value is tidied up properly.
+% \begin{macrocode}
+\fp_new:N \l_@@_angle_fp
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_cos_fp, \l_@@_sin_fp}
+% These are used to hold the calculated sine and cosine values while
+% carrying out a rotation.
+% \begin{macrocode}
+\fp_new:N \l_@@_cos_fp
+\fp_new:N \l_@@_sin_fp
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {\l_@@_top_dim, \l_@@_bottom_dim, \l_@@_left_dim, \l_@@_right_dim}
+% These are the positions of the four edges of a box before
+% manipulation.
+% \begin{macrocode}
+\dim_new:N \l_@@_top_dim
+\dim_new:N \l_@@_bottom_dim
+\dim_new:N \l_@@_left_dim
+\dim_new:N \l_@@_right_dim
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \l_@@_top_new_dim, \l_@@_bottom_new_dim ,
+% \l_@@_left_new_dim, \l_@@_right_new_dim
+% }
+% These are the positions of the four edges of a box after
+% manipulation.
+% \begin{macrocode}
+\dim_new:N \l_@@_top_new_dim
+\dim_new:N \l_@@_bottom_new_dim
+\dim_new:N \l_@@_left_new_dim
+\dim_new:N \l_@@_right_new_dim
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_internal_box}
+% Scratch space, but also needed by some parts of the driver.
+% \begin{macrocode}
+\box_new:N \l_@@_internal_box
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}
+% {
+% \box_rotate:Nn, \box_rotate:cn,
+% \box_grotate:Nn, \box_grotate:cn
+% }
+% \begin{macro}{\@@_rotate:NnN}
+% \begin{macro}{\@@_rotate:N}
+% \begin{macro}{\@@_rotate_xdir:nnN, \@@_rotate_ydir:nnN}
+% \begin{macro}
+% {
+% \@@_rotate_quadrant_one:, \@@_rotate_quadrant_two:,
+% \@@_rotate_quadrant_three:, \@@_rotate_quadrant_four:
+% }
+% Rotation of a box starts with working out the relevant sine and
+% cosine. The actual rotation is in an auxiliary to keep the flow slightly
+% clearer
+% \begin{macrocode}
+\cs_new_protected:Npn \box_rotate:Nn #1#2
+ { \@@_rotate:NnN #1 {#2} \hbox_set:Nn }
+\cs_generate_variant:Nn \box_rotate:Nn { c }
+\cs_new_protected:Npn \box_grotate:Nn #1#2
+ { \@@_rotate:NnN #1 {#2} \hbox_gset:Nn }
+\cs_generate_variant:Nn \box_grotate:Nn { c }
+\cs_new_protected:Npn \@@_rotate:NnN #1#2#3
+ {
+ #3 #1
+ {
+ \fp_set:Nn \l_@@_angle_fp {#2}
+ \fp_set:Nn \l_@@_sin_fp { sind ( \l_@@_angle_fp ) }
+ \fp_set:Nn \l_@@_cos_fp { cosd ( \l_@@_angle_fp ) }
+ \@@_rotate:N #1
+ }
+ }
+% \end{macrocode}
+% The edges of the box are then recorded: the left edge is
+% always at zero. Rotation of the four edges then takes place: this is
+% most efficiently done on a quadrant by quadrant basis.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_rotate:N #1
+ {
+ \dim_set:Nn \l_@@_top_dim { \box_ht:N #1 }
+ \dim_set:Nn \l_@@_bottom_dim { -\box_dp:N #1 }
+ \dim_set:Nn \l_@@_right_dim { \box_wd:N #1 }
+ \dim_zero:N \l_@@_left_dim
+% \end{macrocode}
+% The next step is to work out the $x$ and $y$ coordinates of vertices of
+% the rotated box in relation to its original coordinates. The box can be
+% visualized with vertices $B$, $C$, $D$ and $E$ is illustrated
+% (Figure~\ref{fig:l3box:rotation}). The vertex $O$ is the reference point
+% on the baseline, and in this implementation is also the centre of rotation.
+% \begin{figure}
+% \centering
+% \setlength{\unitlength}{3pt}^^A
+% \begin{picture}(34,36)(12,44)
+% \thicklines
+% \put(20,52){\dashbox{1}(20,21){}}
+% \put(20,80){\line(0,-1){36}}
+% \put(12,58){\line(1, 0){34}}
+% \put(41,59){A}
+% \put(40,74){B}
+% \put(21,74){C}
+% \put(21,49){D}
+% \put(40,49){E}
+% \put(21,59){O}
+% \end{picture}
+% \caption{Co-ordinates of a box prior to rotation.}
+% \label{fig:l3box:rotation}
+% \end{figure}
+% The formulae are, for a point $P$ and angle $\alpha$:
+% \[
+% \begin{array}{l}
+% P'_x = P_x - O_x \\
+% P'_y = P_y - O_y \\
+% P''_x = ( P'_x \cos(\alpha)) - ( P'_y \sin(\alpha) ) \\
+% P''_y = ( P'_x \sin(\alpha)) + ( P'_y \cos(\alpha) ) \\
+% P'''_x = P''_x + O_x + L_x \\
+% P'''_y = P''_y + O_y
+% \end{array}
+% \]
+% The \enquote{extra} horizontal translation $L_x$ at the end is calculated
+% so that the leftmost point of the resulting box has $x$-coordinate $0$.
+% This is desirable as \TeX{} boxes must have the reference point at
+% the left edge of the box. (As $O$ is always $(0,0)$, this part of the
+% calculation is omitted here.)
+% \begin{macrocode}
+ \fp_compare:nNnTF \l_@@_sin_fp > \c_zero_fp
+ {
+ \fp_compare:nNnTF \l_@@_cos_fp > \c_zero_fp
+ { \@@_rotate_quadrant_one: }
+ { \@@_rotate_quadrant_two: }
+ }
+ {
+ \fp_compare:nNnTF \l_@@_cos_fp < \c_zero_fp
+ { \@@_rotate_quadrant_three: }
+ { \@@_rotate_quadrant_four: }
+ }
+% \end{macrocode}
+% The position of the box edges are now known, but the box at this
+% stage be misplaced relative to the current \TeX{} reference point. So the
+% content of the box is moved such that the reference point of the
+% rotated box is in the same place as the original.
+% \begin{macrocode}
+ \hbox_set:Nn \l_@@_internal_box { \box_use:N #1 }
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \__kernel_kern:n { -\l_@@_left_new_dim }
+ \hbox:n
+ {
+ \@@_backend_rotate:Nn
+ \l_@@_internal_box
+ \l_@@_angle_fp
+ }
+ }
+% \end{macrocode}
+% Tidy up the size of the box so that the material is actually inside
+% the bounding box. The result can then be used to reset the original
+% box.
+% \begin{macrocode}
+ \box_set_ht:Nn \l_@@_internal_box { \l_@@_top_new_dim }
+ \box_set_dp:Nn \l_@@_internal_box { -\l_@@_bottom_new_dim }
+ \box_set_wd:Nn \l_@@_internal_box
+ { \l_@@_right_new_dim - \l_@@_left_new_dim }
+ \box_use_drop:N \l_@@_internal_box
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% These functions take a general point $(|#1|, |#2|)$ and rotate its
+% location about the origin, using the previously-set sine and cosine
+% values. Each function gives only one component of the location of the
+% updated point. This is because for rotation of a box each step needs
+% only one value, and so performance is gained by avoiding working
+% out both $x'$ and $y'$ at the same time. Contrast this with
+% the equivalent function in the \pkg{l3coffins} module, where both parts
+% are needed.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_rotate_xdir:nnN #1#2#3
+ {
+ \dim_set:Nn #3
+ {
+ \fp_to_dim:n
+ {
+ \l_@@_cos_fp * \dim_to_fp:n {#1}
+ - \l_@@_sin_fp * \dim_to_fp:n {#2}
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_rotate_ydir:nnN #1#2#3
+ {
+ \dim_set:Nn #3
+ {
+ \fp_to_dim:n
+ {
+ \l_@@_sin_fp * \dim_to_fp:n {#1}
+ + \l_@@_cos_fp * \dim_to_fp:n {#2}
+ }
+ }
+ }
+% \end{macrocode}
+% Rotation of the edges is done using a different formula for each
+% quadrant. In every case, the top and bottom edges only need the
+% resulting $y$-values, whereas the left and right edges need the
+% $x$-values. Each case is a question of picking out which corner
+% ends up at with the maximum top, bottom, left and right value. Doing
+% this by hand means a lot less calculating and avoids lots of
+% comparisons.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_rotate_quadrant_one:
+ {
+ \@@_rotate_ydir:nnN \l_@@_right_dim \l_@@_top_dim
+ \l_@@_top_new_dim
+ \@@_rotate_ydir:nnN \l_@@_left_dim \l_@@_bottom_dim
+ \l_@@_bottom_new_dim
+ \@@_rotate_xdir:nnN \l_@@_left_dim \l_@@_top_dim
+ \l_@@_left_new_dim
+ \@@_rotate_xdir:nnN \l_@@_right_dim \l_@@_bottom_dim
+ \l_@@_right_new_dim
+ }
+\cs_new_protected:Npn \@@_rotate_quadrant_two:
+ {
+ \@@_rotate_ydir:nnN \l_@@_right_dim \l_@@_bottom_dim
+ \l_@@_top_new_dim
+ \@@_rotate_ydir:nnN \l_@@_left_dim \l_@@_top_dim
+ \l_@@_bottom_new_dim
+ \@@_rotate_xdir:nnN \l_@@_right_dim \l_@@_top_dim
+ \l_@@_left_new_dim
+ \@@_rotate_xdir:nnN \l_@@_left_dim \l_@@_bottom_dim
+ \l_@@_right_new_dim
+ }
+\cs_new_protected:Npn \@@_rotate_quadrant_three:
+ {
+ \@@_rotate_ydir:nnN \l_@@_left_dim \l_@@_bottom_dim
+ \l_@@_top_new_dim
+ \@@_rotate_ydir:nnN \l_@@_right_dim \l_@@_top_dim
+ \l_@@_bottom_new_dim
+ \@@_rotate_xdir:nnN \l_@@_right_dim \l_@@_bottom_dim
+ \l_@@_left_new_dim
+ \@@_rotate_xdir:nnN \l_@@_left_dim \l_@@_top_dim
+ \l_@@_right_new_dim
+ }
+\cs_new_protected:Npn \@@_rotate_quadrant_four:
+ {
+ \@@_rotate_ydir:nnN \l_@@_left_dim \l_@@_top_dim
+ \l_@@_top_new_dim
+ \@@_rotate_ydir:nnN \l_@@_right_dim \l_@@_bottom_dim
+ \l_@@_bottom_new_dim
+ \@@_rotate_xdir:nnN \l_@@_left_dim \l_@@_bottom_dim
+ \l_@@_left_new_dim
+ \@@_rotate_xdir:nnN \l_@@_right_dim \l_@@_top_dim
+ \l_@@_right_new_dim
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_scale_x_fp, \l_@@_scale_y_fp}
+% Scaling is potentially-different in the two axes.
+% \begin{macrocode}
+\fp_new:N \l_@@_scale_x_fp
+\fp_new:N \l_@@_scale_y_fp
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}
+% {
+% \box_resize_to_wd_and_ht_plus_dp:Nnn,
+% \box_resize_to_wd_and_ht_plus_dp:cnn,
+% \box_gresize_to_wd_and_ht_plus_dp:Nnn,
+% \box_gresize_to_wd_and_ht_plus_dp:cnn
+% }
+% \begin{macro}{\@@_resize_to_wd_and_ht_plus_dp:NnnN}
+% \begin{macro}{\@@_resize_set_corners:N}
+% \begin{macro}{\@@_resize:N}
+% \begin{macro}{\@@_resize:NNN}
+% Resizing a box starts by working out the various dimensions of the
+% existing box.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_resize_to_wd_and_ht_plus_dp:Nnn #1#2#3
+ {
+ \@@_resize_to_wd_and_ht_plus_dp:NnnN #1 {#2} {#3}
+ \hbox_set:Nn
+ }
+\cs_generate_variant:Nn \box_resize_to_wd_and_ht_plus_dp:Nnn { c }
+\cs_new_protected:Npn \box_gresize_to_wd_and_ht_plus_dp:Nnn #1#2#3
+ {
+ \@@_resize_to_wd_and_ht_plus_dp:NnnN #1 {#2} {#3}
+ \hbox_gset:Nn
+ }
+\cs_generate_variant:Nn \box_gresize_to_wd_and_ht_plus_dp:Nnn { c }
+\cs_new_protected:Npn \@@_resize_to_wd_and_ht_plus_dp:NnnN #1#2#3#4
+ {
+ #4 #1
+ {
+ \@@_resize_set_corners:N #1
+% \end{macrocode}
+% The $x$-scaling and resulting box size is easy enough to work
+% out: the dimension is that given as |#2|, and the scale is simply the
+% new width divided by the old one.
+% \begin{macrocode}
+ \fp_set:Nn \l_@@_scale_x_fp
+ { \dim_to_fp:n {#2} / \dim_to_fp:n { \l_@@_right_dim } }
+% \end{macrocode}
+% The $y$-scaling needs both the height and the depth of the current box.
+% \begin{macrocode}
+ \fp_set:Nn \l_@@_scale_y_fp
+ {
+ \dim_to_fp:n {#3}
+ / \dim_to_fp:n { \l_@@_top_dim - \l_@@_bottom_dim }
+ }
+% \end{macrocode}
+% Hand off to the auxiliary which does the rest of the work.
+% \begin{macrocode}
+ \@@_resize:N #1
+ }
+ }
+\cs_new_protected:Npn \@@_resize_set_corners:N #1
+ {
+ \dim_set:Nn \l_@@_top_dim { \box_ht:N #1 }
+ \dim_set:Nn \l_@@_bottom_dim { -\box_dp:N #1 }
+ \dim_set:Nn \l_@@_right_dim { \box_wd:N #1 }
+ \dim_zero:N \l_@@_left_dim
+ }
+% \end{macrocode}
+% With at least one real scaling to do, the next phase is to find the new
+% edge co-ordinates. In the $x$~direction this is relatively easy: just
+% scale the right edge. In the $y$~direction, both dimensions have to be
+% scaled, and this again needs the absolute scale value.
+% Once that is all done, the common resize/rescale code can be employed.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_resize:N #1
+ {
+ \@@_resize:NNN \l_@@_right_new_dim
+ \l_@@_scale_x_fp \l_@@_right_dim
+ \@@_resize:NNN \l_@@_bottom_new_dim
+ \l_@@_scale_y_fp \l_@@_bottom_dim
+ \@@_resize:NNN \l_@@_top_new_dim
+ \l_@@_scale_y_fp \l_@@_top_dim
+ \@@_resize_common:N #1
+ }
+\cs_new_protected:Npn \@@_resize:NNN #1#2#3
+ {
+ \dim_set:Nn #1
+ { \fp_to_dim:n { \fp_abs:n { #2 } * \dim_to_fp:n { #3 } } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \box_resize_to_ht:Nn, \box_resize_to_ht:cn,
+% \box_gresize_to_ht:Nn, \box_gresize_to_ht:cn
+% }
+% \begin{macro}{\@@_resize_to_ht:NnN}
+% \begin{macro}
+% {
+% \box_resize_to_ht_plus_dp:Nn, \box_resize_to_ht_plus_dp:cn,
+% \box_gresize_to_ht_plus_dp:Nn, \box_gresize_to_ht_plus_dp:cn
+% }
+% \begin{macro}{\@@_resize_to_ht_plus_dp:NnN}
+% \begin{macro}
+% {
+% \box_resize_to_wd:Nn, \box_resize_to_wd:cn,
+% \box_gresize_to_wd:Nn, \box_gresize_to_wd:cn
+% }
+% \begin{macro}{\@@_resize_to_wd:NnN}
+% \begin{macro}
+% {
+% \box_resize_to_wd_and_ht:Nnn, \box_resize_to_wd_and_ht:cnn,
+% \box_gresize_to_wd_and_ht:Nnn, \box_gresize_to_wd_and_ht:cnn
+% }
+% \begin{macro}{\@@_resize_to_wd_ht:NnnN}
+% Scaling to a (total) height or to a width is a simplified version of the main
+% resizing operation, with the scale simply copied between the two parts. The
+% internal auxiliary is called using the scaling value twice, as the sign for
+% both parts is needed (as this allows the same internal code to be used as
+% for the general case).
+% \begin{macrocode}
+\cs_new_protected:Npn \box_resize_to_ht:Nn #1#2
+ { \@@_resize_to_ht:NnN #1 {#2} \hbox_set:Nn }
+\cs_generate_variant:Nn \box_resize_to_ht:Nn { c }
+\cs_new_protected:Npn \box_gresize_to_ht:Nn #1#2
+ { \@@_resize_to_ht:NnN #1 {#2} \hbox_gset:Nn }
+\cs_generate_variant:Nn \box_gresize_to_ht:Nn { c }
+\cs_new_protected:Npn \@@_resize_to_ht:NnN #1#2#3
+ {
+ #3 #1
+ {
+ \@@_resize_set_corners:N #1
+ \fp_set:Nn \l_@@_scale_y_fp
+ {
+ \dim_to_fp:n {#2}
+ / \dim_to_fp:n { \l_@@_top_dim }
+ }
+ \fp_set_eq:NN \l_@@_scale_x_fp \l_@@_scale_y_fp
+ \@@_resize:N #1
+ }
+ }
+\cs_new_protected:Npn \box_resize_to_ht_plus_dp:Nn #1#2
+ { \@@_resize_to_ht_plus_dp:NnN #1 {#2} \hbox_set:Nn }
+\cs_generate_variant:Nn \box_resize_to_ht_plus_dp:Nn { c }
+\cs_new_protected:Npn \box_gresize_to_ht_plus_dp:Nn #1#2
+ { \@@_resize_to_ht_plus_dp:NnN #1 {#2} \hbox_gset:Nn }
+\cs_generate_variant:Nn \box_gresize_to_ht_plus_dp:Nn { c }
+\cs_new_protected:Npn \@@_resize_to_ht_plus_dp:NnN #1#2#3
+ {
+ #3 #1
+ {
+ \@@_resize_set_corners:N #1
+ \fp_set:Nn \l_@@_scale_y_fp
+ {
+ \dim_to_fp:n {#2}
+ / \dim_to_fp:n { \l_@@_top_dim - \l_@@_bottom_dim }
+ }
+ \fp_set_eq:NN \l_@@_scale_x_fp \l_@@_scale_y_fp
+ \@@_resize:N #1
+ }
+ }
+\cs_new_protected:Npn \box_resize_to_wd:Nn #1#2
+ { \@@_resize_to_wd:NnN #1 {#2} \hbox_set:Nn }
+\cs_generate_variant:Nn \box_resize_to_wd:Nn { c }
+\cs_new_protected:Npn \box_gresize_to_wd:Nn #1#2
+ { \@@_resize_to_wd:NnN #1 {#2} \hbox_gset:Nn }
+\cs_generate_variant:Nn \box_gresize_to_wd:Nn { c }
+\cs_new_protected:Npn \@@_resize_to_wd:NnN #1#2#3
+ {
+ #3 #1
+ {
+ \@@_resize_set_corners:N #1
+ \fp_set:Nn \l_@@_scale_x_fp
+ { \dim_to_fp:n {#2} / \dim_to_fp:n { \l_@@_right_dim } }
+ \fp_set_eq:NN \l_@@_scale_y_fp \l_@@_scale_x_fp
+ \@@_resize:N #1
+ }
+ }
+\cs_new_protected:Npn \box_resize_to_wd_and_ht:Nnn #1#2#3
+ { \@@_resize_to_wd_and_ht:NnnN #1 {#2} {#3} \hbox_set:Nn }
+\cs_generate_variant:Nn \box_resize_to_wd_and_ht:Nnn { c }
+\cs_new_protected:Npn \box_gresize_to_wd_and_ht:Nnn #1#2#3
+ { \@@_resize_to_wd_and_ht:NnnN #1 {#2} {#3} \hbox_gset:Nn }
+\cs_generate_variant:Nn \box_gresize_to_wd_and_ht:Nnn { c }
+\cs_new_protected:Npn \@@_resize_to_wd_and_ht:NnnN #1#2#3#4
+ {
+ #4 #1
+ {
+ \@@_resize_set_corners:N #1
+ \fp_set:Nn \l_@@_scale_x_fp
+ { \dim_to_fp:n {#2} / \dim_to_fp:n { \l_@@_right_dim } }
+ \fp_set:Nn \l_@@_scale_y_fp
+ {
+ \dim_to_fp:n {#3}
+ / \dim_to_fp:n { \l_@@_top_dim }
+ }
+ \@@_resize:N #1
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \box_scale:Nnn, \box_scale:cnn,
+% \box_gscale:Nnn, \box_gscale:cnn
+% }
+% \begin{macro}{\@@_scale:NnnN}
+% \begin{macro}{\@@_scale:N}
+% When scaling a box, setting the scaling itself is easy enough. The
+% new dimensions are also relatively easy to find, allowing only for
+% the need to keep them positive in all cases. Once that is done then
+% after a check for the trivial scaling a hand-off can be made to the
+% common code. The code here is split into two as this allows sharing
+% with the auto-resizing functions.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_scale:Nnn #1#2#3
+ { \@@_scale:NnnN #1 {#2} {#3} \hbox_set:Nn }
+\cs_generate_variant:Nn \box_scale:Nnn { c }
+\cs_new_protected:Npn \box_gscale:Nnn #1#2#3
+ { \@@_scale:NnnN #1 {#2} {#3} \hbox_gset:Nn }
+\cs_generate_variant:Nn \box_gscale:Nnn { c }
+\cs_new_protected:Npn \@@_scale:NnnN #1#2#3#4
+ {
+ #4 #1
+ {
+ \fp_set:Nn \l_@@_scale_x_fp {#2}
+ \fp_set:Nn \l_@@_scale_y_fp {#3}
+ \@@_scale:N #1
+ }
+ }
+\cs_new_protected:Npn \@@_scale:N #1
+ {
+ \dim_set:Nn \l_@@_top_dim { \box_ht:N #1 }
+ \dim_set:Nn \l_@@_bottom_dim { -\box_dp:N #1 }
+ \dim_set:Nn \l_@@_right_dim { \box_wd:N #1 }
+ \dim_zero:N \l_@@_left_dim
+ \dim_set:Nn \l_@@_top_new_dim
+ { \fp_abs:n { \l_@@_scale_y_fp } \l_@@_top_dim }
+ \dim_set:Nn \l_@@_bottom_new_dim
+ { \fp_abs:n { \l_@@_scale_y_fp } \l_@@_bottom_dim }
+ \dim_set:Nn \l_@@_right_new_dim
+ { \fp_abs:n { \l_@@_scale_x_fp } \l_@@_right_dim }
+ \@@_resize_common:N #1
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \box_autosize_to_wd_and_ht:Nnn ,
+% \box_autosize_to_wd_and_ht:cnn ,
+% \box_gautosize_to_wd_and_ht:Nnn ,
+% \box_gautosize_to_wd_and_ht:cnn ,
+% \box_autosize_to_wd_and_ht_plus_dp:Nnn ,
+% \box_autosize_to_wd_and_ht_plus_dp:cnn ,
+% \box_gautosize_to_wd_and_ht_plus_dp:Nnn ,
+% \box_gautosize_to_wd_and_ht_plus_dp:cnn
+% }
+% \begin{macro}{\@@_autosize:NnnnN}
+% Although autosizing a box uses dimensions, it has more in common in
+% implementation with scaling. As such, most of the real work here is
+% done elsewhere.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_autosize_to_wd_and_ht:Nnn #1#2#3
+ { \@@_autosize:NnnnN #1 {#2} {#3} { \box_ht:N #1 } \hbox_set:Nn }
+\cs_generate_variant:Nn \box_autosize_to_wd_and_ht:Nnn { c }
+\cs_new_protected:Npn \box_gautosize_to_wd_and_ht:Nnn #1#2#3
+ { \@@_autosize:NnnnN #1 {#2} {#3} { \box_ht:N #1 } \hbox_gset:Nn }
+\cs_generate_variant:Nn \box_gautosize_to_wd_and_ht:Nnn { c }
+\cs_new_protected:Npn \box_autosize_to_wd_and_ht_plus_dp:Nnn #1#2#3
+ {
+ \@@_autosize:NnnnN #1 {#2} {#3} { \box_ht:N #1 + \box_dp:N #1 }
+ \hbox_set:Nn
+ }
+\cs_generate_variant:Nn \box_autosize_to_wd_and_ht_plus_dp:Nnn { c }
+\cs_new_protected:Npn \box_gautosize_to_wd_and_ht_plus_dp:Nnn #1#2#3
+ {
+ \@@_autosize:NnnnN #1 {#2} {#3} { \box_ht:N #1 + \box_dp:N #1 }
+ \hbox_gset:Nn
+ }
+\cs_generate_variant:Nn \box_gautosize_to_wd_and_ht_plus_dp:Nnn { c }
+\cs_new_protected:Npn \@@_autosize:NnnnN #1#2#3#4#5
+ {
+ #5 #1
+ {
+ \fp_set:Nn \l_@@_scale_x_fp { ( \dim_to_fp:n {#2} ) / \box_wd:N #1 }
+ \fp_set:Nn \l_@@_scale_y_fp
+ { ( \dim_to_fp:n {#3} ) / ( \dim_to_fp:n {#4} ) }
+ \fp_compare:nNnTF \l_@@_scale_x_fp > \l_@@_scale_y_fp
+ { \fp_set_eq:NN \l_@@_scale_x_fp \l_@@_scale_y_fp }
+ { \fp_set_eq:NN \l_@@_scale_y_fp \l_@@_scale_x_fp }
+ \@@_scale:N #1
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_resize_common:N}
+% The main resize function places its input into a box which start
+% off with zero width, and includes the handles for engine rescaling.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_resize_common:N #1
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \@@_backend_scale:Nnn
+ #1
+ \l_@@_scale_x_fp
+ \l_@@_scale_y_fp
+ }
+% \end{macrocode}
+% The new height and depth can be applied directly.
+% \begin{macrocode}
+ \fp_compare:nNnTF \l_@@_scale_y_fp > \c_zero_fp
+ {
+ \box_set_ht:Nn \l_@@_internal_box { \l_@@_top_new_dim }
+ \box_set_dp:Nn \l_@@_internal_box { -\l_@@_bottom_new_dim }
+ }
+ {
+ \box_set_dp:Nn \l_@@_internal_box { \l_@@_top_new_dim }
+ \box_set_ht:Nn \l_@@_internal_box { -\l_@@_bottom_new_dim }
+ }
+% \end{macrocode}
+% Things are not quite as obvious for the width, as the reference point
+% needs to remain unchanged. For positive scaling factors resizing the
+% box is all that is needed. However, for case of a negative scaling
+% the material must be shifted such that the reference point ends up in
+% the right place.
+% \begin{macrocode}
+ \fp_compare:nNnTF \l_@@_scale_x_fp < \c_zero_fp
+ {
+ \hbox_to_wd:nn { \l_@@_right_new_dim }
+ {
+ \__kernel_kern:n { \l_@@_right_new_dim }
+ \box_use_drop:N \l_@@_internal_box
+ \tex_hss:D
+ }
+ }
+ {
+ \box_set_wd:Nn \l_@@_internal_box { \l_@@_right_new_dim }
+ \hbox:n
+ {
+ \__kernel_kern:n { 0pt }
+ \box_use_drop:N \l_@@_internal_box
+ \tex_hss:D
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Viewing part of a box}
+%
+% \begin{macro}{\box_set_clipped:N, \box_set_clipped:c, \box_gset_clipped:N, \box_gset_clipped:c}
+% A wrapper around the driver-dependent code.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_set_clipped:N #1
+ { \hbox_set:Nn #1 { \@@_backend_clip:N #1 } }
+\cs_generate_variant:Nn \box_set_clipped:N { c }
+\cs_new_protected:Npn \box_gset_clipped:N #1
+ { \hbox_gset:Nn #1 { \@@_backend_clip:N #1 } }
+\cs_generate_variant:Nn \box_gset_clipped:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \box_set_trim:Nnnnn, \box_set_trim:cnnnn,
+% \box_gset_trim:Nnnnn, \box_gset_trim:cnnnn
+% }
+% \begin{macro}{\@@_set_trim:NnnnnN}
+% Trimming from the left- and right-hand edges of the box is easy: kern the
+% appropriate parts off each side.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_set_trim:Nnnnn #1#2#3#4#5
+ { \@@_set_trim:NnnnnN #1 {#2} {#3} {#4} {#5} \box_set_eq:NN }
+\cs_generate_variant:Nn \box_set_trim:Nnnnn { c }
+\cs_new_protected:Npn \box_gset_trim:Nnnnn #1#2#3#4#5
+ { \@@_set_trim:NnnnnN #1 {#2} {#3} {#4} {#5} \box_gset_eq:NN }
+\cs_generate_variant:Nn \box_gset_trim:Nnnnn { c }
+\cs_new_protected:Npn \@@_set_trim:NnnnnN #1#2#3#4#5#6
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \__kernel_kern:n { -#2 }
+ \box_use:N #1
+ \__kernel_kern:n { -#4 }
+ }
+% \end{macrocode}
+% For the height and depth, there is a need to watch the baseline is
+% respected. Material always has to stay on the correct side, so trimming
+% has to check that there is enough material to trim. First, the bottom
+% edge. If there is enough depth, simply set the depth, or if not move
+% down so the result is zero depth. \cs{box_move_down:nn} is used in both
+% cases so the resulting box always contains a \tn{lower} primitive.
+% The internal box is used here as it allows safe use of \cs{box_set_dp:Nn}.
+% \begin{macrocode}
+ \dim_compare:nNnTF { \box_dp:N #1 } > {#3}
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \box_move_down:nn \c_zero_dim
+ { \box_use_drop:N \l_@@_internal_box }
+ }
+ \box_set_dp:Nn \l_@@_internal_box { \box_dp:N #1 - (#3) }
+ }
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \box_move_down:nn { (#3) - \box_dp:N #1 }
+ { \box_use_drop:N \l_@@_internal_box }
+ }
+ \box_set_dp:Nn \l_@@_internal_box \c_zero_dim
+ }
+% \end{macrocode}
+% Same thing, this time from the top of the box.
+% \begin{macrocode}
+ \dim_compare:nNnTF { \box_ht:N \l_@@_internal_box } > {#5}
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \box_move_up:nn \c_zero_dim
+ { \box_use_drop:N \l_@@_internal_box }
+ }
+ \box_set_ht:Nn \l_@@_internal_box
+ { \box_ht:N \l_@@_internal_box - (#5) }
+ }
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \box_move_up:nn { (#5) - \box_ht:N \l_@@_internal_box }
+ { \box_use_drop:N \l_@@_internal_box }
+ }
+ \box_set_ht:Nn \l_@@_internal_box \c_zero_dim
+ }
+ #6 #1 \l_@@_internal_box
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \box_set_viewport:Nnnnn, \box_set_viewport:cnnnn,
+% \box_gset_viewport:Nnnnn, \box_gset_viewport:cnnnn
+% }
+% \begin{macro}{\@@_viewport:NnnnnN}
+% The same general logic as for the trim operation, but with absolute
+% dimensions. As a result, there are some things to watch out for in the
+% vertical direction.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_set_viewport:Nnnnn #1#2#3#4#5
+ { \@@_set_viewport:NnnnnN #1 {#2} {#3} {#4} {#5} \box_set_eq:NN }
+\cs_generate_variant:Nn \box_set_viewport:Nnnnn { c }
+\cs_new_protected:Npn \box_gset_viewport:Nnnnn #1#2#3#4#5
+ { \@@_set_viewport:NnnnnN #1 {#2} {#3} {#4} {#5} \box_gset_eq:NN }
+\cs_generate_variant:Nn \box_gset_viewport:Nnnnn { c }
+\cs_new_protected:Npn \@@_set_viewport:NnnnnN #1#2#3#4#5#6
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \__kernel_kern:n { -#2 }
+ \box_use:N #1
+ \__kernel_kern:n { #4 - \box_wd:N #1 }
+ }
+ \dim_compare:nNnTF {#3} < \c_zero_dim
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \box_move_down:nn \c_zero_dim
+ { \box_use_drop:N \l_@@_internal_box }
+ }
+ \box_set_dp:Nn \l_@@_internal_box { - \@@_dim_eval:n {#3} }
+ }
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ { \box_move_down:nn {#3} { \box_use_drop:N \l_@@_internal_box } }
+ \box_set_dp:Nn \l_@@_internal_box \c_zero_dim
+ }
+ \dim_compare:nNnTF {#5} > \c_zero_dim
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \box_move_up:nn \c_zero_dim
+ { \box_use_drop:N \l_@@_internal_box }
+ }
+ \box_set_ht:Nn \l_@@_internal_box
+ {
+ (#5)
+ \dim_compare:nNnT {#3} > \c_zero_dim
+ { - (#3) }
+ }
+ }
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \box_move_up:nn { - \@@_dim_eval:n {#5} }
+ { \box_use_drop:N \l_@@_internal_box }
+ }
+ \box_set_ht:Nn \l_@@_internal_box \c_zero_dim
+ }
+ #6 #1 \l_@@_internal_box
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3cctab.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3cctab.dtx
new file mode 100644
index 00000000000..dd35fb00a3d
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3cctab.dtx
@@ -0,0 +1,975 @@
+% \iffalse meta-comment
+%
+%% File: l3cctab.dtx
+%
+% Copyright (C) 2018-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% http://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3cctab} module\\ Category code tables^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% A category code table enables rapid switching of all category codes in
+% one operation. For \LuaTeX{}, this is possible over the entire Unicode
+% range. For other engines, only the $8$-bit range ($0$--$255$) is covered by
+% such tables. The implementation of category code tables in \pkg{expl3}
+% also saves and restores the \TeX{} \tn{endlinechar} primitive value, meaning
+% they could be used for example to implement \cs{ExplSyntaxOn}.
+%
+% \section{Creating and initialising category code tables}
+%
+% \begin{function}[updated = 2020-07-02]{\cctab_new:N,\cctab_new:c}
+% \begin{syntax}
+% \cs{cctab_new:N} \meta{category code table}
+% \end{syntax}
+% Creates a new \meta{category code table} variable or raises an error if
+% the name is already taken. The declaration is global. The
+% \meta{category code table} is initialised with the codes
+% as used by \IniTeX{}.
+% \end{function}
+%
+% \begin{function}[updated = 2020-07-07]{\cctab_const:Nn,\cctab_const:cn}
+% \begin{syntax}
+% \cs{cctab_const:Nn} \meta{category code table} \Arg{category code set up}
+% \end{syntax}
+% Creates a new \meta{category code table}, applies (in a group) the
+% \meta{category code set up} on top of \IniTeX{} settings,
+% then saves them globally as a constant
+% table. The \meta{category code set up} can include a call to
+% \cs{cctab_select:N}.
+% \end{function}
+%
+% \begin{function}[updated = 2020-07-07]{\cctab_gset:Nn,\cctab_gset:cn}
+% \begin{syntax}
+% \cs{cctab_gset:Nn} \meta{category code table} \Arg{category code set up}
+% \end{syntax}
+% Starting from the \IniTeX{} category codes,
+% applies (in a group) the \meta{category code set up}, then saves them
+% globally in the \meta{category code table}. The \meta{category code set up}
+% can include a call to \cs{cctab_select:N}.
+% \end{function}
+%
+% \begin{function}[added = 2023-05-26]
+% {\cctab_gsave_current:N,\cctab_gsave_current:c}
+% \begin{syntax}
+% \cs{cctab_gsave_current:N} \meta{category code table}
+% \end{syntax}
+% Saves the current prevailing category codes in the
+% \meta{category code table}.
+% \end{function}
+%
+% \section{Using category code tables}
+%
+% \begin{function}[updated = 2020-07-02]{\cctab_begin:N,\cctab_begin:c}
+% \begin{syntax}
+% \cs{cctab_begin:N} \meta{category code table}
+% \end{syntax}
+% Switches locally the category codes in force to those stored in the
+% \meta{category code table}. The prevailing codes before the
+% function is called are added to a stack, for use with
+% \cs{cctab_end:}. This function does not start a \TeX{} group.
+% \end{function}
+%
+% \begin{function}[updated = 2020-07-02]{\cctab_end:}
+% \begin{syntax}
+% \cs{cctab_end:}
+% \end{syntax}
+% Ends the scope of a \meta{category code table} started using
+% \cs{cctab_begin:N}, returning the codes to those in force before the
+% matching \cs{cctab_begin:N} was used. This must be used within the
+% same \TeX{} group (and at the same \TeX{} group level) as the
+% matching \cs{cctab_begin:N}.
+% \end{function}
+%
+% \begin{function}[added = 2020-05-19, updated = 2020-07-02]{\cctab_select:N, \cctab_select:c}
+% \begin{syntax}
+% \cs{cctab_select:N} \meta{category code table}
+% \end{syntax}
+% Selects the \meta{category code table} for the scope of the current
+% group. This is in particular useful in the \meta{setup} arguments
+% of \cs{tl_set_rescan:Nnn}, \cs{tl_rescan:nn}, \cs{cctab_const:Nn},
+% and \cs{cctab_gset:Nn}.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2021-05-10]{\cctab_item:Nn, \cctab_item:cn}
+% \begin{syntax}
+% \cs{cctab_item:Nn} \meta{category code table} \Arg{int expr}
+% \end{syntax}
+% Determines the \meta{character} with character code given by the
+% \meta{int expr} and expands to its category code specified
+% by the \meta{category code table}.
+% \end{function}
+%
+% \section{Category code table conditionals}
+%
+% \begin{function}[pTF]{\cctab_if_exist:N, \cctab_if_exist:c}
+% \begin{syntax}
+% \cs{cctab_if_exist_p:N} \meta{category code table}
+% \cs{cctab_if_exist:NTF} \meta{category code table} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{category code table} is currently defined.
+% This does not check that the \meta{category code table} really is a
+% category code table.
+% \end{function}
+%
+% \section{Constant and scratch category code tables}
+%
+% \begin{variable}[updated = 2020-07-10]{\c_code_cctab}
+% Category code table for the \pkg{expl3} code environment; this does
+% \emph{not} include \texttt{@}, which is retained as an \enquote{other}
+% character. Sets the \tn{endlinechar} value to $32$ (a space).
+% \end{variable}
+%
+% \begin{variable}[updated = 2020-07-08]{\c_document_cctab}
+% Category code table for a standard \LaTeX{} document, as set by the \LaTeX{}
+% kernel. In particular, the upper-half of the $8$-bit range will be set to
+% \enquote{active} with \pdfTeX{} \emph{only}. No \pkg{babel} shorthands
+% will be activated. Sets the \tn{endlinechar} value to $13$ (normal
+% line ending).
+% \end{variable}
+%
+% \begin{variable}[updated = 2020-07-02]{\c_initex_cctab}
+% Category code table as set up by \IniTeX{}.
+% \end{variable}
+%
+% \begin{variable}[updated = 2020-07-02]{\c_other_cctab}
+% Category code table where all characters have category code $12$
+% (other). Sets the \tn{endlinechar} value to $-1$.
+% \end{variable}
+%
+% \begin{variable}[updated = 2020-07-02]{\c_str_cctab}
+% Category code table where all characters have category code $12$
+% (other) with the exception of spaces, which have category code
+% $10$ (space). Sets the \tn{endlinechar} value to $-1$.
+% \end{variable}
+%
+% \begin{variable}[added = 2023-05-26]{\g_tmpa_cctab, \g_tmpb_cctab}
+% Scratch category code tables.
+% \end{variable}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3cctab} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=cctab>
+% \end{macrocode}
+%
+% As \LuaTeX{} offers engine support for category code tables, and this
+% is entirely lacking from the other engines, we need two complementary
+% approaches. (Some future \XeTeX{} may add support, at which point the
+% conditionals below would be different.)
+%
+% \subsection{Variables}
+%
+% \begin{variable}{\g_@@_stack_seq, \g_@@_unused_seq}
+% List of catcode tables saved by nested \cs{cctab_begin:N}, to
+% restore catcodes at the matching \cs{cctab_end:}. When popped from
+% the \cs{g_@@_stack_seq} the table numbers are stored in
+% \cs{g_@@_unused_seq} for later reuse.
+% \begin{macrocode}
+\seq_new:N \g_@@_stack_seq
+\seq_new:N \g_@@_unused_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_group_seq}
+% A stack to store the group level when a catcode table started.
+% \begin{macrocode}
+\seq_new:N \g_@@_group_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_allocate_int}
+% Integer to keep track of what category code table to allocate. In
+% \LuaTeX{} it is only used in format mode to implement
+% \cs{cctab_new:N}. In other engines it is used to make csnames for
+% dynamic tables.
+% \begin{macrocode}
+\int_new:N \g_@@_allocate_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_internal_a_tl,\l_@@_internal_b_tl}
+% Scratch space. For instance, when popping
+% \cs{g_@@_stack_seq}/\cs{g_@@_unused_seq}, consists of the
+% catcodetable number (integer denotation) in \LuaTeX{}, or of an
+% intarray variable (as a single token) in other engines.
+% \begin{macrocode}
+\tl_new:N \l_@@_internal_a_tl
+\tl_new:N \l_@@_internal_b_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_endlinechar_prop}
+% In \LuaTeX{} we store the \tn{endlinechar} associated to each
+% \tn{catcodetable} in a property list, unless it is the default
+% value~$13$.
+% \begin{macrocode}
+\prop_new:N \g_@@_endlinechar_prop
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Allocating category code tables}
+%
+% \begin{macro}{\cctab_new:N, \cctab_new:c, \@@_new:N, \@@_gstore:Nnn}
+% The \cs{@@_new:N} auxiliary allocates a new catcode table but does
+% not attempt to set its value consistently across engines. It is
+% used both in \cs{cctab_new:N}, which sets catcodes to \IniTeX{}
+% values, and in \cs{cctab_begin:N}/\cs{cctab_end:} for dynamically
+% allocated tables.
+%
+% First, the \LuaTeX{} case.
+% Creating a new category code table is done like other registers.
+% In Con\TeX{}t, \tn{newcatcodetable} does not include the initialisation,
+% so that is added explicitly.
+% \begin{macrocode}
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \cctab_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \@@_new:N #1
+ }
+ \cs_new_protected:Npn \@@_new:N #1
+ {
+ \newcatcodetable #1
+ \tex_initcatcodetable:D #1
+ }
+ }
+% \end{macrocode}
+% Now the case for other engines. Here, each table is an integer
+% array. Following the \LuaTeX{} pattern, a new table starts with
+% \IniTeX{} codes. The \cs{debug_suspend:} and \cs{debug_resume:}
+% functions prevent errors and logging from \texttt{debug} commands
+% which are either duplicate or false when \cs{@@_new:N} is used
+% by \cs{cctab_new:N} or \cs{cctab_const:Nn}.
+% The index base is out-by-one, so we have an
+% internal function to handle that. The \IniTeX{} \tn{endlinechar} is
+% $13$.
+% \begin{macrocode}
+ {
+ \cs_new_protected:Npn \@@_new:N #1
+ {
+ \debug_suspend:
+ \intarray_new:Nn #1 { 257 }
+ \debug_resume:
+ }
+ \cs_new_protected:Npn \@@_gstore:Nnn #1#2#3
+ { \intarray_gset:Nnn #1 { #2 + 1 } {#3} }
+ \cs_new_protected:Npn \cctab_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \@@_new:N #1
+ \int_step_inline:nn { 256 }
+ { \__kernel_intarray_gset:Nnn #1 {##1} { 12 } }
+ \__kernel_intarray_gset:Nnn #1 { 257 } { 13 }
+ \@@_gstore:Nnn #1 { 0 } { 9 }
+ \@@_gstore:Nnn #1 { 13 } { 5 }
+ \@@_gstore:Nnn #1 { 32 } { 10 }
+ \@@_gstore:Nnn #1 { 37 } { 14 }
+ \int_step_inline:nnn { 65 } { 90 }
+ { \@@_gstore:Nnn #1 {##1} { 11 } }
+ \@@_gstore:Nnn #1 { 92 } { 0 }
+ \int_step_inline:nnn { 97 } { 122 }
+ { \@@_gstore:Nnn #1 {##1} { 11 } }
+ \@@_gstore:Nnn #1 { 127 } { 15 }
+ }
+ }
+\cs_generate_variant:Nn \cctab_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Saving category code tables}
+%
+% \begin{macro}{\@@_gset:n, \@@_gset_aux:n}
+% In various functions we need to save the current catcodes (globally)
+% in a table. In \LuaTeX{}, saving the catcodes is a primitives, but
+% the \tn{endlinechar} needs more work: to avoid filling
+% \cs{g_@@_endlinechar_prop} with many entries we special-case the
+% default value $13$. In other engines we store $256$ current
+% catcodes and the \tn{endlinechar} in an intarray variable.
+% \begin{macrocode}
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \@@_gset:n #1
+ { \exp_args:Nf \@@_gset_aux:n { \int_eval:n {#1} } }
+ \cs_new_protected:Npn \@@_gset_aux:n #1
+ {
+ \tex_savecatcodetable:D #1 \scan_stop:
+ \int_compare:nNnTF { \tex_endlinechar:D } = { 13 }
+ { \prop_gremove:Nn \g_@@_endlinechar_prop {#1} }
+ {
+ \prop_gput:NnV \g_@@_endlinechar_prop {#1}
+ \tex_endlinechar:D
+ }
+ }
+ }
+ {
+ \cs_new_protected:Npn \@@_gset:n #1
+ {
+ \int_step_inline:nn { 256 }
+ {
+ \__kernel_intarray_gset:Nnn #1 {##1}
+ { \char_value_catcode:n { ##1 - 1 } }
+ }
+ \__kernel_intarray_gset:Nnn #1 { 257 }
+ { \tex_endlinechar:D }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\cctab_gset:Nn, \cctab_gset:cn}
+% Category code tables are always global, so only one version of
+% assignments is needed. Simply run the setup in a group and save the
+% result in a category code table~|#1|, provided it is valid. The
+% internal function is defined above depending on the engine.
+% \begin{macrocode}
+\cs_new_protected:Npn \cctab_gset:Nn #1#2
+ {
+ \@@_chk_if_valid:NT #1
+ {
+ \group_begin:
+ \cctab_select:N \c_initex_cctab
+ #2 \scan_stop:
+ \@@_gset:n {#1}
+ \group_end:
+ }
+ }
+\cs_generate_variant:Nn \cctab_gset:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\cctab_gsave_current:N, \cctab_gsave_current:c}
+% Very simple.
+% \begin{macrocode}
+\cs_new_protected:Npn \cctab_gsave_current:N #1
+ {
+ \@@_chk_if_valid:NT #1
+ { \@@_gset:n {#1} }
+ }
+\cs_generate_variant:Nn \cctab_gsave_current:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Using category code tables}
+%
+% \begin{variable}{\g_@@_internal_cctab}
+% \begin{macro}[EXP]{\@@_internal_cctab_name:}
+% In \LuaTeX{}, we must ensure that the saved tables are read-only.
+% This is done by applying the saved table, then switching immediately
+% to a scratch table. Any later catcode assignment will affect that
+% scratch table rather than the saved one. If we simply switched to
+% the saved tables, then \cs{char_set_catcode_other:N} in the example
+% below would change \cs{c_document_cctab} and a later use of that
+% table would give the wrong category code to |_|.
+% \begin{verbatim}
+% \use:n
+% {
+% \cctab_begin:N \c_document_cctab
+% \char_set_catcode_other:N \_
+% \cctab_end:
+% \cctab_begin:N \c_document_cctab
+% \int_compare:nTF { \char_value_catcode:n { `_ } = 8 }
+% { \TRUE } { \ERROR }
+% \cctab_end:
+% }
+% \end{verbatim}
+% We must also make sure that a scratch table is never reused in a
+% nested group: in the following example, the scratch table used by
+% the first \cs{cctab_begin:N} would be changed globally by the second
+% one issuing \tn{savecatcodetable}, and after \cs{group_end:} the
+% wrong category codes (those of \cs{c_str_cctab}) would be imposed.
+% Note that the inner \cs{cctab_end:} restores the correct catcodes
+% only locally, so the problem really comes up because of the
+% different grouping level. The simplest is to use a scratch table
+% labeled by the \tn{currentgrouplevel}. We initialize one of them as
+% an example.
+% \begin{verbatim}
+% \use:n
+% {
+% \cctab_begin:N \c_document_cctab
+% \group_begin:
+% \cctab_begin:N \c_str_cctab
+% \cctab_end:
+% \group_end:
+% \cctab_end:
+% }
+% \end{verbatim}
+% \begin{macrocode}
+\sys_if_engine_luatex:T
+ {
+ \@@_new:N \g_@@_internal_cctab
+ \cs_new:Npn \@@_internal_cctab_name:
+ {
+ g_@@_internal
+ \tex_romannumeral:D \tex_currentgrouplevel:D
+ _cctab
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{variable}
+%
+% \begin{macro}{\cctab_select:N, \cctab_select:c}
+% \begin{macro}{\@@_select:N}
+% The public function simply checks the \meta{cctab~var} exists before
+% using the engine-dependent \cs{@@_select:N}. Skipping these checks
+% would result in low-level engine-dependent errors. First, the
+% \LuaTeX{} case. In other engines, selecting a catcode table is a matter
+% of doing $256$ catcode assignments and setting the \tn{endlinechar}.
+% \begin{macrocode}
+\cs_new_protected:Npn \cctab_select:N #1
+ { \@@_chk_if_valid:NT #1 { \@@_select:N #1 } }
+\cs_generate_variant:Nn \cctab_select:N { c }
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \@@_select:N #1
+ {
+ \tex_catcodetable:D #1
+ \prop_get:NVNTF \g_@@_endlinechar_prop #1 \l_@@_internal_a_tl
+ { \int_set:Nn \tex_endlinechar:D { \l_@@_internal_a_tl } }
+ { \int_set:Nn \tex_endlinechar:D { 13 } }
+ \cs_if_exist:cF { \@@_internal_cctab_name: }
+ { \exp_args:Nc \@@_new:N { \@@_internal_cctab_name: } }
+ \exp_args:Nc \tex_savecatcodetable:D { \@@_internal_cctab_name: }
+ \exp_args:Nc \tex_catcodetable:D { \@@_internal_cctab_name: }
+ }
+ }
+ {
+ \cs_new_protected:Npn \@@_select:N #1
+ {
+ \int_step_inline:nn { 256 }
+ {
+ \char_set_catcode:nn { ##1 - 1 }
+ { \__kernel_intarray_item:Nn #1 {##1} }
+ }
+ \int_set:Nn \tex_endlinechar:D
+ { \__kernel_intarray_item:Nn #1 { 257 } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\g_@@_next_cctab}
+% \begin{macro}{\@@_begin_aux:}
+% For \cs{cctab_begin:N}/\cs{cctab_end:} we will need to allocate
+% dynamic tables. This is done here by \cs{@@_begin_aux:}, which puts
+% a table number (in \LuaTeX{}) or name (in other engines) into
+% \cs{l_@@_internal_a_tl}. In \LuaTeX{} this simply calls \cs{@@_new:N}
+% and uses the resulting catcodetable number; in other engines we need
+% to give a name to the intarray variable and use that. In \LuaTeX{},
+% to restore catcodes at \cs{cctab_end:} we cannot just set
+% \tn{catcodetable} to its value before \cs{cctab_begin:N}, because
+% that table may have been altered by other code in the mean time. So
+% we must make sure to save the catcodes in a table we control and
+% restore them at \cs{cctab_end:}.
+% \begin{macrocode}
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \@@_begin_aux:
+ {
+ \@@_new:N \g_@@_next_cctab
+ \tl_set:NV \l_@@_internal_a_tl \g_@@_next_cctab
+ \cs_undefine:N \g_@@_next_cctab
+ }
+ }
+ {
+ \cs_new_protected:Npn \@@_begin_aux:
+ {
+ \int_gincr:N \g_@@_allocate_int
+ \exp_args:Nc \@@_new:N
+ { g_@@_ \int_use:N \g_@@_allocate_int _cctab }
+ \exp_args:NNc \tl_set:Nn \l_@@_internal_a_tl
+ { g_@@_ \int_use:N \g_@@_allocate_int _cctab }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{variable}
+%
+% \begin{macro}{\cctab_begin:N, \cctab_begin:c}
+% Check the \meta{cctab~var} exists, to avoid low-level errors. Get
+% in \cs{l_@@_internal_a_tl} the number/name of a dynamic table, either
+% from \cs{g_@@_unused_seq} where we save tables that are not
+% currently in use, or from \cs{@@_begin_aux:} if none are available.
+% Then save the current catcodes into the table (pointed to by)
+% \cs{l_@@_internal_a_tl} and save that table number in a stack before
+% selecting the desired catcodes.
+% \begin{macrocode}
+\cs_new_protected:Npn \cctab_begin:N #1
+ {
+ \@@_chk_if_valid:NT #1
+ {
+ \seq_gpop:NNF \g_@@_unused_seq \l_@@_internal_a_tl
+ { \@@_begin_aux: }
+ \@@_chk_group_begin:e
+ { \@@_nesting_number:N \l_@@_internal_a_tl }
+ \seq_gpush:NV \g_@@_stack_seq \l_@@_internal_a_tl
+ \exp_args:NV \@@_gset:n \l_@@_internal_a_tl
+ \@@_select:N #1
+ }
+ }
+\cs_generate_variant:Nn \cctab_begin:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\cctab_end:}
+% Make sure a \cs{cctab_begin:N} was used some time earlier, get in
+% \cs{l_@@_internal_a_tl} the catcode table number/name in which the
+% prevailing catcodes were stored, then restore these catcodes. The
+% dynamic table is now unused hence stored in \cs{g_@@_unused_seq} for
+% recycling by later \cs{cctab_begin:N}.
+% \begin{macrocode}
+\cs_new_protected:Npn \cctab_end:
+ {
+ \seq_gpop:NNTF \g_@@_stack_seq \l_@@_internal_a_tl
+ {
+ \seq_gpush:NV \g_@@_unused_seq \l_@@_internal_a_tl
+ \exp_args:Ne \@@_chk_group_end:n
+ { \@@_nesting_number:N \l_@@_internal_a_tl }
+ \@@_select:N \l_@@_internal_a_tl
+ }
+ { \msg_error:nn { cctab } { extra-end } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_chk_group_begin:n,\@@_chk_group_begin:e, \@@_chk_group_end:n}
+% Catcode tables are not allowed to be intermixed with groups, so here
+% we check that they are properly nested regarding \TeX{} groups.
+% \cs{@@_chk_group_begin:n} stores the current group level in a stack,
+% and locally defines a dummy control sequence
+% \cs[no-index]{@@_group_\meta{cctab-level}_chk:}.
+%
+% \cs{@@_chk_group_end:n} pops the stack, and compares the returned
+% value with \cs{tex_currentgrouplevel:D}. If they differ,
+% \cs{cctab_end:} is in a different grouping level than the matching
+% \cs{cctab_begin:N}. If they are the same, both happened at the same
+% level, however a group might have ended and another started between
+% \cs{cctab_begin:N} and \cs{cctab_end:}:
+% \begin{verbatim}
+% \group_begin:
+% \cctab_begin:N \c_document_cctab
+% \group_end:
+% \group_begin:
+% \cctab_end:
+% \group_end:
+% \end{verbatim}
+% In this case checking \cs{tex_currentgrouplevel:D} is not enough, so
+% we locally define \cs[no-index]{@@_group_\meta{cctab-level}_chk:},
+% and then check if it exist in \cs{cctab_end:}. If it doesn't,
+% we know there was a group end where it shouldn't.
+%
+% The \meta{cctab-level} in the sentinel macro above cannot be
+% replaced by the more convenient \cs{tex_currentgrouplevel:D} because
+% with the latter we might be tricked. Suppose:
+% \begin{verbatim}
+% \group_begin:
+% \cctab_begin:N \c_code_cctab % A
+% \group_end:
+% \group_begin:
+% \cctab_begin:N \c_code_cctab % B
+% \cctab_end: % C
+% \cctab_end: % D
+% \group_end:
+% \end{verbatim}
+% The line marked with |A| would start a |cctab| with a sentinel token
+% named \cs[no-index]{@@_group_1_chk:}, which would disappear at the
+% \cs{group_end:} that follows. But |B| would create the same
+% sentinel token, since both are at the same group level. Line |C|
+% would end the |cctab| from line |B| correctly, but so would line |D|
+% because line |B| created the same sentinel token. Using
+% \meta{cctab-level} works correctly because it signals that certain
+% |cctab| level was activated somewhere, but if it doesn't exist when
+% the \cs{cctab_end:} is reached, we had a problem.
+%
+% Unfortunately these tests only flag the wrong usage at the
+% \cs{cctab_end:}, which might be far from the \cs{cctab_begin:N}.
+% However it isn't possible to signal the wrong usage at the
+% \cs{group_end:} without using \cs{tex_aftergroup:D}, which is
+% unsafe in certain types of groups.
+%
+% The three cases checked here just raise an error, and no recovery is
+% attempted: usually interleaving groups and catcode tables will work
+% predictably.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_chk_group_begin:n #1
+ {
+ \seq_gpush:Ne \g_@@_group_seq
+ { \int_use:N \tex_currentgrouplevel:D }
+ \cs_set_eq:cN { @@_group_ #1 _chk: } \prg_do_nothing:
+ }
+\cs_generate_variant:Nn \@@_chk_group_begin:n { e }
+\cs_new_protected:Npn \@@_chk_group_end:n #1
+ {
+ \seq_gpop:NN \g_@@_group_seq \l_@@_internal_b_tl
+ \bool_lazy_and:nnF
+ {
+ \int_compare_p:nNn
+ { \tex_currentgrouplevel:D } = { \l_@@_internal_b_tl }
+ }
+ { \cs_if_exist_p:c { @@_group_ #1 _chk: } }
+ {
+ \msg_error:nne { cctab } { group-mismatch }
+ {
+ \int_sign:n
+ { \tex_currentgrouplevel:D - \l_@@_internal_b_tl }
+ }
+ }
+ \cs_undefine:c { @@_group_ #1 _chk: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_nesting_number:N,\@@_nesting_number:w}
+% This macro returns the numeric index of the current catcode table.
+% In \LuaTeX{} this is just the argument, which is a count reference
+% to a \tn{catcodetable} register. In other engines, the number is
+% extracted from the |cctab| variable.
+% \begin{macrocode}
+\sys_if_engine_luatex:TF
+ { \cs_new:Npn \@@_nesting_number:N #1 {#1} }
+ {
+ \cs_new:Npn \@@_nesting_number:N #1
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_nesting_number:w
+ \exp_after:wN \token_to_str:N #1
+ }
+ \use:e
+ {
+ \cs_new:Npn \exp_not:N \@@_nesting_number:w
+ #1 \tl_to_str:n { g_@@_ } #2 \tl_to_str:n { _cctab } {#2}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% Finally, install some code at the end of the \TeX{} run to check that
+% all \cs{cctab_begin:N} were ended by some \cs{cctab_end:}.
+% \begin{macrocode}
+\cs_if_exist:NT \hook_gput_code:nnn
+ {
+ \hook_gput_code:nnn { enddocument/end } { cctab }
+ {
+ \seq_if_empty:NF \g_@@_stack_seq
+ { \msg_error:nn { cctab } { missing-end } }
+ }
+ }
+% \end{macrocode}
+%
+%
+% \begin{macro}{\cctab_item:Nn, \cctab_item:cn}
+% Evaluate the integer argument only once. In most engines the
+% |cctab| variable only has $256$ entries so we only look up the
+% catcode for these entries, otherwise we use the current catcode. In
+% particular, for out-of-range values we use whatever fall-back
+% \cs{char_value_catcode:n}. In \LuaTeX{}, we use the
+% |tex.getcatcode| function.
+% \begin{macrocode}
+\cs_new:Npn \cctab_item:Nn #1#2
+ { \exp_args:Nf \@@_item:nN { \int_eval:n {#2} } #1 }
+\sys_if_engine_luatex:TF
+ {
+ \cs_new:Npn \@@_item:nN #1#2
+ { \lua_now:e { tex.print(-2, tex.getcatcode(\int_use:N #2, #1)) } }
+ }
+ {
+ \cs_new:Npn \@@_item:nN #1#2
+ {
+ \int_compare:nNnTF {#1} < { 256 }
+ { \intarray_item:Nn #2 { #1 + 1 } }
+ { \char_value_catcode:n {#1} }
+ }
+ }
+\cs_generate_variant:Nn \cctab_item:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Category code table conditionals}
+%
+% \begin{macro}[pTF]{\cctab_if_exist:N,\cctab_if_exist:c}
+% Checks whether a \meta{cctab~var} is defined.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \cctab_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \cctab_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[TF]{\@@_chk_if_valid:N}
+% \begin{macro}{\@@_chk_if_valid_aux:NTF}
+% Checks whether the argument is defined and whether it is a valid
+% \meta{cctab~var}. In \LuaTeX{} the validity of the \meta{cctab~var}
+% is checked by the engine, which complains if the argument is not a
+% \cs{chardef}'ed constant. In other engines, check if the given
+% command is an intarray variable (the underlying definition is a copy
+% of the \texttt{cmr10} font).
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \@@_chk_if_valid:N #1
+ { TF , T , F }
+ {
+ \cctab_if_exist:NTF #1
+ {
+ \@@_chk_if_valid_aux:NTF #1
+ { \prg_return_true: }
+ {
+ \msg_error:nne { cctab } { invalid-cctab }
+ { \token_to_str:N #1 }
+ \prg_return_false:
+ }
+ }
+ {
+ \msg_error:nne { kernel } { command-not-defined }
+ { \token_to_str:N #1 }
+ \prg_return_false:
+ }
+ }
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \@@_chk_if_valid_aux:NTF #1
+ {
+ \int_compare:nNnTF {#1-1} < { \e@alloc@ccodetable@count }
+ }
+ \cs_if_exist:NT \c_syst_catcodes_n
+ {
+ \cs_gset_protected:Npn \@@_chk_if_valid_aux:NTF #1
+ {
+ \int_compare:nTF { #1 <= \c_syst_catcodes_n }
+ }
+ }
+ }
+ {
+ \cs_new_protected:Npn \@@_chk_if_valid_aux:NTF #1
+ {
+ \exp_args:Nf \str_if_in:nnTF
+ { \cs_meaning:N #1 }
+ { select~font~cmr10~at~ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Constant category code tables}
+%
+% \begin{macro}{\cctab_const:Nn,\cctab_const:cn}
+% Creates a new \meta{cctab~var} then sets it with the \IniTeX{} and
+% user-supplied codes. To avoid false \texttt{debug} errors, we write
+% out implementation of \cs{cctab_new:N} and \cs{cctab_gset:Nn}
+% instead of directly using them here. The initialization part in
+% \cs{cctab_new:N} in non-\LuaTeX{} is omitted as it's covered by
+% the \IniTeX{} settings.
+% \begin{macrocode}
+\cs_new_protected:Npn \cctab_const:Nn #1#2
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \@@_new:N #1
+ \group_begin:
+ \cctab_select:N \c_initex_cctab
+ #2 \scan_stop:
+ \@@_gset:n {#1}
+ \group_end:
+ }
+\cs_generate_variant:Nn \cctab_const:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}
+% {
+% \c_initex_cctab ,
+% \c_other_cctab ,
+% \c_str_cctab
+% }
+% Creating category code tables means thinking starting from \IniTeX{}.
+% For all-other and the standard \enquote{string} tables that's easy.
+% \begin{macrocode}
+\cctab_new:N \c_initex_cctab
+\cctab_const:Nn \c_other_cctab
+ {
+ \cctab_select:N \c_initex_cctab
+ \int_set:Nn \tex_endlinechar:D { -1 }
+ \int_step_inline:nnn { 0 } { 127 }
+ { \char_set_catcode_other:n {#1} }
+ }
+\cctab_const:Nn \c_str_cctab
+ {
+ \cctab_select:N \c_other_cctab
+ \char_set_catcode_space:n { 32 }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_code_cctab, \c_document_cctab}
+% To pick up document-level category codes, we need to delay set up to the
+% end of the format, where that's possible. Also, as there are a \emph{lot}
+% of category codes to set, we avoid using the official interface and store the
+% document codes using internal code. Depending on whether we are in the hook
+% or not, the catcodes may be code or document, so we explicitly set up both
+% correctly.
+% \begin{macrocode}
+\cs_if_exist:NTF \@expl@finalise@setup@@@@
+ { \tl_gput_right:Nn \@expl@finalise@setup@@@@ }
+ { \use:n }
+ {
+ \@@_new:N \c_code_cctab
+ \group_begin:
+ \int_set:Nn \tex_endlinechar:D { 32 }
+ \char_set_catcode_invalid:n { 0 }
+ \bool_lazy_or:nnTF
+ { \sys_if_engine_xetex_p: } { \sys_if_engine_luatex_p: }
+ { \int_step_function:nN { 31 } \char_set_catcode_invalid:n }
+ { \int_step_function:nN { 31 } \char_set_catcode_active:n }
+ \int_step_function:nnN { 33 } { 64 } \char_set_catcode_other:n
+ \int_step_function:nnN { 65 } { 90 } \char_set_catcode_letter:n
+ \int_step_function:nnN { 91 } { 96 } \char_set_catcode_other:n
+ \int_step_function:nnN { 97 } { 122 } \char_set_catcode_letter:n
+ \char_set_catcode_ignore:n { 9 } % tab
+ \char_set_catcode_other:n { 10 } % lf
+ \char_set_catcode_active:n { 12 } % ff
+ \char_set_catcode_end_line:n { 13 } % cr
+ \char_set_catcode_ignore:n { 32 } % space
+ \char_set_catcode_parameter:n { 35 } % hash
+ \char_set_catcode_math_toggle:n { 36 } % dollar
+ \char_set_catcode_comment:n { 37 } % percent
+ \char_set_catcode_alignment:n { 38 } % ampersand
+ \char_set_catcode_letter:n { 58 } % colon
+ \char_set_catcode_escape:n { 92 } % backslash
+ \char_set_catcode_math_superscript:n { 94 } % circumflex
+ \char_set_catcode_letter:n { 95 } % underscore
+ \char_set_catcode_group_begin:n { 123 } % left brace
+ \char_set_catcode_other:n { 124 } % pipe
+ \char_set_catcode_group_end:n { 125 } % right brace
+ \char_set_catcode_space:n { 126 } % tilde
+ \char_set_catcode_invalid:n { 127 } % ^^?
+ \bool_lazy_or:nnF
+ { \sys_if_engine_xetex_p: } { \sys_if_engine_luatex_p: }
+ { \int_step_function:nnN { 128 } { 255 } \char_set_catcode_active:n }
+ \@@_gset:n { \c_code_cctab }
+ \group_end:
+ \cctab_const:Nn \c_document_cctab
+ {
+ \cctab_select:N \c_code_cctab
+ \int_set:Nn \tex_endlinechar:D { 13 }
+ \char_set_catcode_space:n { 9 }
+ \char_set_catcode_space:n { 32 }
+ \char_set_catcode_other:n { 58 }
+ \char_set_catcode_math_subscript:n { 95 }
+ \char_set_catcode_active:n { 126 }
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_tmpa_cctab, \g_tmpb_cctab}
+% \begin{macrocode}
+\cctab_new:N \g_tmpa_cctab
+\cctab_new:N \g_tmpb_cctab
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Messages}
+%
+% \begin{macrocode}
+\msg_new:nnnn { cctab } { stack-full }
+ { The~category~code~table~stack~is~exhausted. }
+ {
+ LaTeX~has~been~asked~to~switch~to~a~new~category~code~table,~
+ but~there~is~no~more~space~to~do~this!
+ }
+\msg_new:nnnn { cctab } { extra-end }
+ { Extra~\iow_char:N\\cctab_end:~ignored~\msg_line_context:. }
+ {
+ LaTeX~came~across~a~\iow_char:N\\cctab_end:~without~a~matching~
+ \iow_char:N\\cctab_begin:N.~This~command~will~be~ignored.
+ }
+\msg_new:nnnn { cctab } { missing-end }
+ { Missing~\iow_char:N\\cctab_end:~before~end~of~TeX~run. }
+ {
+ LaTeX~came~across~more~\iow_char:N\\cctab_begin:N~than~
+ \iow_char:N\\cctab_end:.
+ }
+\msg_new:nnnn { cctab } { invalid-cctab }
+ { Invalid~\iow_char:N\\catcode~table. }
+ {
+ You~can~only~switch~to~a~\iow_char:N\\catcode~table~that~is~
+ initialized~using~\iow_char:N\\cctab_new:N~or~
+ \iow_char:N\\cctab_const:Nn.
+ }
+\msg_new:nnnn { cctab } { group-mismatch }
+ {
+ \iow_char:N\\cctab_end:~occurred~in~a~
+ \int_case:nn {#1}
+ {
+ { 0 } { different~group }
+ { 1 } { higher~group~level }
+ { -1 } { lower~group~level }
+ } ~than~
+ the~matching~\iow_char:N\\cctab_begin:N.
+ }
+ {
+ Catcode~tables~and~groups~must~be~properly~nested,~but~
+ you~tried~to~interleave~them.~LaTeX~will~try~to~proceed,~
+ but~results~may~be~unexpected.
+ }
+\prop_gput:Nnn \g_msg_module_name_prop { cctab } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { cctab } { }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+%\PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3clist.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3clist.dtx
new file mode 100644
index 00000000000..c67f70db2c8
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3clist.dtx
@@ -0,0 +1,2248 @@
+% \iffalse meta-comment
+%
+%% File: l3clist.dtx
+%
+% Copyright (C) 2004-2011 Frank Mittelbach, The LaTeX Project
+% (C) 2012-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3clist} module\\ Comma separated lists^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% Comma lists (in short, |clist|) contain ordered data where items can
+% be added to the left or right end of the list. This data type allows
+% basic list
+% manipulations such as adding/removing items, applying a function to
+% every item, removing duplicate items, extracting a given item, using
+% the comma list with specified separators, and so on. Sequences
+% (defined in \pkg{l3seq}) are safer, faster, and provide more features,
+% so they should often be preferred to comma lists. Comma lists are
+% mostly useful when interfacing with \LaTeXe{} or other code that
+% expects or provides items separated by commas.
+%
+% Several items can be added at once. To ease input of comma lists from
+% data provided by a user outside an \cs{ExplSyntaxOn} \ldots{}
+% \cs{ExplSyntaxOff} block, spaces are removed from both sides of each
+% comma-delimited argument upon input. Blank arguments are ignored, to
+% allow for trailing commas or repeated commas (which may otherwise
+% arise when concatenating comma lists \enquote{by hand}). In addition,
+% a set of braces is removed if the result of space-trimming is braced:
+% this allows the storage of any item in a comma list. For instance,
+% \begin{verbatim}
+% \clist_new:N \l_my_clist
+% \clist_put_left:Nn \l_my_clist { ~a~ , ~{b}~ , c~\d }
+% \clist_put_right:Nn \l_my_clist { ~{e~} , , {{f}} , }
+% \end{verbatim}
+% results in |\l_my_clist| containing |a,b,c~\d,{e~},{{f}}| namely the
+% five items |a|, |b|, |c~\d|, |e~| and~|{f}|. Comma lists normally do
+% not contain empty or blank items so the following gives an empty comma list:
+% \begin{verbatim}
+% \clist_clear_new:N \l_my_clist
+% \clist_set:Nn \l_my_clist { , ~ , , }
+% \clist_if_empty:NTF \l_my_clist { true } { false }
+% \end{verbatim}
+% and it leaves \texttt{true} in the input stream. To include an
+% \enquote{unsafe} item (empty, or one that contains a comma, or starts
+% or ends with a space, or is a single brace group), surround it with
+% braces.
+%
+% Any |n|-type token list is a valid comma list input for \pkg{l3clist}
+% functions, which will split the token list at every comma and process
+% the items as described above. On the other hand, |N|-type functions
+% expect comma list variables, which are particular token list variables
+% in which this processing of items (and removal of blank items) has
+% already occurred. Because comma list variables are token list
+% variables, expanding them once yields their items separated by commas,
+% and \pkg{l3tl} functions such as \cs{tl_show:N} can be applied to
+% them. (These functions often have \pkg{l3clist} analogues, which
+% should be preferred.)
+%
+% Almost all operations on comma lists are
+% noticeably slower than those on sequences so converting the data to
+% sequences using \cs{seq_set_from_clist:Nn} (see \pkg{l3seq}) may be
+% advisable if speed is important. The exception is that
+% \cs{clist_if_in:NnTF} and \cs{clist_remove_duplicates:N} may be faster
+% than their sequence analogues for large lists. However, these
+% functions work slowly for \enquote{unsafe} items that must be braced,
+% and may produce errors when their argument contains |{|, |}| or |#|
+% (assuming the usual \TeX{} category codes apply). The sequence
+% data type should thus certainly be preferred to comma lists to store
+% such items.
+%
+% \section{Creating and initialising comma lists}
+%
+% \begin{function}{\clist_new:N, \clist_new:c}
+% \begin{syntax}
+% \cs{clist_new:N} \meta{clist~var}
+% \end{syntax}
+% Creates a new \meta{clist~var} or raises an error if the name is
+% already taken. The declaration is global. The \meta{clist~var}
+% initially contains no items.
+% \end{function}
+%
+% \begin{function}[added = 2014-07-05]
+% {
+% \clist_const:Nn, \clist_const:Ne,
+% \clist_const:cn, \clist_const:ce
+% }
+% \begin{syntax}
+% \cs{clist_const:Nn} \meta{clist~var} \Arg{comma list}
+% \end{syntax}
+% Creates a new constant \meta{clist~var} or raises an error
+% if the name is already taken. The value of the
+% \meta{clist~var} is set globally to the
+% \meta{comma list}.
+% \end{function}
+%
+% \begin{function}
+% {\clist_clear:N, \clist_clear:c, \clist_gclear:N, \clist_gclear:c}
+% \begin{syntax}
+% \cs{clist_clear:N} \meta{clist~var}
+% \end{syntax}
+% Clears all items from the \meta{clist~var}.
+% \end{function}
+%
+% \begin{function}
+% {
+% \clist_clear_new:N, \clist_clear_new:c,
+% \clist_gclear_new:N, \clist_gclear_new:c
+% }
+% \begin{syntax}
+% \cs{clist_clear_new:N} \meta{clist~var}
+% \end{syntax}
+% Ensures that the \meta{clist~var} exists globally by applying
+% \cs{clist_new:N} if necessary, then applies
+% \cs[index=clist_clear:N]{clist_(g)clear:N} to leave
+% the list empty.
+% \end{function}
+%
+% \begin{function}
+% {
+% \clist_set_eq:NN, \clist_set_eq:cN,
+% \clist_set_eq:Nc, \clist_set_eq:cc,
+% \clist_gset_eq:NN, \clist_gset_eq:cN,
+% \clist_gset_eq:Nc, \clist_gset_eq:cc
+% }
+% \begin{syntax}
+% \cs{clist_set_eq:NN} \meta{clist~var_1} \meta{clist~var_2}
+% \end{syntax}
+% Sets the content of \meta{clist~var_1} equal to that of
+% \meta{clist~var_2}. To set a token list variable equal to a comma
+% list variable, use \cs{tl_set_eq:NN}. Conversely, setting a comma
+% list variable to a token list is unadvisable unless one checks
+% space-trimming and related issues.
+% \end{function}
+%
+% \begin{function}[added = 2014-07-17]
+% {
+% \clist_set_from_seq:NN, \clist_set_from_seq:cN,
+% \clist_set_from_seq:Nc, \clist_set_from_seq:cc,
+% \clist_gset_from_seq:NN, \clist_gset_from_seq:cN,
+% \clist_gset_from_seq:Nc, \clist_gset_from_seq:cc
+% }
+% \begin{syntax}
+% \cs{clist_set_from_seq:NN} \meta{clist~var} \meta{seq~var}
+% \end{syntax}
+% Converts the data in the \meta{seq~var} into a \meta{clist~var}:
+% the original \meta{seq~var} is unchanged.
+% Items which contain either spaces or commas are surrounded by braces.
+% \end{function}
+%
+% \begin{function}
+% {
+% \clist_concat:NNN, \clist_concat:ccc,
+% \clist_gconcat:NNN, \clist_gconcat:ccc
+% }
+% \begin{syntax}
+% \cs{clist_concat:NNN} \meta{clist~var_1} \meta{clist~var_2} \meta{clist~var_3}
+% \end{syntax}
+% Concatenates the content of \meta{clist~var_2} and \meta{clist~var_3}
+% together and saves the result in \meta{clist~var_1}. The items in
+% \meta{clist~var_2} are placed at the left side of the new comma list.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added=2012-03-03]
+% {\clist_if_exist:N, \clist_if_exist:c}
+% \begin{syntax}
+% \cs{clist_if_exist_p:N} \meta{clist~var}
+% \cs{clist_if_exist:NTF} \meta{clist~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{clist~var} is currently defined. This does
+% not check that the \meta{clist~var} really is a comma list.
+% \end{function}
+%
+% \section{Adding data to comma lists}
+%
+% \begin{function}[added = 2011-09-06]
+% {
+% \clist_set:Nn, \clist_set:NV, \clist_set:Ne,
+% \clist_set:No,
+% \clist_set:cn, \clist_set:cV, \clist_set:ce,
+% \clist_set:co,
+% \clist_gset:Nn, \clist_gset:NV, \clist_gset:Ne,
+% \clist_gset:No,
+% \clist_gset:cn, \clist_gset:cV, \clist_gset:ce,
+% \clist_gset:co
+% }
+% \begin{syntax}
+% \cs{clist_set:Nn} \meta{clist~var} |{|\meta{item_1},\ldots{},\meta{item_n}|}|
+% \end{syntax}
+% Sets \meta{clist~var} to contain the \meta{items},
+% removing any previous content from the variable.
+% Blank items are omitted, spaces are removed from both sides of each
+% item, then a set of braces is removed if the resulting space-trimmed
+% item is braced.
+% To store some \meta{tokens} as a single \meta{item} even if the
+% \meta{tokens} contain commas or spaces, add a set of braces:
+% \cs{clist_set:Nn} \meta{clist~var} |{| \Arg{tokens} |}|.
+% \end{function}
+%
+% \begin{function}[updated = 2011-09-05]
+% {
+% \clist_put_left:Nn, \clist_put_left:NV,
+% \clist_put_left:Nv, \clist_put_left:Ne,
+% \clist_put_left:No,
+% \clist_put_left:cn, \clist_put_left:cV,
+% \clist_put_left:cv, \clist_put_left:ce,
+% \clist_put_left:co,
+% \clist_gput_left:Nn, \clist_gput_left:NV,
+% \clist_gput_left:Nv, \clist_gput_left:Ne,
+% \clist_gput_left:No,
+% \clist_gput_left:cn, \clist_gput_left:cV,
+% \clist_gput_left:cv, \clist_gput_left:ce,
+% \clist_gput_left:co
+% }
+% \begin{syntax}
+% \cs{clist_put_left:Nn} \meta{clist~var} |{|\meta{item_1},\ldots{},\meta{item_n}|}|
+% \end{syntax}
+% Appends the \meta{items} to the left of the \meta{clist~var}.
+% Blank items are omitted, spaces are removed from both sides of each
+% item, then a set of braces is removed if the resulting space-trimmed
+% item is braced.
+% To append some \meta{tokens} as a single \meta{item} even if the
+% \meta{tokens} contain commas or spaces, add a set of braces:
+% \cs{clist_put_left:Nn} \meta{clist~var} |{| \Arg{tokens} |}|.
+% \end{function}
+%
+% \begin{function}[updated = 2011-09-05]
+% {
+% \clist_put_right:Nn, \clist_put_right:NV,
+% \clist_put_right:Nv, \clist_put_right:Ne,
+% \clist_put_right:No,
+% \clist_put_right:cn, \clist_put_right:cV,
+% \clist_put_right:cv, \clist_put_right:ce,
+% \clist_put_right:co,
+% \clist_gput_right:Nn, \clist_gput_right:NV,
+% \clist_gput_right:Nv, \clist_gput_right:Ne,
+% \clist_gput_right:No,
+% \clist_gput_right:cn, \clist_gput_right:cV,
+% \clist_gput_right:cv, \clist_gput_right:ce,
+% \clist_gput_right:co
+% }
+% \begin{syntax}
+% \cs{clist_put_right:Nn} \meta{clist~var} |{|\meta{item_1},\ldots{},\meta{item_n}|}|
+% \end{syntax}
+% Appends the \meta{items} to the right of the \meta{clist~var}.
+% Blank items are omitted, spaces are removed from both sides of each
+% item, then a set of braces is removed if the resulting space-trimmed
+% item is braced.
+% To append some \meta{tokens} as a single \meta{item} even if the
+% \meta{tokens} contain commas or spaces, add a set of braces:
+% \cs{clist_put_right:Nn} \meta{clist~var} |{| \Arg{tokens} |}|.
+% \end{function}
+%
+% \section{Modifying comma lists}
+%
+% While comma lists are normally used as ordered lists, it may be
+% necessary to modify the content. The functions here may be used
+% to update comma lists, while retaining the order of the unaffected
+% entries.
+%
+% \begin{function}
+% {
+% \clist_remove_duplicates:N, \clist_remove_duplicates:c,
+% \clist_gremove_duplicates:N, \clist_gremove_duplicates:c
+% }
+% \begin{syntax}
+% \cs{clist_remove_duplicates:N} \meta{clist~var}
+% \end{syntax}
+% Removes duplicate items from the \meta{clist~var}, leaving the
+% left most copy of each item in the \meta{clist~var}. The \meta{item}
+% comparison takes place on a token basis, as for \cs{tl_if_eq:nnTF}.
+% \begin{texnote}
+% This function iterates through every item in the \meta{clist~var} and
+% does a comparison with the \meta{items} already checked. It is therefore
+% relatively slow with large comma lists.
+% Furthermore, it may fail if any of the items in the
+% \meta{clist~var} contains |{|, |}|, or |#|
+% (assuming the usual \TeX{} category codes apply).
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[updated = 2011-09-06]
+% {
+% \clist_remove_all:Nn, \clist_remove_all:cn,
+% \clist_remove_all:NV, \clist_remove_all:cV,
+% \clist_gremove_all:Nn, \clist_gremove_all:cn,
+% \clist_gremove_all:NV, \clist_gremove_all:cV
+% }
+% \begin{syntax}
+% \cs{clist_remove_all:Nn} \meta{clist~var} \Arg{item}
+% \end{syntax}
+% Removes every occurrence of \meta{item} from the \meta{clist~var}.
+% The \meta{item} comparison takes place on a token basis, as for
+% \cs{tl_if_eq:nnTF}.
+% \begin{texnote}
+% The function may fail if the \meta{item} contains |{|, |}|, or |#|
+% (assuming the usual \TeX{} category codes apply).
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2014-07-18]
+% {
+% \clist_reverse:N, \clist_reverse:c,
+% \clist_greverse:N, \clist_greverse:c
+% }
+% \begin{syntax}
+% \cs{clist_reverse:N} \meta{clist~var}
+% \end{syntax}
+% Reverses the order of items stored in the \meta{clist~var}.
+% \end{function}
+%
+% \begin{function}[added = 2014-07-18]{\clist_reverse:n}
+% \begin{syntax}
+% \cs{clist_reverse:n} \Arg{comma list}
+% \end{syntax}
+% Leaves the items in the \meta{comma list} in the input stream in
+% reverse order. Contrarily to other what is done for other
+% \texttt{n}-type \meta{comma list} arguments, braces and spaces are
+% preserved by this process.
+% \begin{texnote}
+% The result is returned within \tn{unexpanded}, which means that the
+% comma list does not expand further when appearing in an
+% \texttt{e}-type or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2017-02-06]
+% {\clist_sort:Nn, \clist_sort:cn, \clist_gsort:Nn, \clist_gsort:cn}
+% \begin{syntax}
+% \cs{clist_sort:Nn} \meta{clist var} \Arg{comparison code}
+% \end{syntax}
+% Sorts the items in the \meta{clist var} according to the
+% \meta{comparison code}, and assigns the result to
+% \meta{clist var}. The details of sorting comparison are
+% described in Section~\ref{sec:l3sort:mech}.
+% \end{function}
+%
+% \section{Comma list conditionals}
+%
+% \begin{function}[EXP,pTF]{\clist_if_empty:N, \clist_if_empty:c}
+% \begin{syntax}
+% \cs{clist_if_empty_p:N} \meta{clist~var}
+% \cs{clist_if_empty:NTF} \meta{clist~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{clist~var} is empty (containing no items).
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added = 2014-07-05]{\clist_if_empty:n}
+% \begin{syntax}
+% \cs{clist_if_empty_p:n} \Arg{comma list}
+% \cs{clist_if_empty:nTF} \Arg{comma list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{clist~var} is empty (containing no items).
+% The rules for space trimming are as for other \texttt{n}-type
+% comma-list functions, hence the comma list |{~,~,,~}| (without
+% outer braces) is empty, while |{~,{},}| (without outer braces)
+% contains one element, which happens to be empty: the comma-list
+% is not empty.
+% \end{function}
+%
+% \begin{function}[updated = 2011-09-06, TF]
+% {
+% \clist_if_in:Nn, \clist_if_in:NV, \clist_if_in:No,
+% \clist_if_in:cn, \clist_if_in:cV, \clist_if_in:co,
+% \clist_if_in:nn, \clist_if_in:nV, \clist_if_in:no
+% }
+% \begin{syntax}
+% \cs{clist_if_in:NnTF} \meta{clist~var} \Arg{item} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{item} is present in the \meta{clist~var}.
+% In the case of an \texttt{n}-type \meta{comma list}, the usual rules
+% of space trimming and brace stripping apply. Hence,
+% \begin{verbatim}
+% \clist_if_in:nnTF { a , {b}~ , {b} , c } { b } {true} {false}
+% \end{verbatim}
+% yields \texttt{true}.
+% \begin{texnote}
+% The function may fail if the \meta{item} contains |{|, |}|, or |#|
+% (assuming the usual \TeX{} category codes apply).
+% \end{texnote}
+% \end{function}
+%
+% \section{Mapping over comma lists}
+%
+% The functions described in this section apply a specified function
+% to each item of a comma list.
+% All mappings are done at the current group level, \emph{i.e.}~any
+% local assignments made by the \meta{function} or \meta{code} discussed
+% below remain in effect after the loop.
+%
+% When the comma list is given explicitly, as an \texttt{n}-type argument,
+% spaces are trimmed around each item.
+% If the result of trimming spaces is empty, the item is ignored.
+% Otherwise, if the item is surrounded by braces, one set is removed,
+% and the result is passed to the mapped function. Thus, if the
+% comma list that is being mapped is \verb*|{a , {{b} }, ,{}, {c},}|
+% then the arguments passed to the mapped function are
+% `\verb*|a|', `\verb*|{b} |', an empty argument, and `\verb*|c|'.
+%
+% When the comma list is given as an \texttt{N}-type argument, spaces
+% have already been trimmed on input, and items are simply stripped
+% of one set of braces if any. This case is more efficient than using
+% \texttt{n}-type comma lists.
+%
+% \begin{function}[rEXP, updated = 2012-06-29]
+% {\clist_map_function:NN, \clist_map_function:cN, \clist_map_function:nN, \clist_map_function:eN}
+% \begin{syntax}
+% \cs{clist_map_function:NN} \meta{clist~var} \meta{function}
+% \end{syntax}
+% Applies \meta{function} to every \meta{item} stored in the
+% \meta{clist~var}. The \meta{function} receives one argument for
+% each iteration. The \meta{items} are returned from left to right.
+% The function \cs{clist_map_inline:Nn} is in general more efficient
+% than \cs{clist_map_function:NN}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-06-29]
+% {\clist_map_inline:Nn, \clist_map_inline:cn, \clist_map_inline:nn}
+% \begin{syntax}
+% \cs{clist_map_inline:Nn} \meta{clist~var} \Arg{inline function}
+% \end{syntax}
+% Applies \meta{inline function} to every \meta{item} stored
+% within the \meta{clist~var}. The \meta{inline function} should
+% consist of code which receives the \meta{item} as |#1|.
+% The \meta{items} are returned from left to right.
+% \end{function}
+%
+% \begin{function}[updated = 2012-06-29]
+% {\clist_map_variable:NNn, \clist_map_variable:cNn, \clist_map_variable:nNn}
+% \begin{syntax}
+% \cs{clist_map_variable:NNn} \meta{clist~var} \meta{variable} \Arg{code}
+% \end{syntax}
+% Stores each \meta{item} of the \meta{clist~var} in turn in the
+% (token list) \meta{variable} and applies the \meta{code}. The
+% \meta{code} will usually make use of the \meta{variable}, but this
+% is not enforced. The assignments to the \meta{variable} are local.
+% Its value after the loop is the last \meta{item} in the \meta{clist~var},
+% or its original value if there were no \meta{item}. The
+% \meta{items} are returned from left to right.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2021-05-05]
+% {\clist_map_tokens:Nn, \clist_map_tokens:cn, \clist_map_tokens:nn}
+% \begin{syntax}
+% \cs{clist_map_tokens:Nn} \meta{clist~var} \Arg{code}
+% \cs{clist_map_tokens:nn} \Arg{comma list} \Arg{code}
+% \end{syntax}
+% Calls \meta{code} \Arg{item} for every \meta{item} stored in the
+% \meta{clist~var}. The \meta{code} receives each \meta{item} as a
+% trailing brace group. If the \meta{code} consists of a single
+% function this is equivalent to \cs{clist_map_function:nN}.
+% \end{function}
+%
+% \begin{function}[rEXP, updated = 2012-06-29]{\clist_map_break:}
+% \begin{syntax}
+% \cs{clist_map_break:}
+% \end{syntax}
+% Used to terminate a \cs[no-index]{clist_map_\ldots{}} function before all
+% entries in the \meta{comma list} have been processed. This
+% normally takes place within a conditional statement, for example
+% \begin{verbatim}
+% \clist_map_inline:Nn \l_my_clist
+% {
+% \str_if_eq:nnTF { #1 } { bingo }
+% { \clist_map_break: }
+% {
+% % Do something useful
+% }
+% }
+% \end{verbatim}
+% Use outside of a \cs[no-index]{clist_map_\ldots{}} scenario leads to low
+% level \TeX{} errors.
+% \begin{texnote}
+% When the mapping is broken, additional tokens may be inserted
+% before further items are taken
+% from the input stream. This depends on the design of the mapping
+% function.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[updated = 2012-06-29, rEXP]{\clist_map_break:n}
+% \begin{syntax}
+% \cs{clist_map_break:n} \Arg{code}
+% \end{syntax}
+% Used to terminate a \cs[no-index]{clist_map_\ldots{}} function before all
+% entries in the \meta{comma list} have been processed, inserting
+% the \meta{code} after the mapping has ended. This
+% normally takes place within a conditional statement, for example
+% \begin{verbatim}
+% \clist_map_inline:Nn \l_my_clist
+% {
+% \str_if_eq:nnTF { #1 } { bingo }
+% { \clist_map_break:n { <code> } }
+% {
+% % Do something useful
+% }
+% }
+% \end{verbatim}
+% Use outside of a \cs[no-index]{clist_map_\ldots{}} scenario leads to low
+% level \TeX{} errors.
+% \begin{texnote}
+% When the mapping is broken, additional tokens may be inserted
+% before the \meta{code} is
+% inserted into the input stream.
+% This depends on the design of the mapping function.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2012-07-13]
+% {\clist_count:N, \clist_count:c, \clist_count:n, \clist_count:e}
+% \begin{syntax}
+% \cs{clist_count:N} \meta{clist~var}
+% \end{syntax}
+% Leaves the number of items in the \meta{clist~var} in the input
+% stream as an \meta{integer denotation}. The total number of items
+% in a \meta{clist~var} includes those which are duplicates,
+% \emph{i.e.}~every item in a \meta{clist~var} is counted.
+% \end{function}
+%
+% \section{Using the content of comma lists directly}
+%
+% \begin{function}[EXP, added = 2013-05-26]{\clist_use:Nnnn, \clist_use:cnnn}
+% \begin{syntax}
+% \cs{clist_use:Nnnn} \meta{clist~var} \Arg{separator~between~two} \Arg{separator~between~more~than~two} \Arg{separator~between~final~two}
+% \end{syntax}
+% Places the contents of the \meta{clist~var} in the input stream,
+% with the appropriate \meta{separator} between the items. Namely, if
+% the comma list has more than two items, the \meta{separator between
+% more than two} is placed between each pair of items except the
+% last, for which the \meta{separator between final two} is used. If
+% the comma list has exactly two items, then they are placed in the input
+% stream separated by the \meta{separator between two}. If the comma
+% list has a single item, it is placed in the input stream, and a comma
+% list with no items produces no output. An error is raised if
+% the variable does not exist or if it is invalid.
+%
+% For example,
+% \begin{verbatim}
+% \clist_set:Nn \l_tmpa_clist { a , b , , c , {de} , f }
+% \clist_use:Nnnn \l_tmpa_clist { ~and~ } { ,~ } { ,~and~ }
+% \end{verbatim}
+% inserts \enquote{\texttt{a, b, c, de, and f}} in the input
+% stream. The first separator argument is not used in this case
+% because the comma list has more than $2$ items.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{items}
+% do not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2013-05-26]{\clist_use:Nn, \clist_use:cn}
+% \begin{syntax}
+% \cs{clist_use:Nn} \meta{clist~var} \Arg{separator}
+% \end{syntax}
+% Places the contents of the \meta{clist~var} in the input stream,
+% with the \meta{separator} between the items. If the comma
+% list has a single item, it is placed in the input stream, and a comma
+% list with no items produces no output. An error is raised if
+% the variable does not exist or if it is invalid.
+%
+% For example,
+% \begin{verbatim}
+% \clist_set:Nn \l_tmpa_clist { a , b , , c , {de} , f }
+% \clist_use:Nn \l_tmpa_clist { ~and~ }
+% \end{verbatim}
+% inserts \enquote{\texttt{a and b and c and de and f}} in the input
+% stream.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{items}
+% do not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2021-05-10]{\clist_use:nnnn, \clist_use:nn}
+% \begin{syntax}
+% \cs{clist_use:nnnn} \meta{comma~list} \Arg{separator~between~two} \Arg{separator~between~more~than~two} \Arg{separator~between~final~two}
+% \cs{clist_use:nn} \meta{comma~list} \Arg{separator}
+% \end{syntax}
+% Places the contents of the \meta{comma~list} in the input stream,
+% with the appropriate \meta{separator} between the items. As for
+% \cs{clist_set:Nn}, blank items are omitted, spaces are removed from
+% both sides of each item, then a set of braces is removed if the
+% resulting space-trimmed item is braced. The \meta{separators} are
+% then inserted in the same way as for \cs{clist_use:Nnnn} and
+% \cs{clist_use:Nn}, respectively.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{items}
+% do not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \section{Comma lists as stacks}
+%
+% Comma lists can be used as stacks, where data is pushed to and popped
+% from the top of the comma list. (The left of a comma list is the top, for
+% performance reasons.) The stack functions for comma lists are not
+% intended to be mixed with the general ordered data functions detailed
+% in the previous section: a comma list should either be used as an
+% ordered data type or as a stack, but not in both ways.
+%
+% \begin{function}[noTF, added = 2012-05-14, updated = 2019-02-16]
+% {\clist_get:NN, \clist_get:cN}
+% \begin{syntax}
+% \cs{clist_get:NN} \meta{clist~var} \meta{token list variable}
+% \end{syntax}
+% Stores the left-most item from a \meta{clist~var} in the
+% \meta{token list variable} without removing it from the
+% \meta{clist~var}. The \meta{token list variable} is assigned locally.
+% In the non-branching version, if the \meta{clist~var} is empty the
+% \meta{token list variable} is set to the marker value \cs{q_no_value}.
+% \end{function}
+%
+% \begin{function}[updated = 2011-09-06]{\clist_pop:NN, \clist_pop:cN}
+% \begin{syntax}
+% \cs{clist_pop:NN} \meta{clist~var} \meta{token list variable}
+% \end{syntax}
+% Pops the left-most item from a \meta{clist~var} into the
+% \meta{token list variable}, \emph{i.e.}~removes the item from the
+% comma list and stores it in the \meta{token list variable}.
+% Both of the variables are assigned locally.
+% \end{function}
+%
+% \begin{function}{\clist_gpop:NN, \clist_gpop:cN}
+% \begin{syntax}
+% \cs{clist_gpop:NN} \meta{clist~var} \meta{token list variable}
+% \end{syntax}
+% Pops the left-most item from a \meta{clist~var} into the
+% \meta{token list variable}, \emph{i.e.}~removes the item from the
+% comma list and stores it in the \meta{token list variable}.
+% The \meta{clist~var} is modified globally, while the assignment of
+% the \meta{token list variable} is local.
+% \end{function}
+%
+% \begin{function}[TF, added = 2012-05-14]{\clist_pop:NN, \clist_pop:cN}
+% \begin{syntax}
+% \cs{clist_pop:NNTF} \meta{clist~var} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{clist~var} is empty, leaves the \meta{false code} in the
+% input stream. The value of the \meta{token list variable} is
+% not defined in this case and should not be relied upon. If the
+% \meta{clist~var} is non-empty, pops the top item from the
+% \meta{clist~var} in the \meta{token list variable}, \emph{i.e.}~removes
+% the item from the \meta{clist~var}. Both the \meta{clist~var} and the
+% \meta{token list variable} are assigned locally.
+% \end{function}
+%
+% \begin{function}[TF, added = 2012-05-14]{\clist_gpop:NN, \clist_gpop:cN}
+% \begin{syntax}
+% \cs{clist_gpop:NNTF} \meta{clist~var} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{clist~var} is empty, leaves the \meta{false code} in the
+% input stream. The value of the \meta{token list variable} is
+% not defined in this case and should not be relied upon. If the
+% \meta{clist~var} is non-empty, pops the top item from the
+% \meta{clist~var} in the \meta{token list variable}, \emph{i.e.}~removes
+% the item from the \meta{clist~var}. The \meta{clist~var} is modified
+% globally, while the \meta{token list variable} is assigned locally.
+% \end{function}
+%
+% \begin{function}
+% {
+% \clist_push:Nn, \clist_push:NV, \clist_push:No,
+% \clist_push:cn, \clist_push:cV, \clist_push:co,
+% \clist_gpush:Nn, \clist_gpush:NV, \clist_gpush:No,
+% \clist_gpush:cn, \clist_gpush:cV, \clist_gpush:co,
+% }
+% \begin{syntax}
+% \cs{clist_push:Nn} \meta{clist~var} \Arg{items}
+% \end{syntax}
+% Adds the \Arg{items} to the top of the \meta{clist~var}.
+% Spaces are removed from both sides of each item as for any
+% \texttt{n}-type comma list.
+% \end{function}
+%
+% \section{Using a single item}
+%
+% \begin{function}[added = 2014-07-17, EXP]
+% {\clist_item:Nn, \clist_item:cn, \clist_item:nn, \clist_item:en}
+% \begin{syntax}
+% \cs{clist_item:Nn} \meta{clist~var} \Arg{int expr}
+% \end{syntax}
+% Indexing items in the \meta{clist~var} from~$1$ at the top (left), this
+% function evaluates the \meta{int expr} and leaves the
+% appropriate item from the comma list in the input stream. If the
+% \meta{int expr} is negative, indexing occurs from the
+% bottom (right) of the comma list. When the \meta{int expr}
+% is larger than the number of items in the \meta{clist~var} (as
+% calculated by \cs{clist_count:N}) then the function expands to
+% nothing.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{item}
+% does not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2016-12-06]
+% {\clist_rand_item:N, \clist_rand_item:n, \clist_rand_item:c}
+% \begin{syntax}
+% \cs{clist_rand_item:N} \meta{clist~var}
+% \cs{clist_rand_item:n} \Arg{comma list}
+% \end{syntax}
+% Selects a pseudo-random item of the \meta{clist~var}/\meta{comma list}.
+% If the \meta{comma list} has no item, the result is empty.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{item}
+% does not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \section{Viewing comma lists}
+%
+% \begin{function}[updated = 2021-04-29]{\clist_show:N, \clist_show:c}
+% \begin{syntax}
+% \cs{clist_show:N} \meta{clist~var}
+% \end{syntax}
+% Displays the entries in the \meta{clist~var} in the terminal.
+% \end{function}
+%
+% \begin{function}[updated = 2013-08-03]{\clist_show:n}
+% \begin{syntax}
+% \cs{clist_show:n} \Arg{tokens}
+% \end{syntax}
+% Displays the entries in the comma list in the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2021-04-29]{\clist_log:N, \clist_log:c}
+% \begin{syntax}
+% \cs{clist_log:N} \meta{clist~var}
+% \end{syntax}
+% Writes the entries in the \meta{clist~var} in the log file. See
+% also \cs{clist_show:N} which displays the result in the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22]{\clist_log:n}
+% \begin{syntax}
+% \cs{clist_log:n} \Arg{tokens}
+% \end{syntax}
+% Writes the entries in the comma list in the log file. See also
+% \cs{clist_show:n} which displays the result in the terminal.
+% \end{function}
+%
+% \section{Constant and scratch comma lists}
+%
+% \begin{variable}[added = 2012-07-02]{\c_empty_clist}
+% Constant that is always empty.
+% \end{variable}
+%
+% \begin{variable}[added = 2011-09-06]{\l_tmpa_clist, \l_tmpb_clist}
+% Scratch comma lists for local assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \begin{variable}[added = 2011-09-06]{\g_tmpa_clist, \g_tmpb_clist}
+% Scratch comma lists for global assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3clist} implementation}
+%
+% \TestFiles{m3clist002}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=clist>
+% \end{macrocode}
+%
+% \begin{variable}{\c_empty_clist}
+% An empty comma list is simply an empty token list.
+% \begin{macrocode}
+\cs_new_eq:NN \c_empty_clist \c_empty_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_internal_clist}
+% Scratch space for various internal uses. This comma list variable
+% cannot be declared as such because it comes before \cs{clist_new:N}
+% \begin{macrocode}
+\tl_new:N \l_@@_internal_clist
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\s_@@_mark,\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{
+% \@@_use_none_delimit_by_s_mark:w,
+% \@@_use_none_delimit_by_s_stop:w,
+% \@@_use_i_delimit_by_s_stop:nw
+% }
+% Functions to gobble up to a scan mark.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_none_delimit_by_s_mark:w #1 \s_@@_mark { }
+\cs_new:Npn \@@_use_none_delimit_by_s_stop:w #1 \s_@@_stop { }
+\cs_new:Npn \@@_use_i_delimit_by_s_stop:nw #1 #2 \s_@@_stop {#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_tmp:w}
+% A temporary function for various purposes.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_tmp:w { }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Removing spaces around items}
+%
+% \begin{macro}{\@@_trim_next:w}
+% Called as \cs{exp:w} \cs{@@_trim_next:w} \cs{prg_do_nothing:}
+% \meta{comma list} \ldots{} it expands to \Arg{trimmed item} where
+% the \meta{trimmed item} is the first non-empty result from removing
+% spaces from both ends of comma-delimited items in the \meta{comma
+% list}. The \cs{prg_do_nothing:} marker avoids losing braces. The
+% test for blank items is a somewhat optimized \cs{tl_if_empty:oTF}
+% construction; if blank, another item is sought, otherwise trim
+% spaces.
+% \begin{macrocode}
+\cs_new:Npn \@@_trim_next:w #1 ,
+ {
+ \tl_if_empty:oTF { \use_none:nn #1 ? }
+ { \@@_trim_next:w \prg_do_nothing: }
+ { \tl_trim_spaces_apply:oN {#1} \exp_end: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_sanitize:n}
+% \begin{macro}{\@@_sanitize:Nn}
+% The auxiliary \cs{@@_sanitize:Nn} receives a delimiter
+% (\cs{c_empty_tl} the first time, afterwards a comma) and that item
+% as arguments. Unless we are done with the loop it calls
+% \cs{@@_wrap_item:w} to unbrace the item (using a comma delimiter is
+% safe since |#2| came from removing spaces from an argument delimited
+% by a comma) and possibly re-brace it if needed.
+% \begin{macrocode}
+\cs_new:Npn \@@_sanitize:n #1
+ {
+ \exp_after:wN \@@_sanitize:Nn \exp_after:wN \c_empty_tl
+ \exp:w \@@_trim_next:w \prg_do_nothing:
+ #1 , \s_@@_stop \prg_break: , \prg_break_point:
+ }
+\cs_new:Npn \@@_sanitize:Nn #1#2
+ {
+ \@@_use_none_delimit_by_s_stop:w #2 \s_@@_stop
+ #1 \@@_wrap_item:w #2 ,
+ \exp_after:wN \@@_sanitize:Nn \exp_after:wN ,
+ \exp:w \@@_trim_next:w \prg_do_nothing:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[TF]{\@@_if_wrap:n}
+% \begin{macro}{\@@_if_wrap:w}
+% True if the argument must be wrapped to avoid getting altered by some
+% clist operations. That is the case whenever the argument
+% \begin{itemize}
+% \item starts or end with a space or contains a comma,
+% \item is empty, or
+% \item consists of a single braced group.
+% \end{itemize}
+% If the argument starts or ends with a space or contains
+% a comma then one of the three arguments of \cs{@@_if_wrap:w} will
+% have its end delimiter (partly) in one of the three copies of |#1|
+% in \cs{@@_if_wrap:nTF}; this has a knock-on effect meaning that the
+% result of the expansion is not empty; in that case, wrap.
+% Otherwise, the argument is safe unless it starts with a brace group
+% (or is empty) and it is empty or consists of a single
+% \texttt{n}-type argument.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_if_wrap:n #1 { TF }
+ {
+ \tl_if_empty:oTF
+ {
+ \@@_if_wrap:w
+ \s_@@_mark ? #1 ~ \s_@@_mark ? ~ #1
+ \s_@@_mark , ~ \s_@@_mark #1 ,
+ }
+ {
+ \tl_if_head_is_group:nTF { #1 { } }
+ {
+ \tl_if_empty:nTF {#1}
+ { \prg_return_true: }
+ {
+ \tl_if_empty:oTF { \use_none:n #1}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ }
+ { \prg_return_false: }
+ }
+ { \prg_return_true: }
+ }
+\cs_new:Npn \@@_if_wrap:w #1 \s_@@_mark ? ~ #2 ~ \s_@@_mark #3 , { }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_wrap_item:w}
+% Safe items are put in \cs{exp_not:n}, otherwise we put an extra set
+% of braces.
+% \begin{macrocode}
+\cs_new:Npn \@@_wrap_item:w #1 ,
+ { \@@_if_wrap:nTF {#1} { \exp_not:n { {#1} } } { \exp_not:n {#1} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Allocation and initialisation}
+%
+% \begin{macro}{\clist_new:N, \clist_new:c}
+% \UnitTested
+% Internally, comma lists are just token lists.
+% \begin{macrocode}
+\cs_new_eq:NN \clist_new:N \tl_new:N
+\cs_new_eq:NN \clist_new:c \tl_new:c
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \clist_const:Nn, \clist_const:Ne, \clist_const:Nx,
+% \clist_const:cn, \clist_const:ce, \clist_const:cx
+% }
+% Creating and initializing a constant comma list is done by
+% sanitizing all items (stripping spaces and braces).
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_const:Nn #1#2
+ { \tl_const:Ne #1 { \@@_sanitize:n {#2} } }
+\cs_generate_variant:Nn \clist_const:Nn { Ne , c , ce }
+\cs_generate_variant:Nn \clist_const:Nn { Nx , cx }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\clist_clear:N, \clist_clear:c}
+% \UnitTested
+% \begin{macro}{\clist_gclear:N, \clist_gclear:c}
+% \UnitTested
+% Clearing comma lists is just the same as clearing token lists.
+% \begin{macrocode}
+\cs_new_eq:NN \clist_clear:N \tl_clear:N
+\cs_new_eq:NN \clist_clear:c \tl_clear:c
+\cs_new_eq:NN \clist_gclear:N \tl_gclear:N
+\cs_new_eq:NN \clist_gclear:c \tl_gclear:c
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_clear_new:N, \clist_clear_new:c}
+% \UnitTested
+% \begin{macro}{\clist_gclear_new:N, \clist_gclear_new:c}
+% \UnitTested
+% Once again a copy from the token list functions.
+% \begin{macrocode}
+\cs_new_eq:NN \clist_clear_new:N \tl_clear_new:N
+\cs_new_eq:NN \clist_clear_new:c \tl_clear_new:c
+\cs_new_eq:NN \clist_gclear_new:N \tl_gclear_new:N
+\cs_new_eq:NN \clist_gclear_new:c \tl_gclear_new:c
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\clist_set_eq:NN, \clist_set_eq:cN, \clist_set_eq:Nc, \clist_set_eq:cc}
+% \UnitTested
+% \begin{macro}
+% {
+% \clist_gset_eq:NN, \clist_gset_eq:cN,
+% \clist_gset_eq:Nc, \clist_gset_eq:cc
+% }
+% \UnitTested
+% Once again, these are simple copies from the token list functions.
+% \begin{macrocode}
+\cs_new_eq:NN \clist_set_eq:NN \tl_set_eq:NN
+\cs_new_eq:NN \clist_set_eq:Nc \tl_set_eq:Nc
+\cs_new_eq:NN \clist_set_eq:cN \tl_set_eq:cN
+\cs_new_eq:NN \clist_set_eq:cc \tl_set_eq:cc
+\cs_new_eq:NN \clist_gset_eq:NN \tl_gset_eq:NN
+\cs_new_eq:NN \clist_gset_eq:Nc \tl_gset_eq:Nc
+\cs_new_eq:NN \clist_gset_eq:cN \tl_gset_eq:cN
+\cs_new_eq:NN \clist_gset_eq:cc \tl_gset_eq:cc
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \clist_set_from_seq:NN, \clist_set_from_seq:cN,
+% \clist_set_from_seq:Nc, \clist_set_from_seq:cc
+% }
+% \UnitTested
+% \begin{macro}
+% {
+% \clist_gset_from_seq:NN, \clist_gset_from_seq:cN,
+% \clist_gset_from_seq:Nc, \clist_gset_from_seq:cc
+% }
+% \UnitTested
+% \begin{macro}{\@@_set_from_seq:NNNN, \@@_set_from_seq:n}
+% Setting a comma list from a comma-separated list is done using a
+% simple mapping. Safe items are put in \cs{exp_not:n}, otherwise we
+% put an extra set of braces. The first comma must be removed, except
+% in the case of an empty comma-list.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_set_from_seq:NN
+ { \@@_set_from_seq:NNNN \clist_clear:N \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \clist_gset_from_seq:NN
+ { \@@_set_from_seq:NNNN \clist_gclear:N \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \@@_set_from_seq:NNNN #1#2#3#4
+ {
+ \seq_if_empty:NTF #4
+ { #1 #3 }
+ {
+ #2 #3
+ {
+ \exp_after:wN \use_none:n \exp:w \exp_end_continue_f:w
+ \seq_map_function:NN #4 \@@_set_from_seq:n
+ }
+ }
+ }
+\cs_new:Npn \@@_set_from_seq:n #1
+ {
+ ,
+ \@@_if_wrap:nTF {#1}
+ { \exp_not:n { {#1} } }
+ { \exp_not:n {#1} }
+ }
+\cs_generate_variant:Nn \clist_set_from_seq:NN { Nc }
+\cs_generate_variant:Nn \clist_set_from_seq:NN { c , cc }
+\cs_generate_variant:Nn \clist_gset_from_seq:NN { Nc }
+\cs_generate_variant:Nn \clist_gset_from_seq:NN { c , cc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_concat:NNN, \clist_concat:ccc}
+% \UnitTested
+% \begin{macro}{\clist_gconcat:NNN, \clist_gconcat:ccc}
+% \UnitTested
+% \begin{macro}{\@@_concat:NNNN}
+% Concatenating comma lists is not quite as easy as it seems, as
+% there needs to be the correct addition of a comma to the output. So
+% a little work to do.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_concat:NNN
+ { \@@_concat:NNNN \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \clist_gconcat:NNN
+ { \@@_concat:NNNN \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \@@_concat:NNNN #1#2#3#4
+ {
+ #1 #2
+ {
+ \exp_not:o #3
+ \clist_if_empty:NF #3 { \clist_if_empty:NF #4 { , } }
+ \exp_not:o #4
+ }
+ }
+\cs_generate_variant:Nn \clist_concat:NNN { ccc }
+\cs_generate_variant:Nn \clist_gconcat:NNN { ccc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\clist_if_exist:N, \clist_if_exist:c}
+% Copies of the \texttt{cs} functions defined in \pkg{l3basics}.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \clist_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \clist_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Adding data to comma lists}
+%
+% \begin{macro}
+% {
+% \clist_set:Nn, \clist_set:NV, \clist_set:Ne,
+% \clist_set:No, \clist_set:Nx,
+% \clist_set:cn, \clist_set:cV, \clist_set:ce,
+% \clist_set:co, \clist_set:cx
+% }
+% \begin{macro}
+% {
+% \clist_gset:Nn, \clist_gset:NV, \clist_gset:Ne,
+% \clist_gset:No, \clist_gset:Nx,
+% \clist_gset:cn, \clist_gset:cV, \clist_gset:ce,
+% \clist_gset:co, \clist_gset:cx,
+% }
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_set:Nn #1#2
+ { \__kernel_tl_set:Nx #1 { \@@_sanitize:n {#2} } }
+\cs_new_protected:Npn \clist_gset:Nn #1#2
+ { \__kernel_tl_gset:Nx #1 { \@@_sanitize:n {#2} } }
+\cs_generate_variant:Nn \clist_set:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \clist_set:Nn { No , Nx , co , cx }
+\cs_generate_variant:Nn \clist_gset:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \clist_gset:Nn { No , Nx , co , cx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \clist_put_left:Nn, \clist_put_left:NV,
+% \clist_put_left:Nv, \clist_put_left:Ne,
+% \clist_put_left:No, \clist_put_left:Nx,
+% \clist_put_left:cn, \clist_put_left:cV,
+% \clist_put_left:cv, \clist_put_left:ce,
+% \clist_put_left:co, \clist_put_left:cx,
+% }
+% \UnitTested
+% \begin{macro}
+% {
+% \clist_gput_left:Nn, \clist_gput_left:NV,
+% \clist_gput_left:Nv, \clist_gput_left:Ne,
+% \clist_gput_left:No, \clist_gput_left:Nx,
+% \clist_gput_left:cn, \clist_gput_left:cV,
+% \clist_gput_left:cv, \clist_gput_left:ce,
+% \clist_gput_left:co, \clist_gput_left:cx,
+% }
+% \UnitTested
+% \begin{macro}{\@@_put_left:NNNn}
+% Everything is based on concatenation after storing in
+% \cs{l_@@_internal_clist}. This avoids having to worry here about
+% space-trimming and so on.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_put_left:Nn
+ { \@@_put_left:NNNn \clist_concat:NNN \clist_set:Nn }
+\cs_new_protected:Npn \clist_gput_left:Nn
+ { \@@_put_left:NNNn \clist_gconcat:NNN \clist_set:Nn }
+\cs_new_protected:Npn \@@_put_left:NNNn #1#2#3#4
+ {
+ #2 \l_@@_internal_clist {#4}
+ #1 #3 \l_@@_internal_clist #3
+ }
+\cs_generate_variant:Nn \clist_put_left:Nn { NV , Nv , Ne , c , cV , cv , ce }
+\cs_generate_variant:Nn \clist_put_left:Nn { No , Nx , co , cx }
+\cs_generate_variant:Nn \clist_gput_left:Nn { NV , Nv , Ne , c , cV , cv , ce }
+\cs_generate_variant:Nn \clist_gput_left:Nn { No , Nx , co , cx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \clist_put_right:Nn, \clist_put_right:NV,
+% \clist_put_right:Nv, \clist_put_right:Ne,
+% \clist_put_right:No, \clist_put_right:Nx,
+% \clist_put_right:cn, \clist_put_right:cV,
+% \clist_put_right:cv, \clist_put_right:ce,
+% \clist_put_right:co, \clist_put_right:cx
+% }
+% \UnitTested
+% \begin{macro}
+% {
+% \clist_gput_right:Nn, \clist_gput_right:NV,
+% \clist_gput_right:Nv, \clist_gput_right:Ne,
+% \clist_gput_right:No, \clist_gput_right:Nx,
+% \clist_gput_right:cn, \clist_gput_right:cV,
+% \clist_gput_right:cv, \clist_gput_right:ce,
+% \clist_gput_right:cx, \clist_gput_right:co
+% }
+% \UnitTested
+% \begin{macro}{\@@_put_right:NNNn}
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_put_right:Nn
+ { \@@_put_right:NNNn \clist_concat:NNN \clist_set:Nn }
+\cs_new_protected:Npn \clist_gput_right:Nn
+ { \@@_put_right:NNNn \clist_gconcat:NNN \clist_set:Nn }
+\cs_new_protected:Npn \@@_put_right:NNNn #1#2#3#4
+ {
+ #2 \l_@@_internal_clist {#4}
+ #1 #3 #3 \l_@@_internal_clist
+ }
+\cs_generate_variant:Nn \clist_put_right:Nn
+ { NV , Nv , Ne , c , cV , cv , ce }
+\cs_generate_variant:Nn \clist_put_right:Nn
+ { No , Nx , co , cx }
+\cs_generate_variant:Nn \clist_gput_right:Nn
+ { NV , Nv , Ne , c , cV , cv , ce }
+\cs_generate_variant:Nn \clist_gput_right:Nn
+ { No , Nx , co , cx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Comma lists as stacks}
+%
+% \begin{macro}{\clist_get:NN, \clist_get:cN}
+% \UnitTested
+% \begin{macro}{\@@_get:wN}
+% Getting an item from the left of a comma list is pretty easy: just
+% trim off the first item using the comma. No need to trim spaces as
+% comma-list \emph{variables} are assumed to have \enquote{cleaned-up}
+% items. (Note that grabbing a comma-delimited item removes an outer
+% pair of braces if present, exactly as needed to uncover the
+% underlying item.)
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_get:NN #1#2
+ {
+ \if_meaning:w #1 \c_empty_clist
+ \tl_set:Nn #2 { \q_no_value }
+ \else:
+ \exp_after:wN \@@_get:wN #1 , \s_@@_stop #2
+ \fi:
+ }
+\cs_new_protected:Npn \@@_get:wN #1 , #2 \s_@@_stop #3
+ { \tl_set:Nn #3 {#1} }
+\cs_generate_variant:Nn \clist_get:NN { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_pop:NN, \clist_pop:cN}
+% \UnitTested
+% \begin{macro}{\clist_gpop:NN, \clist_gpop:cN}
+% \UnitTested
+% \begin{macro}
+% {\@@_pop:NNN, \@@_pop:wwNNN, \@@_pop:wN}
+% An empty clist leads to \cs{q_no_value}, otherwise grab until the
+% first comma and assign to the variable. The second argument of
+% \cs{@@_pop:wwNNN} is a comma list ending in a comma and
+% \cs{s_@@_mark}, unless the original clist contained exactly one item:
+% then the argument is just \cs{s_@@_mark}. The next auxiliary picks
+% either \cs{exp_not:n} or \cs{use_none:n} as |#2|, ensuring that the
+% result can safely be an empty comma list.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_pop:NN
+ { \@@_pop:NNN \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \clist_gpop:NN
+ { \@@_pop:NNN \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \@@_pop:NNN #1#2#3
+ {
+ \if_meaning:w #2 \c_empty_clist
+ \tl_set:Nn #3 { \q_no_value }
+ \else:
+ \exp_after:wN \@@_pop:wwNNN #2 , \s_@@_mark \s_@@_stop #1#2#3
+ \fi:
+ }
+\cs_new_protected:Npn \@@_pop:wwNNN #1 , #2 \s_@@_stop #3#4#5
+ {
+ \tl_set:Nn #5 {#1}
+ #3 #4
+ {
+ \@@_pop:wN \prg_do_nothing:
+ #2 \exp_not:o
+ , \s_@@_mark \use_none:n
+ \s_@@_stop
+ }
+ }
+\cs_new:Npn \@@_pop:wN #1 , \s_@@_mark #2 #3 \s_@@_stop { #2 {#1} }
+\cs_generate_variant:Nn \clist_pop:NN { c }
+\cs_generate_variant:Nn \clist_gpop:NN { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[TF]{\clist_get:NN, \clist_get:cN}
+% \begin{macro}[TF]{\clist_pop:NN, \clist_pop:cN}
+% \begin{macro}[TF]{\clist_gpop:NN, \clist_gpop:cN}
+% \begin{macro}{\@@_pop_TF:NNN}
+% The same, as branching code: very similar to the above.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \clist_get:NN #1#2 { T , F , TF }
+ {
+ \if_meaning:w #1 \c_empty_clist
+ \prg_return_false:
+ \else:
+ \exp_after:wN \@@_get:wN #1 , \s_@@_stop #2
+ \prg_return_true:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \clist_get:NN { c } { T , F , TF }
+\prg_new_protected_conditional:Npnn \clist_pop:NN #1#2 { T , F , TF }
+ { \@@_pop_TF:NNN \__kernel_tl_set:Nx #1 #2 }
+\prg_new_protected_conditional:Npnn \clist_gpop:NN #1#2 { T , F , TF }
+ { \@@_pop_TF:NNN \__kernel_tl_gset:Nx #1 #2 }
+\cs_new_protected:Npn \@@_pop_TF:NNN #1#2#3
+ {
+ \if_meaning:w #2 \c_empty_clist
+ \prg_return_false:
+ \else:
+ \exp_after:wN \@@_pop:wwNNN #2 , \s_@@_mark \s_@@_stop #1#2#3
+ \prg_return_true:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \clist_pop:NN { c } { T , F , TF }
+\prg_generate_conditional_variant:Nnn \clist_gpop:NN { c } { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{
+% \clist_push:Nn, \clist_push:NV, \clist_push:No, \clist_push:Nx,
+% \clist_push:cn, \clist_push:cV, \clist_push:co, \clist_push:cx,
+% }
+% \UnitTested
+% \begin{macro}{
+% \clist_gpush:Nn, \clist_gpush:NV, \clist_gpush:No, \clist_gpush:Nx,
+% \clist_gpush:cn, \clist_gpush:cV, \clist_gpush:co, \clist_gpush:cx,
+% }
+% \UnitTested
+% Pushing to a comma list is the same as adding on the left.
+% \begin{macrocode}
+\cs_new_eq:NN \clist_push:Nn \clist_put_left:Nn
+\cs_generate_variant:Nn \clist_push:Nn { NV , No , Nx , c , cV , co , cx }
+\cs_new_eq:NN \clist_gpush:Nn \clist_gput_left:Nn
+\cs_generate_variant:Nn \clist_gpush:Nn { NV , No , Nx , c , cV , co , cx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Modifying comma lists}
+%
+% \begin{variable}{\l_@@_internal_remove_clist, \l_@@_internal_remove_seq}
+% An internal comma list and a sequence for the removal routines.
+% \begin{macrocode}
+\clist_new:N \l_@@_internal_remove_clist
+\seq_new:N \l_@@_internal_remove_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\clist_remove_duplicates:N, \clist_remove_duplicates:c}
+% \UnitTested
+% \begin{macro}{\clist_gremove_duplicates:N, \clist_gremove_duplicates:c}
+% \UnitTested
+% \begin{macro}{\@@_remove_duplicates:NN}
+% Removing duplicates means making a new list then copying it.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_remove_duplicates:N
+ { \@@_remove_duplicates:NN \clist_set_eq:NN }
+\cs_new_protected:Npn \clist_gremove_duplicates:N
+ { \@@_remove_duplicates:NN \clist_gset_eq:NN }
+\cs_new_protected:Npn \@@_remove_duplicates:NN #1#2
+ {
+ \clist_clear:N \l_@@_internal_remove_clist
+ \clist_map_inline:Nn #2
+ {
+ \clist_if_in:NnF \l_@@_internal_remove_clist {##1}
+ {
+ \tl_put_right:Ne \l_@@_internal_remove_clist
+ {
+ \clist_if_empty:NF \l_@@_internal_remove_clist { , }
+ \@@_if_wrap:nTF {##1} { \exp_not:n { {##1} } } { \exp_not:n {##1} }
+ }
+ }
+ }
+ #1 #2 \l_@@_internal_remove_clist
+ }
+\cs_generate_variant:Nn \clist_remove_duplicates:N { c }
+\cs_generate_variant:Nn \clist_gremove_duplicates:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \clist_remove_all:Nn, \clist_remove_all:cn,
+% \clist_remove_all:NV, \clist_remove_all:cV
+% }
+% \UnitTested
+% \begin{macro}
+% {
+% \clist_gremove_all:Nn, \clist_gremove_all:cn,
+% \clist_gremove_all:NV, \clist_gremove_all:cV
+% }
+% \UnitTested
+% \begin{macro}{\@@_remove_all:NNNn}
+% \begin{macro}{\@@_remove_all:w}
+% \begin{macro}{\@@_remove_all:}
+% The method used here for safe items is very similar to
+% \cs{tl_replace_all:Nnn}. However, if the item contains commas or
+% leading/trailing spaces, or is empty, or consists of a single brace
+% group, we know that it can only appear within braces so the code
+% would fail; instead just convert to a sequence and do the removal
+% with \pkg{l3seq} code (it involves somewhat elaborate code to do
+% most of the work expandably but the final token list comparisons
+% non-expandably).
+%
+% For \enquote{safe} items, build a function delimited by the
+% \meta{item} that should be removed,
+% surrounded with commas, and call that function followed by
+% the expanded comma list, and another copy of the \meta{item}.
+% The loop is controlled by the argument grabbed by
+% \cs{@@_remove_all:w}: when the item was found,
+% the \cs{s_@@_mark} delimiter used is the one inserted by
+% \cs{@@_tmp:w}, and \cs{@@_use_none_delimit_by_s_stop:w}
+% is deleted. At the end, the final \meta{item} is
+% grabbed, and the argument of \cs{@@_tmp:w} contains
+% \cs{s_@@_mark}: in that case, \cs{@@_remove_all:w}
+% removes the second \cs{s_@@_mark} (inserted by \cs{@@_tmp:w}),
+% and lets \cs{@@_use_none_delimit_by_s_stop:w} act.
+%
+% No brace is lost because items are always grabbed with a leading comma.
+% The result of the first assignment has an extra leading comma,
+% which we remove in a second assignment.
+% Two exceptions: if the clist lost all of its elements, the result
+% is empty, and we shouldn't remove anything; if the clist started up
+% empty, the first step happens to turn it into a single comma, and
+% the second step removes it.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_remove_all:Nn
+ { \@@_remove_all:NNNn \clist_set_from_seq:NN \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \clist_gremove_all:Nn
+ { \@@_remove_all:NNNn \clist_gset_from_seq:NN \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \@@_remove_all:NNNn #1#2#3#4
+ {
+ \@@_if_wrap:nTF {#4}
+ {
+ \seq_set_from_clist:NN \l_@@_internal_remove_seq #3
+ \seq_remove_all:Nn \l_@@_internal_remove_seq {#4}
+ #1 #3 \l_@@_internal_remove_seq
+ }
+ {
+ \cs_set:Npn \@@_tmp:w ##1 , #4 ,
+ {
+ ##1
+ , \s_@@_mark , \@@_use_none_delimit_by_s_stop:w ,
+ \@@_remove_all:
+ }
+ #2 #3
+ {
+ \exp_after:wN \@@_remove_all:
+ #3 , \s_@@_mark , #4 , \s_@@_stop
+ }
+ \clist_if_empty:NF #3
+ {
+ #2 #3
+ {
+ \exp_args:No \exp_not:o
+ { \exp_after:wN \use_none:n #3 }
+ }
+ }
+ }
+ }
+\cs_new:Npn \@@_remove_all:
+ { \exp_after:wN \@@_remove_all:w \@@_tmp:w , }
+\cs_new:Npn \@@_remove_all:w #1 , \s_@@_mark , #2 , { \exp_not:n {#1} }
+\cs_generate_variant:Nn \clist_remove_all:Nn { c , NV , cV }
+\cs_generate_variant:Nn \clist_gremove_all:Nn { c , NV , cV }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \clist_reverse:N, \clist_reverse:c,
+% \clist_greverse:N, \clist_greverse:c
+% }
+% Use \cs{clist_reverse:n} in an \texttt{e}-expanding assignment. The
+% extra work that \cs{clist_reverse:n} does to preserve braces and
+% spaces would not be needed for the well-controlled case of
+% \texttt{N}-type comma lists, but the slow-down is not too bad.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_reverse:N #1
+ { \__kernel_tl_set:Nx #1 { \exp_args:No \clist_reverse:n {#1} } }
+\cs_new_protected:Npn \clist_greverse:N #1
+ { \__kernel_tl_gset:Nx #1 { \exp_args:No \clist_reverse:n {#1} } }
+\cs_generate_variant:Nn \clist_reverse:N { c }
+\cs_generate_variant:Nn \clist_greverse:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\clist_reverse:n}
+% \begin{macro}[EXP]{\@@_reverse:wwNww, \@@_reverse_end:ww}
+% The reversed token list is built one item at a time, and stored
+% between \cs{s_@@_stop} and \cs{s_@@_mark}, in the form of |?| followed by
+% zero or more instances of \enquote{\meta{item}\texttt{,}}. We start from a comma
+% list \enquote{\meta{item_1}\texttt{,\ldots,}\meta{item_n}}. During the loop,
+% the auxiliary \cs{@@_reverse:wwNww} receives \enquote{\texttt{?}\meta{item_i}} as
+% |#1|, \enquote{\meta{item_{i+1}}\texttt{,\ldots,}\meta{item_n}} as |#2|,
+% \cs{@@_reverse:wwNww} as |#3|, what remains until \cs{s_@@_stop} as
+% |#4|, and \enquote{\meta{item_{i-1}}\texttt{,\ldots,}\meta{item_1}\texttt{,}} as |#5|.
+% The auxiliary moves |#1| just before |#5|, with a comma, and calls
+% itself (|#3|). After the last item is moved, \cs{@@_reverse:wwNww}
+% receives \enquote{\cs{s_@@_mark} \cs{@@_reverse:wwNww} \texttt{!}} as its argument
+% |#1|, thus \cs{@@_reverse_end:ww} as its argument |#3|. This second
+% auxiliary cleans up until the marker~|!|, removes the trailing comma
+% (introduced when the first item was moved after \cs{s_@@_stop}), and
+% leaves its argument~|#1| within \cs{exp_not:n}. There is also a
+% need to remove a leading comma, hence \cs{exp_not:o} and
+% \cs{use_none:n}.
+% \begin{macrocode}
+\cs_new:Npn \clist_reverse:n #1
+ {
+ \@@_reverse:wwNww ? #1 ,
+ \s_@@_mark \@@_reverse:wwNww ! ,
+ \s_@@_mark \@@_reverse_end:ww
+ \s_@@_stop ? \s_@@_mark
+ }
+\cs_new:Npn \@@_reverse:wwNww
+ #1 , #2 \s_@@_mark #3 #4 \s_@@_stop ? #5 \s_@@_mark
+ { #3 ? #2 \s_@@_mark #3 #4 \s_@@_stop #1 , #5 \s_@@_mark }
+\cs_new:Npn \@@_reverse_end:ww #1 ! #2 , \s_@@_mark
+ { \exp_not:o { \use_none:n #2 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\clist_sort:Nn, \clist_sort:cn, \clist_gsort:Nn, \clist_gsort:cn}
+% Implemented in \pkg{l3sort}.
+% \end{macro}
+%
+% \subsection{Comma list conditionals}
+%
+% \begin{macro}[pTF]{\clist_if_empty:N, \clist_if_empty:c}
+% \UnitTested
+% Simple copies from the token list variable material.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \clist_if_empty:N \tl_if_empty:N
+ { p , T , F , TF }
+\prg_new_eq_conditional:NNn \clist_if_empty:c \tl_if_empty:c
+ { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, pTF]{\clist_if_empty:n}
+% \begin{macro}[EXP]{\@@_if_empty_n:w}
+% \begin{macro}[EXP]{\@@_if_empty_n:wNw}
+% As usual, we insert a token (here |?|) before grabbing
+% any argument: this avoids losing braces. The argument
+% of \cs{tl_if_empty:oTF} is empty if |#1| is |?| followed
+% by blank spaces (besides, this particular variant of
+% the emptiness test is optimized). If the item of the
+% comma list is blank, grab the next one. As soon as one
+% item is non-blank, exit: the second auxiliary grabs
+% \cs{prg_return_false:} as |#2|, unless every item in
+% the comma list was blank and the loop actually got broken
+% by the trailing \cs{s_@@_mark} \cs{prg_return_false:} item.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \clist_if_empty:n #1 { p , T , F , TF }
+ {
+ \@@_if_empty_n:w ? #1
+ , \s_@@_mark \prg_return_false:
+ , \s_@@_mark \prg_return_true:
+ \s_@@_stop
+ }
+\cs_new:Npn \@@_if_empty_n:w #1 ,
+ {
+ \tl_if_empty:oTF { \use_none:nn #1 ? }
+ { \@@_if_empty_n:w ? }
+ { \@@_if_empty_n:wNw }
+ }
+\cs_new:Npn \@@_if_empty_n:wNw #1 \s_@@_mark #2#3 \s_@@_stop {#2}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[TF]
+% {
+% \clist_if_in:Nn, \clist_if_in:NV, \clist_if_in:No,
+% \clist_if_in:cn, \clist_if_in:cV, \clist_if_in:co,
+% \clist_if_in:nn, \clist_if_in:nV, \clist_if_in:no
+% }
+% \begin{macro}{\@@_if_in_return:nnN}
+% \UnitTested
+% For \enquote{safe} items, we simply surround the comma list, and the
+% item, with commas, then use the same code as for \cs{tl_if_in:Nn}.
+% For \enquote{unsafe} items we follow the same route as
+% \cs{seq_if_in:Nn}, mapping through the list a comparison function.
+% If found, return \texttt{true} and remove \cs{prg_return_false:}.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \clist_if_in:Nn #1#2 { T , F , TF }
+ {
+ \exp_args:No \@@_if_in_return:nnN #1 {#2} #1
+ }
+\prg_new_protected_conditional:Npnn \clist_if_in:nn #1#2 { T , F , TF }
+ {
+ \clist_set:Nn \l_@@_internal_clist {#1}
+ \exp_args:No \@@_if_in_return:nnN \l_@@_internal_clist {#2}
+ \l_@@_internal_clist
+ }
+\cs_new_protected:Npn \@@_if_in_return:nnN #1#2#3
+ {
+ \@@_if_wrap:nTF {#2}
+ {
+ \cs_set:Npe \@@_tmp:w ##1
+ {
+ \exp_not:N \tl_if_eq:nnT {##1}
+ \exp_not:n
+ {
+ {#2}
+ { \clist_map_break:n { \prg_return_true: \use_none:n } }
+ }
+ }
+ \clist_map_function:NN #3 \@@_tmp:w
+ \prg_return_false:
+ }
+ {
+ \cs_set:Npn \@@_tmp:w ##1 ,#2, { }
+ \tl_if_empty:oTF
+ { \@@_tmp:w ,#1, {} {} ,#2, }
+ { \prg_return_false: } { \prg_return_true: }
+ }
+ }
+\prg_generate_conditional_variant:Nnn \clist_if_in:Nn
+ { NV , No , c , cV , co } { T , F , TF }
+\prg_generate_conditional_variant:Nnn \clist_if_in:nn
+ { nV , no } { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Mapping over comma lists}
+%
+% \begin{macro}{\clist_map_function:NN, \clist_map_function:cN}
+% \UnitTested
+% \begin{macro}{\@@_map_function:Nw, \@@_map_function_end:w}
+% If the variable is empty, the mapping is skipped (otherwise,
+% that comma-list would be seen as consisting of one empty item).
+% Then loop over the comma-list, grabbing eight comma-delimited items
+% at a time. The end is marked by \cs{s_@@_stop}, which may not appear
+% in any of the items. Once the last group of eight items has been
+% reached, we go through them more slowly using
+% \cs{@@_map_function_end:w}. The auxiliary function
+% \cs{@@_map_function:Nw} is also used in some other clist mappings.
+% \begin{macrocode}
+\cs_new:Npn \clist_map_function:NN #1#2
+ {
+ \clist_if_empty:NF #1
+ {
+ \exp_after:wN \@@_map_function:Nw \exp_after:wN #2 #1 ,
+ \s_@@_stop , \s_@@_stop , \s_@@_stop , \s_@@_stop ,
+ \s_@@_stop , \s_@@_stop , \s_@@_stop , \s_@@_stop ,
+ \prg_break_point:Nn \clist_map_break: { }
+ }
+ }
+\cs_new:Npn \@@_map_function:Nw #1 #2, #3, #4, #5, #6, #7, #8, #9,
+ {
+ \@@_use_none_delimit_by_s_stop:w
+ #9 \@@_map_function_end:w \s_@@_stop
+ #1 {#2} #1 {#3} #1 {#4} #1 {#5} #1 {#6} #1 {#7} #1 {#8} #1 {#9}
+ \@@_map_function:Nw #1
+ }
+\cs_new:Npn \@@_map_function_end:w \s_@@_stop #1#2
+ {
+ \@@_use_none_delimit_by_s_stop:w #2 \clist_map_break: \s_@@_stop
+ #1 {#2}
+ \@@_map_function_end:w \s_@@_stop
+ }
+\cs_generate_variant:Nn \clist_map_function:NN { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_map_function:nN, \clist_map_function:eN}
+% \UnitTested
+% \begin{macro}{\@@_map_function_n:Nn}
+% \begin{macro}{\@@_map_unbrace:wn}
+% The \texttt{n}-type mapping function is a bit more awkward,
+% since spaces must be trimmed from each item.
+% Space trimming is again based on \cs{@@_trim_next:w}.
+% The auxiliary \cs{@@_map_function_n:Nn} receives as arguments the
+% function, and the next non-empty item (after space trimming but
+% before brace removal). One level of braces is removed by
+% \cs{@@_map_unbrace:wn}.
+% \begin{macrocode}
+\cs_new:Npn \clist_map_function:nN #1#2
+ {
+ \exp_after:wN \@@_map_function_n:Nn \exp_after:wN #2
+ \exp:w \@@_trim_next:w \prg_do_nothing: #1 ,
+ \s_@@_stop \clist_map_break: ,
+ \prg_break_point:Nn \clist_map_break: { }
+ }
+\cs_generate_variant:Nn \clist_map_function:nN { e }
+\cs_new:Npn \@@_map_function_n:Nn #1 #2
+ {
+ \@@_use_none_delimit_by_s_stop:w #2 \s_@@_stop
+ \@@_map_unbrace:wn #2 , #1
+ \exp_after:wN \@@_map_function_n:Nn \exp_after:wN #1
+ \exp:w \@@_trim_next:w \prg_do_nothing:
+ }
+\cs_new:Npn \@@_map_unbrace:wn #1, #2 { #2 {#1} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_map_inline:Nn, \clist_map_inline:cn}
+% \UnitTested
+% \begin{macro}{\clist_map_inline:nn}
+% \UnitTested
+% Inline mapping is done by creating a suitable function
+% \enquote{on the fly}: this is done globally to avoid
+% any issues with \TeX{}'s groups. We use a different
+% function for each level of nesting.
+%
+% Since the mapping is non-expandable, we can perform
+% the space-trimming needed by the \texttt{n} version
+% simply by storing the comma-list in a variable. We
+% don't need a different comma-list for each nesting
+% level: the comma-list is expanded before the mapping
+% starts.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_map_inline:Nn #1#2
+ {
+ \clist_if_empty:NF #1
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \cs_gset_protected:cpn
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w } ##1 {#2}
+ \exp_last_unbraced:Nco \@@_map_function:Nw
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w }
+ #1 ,
+ \s_@@_stop , \s_@@_stop , \s_@@_stop , \s_@@_stop ,
+ \s_@@_stop , \s_@@_stop , \s_@@_stop , \s_@@_stop ,
+ \prg_break_point:Nn \clist_map_break:
+ { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+ }
+\cs_new_protected:Npn \clist_map_inline:nn #1
+ {
+ \clist_set:Nn \l_@@_internal_clist {#1}
+ \clist_map_inline:Nn \l_@@_internal_clist
+ }
+\cs_generate_variant:Nn \clist_map_inline:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_map_variable:NNn, \clist_map_variable:cNn}
+% \UnitTested
+% \begin{macro}{\clist_map_variable:nNn}
+% \begin{macro}{\@@_map_variable:Nnn}
+% The |N|-type version is a straightforward application of
+% \cs{clist_map_tokens:Nn}, calling \cs{@@_map_variable:Nnn} for each
+% item to assign the variable and run the user's code. The |n|-type
+% version is \emph{not} implemented in terms of the |n|-type function
+% \cs{clist_map_tokens:Nn}, because here we are allowed to clean up
+% the |n|-type comma list non-expandably.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_map_variable:NNn #1#2#3
+ { \clist_map_tokens:Nn #1 { \@@_map_variable:Nnn #2 {#3} } }
+\cs_generate_variant:Nn \clist_map_variable:NNn { c }
+\cs_new_protected:Npn \@@_map_variable:Nnn #1#2#3
+ { \tl_set:Nn #1 {#3} #2 }
+\cs_new_protected:Npn \clist_map_variable:nNn #1
+ {
+ \clist_set:Nn \l_@@_internal_clist {#1}
+ \clist_map_variable:NNn \l_@@_internal_clist
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_map_tokens:Nn, \clist_map_tokens:cn}
+% \begin{macro}{\@@_map_tokens:nw, \@@_map_tokens_end:w}
+% Essentially a copy of \cs{clist_map_function:NN} with braces added.
+% \begin{macrocode}
+\cs_new:Npn \clist_map_tokens:Nn #1#2
+ {
+ \clist_if_empty:NF #1
+ {
+ \exp_last_unbraced:Nno \@@_map_tokens:nw {#2} #1 ,
+ \s_@@_stop , \s_@@_stop , \s_@@_stop , \s_@@_stop ,
+ \s_@@_stop , \s_@@_stop , \s_@@_stop , \s_@@_stop ,
+ \prg_break_point:Nn \clist_map_break: { }
+ }
+ }
+\cs_new:Npn \@@_map_tokens:nw #1 #2, #3, #4, #5, #6, #7, #8, #9,
+ {
+ \@@_use_none_delimit_by_s_stop:w
+ #9 \@@_map_tokens_end:w \s_@@_stop
+ \use:n {#1} {#2} \use:n {#1} {#3} \use:n {#1} {#4} \use:n {#1} {#5}
+ \use:n {#1} {#6} \use:n {#1} {#7} \use:n {#1} {#8} \use:n {#1} {#9}
+ \@@_map_tokens:nw {#1}
+ }
+\cs_new:Npn \@@_map_tokens_end:w \s_@@_stop \use:n #1#2
+ {
+ \@@_use_none_delimit_by_s_stop:w #2 \clist_map_break: \s_@@_stop
+ #1 {#2}
+ \@@_map_tokens_end:w \s_@@_stop
+ }
+\cs_generate_variant:Nn \clist_map_tokens:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_map_tokens:nn, \@@_map_tokens_n:nw}
+% Similar to \cs{clist_map_function:nN} but with a different way of
+% grabbing items because we cannot use \cs{exp_after:wN} to pass the
+% \meta{code}.
+% \begin{macrocode}
+\cs_new:Npn \clist_map_tokens:nn #1#2
+ {
+ \@@_map_tokens_n:nw {#2}
+ \prg_do_nothing: #1 , \s_@@_stop \clist_map_break: ,
+ \prg_break_point:Nn \clist_map_break: { }
+ }
+\cs_new:Npn \@@_map_tokens_n:nw #1#2 ,
+ {
+ \tl_if_empty:oF { \use_none:nn #2 ? }
+ {
+ \@@_use_none_delimit_by_s_stop:w #2 \s_@@_stop
+ \tl_trim_spaces_apply:oN {#2} \use_ii_i:nn
+ \@@_map_unbrace:wn , {#1}
+ }
+ \@@_map_tokens_n:nw {#1} \prg_do_nothing:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\clist_map_break:, \clist_map_break:n}
+% The break statements use the general \cs{prg_map_break:Nn} mechanism.
+% \begin{macrocode}
+\cs_new:Npn \clist_map_break:
+ { \prg_map_break:Nn \clist_map_break: { } }
+\cs_new:Npn \clist_map_break:n
+ { \prg_map_break:Nn \clist_map_break: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\clist_count:N, \clist_count:c, \clist_count:n, \clist_count:e}
+% \begin{macro}{\@@_count:n}
+% \begin{macro}{\@@_count:w}
+% Counting the items in a comma list is done using the same approach as for
+% other token count functions: turn each entry into a \texttt{+1} then use
+% integer evaluation to actually do the mathematics.
+% In the case of an \texttt{n}-type comma-list, we could of course use
+% \cs{clist_map_function:nN}, but that is very slow, because it carefully
+% removes spaces. Instead, we loop manually, and skip blank items
+% (but not |{}|, hence the extra spaces).
+% \begin{macrocode}
+\cs_new:Npn \clist_count:N #1
+ {
+ \int_eval:n
+ {
+ 0
+ \clist_map_function:NN #1 \@@_count:n
+ }
+ }
+\cs_generate_variant:Nn \clist_count:N { c }
+\cs_new:Npn \@@_count:n #1 { + 1 }
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_new:Npn \clist_count:n ##1
+ {
+ \int_eval:n
+ {
+ 0
+ \@@_count:w #1
+ ##1 , \s_@@_stop \prg_break: , \prg_break_point:
+ }
+ }
+ \cs_new:Npn \@@_count:w ##1 ,
+ {
+ \@@_use_none_delimit_by_s_stop:w ##1 \s_@@_stop
+ \tl_if_blank:nF {##1} { + 1 }
+ \@@_count:w #1
+ }
+ }
+\exp_args:No \@@_tmp:w \c_space_tl
+\cs_generate_variant:Nn \clist_count:n { e }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Using comma lists}
+%
+% \begin{macro}[EXP]{\clist_use:Nnnn, \clist_use:cnnn}
+% \begin{macro}[EXP]
+% {\@@_use:wwn, \@@_use:nwwwwnwn, \@@_use:nwwn}
+% \begin{macro}[EXP]{\clist_use:Nn, \clist_use:cn}
+% First check that the variable exists. Then count the items in the
+% comma list. If it has none, output nothing. If it has one item,
+% output that item, brace stripped (note that space-trimming has
+% already been done when the comma list was assigned). If it has two,
+% place the \meta{separator~between~two} in the middle.
+%
+% Otherwise, \cs{@@_use:nwwwwnwn} takes the following arguments; 1:
+% a \meta{separator}, 2, 3, 4: three items from the comma list (or
+% quarks), 5: the rest of the comma list, 6: a \meta{continuation}
+% function (\texttt{use_ii} or \texttt{use_iii} with its
+% \meta{separator} argument), 7: junk, and 8: the temporary result,
+% which is built in a brace group following \cs{s_@@_stop}. The
+% \meta{separator} and the first of the three items are placed in the
+% result, then we use the \meta{continuation}, placing the remaining
+% two items after it. When we begin this loop, the three items really
+% belong to the comma list, the first \cs{s_@@_mark} is taken as a
+% delimiter to the \texttt{use_ii} function, and the continuation is
+% \texttt{use_ii} itself. When we reach the last two items of the
+% original token list, \cs{s_@@_mark} is taken as a third item, and now
+% the second \cs{s_@@_mark} serves as a delimiter to \texttt{use_ii},
+% switching to the other \meta{continuation}, \texttt{use_iii}, which
+% uses the \meta{separator between final two}.
+% \begin{macrocode}
+\cs_new:Npn \clist_use:Nnnn #1#2#3#4
+ {
+ \clist_if_exist:NTF #1
+ {
+ \int_case:nnF { \clist_count:N #1 }
+ {
+ { 0 } { }
+ { 1 } { \exp_after:wN \@@_use:wwn #1 , , { } }
+ { 2 } { \exp_after:wN \@@_use:wwn #1 , {#2} }
+ }
+ {
+ \exp_after:wN \@@_use:nwwwwnwn
+ \exp_after:wN { \exp_after:wN } #1 ,
+ \s_@@_mark , { \@@_use:nwwwwnwn {#3} }
+ \s_@@_mark , { \@@_use:nwwn {#4} }
+ \s_@@_stop { }
+ }
+ }
+ {
+ \msg_expandable_error:nnn
+ { kernel } { bad-variable } {#1}
+ }
+ }
+\cs_generate_variant:Nn \clist_use:Nnnn { c }
+\cs_new:Npn \@@_use:wwn #1 , #2 , #3 { \exp_not:n { #1 #3 #2 } }
+\cs_new:Npn \@@_use:nwwwwnwn
+ #1#2 , #3 , #4 , #5 \s_@@_mark , #6#7 \s_@@_stop #8
+ { #6 {#3} , {#4} , #5 \s_@@_mark , {#6} #7 \s_@@_stop { #8 #1 #2 } }
+\cs_new:Npn \@@_use:nwwn #1#2 , #3 \s_@@_stop #4
+ { \exp_not:n { #4 #1 #2 } }
+\cs_new:Npn \clist_use:Nn #1#2
+ { \clist_use:Nnnn #1 {#2} {#2} {#2} }
+\cs_generate_variant:Nn \clist_use:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \clist_use:nnnn, \clist_use:nn, \@@_use:Nw,
+% \@@_use_one:w, \@@_use_end:w, \@@_use_more:w
+% }
+% Items are grabbed by \cs{@@_use:Nw}, which detects blank items with
+% a \cs{tl_if_empty:oTF} test (in which case it recurses). Non-blank
+% items are either the end of the list, in which case the argument
+% |#1| of \cs{@@_use:Nw} is used to properly end the list, or are
+% normal items, which must be trimmed and properly unbraced. As we
+% find successive items, the long list of \cs{@@_use:Nw} calls gets
+% shortened and we end up calling \cs{@@_use_more:w} once we have
+% found $3$ items. This auxiliary leaves the first-found item and the
+% general separator, and calls \cs{@@_use:Nw} to find more items.
+% A subtlety is that we use \cs{@@_use_end:w} both in the case of a
+% two-item list and for the last two items of a general list: to get
+% the correct separator, \cs{@@_use_more:w} replaces the
+% separator-of-two by the last-separator when called, namely as soon
+% as we have found three items.
+% \begin{macrocode}
+\cs_new:Npn \clist_use:nnnn #1#2#3#4
+ {
+ \@@_use:Nw \@@_use_none_delimit_by_s_stop:w
+ \@@_use:Nw \@@_use_one:w
+ \@@_use:Nw \@@_use_end:w
+ \@@_use_more:w ;
+ {#2} {#3} {#4} ;
+ \prg_do_nothing: #1 , \s_@@_mark ,
+ \s_@@_stop
+ }
+\cs_new:Npn \@@_use:Nw #1#2 ; #3 ; #4 ,
+ {
+ \tl_if_empty:oTF { \use_none:nn #4 ? }
+ { \@@_use:Nw #1#2 ; }
+ {
+ \@@_use_none_delimit_by_s_mark:w #4 #1 \s_@@_mark
+ \tl_trim_spaces_apply:oN {#4} \use_ii_i:nn
+ \@@_map_unbrace:wn , { #2 ; }
+ }
+ #3 ; \prg_do_nothing:
+ }
+\cs_new:Npn \@@_use_one:w \s_@@_mark #1 , #2#3#4 \s_@@_stop
+ { \exp_not:n {#3} }
+\cs_new:Npn \@@_use_end:w
+ \s_@@_mark #1 , #2#3#4#5#6 \s_@@_stop
+ { \exp_not:n { #4 #5 #3 } }
+\cs_new:Npn \@@_use_more:w ; #1#2#3#4#5#6 ;
+ {
+ \exp_not:n { #3 #5 }
+ \@@_use:Nw \@@_use_end:w \@@_use_more:w ;
+ {#1} {#2} {#6} {#5} {#6} ;
+ }
+\cs_new:Npn \clist_use:nn #1#2 { \clist_use:nnnn {#1} {#2} {#2} {#2} }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Using a single item}
+%
+% \begin{macro}{\clist_item:Nn, \clist_item:cn}
+% \begin{macro}{\@@_item:nnnN, \@@_item:ffoN, \@@_item:ffnN}
+% \begin{macro}{\@@_item_N_loop:nw}
+% To avoid needing to test the end of the list at each step,
+% we first compute the \meta{length} of the list. If the item number
+% is~$0$, less than $-\meta{length}$, or more than $\meta{length}$,
+% the result is empty. If it is negative, but not less than $-\meta{length}$,
+% add $\meta{length}+1$ to the item number before performing the loop.
+% The loop itself is very simple, return the item if the counter
+% reached~$1$, otherwise, decrease the counter and repeat.
+% \begin{macrocode}
+\cs_new:Npn \clist_item:Nn #1#2
+ {
+ \@@_item:ffoN
+ { \clist_count:N #1 }
+ { \int_eval:n {#2} }
+ #1
+ \@@_item_N_loop:nw
+ }
+\cs_new:Npn \@@_item:nnnN #1#2#3#4
+ {
+ \int_compare:nNnTF {#2} < 0
+ {
+ \int_compare:nNnTF {#2} < { - #1 }
+ { \@@_use_none_delimit_by_s_stop:w }
+ { \exp_args:Nf #4 { \int_eval:n { #2 + 1 + #1 } } }
+ }
+ {
+ \int_compare:nNnTF {#2} > {#1}
+ { \@@_use_none_delimit_by_s_stop:w }
+ { #4 {#2} }
+ }
+ { } , #3 , \s_@@_stop
+ }
+\cs_generate_variant:Nn \@@_item:nnnN { ffo, ff }
+\cs_new:Npn \@@_item_N_loop:nw #1 #2,
+ {
+ \int_compare:nNnTF {#1} = 0
+ { \@@_use_i_delimit_by_s_stop:nw { \exp_not:n {#2} } }
+ { \exp_args:Nf \@@_item_N_loop:nw { \int_eval:n { #1 - 1 } } }
+ }
+\cs_generate_variant:Nn \clist_item:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_item:nn, \clist_item:en}
+% \begin{macro}{
+% \@@_item_n:nw,
+% \@@_item_n_loop:nw,
+% \@@_item_n_end:n,
+% \@@_item_n_strip:n,
+% \@@_item_n_strip:w}
+% This starts in the same way as \cs{clist_item:Nn} by counting the items
+% of the comma list. The final item should be space-trimmed before being
+% brace-stripped, hence we insert a couple of odd-looking
+% \cs{prg_do_nothing:} to avoid losing braces. Blank items are ignored.
+% \begin{macrocode}
+\cs_new:Npn \clist_item:nn #1#2
+ {
+ \@@_item:ffnN
+ { \clist_count:n {#1} }
+ { \int_eval:n {#2} }
+ {#1}
+ \@@_item_n:nw
+ }
+\cs_generate_variant:Nn \clist_item:nn { e }
+\cs_new:Npn \@@_item_n:nw #1
+ { \@@_item_n_loop:nw {#1} \prg_do_nothing: }
+\cs_new:Npn \@@_item_n_loop:nw #1 #2,
+ {
+ \exp_args:No \tl_if_blank:nTF {#2}
+ { \@@_item_n_loop:nw {#1} \prg_do_nothing: }
+ {
+ \int_compare:nNnTF {#1} = 0
+ { \exp_args:No \@@_item_n_end:n {#2} }
+ {
+ \exp_args:Nf \@@_item_n_loop:nw
+ { \int_eval:n { #1 - 1 } }
+ \prg_do_nothing:
+ }
+ }
+ }
+\cs_new:Npn \@@_item_n_end:n #1 #2 \s_@@_stop
+ { \tl_trim_spaces_apply:nN {#1} \@@_item_n_strip:n }
+\cs_new:Npn \@@_item_n_strip:n #1 { \@@_item_n_strip:w #1 , }
+\cs_new:Npn \@@_item_n_strip:w #1 , { \exp_not:n {#1} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_rand_item:n, \clist_rand_item:N, \clist_rand_item:c}
+% \begin{macro}{\@@_rand_item:nn}
+% The |N|-type function is not implemented through the |n|-type
+% function for efficiency: for instance comma-list variables do not
+% require space-trimming of their items. Even testing for emptyness
+% of an |n|-type comma-list is slow, so we count items first and use
+% that both for the emptyness test and the pseudo-random integer.
+% Importantly, \cs{clist_item:Nn} and \cs{clist_item:nn} only evaluate
+% their argument once.
+% \begin{macrocode}
+\cs_new:Npn \clist_rand_item:n #1
+ { \exp_args:Nf \@@_rand_item:nn { \clist_count:n {#1} } {#1} }
+\cs_new:Npn \@@_rand_item:nn #1#2
+ {
+ \int_compare:nNnF {#1} = 0
+ { \clist_item:nn {#2} { \int_rand:nn { 1 } {#1} } }
+ }
+\cs_new:Npn \clist_rand_item:N #1
+ {
+ \clist_if_empty:NF #1
+ { \clist_item:Nn #1 { \int_rand:nn { 1 } { \clist_count:N #1 } } }
+ }
+\cs_generate_variant:Nn \clist_rand_item:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Viewing comma lists}
+%
+% \begin{macro}{\clist_show:N, \clist_show:c, \clist_log:N, \clist_log:c, \@@_show:NN}
+% Apply the general \cs{__kernel_chk_tl_type:NnnT} with \cs{exp_not:o}
+% |#2| serving as a dummy code to prevent a check performed by this
+% auxiliary.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_show:N { \@@_show:NN \msg_show:nneeee }
+\cs_generate_variant:Nn \clist_show:N { c }
+\cs_new_protected:Npn \clist_log:N { \@@_show:NN \msg_log:nneeee }
+\cs_generate_variant:Nn \clist_log:N { c }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \__kernel_chk_tl_type:NnnT #2 { clist } { \exp_not:o #2 }
+ {
+ \int_compare:nNnTF { \clist_count:N #2 }
+ = { \exp_args:No \clist_count:n #2 }
+ {
+ #1 { clist } { show }
+ { \token_to_str:N #2 }
+ { \clist_map_function:NN #2 \msg_show_item:n }
+ { } { }
+ }
+ {
+ \msg_error:nnee { clist } { non-clist }
+ { \token_to_str:N #2 } { \tl_to_str:N #2 }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\clist_show:n, \clist_log:n, \@@_show:Nn}
+% A variant of the above: no existence check, empty first argument for
+% the message.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_show:n { \@@_show:Nn \msg_show:nneeee }
+\cs_new_protected:Npn \clist_log:n { \@@_show:Nn \msg_log:nneeee }
+\cs_new_protected:Npn \@@_show:Nn #1#2
+ {
+ #1 { clist } { show }
+ { } { \clist_map_function:nN {#2} \msg_show_item:n } { } { }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Scratch comma lists}
+%
+% \begin{variable}{\l_tmpa_clist, \l_tmpb_clist}
+% \begin{variable}{\g_tmpa_clist, \g_tmpb_clist}
+% Temporary comma list variables.
+% \begin{macrocode}
+\clist_new:N \l_tmpa_clist
+\clist_new:N \l_tmpb_clist
+\clist_new:N \g_tmpa_clist
+\clist_new:N \g_tmpb_clist
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3coffins.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3coffins.dtx
new file mode 100644
index 00000000000..164d10019db
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3coffins.dtx
@@ -0,0 +1,2590 @@
+% \iffalse meta-comment
+%
+%% File: l3coffins.dtx
+%
+% Copyright (C) 2010-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3coffins} module\\ Coffin code layer^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% The material in this module provides the low-level support system
+% for coffins. For details about the design concept of a coffin, see
+% the \pkg{xcoffins} module (in the \pkg{l3experimental} bundle).
+%
+% \section{Creating and initialising coffins}
+%
+% \begin{function}[added = 2011-08-17]{\coffin_new:N, \coffin_new:c}
+% \begin{syntax}
+% \cs{coffin_new:N} \meta{coffin}
+% \end{syntax}
+% Creates a new \meta{coffin} or raises an error if the name is
+% already taken. The declaration is global. The \meta{coffin} is
+% initially empty.
+% \end{function}
+%
+% \begin{function}[added = 2011-08-17, updated = 2019-01-21]
+% {
+% \coffin_clear:N, \coffin_clear:c,
+% \coffin_gclear:N, \coffin_gclear:c
+% }
+% \begin{syntax}
+% \cs{coffin_clear:N} \meta{coffin}
+% \end{syntax}
+% Clears the content of the \meta{coffin}.
+% \end{function}
+%
+% \begin{function}[added = 2011-08-17, updated = 2019-01-21]
+% {
+% \coffin_set_eq:NN, \coffin_set_eq:Nc,
+% \coffin_set_eq:cN, \coffin_set_eq:cc,
+% \coffin_gset_eq:NN, \coffin_gset_eq:Nc,
+% \coffin_gset_eq:cN, \coffin_gset_eq:cc
+% }
+% \begin{syntax}
+% \cs{coffin_set_eq:NN} \meta{coffin_1} \meta{coffin_2}
+% \end{syntax}
+% Sets both the content and poles of \meta{coffin_1} equal to those
+% of \meta{coffin_2}.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added = 2012-06-20]
+% {\coffin_if_exist:N, \coffin_if_exist:c}
+% \begin{syntax}
+% \cs{coffin_if_exist_p:N} \meta{coffin}
+% \cs{coffin_if_exist:NTF} \meta{coffin} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{coffin} is currently defined.
+% \end{function}
+%
+% \section{Setting coffin content and poles}
+%
+% \begin{function}[added = 2011-08-17, updated = 2019-01-21]
+% {
+% \hcoffin_set:Nn, \hcoffin_set:cn,
+% \hcoffin_gset:Nn, \hcoffin_gset:cn
+% }
+% \begin{syntax}
+% \cs{hcoffin_set:Nn} \meta{coffin} \Arg{material}
+% \end{syntax}
+% Typesets the \meta{material} in horizontal mode, storing the result
+% in the \meta{coffin}. The standard poles for the \meta{coffin} are
+% then set up based on the size of the typeset material.
+% \end{function}
+%
+% \begin{function}[added = 2011-09-10, updated = 2019-01-21]
+% {
+% \hcoffin_set:Nw, \hcoffin_set:cw, \hcoffin_set_end:,
+% \hcoffin_gset:Nw, \hcoffin_gset:cw, \hcoffin_gset_end:
+% }
+% \begin{syntax}
+% \cs{hcoffin_set:Nw} \meta{coffin} \meta{material} \cs{hcoffin_set_end:}
+% \end{syntax}
+% Typesets the \meta{material} in horizontal mode, storing the result
+% in the \meta{coffin}. The standard poles for the \meta{coffin} are
+% then set up based on the size of the typeset material.
+% These functions are useful for setting the entire contents of an
+% environment in a coffin.
+% \end{function}
+%
+% \begin{function}[added = 2011-08-17, updated = 2023-02-03]
+% {
+% \vcoffin_set:Nnn, \vcoffin_set:cnn,
+% \vcoffin_gset:Nnn, \vcoffin_gset:cnn
+% }
+% \begin{syntax}
+% \cs{vcoffin_set:Nnn} \meta{coffin} \Arg{width} \Arg{material}
+% \end{syntax}
+% Typesets the \meta{material} in vertical mode constrained to the
+% given \meta{width} and stores the result in the \meta{coffin}. The
+% standard poles for the \meta{coffin} are then set up based on the
+% size of the typeset material.
+% \end{function}
+%
+% \begin{function}[added = 2011-09-10, updated = 2023-02-03]
+% {
+% \vcoffin_set:Nnw, \vcoffin_set:cnw, \vcoffin_set_end:,
+% \vcoffin_gset:Nnw, \vcoffin_gset:cnw, \vcoffin_gset_end:
+% }
+% \begin{syntax}
+% \cs{vcoffin_set:Nnw} \meta{coffin} \Arg{width} \meta{material} \cs{vcoffin_set_end:}
+% \end{syntax}
+% Typesets the \meta{material} in vertical mode constrained to the
+% given \meta{width} and stores the result in the \meta{coffin}. The
+% standard poles for the \meta{coffin} are then set up based on the
+% size of the typeset material.
+% These functions are useful for setting the entire contents of an
+% environment in a coffin.
+% \end{function}
+%
+% \begin{function}[added = 2012-07-20, updated = 2019-01-21]
+% {
+% \coffin_set_horizontal_pole:Nnn, \coffin_set_horizontal_pole:cnn,
+% \coffin_gset_horizontal_pole:Nnn, \coffin_gset_horizontal_pole:cnn
+% }
+% \begin{syntax}
+% \cs{coffin_set_horizontal_pole:Nnn} \meta{coffin}
+% ~~\Arg{pole} \Arg{offset}
+% \end{syntax}
+% Sets the \meta{pole} to run horizontally through the \meta{coffin}.
+% The \meta{pole} is placed at the \meta{offset} from the
+% baseline of the \meta{coffin}. The
+% \meta{offset} should be given as a dimension expression.
+% \end{function}
+%
+% \begin{function}[added = 2012-07-20, updated = 2019-01-21]
+% {
+% \coffin_set_vertical_pole:Nnn, \coffin_set_vertical_pole:cnn,
+% \coffin_gset_vertical_pole:Nnn, \coffin_gset_vertical_pole:cnn
+% }
+% \begin{syntax}
+% \cs{coffin_set_vertical_pole:Nnn} \meta{coffin} \Arg{pole} \Arg{offset}
+% \end{syntax}
+% Sets the \meta{pole} to run vertically through the \meta{coffin}.
+% The \meta{pole} is placed at the \meta{offset} from the
+% left-hand edge of the bounding box of the \meta{coffin}. The
+% \meta{offset} should be given as a dimension expression.
+% \end{function}
+%
+% \begin{function}[added = 2023-05-17]{\coffin_reset_poles:N, \coffin_greset_poles:N}
+% \begin{syntax}
+% \cs{coffin_reset_poles:N} \meta{coffin}
+% \end{syntax}
+% Resets the poles of the \meta{coffin} to the standard set, removing any
+% custom or inherited poles. The poles will therefore be equal to those that
+% would be obtained from \cs{hcoffin_set:Nn} or similar; the bounding box
+% of the coffin is not reset, so any material outside of the formal bounding box
+% will not influence the poles.
+% \end{function}
+%
+% \section{Coffin affine transformations}
+%
+% \begin{function}[updated = 2019-01-23]
+% {
+% \coffin_resize:Nnn, \coffin_resize:cnn,
+% \coffin_gresize:Nnn, \coffin_gresize:cnn
+% }
+% \begin{syntax}
+% \cs{coffin_resize:Nnn} \meta{coffin} \Arg{width} \Arg{total-height}
+% \end{syntax}
+% Resized the \meta{coffin} to \meta{width} and \meta{total-height},
+% both of which should be given as dimension expressions.
+% \end{function}
+%
+% \begin{function}
+% {
+% \coffin_rotate:Nn, \coffin_rotate:cn,
+% \coffin_grotate:Nn, \coffin_grotate:cn
+% }
+% \begin{syntax}
+% \cs{coffin_rotate:Nn} \meta{coffin} \Arg{angle}
+% \end{syntax}
+% Rotates the \meta{coffin} by the given \meta{angle} (given in
+% degrees counter-clockwise). This process rotates both the
+% coffin content and poles. Multiple rotations do not result in
+% the bounding box of the coffin growing unnecessarily.
+% \end{function}
+%
+% \begin{function}[updated = 2019-01-23]
+% {
+% \coffin_scale:Nnn, \coffin_scale:cnn,
+% \coffin_gscale:Nnn, \coffin_gscale:cnn
+% }
+% \begin{syntax}
+% \cs{coffin_scale:Nnn} \meta{coffin} \Arg{x-scale} \Arg{y-scale}
+% \end{syntax}
+% Scales the \meta{coffin} by a factors \meta{x-scale} and
+% \meta{y-scale} in the horizontal and vertical directions,
+% respectively. The two scale factors should be given as real numbers.
+% \end{function}
+%
+% \section{Joining and using coffins}
+%
+% \begin{function}[updated = 2019-01-22]
+% {
+% \coffin_attach:NnnNnnnn, \coffin_attach:cnnNnnnn,
+% \coffin_attach:Nnncnnnn, \coffin_attach:cnncnnnn,
+% \coffin_gattach:NnnNnnnn, \coffin_gattach:cnnNnnnn,
+% \coffin_gattach:Nnncnnnn, \coffin_gattach:cnncnnnn
+% }
+% \begin{syntax}
+% \cs{coffin_attach:NnnNnnnn}
+% ~~\meta{coffin_1} \Arg{coffin_1-pole_1} \Arg{coffin_1-pole_2}
+% ~~\meta{coffin_2} \Arg{coffin_2-pole_1} \Arg{coffin_2-pole_2}
+% ~~\Arg{x-offset} \Arg{y-offset}
+% \end{syntax}
+% This function attaches \meta{coffin_2} to \meta{coffin_1} such that the bounding box
+% of \meta{coffin_1} is not altered, \emph{i.e.}~\meta{coffin_2} can
+% protrude outside of the bounding box of the coffin. The alignment
+% is carried out by first calculating \meta{handle_1}, the
+% point of intersection of \meta{coffin_1-pole_1} and
+% \meta{coffin_1-pole_2}, and \meta{handle_2}, the point of intersection
+% of \meta{coffin_2-pole_1} and \meta{coffin_2-pole_2}. \meta{coffin_2} is
+% then attached to \meta{coffin_1} such that the relationship between
+% \meta{handle_1} and \meta{handle_2} is described by the \meta{x-offset}
+% and \meta{y-offset}. The two offsets should be given as dimension
+% expressions.
+% \end{function}
+%
+% \begin{function}[updated = 2019-01-22]
+% {
+% \coffin_join:NnnNnnnn, \coffin_join:cnnNnnnn,
+% \coffin_join:Nnncnnnn, \coffin_join:cnncnnnn,
+% \coffin_gjoin:NnnNnnnn, \coffin_gjoin:cnnNnnnn,
+% \coffin_gjoin:Nnncnnnn, \coffin_gjoin:cnncnnnn
+% }
+% \begin{syntax}
+% \cs{coffin_join:NnnNnnnn}
+% ~~\meta{coffin_1} \Arg{coffin_1-pole_1} \Arg{coffin_1-pole_2}
+% ~~\meta{coffin_2} \Arg{coffin_2-pole_1} \Arg{coffin_2-pole_2}
+% ~~\Arg{x-offset} \Arg{y-offset}
+% \end{syntax}
+% This function joins \meta{coffin_2} to \meta{coffin_1} such that the bounding box
+% of \meta{coffin_1} may expand. The new bounding
+% box covers the area containing the bounding boxes of the two
+% original coffins. The alignment is carried out by first calculating
+% \meta{handle_1}, the point of intersection of \meta{coffin_1-pole_1} and
+% \meta{coffin_1-pole_2}, and \meta{handle_2}, the point of intersection
+% of \meta{coffin_2-pole_1} and \meta{coffin_2-pole_2}. \meta{coffin_2} is
+% then attached to \meta{coffin_1} such that the relationship between
+% \meta{handle_1} and \meta{handle_2} is described by the \meta{x-offset}
+% and \meta{y-offset}. The two offsets should be given as dimension
+% expressions.
+% \end{function}
+%
+% \begin{function}[updated = 2012-07-20]
+% {\coffin_typeset:Nnnnn, \coffin_typeset:cnnnn}
+% \begin{syntax}
+% \cs{coffin_typeset:Nnnnn} \meta{coffin} \Arg{pole_1} \Arg{pole_2}
+% ~~\Arg{x-offset} \Arg{y-offset}
+% \end{syntax}
+% Typesetting is carried out by first calculating \meta{handle}, the
+% point of intersection of \meta{pole_1} and \meta{pole_2}. The coffin
+% is then typeset in horizontal mode such that the relationship between the
+% current reference point in the document and the \meta{handle} is described
+% by the \meta{x-offset} and \meta{y-offset}. The two offsets should
+% be given as dimension expressions. Typesetting a coffin is
+% therefore analogous to carrying out an alignment where the
+% \enquote{parent} coffin is the current insertion point.
+% \end{function}
+%
+% \section{Measuring coffins}
+%
+% \begin{function}{\coffin_dp:N, \coffin_dp:c}
+% \begin{syntax}
+% \cs{coffin_dp:N} \meta{coffin}
+% \end{syntax}
+% Calculates the depth (below the baseline) of the \meta{coffin}
+% in a form suitable for use in a \meta{dim expr}.
+% \end{function}
+%
+% \begin{function}{\coffin_ht:N, \coffin_ht:c}
+% \begin{syntax}
+% \cs{coffin_ht:N} \meta{coffin}
+% \end{syntax}
+% Calculates the height (above the baseline) of the \meta{coffin}
+% in a form suitable for use in a \meta{dim expr}.
+% \end{function}
+%
+% \begin{function}{\coffin_wd:N, \coffin_wd:c}
+% \begin{syntax}
+% \cs{coffin_wd:N} \meta{coffin}
+% \end{syntax}
+% Calculates the width of the \meta{coffin} in a form
+% suitable for use in a \meta{dim expr}.
+% \end{function}
+%
+% \section{Coffin diagnostics}
+%
+% \begin{function}[updated = 2011-09-02]
+% {\coffin_display_handles:Nn, \coffin_display_handles:cn}
+% \begin{syntax}
+% \cs{coffin_display_handles:Nn} \meta{coffin} \Arg{color}
+% \end{syntax}
+% This function first calculates the intersections between all of
+% the \meta{poles} of the \meta{coffin} to give a set of
+% \meta{handles}. It then prints the \meta{coffin} at the current
+% location in the source, with the position of the \meta{handles}
+% marked on the coffin. The \meta{handles} are labelled as part
+% of this process: the locations of the \meta{handles} and the labels
+% are both printed in the \meta{color} specified.
+% \end{function}
+%
+% \begin{function}[updated = 2011-09-02]
+% {\coffin_mark_handle:Nnnn, \coffin_mark_handle:cnnn}
+% \begin{syntax}
+% \cs{coffin_mark_handle:Nnnn} \meta{coffin} \Arg{pole_1} \Arg{pole_2} \Arg{color}
+% \end{syntax}
+% This function first calculates the \meta{handle} for the
+% \meta{coffin} as defined by the intersection of \meta{pole_1} and
+% \meta{pole_2}. It then marks the position of the \meta{handle}
+% on the \meta{coffin}. The \meta{handle} are labelled as part of
+% this process: the location of the \meta{handle} and the label are
+% both printed in the \meta{color} specified.
+% \end{function}
+%
+% \begin{function}[updated = 2015-08-01]
+% {\coffin_show_structure:N, \coffin_show_structure:c}
+% \begin{syntax}
+% \cs{coffin_show_structure:N} \meta{coffin}
+% \end{syntax}
+% This function shows the structural information about the
+% \meta{coffin} in the terminal. The width, height and depth of the
+% typeset material are given, along with the location of all of the
+% poles of the coffin.
+%
+% Notice that the poles of a coffin are defined by four values:
+% the $x$ and $y$ co-ordinates of a point that the pole
+% passes through and the $x$- and $y$-components of a
+% vector denoting the direction of the pole. It is the ratio between
+% the later, rather than the absolute values, which determines the
+% direction of the pole.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2015-08-01]
+% {\coffin_log_structure:N, \coffin_log_structure:c}
+% \begin{syntax}
+% \cs{coffin_log_structure:N} \meta{coffin}
+% \end{syntax}
+% This function writes the structural information about the
+% \meta{coffin} in the log file. See also \cs{coffin_show_structure:N}
+% which displays the result in the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2021-05-11]
+% {\coffin_show:N, \coffin_show:c, \coffin_log:N, \coffin_log:c}
+% \begin{syntax}
+% \cs{coffin_show:N} \meta{coffin}
+% \cs{coffin_log:N} \meta{coffin}
+% \end{syntax}
+% Shows full details of poles and contents of the \meta{coffin} in the
+% terminal or log file. See \cs{coffin_show_structure:N} and
+% \cs{box_show:N} to show separately the pole structure and the
+% contents.
+% \end{function}
+%
+% \begin{function}[added = 2021-05-11]
+% {\coffin_show:Nnn, \coffin_show:cnn, \coffin_log:Nnn, \coffin_log:cnn}
+% \begin{syntax}
+% \cs{coffin_show:Nnn} \meta{coffin} \Arg{int expr_1} \Arg{int expr_2}
+% \cs{coffin_log:Nnn} \meta{coffin} \Arg{int expr_1} \Arg{int expr_2}
+% \end{syntax}
+% Shows poles and contents of the \meta{coffin} in the terminal or log
+% file, showing the first \meta{int expr_1} items in the coffin, and
+% descending into \meta{int expr_2} group levels. See
+% \cs{coffin_show_structure:N} and \cs{box_show:Nnn} to show
+% separately the pole structure and the contents.
+% \end{function}
+%
+% \section{Constants and variables}
+%
+% \begin{variable}{\c_empty_coffin}
+% A permanently empty coffin.
+% \end{variable}
+%
+% \begin{variable}[added = 2012-06-19]{\l_tmpa_coffin, \l_tmpb_coffin}
+% Scratch coffins for local assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \begin{variable}[added = 2019-01-24]{\g_tmpa_coffin, \g_tmpb_coffin}
+% Scratch coffins for global assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3coffins} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=coffin>
+% \end{macrocode}
+%
+% \subsection{Coffins: data structures and general variables}
+%
+% \begin{variable}{\l_@@_internal_box}
+% \begin{variable}{\l_@@_internal_dim}
+% \begin{variable}{\l_@@_internal_tl}
+% Scratch variables.
+% \begin{macrocode}
+\box_new:N \l_@@_internal_box
+\dim_new:N \l_@@_internal_dim
+\tl_new:N \l_@@_internal_tl
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_corners_prop}
+% The \enquote{corners}; of a coffin define the real content, as
+% opposed to the \TeX{} bounding box. They all start off in the same
+% place, of course.
+% \begin{macrocode}
+\prop_const_from_keyval:Nn \c_@@_corners_prop
+ {
+ tl = { 0pt } { 0pt } ,
+ tr = { 0pt } { 0pt } ,
+ bl = { 0pt } { 0pt } ,
+ br = { 0pt } { 0pt } ,
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_poles_prop}
+% Pole positions are given for horizontal, vertical and reference-point
+% based values.
+% \begin{macrocode}
+\prop_const_from_keyval:Nn \c_@@_poles_prop
+ {
+ l = { 0pt } { 0pt } { 0pt } { 1000pt } ,
+ hc = { 0pt } { 0pt } { 0pt } { 1000pt } ,
+ r = { 0pt } { 0pt } { 0pt } { 1000pt } ,
+ b = { 0pt } { 0pt } { 1000pt } { 0pt } ,
+ vc = { 0pt } { 0pt } { 1000pt } { 0pt } ,
+ t = { 0pt } { 0pt } { 1000pt } { 0pt } ,
+ B = { 0pt } { 0pt } { 1000pt } { 0pt } ,
+ H = { 0pt } { 0pt } { 1000pt } { 0pt } ,
+ T = { 0pt } { 0pt } { 1000pt } { 0pt } ,
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_slope_A_fp}
+% \begin{variable}{\l_@@_slope_B_fp}
+% Used for calculations of intersections.
+% \begin{macrocode}
+\fp_new:N \l_@@_slope_A_fp
+\fp_new:N \l_@@_slope_B_fp
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_error_bool}
+% For propagating errors so that parts of the code can work around them.
+% \begin{macrocode}
+\bool_new:N \l_@@_error_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_offset_x_dim}
+% \begin{variable}{\l_@@_offset_y_dim}
+% The offset between two sets of coffin handles when typesetting. These
+% values are corrected from those requested in an alignment for the
+% positions of the handles.
+% \begin{macrocode}
+\dim_new:N \l_@@_offset_x_dim
+\dim_new:N \l_@@_offset_y_dim
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_pole_a_tl}
+% \begin{variable}{\l_@@_pole_b_tl}
+% Needed for finding the intersection of two poles.
+% \begin{macrocode}
+\tl_new:N \l_@@_pole_a_tl
+\tl_new:N \l_@@_pole_b_tl
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_x_dim}
+% \begin{variable}{\l_@@_y_dim}
+% \begin{variable}{\l_@@_x_prime_dim}
+% \begin{variable}{\l_@@_y_prime_dim}
+% For calculating intersections and so forth.
+% \begin{macrocode}
+\dim_new:N \l_@@_x_dim
+\dim_new:N \l_@@_y_dim
+\dim_new:N \l_@@_x_prime_dim
+\dim_new:N \l_@@_y_prime_dim
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+% \end{variable}
+% \end{variable}
+%
+% \subsection{Basic coffin functions}
+%
+% There are a number of basic functions needed for creating coffins and
+% placing material in them. This all relies on the following data
+% structures.
+%
+% \begin{macro}[EXP]{\@@_to_value:N}
+% Coffins are a two-part structure and we rely on the internal nature of
+% box allocation to make everything work. As such, we need an interface
+% to turn coffin identifiers into numbers. For the purposes here, the
+% signature allowed is |N| despite the nature of the underlying primitive.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_to_value:N \tex_number:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, pTF]{\coffin_if_exist:N, \coffin_if_exist:c}
+% Several of the higher-level coffin functions would give multiple
+% errors if the coffin does not exist. A cleaner way to handle this
+% is provided here: both the box and the coffin structure are
+% checked.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \coffin_if_exist:N #1 { p , T , F , TF }
+ {
+ \cs_if_exist:NTF #1
+ {
+ \cs_if_exist:cTF { coffin ~ \@@_to_value:N #1 ~ poles }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ { \prg_return_false: }
+ }
+\prg_generate_conditional_variant:Nnn \coffin_if_exist:N
+ { c } { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_if_exist:NT}
+% Several of the higher-level coffin functions would give multiple
+% errors if the coffin does not exist. So a wrapper is provided to deal
+% with this correctly, issuing an error on erroneous use.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_if_exist:NT #1#2
+ {
+ \coffin_if_exist:NTF #1
+ { #2 }
+ {
+ \msg_error:nne { coffin } { unknown }
+ { \token_to_str:N #1 }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \coffin_clear:N, \coffin_clear:c,
+% \coffin_gclear:N, \coffin_gclear:c
+% }
+% Clearing coffins means emptying the box and resetting all of the
+% structures.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_clear:N #1
+ {
+ \@@_if_exist:NT #1
+ {
+ \box_clear:N #1
+ \@@_reset_structure:N #1
+ }
+ }
+\cs_generate_variant:Nn \coffin_clear:N { c }
+\cs_new_protected:Npn \coffin_gclear:N #1
+ {
+ \@@_if_exist:NT #1
+ {
+ \box_gclear:N #1
+ \@@_greset_structure:N #1
+ }
+ }
+\cs_generate_variant:Nn \coffin_gclear:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\coffin_new:N, \coffin_new:c}
+% Creating a new coffin means making the underlying box and adding the
+% data structures. The \cs{debug_suspend:} and \cs{debug_resume:} functions
+% prevent \cs{prop_gclear_new:c} from writing useless information to
+% the log file.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_new:N #1
+ {
+ \box_new:N #1
+ \debug_suspend:
+ \prop_gclear_new:c { coffin ~ \@@_to_value:N #1 ~ corners }
+ \prop_gclear_new:c { coffin ~ \@@_to_value:N #1 ~ poles }
+ \prop_gset_eq:cN { coffin ~ \@@_to_value:N #1 ~ corners }
+ \c_@@_corners_prop
+ \prop_gset_eq:cN { coffin ~ \@@_to_value:N #1 ~ poles }
+ \c_@@_poles_prop
+ \debug_resume:
+ }
+\cs_generate_variant:Nn \coffin_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \hcoffin_set:Nn, \hcoffin_set:cn,
+% \hcoffin_gset:Nn, \hcoffin_gset:cn
+% }
+% Horizontal coffins are relatively easy: set the appropriate box,
+% reset the structures then update the handle positions.
+% \begin{macrocode}
+\cs_new_protected:Npn \hcoffin_set:Nn #1#2
+ {
+ \@@_if_exist:NT #1
+ {
+ \hbox_set:Nn #1
+ {
+ \color_ensure_current:
+ #2
+ }
+ \coffin_reset_poles:N #1
+ }
+ }
+\cs_generate_variant:Nn \hcoffin_set:Nn { c }
+\cs_new_protected:Npn \hcoffin_gset:Nn #1#2
+ {
+ \@@_if_exist:NT #1
+ {
+ \hbox_gset:Nn #1
+ {
+ \color_ensure_current:
+ #2
+ }
+ \coffin_greset_poles:N #1
+ }
+ }
+\cs_generate_variant:Nn \hcoffin_gset:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \vcoffin_set:Nnn, \vcoffin_set:cnn,
+% \vcoffin_gset:Nnn, \vcoffin_gset:cnn
+% }
+% \begin{macro}{\@@_set_vertical:NnnNNN}
+% \begin{macro}{\@@_set_vertical_aux:}
+% Setting vertical coffins is more complex. First, the material is
+% typeset with a given width. The default handles and poles are set as
+% for a horizontal coffin, before finding the top baseline using a
+% temporary box. No \cs{color_ensure_current:} here as that would add a
+% whatsit to the start of the vertical box and mess up the location of the
+% \texttt{T}~pole (see \emph{\TeX{} by Topic} for discussion of the
+% \tn{vtop} primitive, used to do the measuring).
+% \begin{macrocode}
+\cs_new_protected:Npn \vcoffin_set:Nnn #1#2#3
+ {
+ \@@_set_vertical:NnnNNN #1 {#2} {#3}
+ \vbox_set:Nn \coffin_reset_poles:N \@@_set_pole:Nnn
+ }
+\cs_generate_variant:Nn \vcoffin_set:Nnn { c }
+\cs_new_protected:Npn \vcoffin_gset:Nnn #1#2#3
+ {
+ \@@_set_vertical:NnnNNN #1 {#2} {#3}
+ \vbox_gset:Nn \coffin_greset_poles:N \@@_gset_pole:Nnn
+ }
+\cs_generate_variant:Nn \vcoffin_gset:Nnn { c }
+\cs_new_protected:Npn \@@_set_vertical:NnnNNN #1#2#3#4#5#6
+ {
+ \@@_if_exist:NT #1
+ {
+ #4 #1
+ {
+ \dim_set:Nn \tex_hsize:D {#2}
+ \@@_set_vertical_aux:
+ #3
+ }
+ #5 #1
+ \vbox_set_top:Nn \l_@@_internal_box { \vbox_unpack:N #1 }
+ #6 #1 { T }
+ {
+ { 0pt }
+ {
+ \dim_eval:n
+ { \box_ht:N #1 - \box_ht:N \l_@@_internal_box }
+ }
+ { 1000pt }
+ { 0pt }
+ }
+ \box_clear:N \l_@@_internal_box
+ }
+ }
+\cs_new_protected:Npe \@@_set_vertical_aux:
+ {
+ \bool_lazy_and:nnT
+ { \cs_if_exist_p:N \fmtname }
+ { \str_if_eq_p:Vn \fmtname { LaTeX2e } }
+ {
+ \dim_set_eq:NN \exp_not:N \linewidth \tex_hsize:D
+ \dim_set_eq:NN \exp_not:N \columnwidth \tex_hsize:D
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\hcoffin_set:Nw, \hcoffin_set:cw, \hcoffin_gset:Nw, \hcoffin_gset:cw}
+% \begin{macro}{\hcoffin_set_end:, \hcoffin_gset_end:}
+% These are the \enquote{begin}/\enquote{end} versions of the above:
+% watch the grouping!
+% \begin{macrocode}
+\cs_new_protected:Npn \hcoffin_set:Nw #1
+ {
+ \@@_if_exist:NT #1
+ {
+ \hbox_set:Nw #1 \color_ensure_current:
+ \cs_set_protected:Npn \hcoffin_set_end:
+ {
+ \hbox_set_end:
+ \coffin_reset_poles:N #1
+ }
+ }
+ }
+\cs_generate_variant:Nn \hcoffin_set:Nw { c }
+\cs_new_protected:Npn \hcoffin_gset:Nw #1
+ {
+ \@@_if_exist:NT #1
+ {
+ \hbox_gset:Nw #1 \color_ensure_current:
+ \cs_set_protected:Npn \hcoffin_gset_end:
+ {
+ \hbox_gset_end:
+ \coffin_greset_poles:N #1
+ }
+ }
+ }
+\cs_generate_variant:Nn \hcoffin_gset:Nw { c }
+\cs_new_protected:Npn \hcoffin_set_end: { }
+\cs_new_protected:Npn \hcoffin_gset_end: { }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\vcoffin_set:Nnw, \vcoffin_set:cnw, \vcoffin_gset:Nnw, \vcoffin_gset:cnw}
+% \begin{macro}{\@@_set_vertical:NnNNNNNw}
+% \begin{macro}{\vcoffin_set_end:, \vcoffin_gset_end:}
+% The same for vertical coffins.
+% \begin{macrocode}
+\cs_new_protected:Npn \vcoffin_set:Nnw #1#2
+ {
+ \@@_set_vertical:NnNNNNNw #1 {#2} \vbox_set:Nw
+ \vcoffin_set_end:
+ \vbox_set_end: \coffin_reset_poles:N \@@_set_pole:Nnn
+ }
+\cs_generate_variant:Nn \vcoffin_set:Nnw { c }
+\cs_new_protected:Npn \vcoffin_gset:Nnw #1#2
+ {
+ \@@_set_vertical:NnNNNNNw #1 {#2} \vbox_gset:Nw
+ \vcoffin_gset_end:
+ \vbox_gset_end: \coffin_greset_poles:N \@@_gset_pole:Nnn
+ }
+\cs_generate_variant:Nn \vcoffin_gset:Nnw { c }
+\cs_new_protected:Npn \@@_set_vertical:NnNNNNNw #1#2#3#4#5#6#7
+ {
+ \@@_if_exist:NT #1
+ {
+ #3 #1
+ \dim_set:Nn \tex_hsize:D {#2}
+ \@@_set_vertical_aux:
+ \cs_set_protected:Npn #4
+ {
+ #5
+ #6 #1
+ \vbox_set_top:Nn \l_@@_internal_box { \vbox_unpack:N #1 }
+ #7 #1 { T }
+ {
+ { 0pt }
+ {
+ \dim_eval:n
+ { \box_ht:N #1 - \box_ht:N \l_@@_internal_box }
+ }
+ { 1000pt }
+ { 0pt }
+ }
+ \box_clear:N \l_@@_internal_box
+ }
+ }
+ }
+\cs_new_protected:Npn \vcoffin_set_end: { }
+\cs_new_protected:Npn \vcoffin_gset_end: { }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \coffin_set_eq:NN, \coffin_set_eq:Nc,
+% \coffin_set_eq:cN, \coffin_set_eq:cc,
+% \coffin_gset_eq:NN, \coffin_gset_eq:Nc,
+% \coffin_gset_eq:cN, \coffin_gset_eq:cc
+% }
+% Setting two coffins equal is just a wrapper around other functions.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_set_eq:NN #1#2
+ {
+ \@@_if_exist:NT #1
+ {
+ \box_set_eq:NN #1 #2
+ \prop_set_eq:cc { coffin ~ \@@_to_value:N #1 ~ corners }
+ { coffin ~ \@@_to_value:N #2 ~ corners }
+ \prop_set_eq:cc { coffin ~ \@@_to_value:N #1 ~ poles }
+ { coffin ~ \@@_to_value:N #2 ~ poles }
+ }
+ }
+\cs_generate_variant:Nn \coffin_set_eq:NN { c , Nc , cc }
+\cs_new_protected:Npn \coffin_gset_eq:NN #1#2
+ {
+ \@@_if_exist:NT #1
+ {
+ \box_gset_eq:NN #1 #2
+ \prop_gset_eq:cc { coffin ~ \@@_to_value:N #1 ~ corners }
+ { coffin ~ \@@_to_value:N #2 ~ corners }
+ \prop_gset_eq:cc { coffin ~ \@@_to_value:N #1 ~ poles }
+ { coffin ~ \@@_to_value:N #2 ~ poles }
+ }
+ }
+\cs_generate_variant:Nn \coffin_gset_eq:NN { c , Nc , cc }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\c_empty_coffin}
+% \begin{variable}{\l_@@_aligned_coffin}
+% \begin{variable}{\l_@@_aligned_internal_coffin}
+% Special coffins: these cannot be set up earlier as they need
+% \cs{coffin_new:N}. The empty coffin is set as a box as the full
+% coffin-setting system needs some material which is not yet available.
+% The empty coffin is creted entirely by hand: not everything is in place
+% yet.
+% \begin{macrocode}
+\coffin_new:N \c_empty_coffin
+\coffin_new:N \l_@@_aligned_coffin
+\coffin_new:N \l_@@_aligned_internal_coffin
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}
+% {\l_tmpa_coffin, \l_tmpb_coffin, \g_tmpa_coffin, \g_tmpb_coffin}
+% The usual scratch space.
+% \begin{macrocode}
+\coffin_new:N \l_tmpa_coffin
+\coffin_new:N \l_tmpb_coffin
+\coffin_new:N \g_tmpa_coffin
+\coffin_new:N \g_tmpb_coffin
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Measuring coffins}
+%
+% \begin{macro}
+% {
+% \coffin_dp:N, \coffin_dp:c, \coffin_ht:N, \coffin_ht:c,
+% \coffin_wd:N, \coffin_wd:c
+% }
+% Coffins are just boxes when it comes to measurement. However, semantically
+% a separate set of functions are required.
+% \begin{macrocode}
+\cs_new_eq:NN \coffin_dp:N \box_dp:N
+\cs_new_eq:NN \coffin_dp:c \box_dp:c
+\cs_new_eq:NN \coffin_ht:N \box_ht:N
+\cs_new_eq:NN \coffin_ht:c \box_ht:c
+\cs_new_eq:NN \coffin_wd:N \box_wd:N
+\cs_new_eq:NN \coffin_wd:c \box_wd:c
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Coffins: handle and pole management}
+%
+% \begin{macro}{\@@_get_pole:NnN}
+% A simple wrapper around the recovery of a coffin pole, with some
+% error checking and recovery built-in.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_get_pole:NnN #1#2#3
+ {
+ \prop_get:cnNF
+ { coffin ~ \@@_to_value:N #1 ~ poles } {#2} #3
+ {
+ \msg_error:nnee { coffin } { unknown-pole }
+ { \exp_not:n {#2} } { \token_to_str:N #1 }
+ \tl_set:Nn #3 { { 0pt } { 0pt } { 0pt } { 0pt } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_reset_structure:N, \@@_greset_structure:N}
+% Resetting the structure is a simple copy job.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_reset_structure:N #1
+ {
+ \prop_set_eq:cN { coffin ~ \@@_to_value:N #1 ~ corners }
+ \c_@@_corners_prop
+ \prop_set_eq:cN { coffin ~ \@@_to_value:N #1 ~ poles }
+ \c_@@_poles_prop
+ }
+\cs_new_protected:Npn \@@_greset_structure:N #1
+ {
+ \prop_gset_eq:cN { coffin ~ \@@_to_value:N #1 ~ corners }
+ \c_@@_corners_prop
+ \prop_gset_eq:cN { coffin ~ \@@_to_value:N #1 ~ poles }
+ \c_@@_poles_prop
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \coffin_set_horizontal_pole:Nnn, \coffin_set_horizontal_pole:cnn,
+% \coffin_gset_horizontal_pole:Nnn, \coffin_gset_horizontal_pole:cnn
+% }
+% \begin{macro}{\@@_set_horizontal_pole:NnnN}
+% \begin{macro}
+% {
+% \coffin_set_vertical_pole:Nnn, \coffin_set_vertical_pole:cnn,
+% \coffin_gset_vertical_pole:Nnn, \coffin_gset_vertical_pole:cnn
+% }
+% \begin{macro}{\@@_set_vertical_pole:NnnN}
+% \begin{macro}{\@@_set_pole:Nnn, \@@_gset_pole:Nnn}
+% Setting the pole of a coffin at the user/designer level requires a
+% bit more care. The idea here is to provide a reasonable interface to
+% the system, then to do the setting with full expansion. The
+% three-argument version is used internally to do a direct setting.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_set_horizontal_pole:Nnn #1#2#3
+ { \@@_set_horizontal_pole:NnnN #1 {#2} {#3} \prop_put:cne }
+\cs_generate_variant:Nn \coffin_set_horizontal_pole:Nnn { c }
+\cs_new_protected:Npn \coffin_gset_horizontal_pole:Nnn #1#2#3
+ { \@@_set_horizontal_pole:NnnN #1 {#2} {#3} \prop_gput:cne }
+\cs_generate_variant:Nn \coffin_gset_horizontal_pole:Nnn { c }
+\cs_new_protected:Npn \@@_set_horizontal_pole:NnnN #1#2#3#4
+ {
+ \@@_if_exist:NT #1
+ {
+ #4 { coffin ~ \@@_to_value:N #1 ~ poles }
+ {#2}
+ {
+ { 0pt } { \dim_eval:n {#3} }
+ { 1000pt } { 0pt }
+ }
+ }
+ }
+\cs_new_protected:Npn \coffin_set_vertical_pole:Nnn #1#2#3
+ { \@@_set_vertical_pole:NnnN #1 {#2} {#3} \prop_put:cne }
+\cs_generate_variant:Nn \coffin_set_vertical_pole:Nnn { c }
+\cs_new_protected:Npn \coffin_gset_vertical_pole:Nnn #1#2#3
+ { \@@_set_vertical_pole:NnnN #1 {#2} {#3} \prop_gput:cne }
+ \cs_generate_variant:Nn \coffin_gset_vertical_pole:Nnn { c }
+\cs_new_protected:Npn \@@_set_vertical_pole:NnnN #1#2#3#4
+ {
+ \@@_if_exist:NT #1
+ {
+ #4 { coffin ~ \@@_to_value:N #1 ~ poles }
+ {#2}
+ {
+ { \dim_eval:n {#3} } { 0pt }
+ { 0pt } { 1000pt }
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_set_pole:Nnn #1#2#3
+ {
+ \prop_put:cne { coffin ~ \@@_to_value:N #1 ~ poles }
+ {#2} {#3}
+ }
+\cs_new_protected:Npn \@@_gset_pole:Nnn #1#2#3
+ {
+ \prop_gput:cne { coffin ~ \@@_to_value:N #1 ~ poles }
+ {#2} {#3}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\coffin_reset_poles:N, \coffin_greset_poles:N}
+% Simple shortcuts.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_reset_poles:N #1
+ {
+ \@@_reset_structure:N #1
+ \@@_update_corners:N #1
+ \@@_update_poles:N #1
+ }
+\cs_new_protected:Npn \coffin_greset_poles:N #1
+ {
+ \@@_greset_structure:N #1
+ \@@_gupdate_corners:N #1
+ \@@_gupdate_poles:N #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_update_corners:N, \@@_gupdate_corners:N}
+% \begin{macro}{\@@_update_corners:NN}
+% \begin{macro}{\@@_update_corners:NNN}
+% Updating the corners of a coffin is straight-forward as at this stage
+% there can be no rotation. So the corners of the content are just those
+% of the underlying \TeX{} box.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_update_corners:N #1
+ { \@@_update_corners:NN #1 \prop_put:Nne }
+\cs_new_protected:Npn \@@_gupdate_corners:N #1
+ { \@@_update_corners:NN #1 \prop_gput:Nne }
+\cs_new_protected:Npn \@@_update_corners:NN #1#2
+ {
+ \exp_args:Nc \@@_update_corners:NNN
+ { coffin ~ \@@_to_value:N #1 ~ corners }
+ #1 #2
+ }
+\cs_new_protected:Npn \@@_update_corners:NNN #1#2#3
+ {
+ #3 #1
+ { tl }
+ { { 0pt } { \dim_eval:n { \box_ht:N #2 } } }
+ #3 #1
+ { tr }
+ {
+ { \dim_eval:n { \box_wd:N #2 } }
+ { \dim_eval:n { \box_ht:N #2 } }
+ }
+ #3 #1
+ { bl }
+ { { 0pt } { \dim_eval:n { -\box_dp:N #2 } } }
+ #3 #1
+ { br }
+ {
+ { \dim_eval:n { \box_wd:N #2 } }
+ { \dim_eval:n { -\box_dp:N #2 } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_update_poles:N, \@@_gupdate_poles:N}
+% \begin{macro}{\@@_update_poles:NN}
+% \begin{macro}{\@@_update_poles:NNN}
+% This function is called when a coffin is set, and updates the poles to
+% reflect the nature of size of the box. Thus this function only alters
+% poles where the default position is dependent on the size of the box.
+% It also does not set poles which are relevant only to vertical
+% coffins.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_update_poles:N #1
+ { \@@_update_poles:NN #1 \prop_put:Nne }
+\cs_new_protected:Npn \@@_gupdate_poles:N #1
+ { \@@_update_poles:NN #1 \prop_gput:Nne }
+\cs_new_protected:Npn \@@_update_poles:NN #1#2
+ {
+ \exp_args:Nc \@@_update_poles:NNN
+ { coffin ~ \@@_to_value:N #1 ~ poles }
+ #1 #2
+ }
+\cs_new_protected:Npn \@@_update_poles:NNN #1#2#3
+ {
+ #3 #1 { hc }
+ {
+ { \dim_eval:n { 0.5 \box_wd:N #2 } }
+ { 0pt } { 0pt } { 1000pt }
+ }
+ #3 #1 { r }
+ {
+ { \dim_eval:n { \box_wd:N #2 } }
+ { 0pt } { 0pt } { 1000pt }
+ }
+ #3 #1 { vc }
+ {
+ { 0pt }
+ { \dim_eval:n { ( \box_ht:N #2 - \box_dp:N #2 ) / 2 } }
+ { 1000pt }
+ { 0pt }
+ }
+ #3 #1 { t }
+ {
+ { 0pt }
+ { \dim_eval:n { \box_ht:N #2 } }
+ { 1000pt }
+ { 0pt }
+ }
+ #3 #1 { b }
+ {
+ { 0pt }
+ { \dim_eval:n { -\box_dp:N #2 } }
+ { 1000pt }
+ { 0pt }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Coffins: calculation of pole intersections}
+%
+% \begin{macro}{\@@_calculate_intersection:Nnn}
+% \begin{macro}{\@@_calculate_intersection:nnnnnnnn}
+% \begin{macro}{\@@_calculate_intersection:nnnnnn}
+% The lead off in finding intersections is to recover the two poles
+% and then hand off to the auxiliary for the actual calculation. There
+% may of course not be an intersection, for which an error trap is
+% needed.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_calculate_intersection:Nnn #1#2#3
+ {
+ \@@_get_pole:NnN #1 {#2} \l_@@_pole_a_tl
+ \@@_get_pole:NnN #1 {#3} \l_@@_pole_b_tl
+ \bool_set_false:N \l_@@_error_bool
+ \exp_last_two_unbraced:Noo
+ \@@_calculate_intersection:nnnnnnnn
+ \l_@@_pole_a_tl \l_@@_pole_b_tl
+ \bool_if:NT \l_@@_error_bool
+ {
+ \msg_error:nn { coffin } { no-pole-intersection }
+ \dim_zero:N \l_@@_x_dim
+ \dim_zero:N \l_@@_y_dim
+ }
+ }
+% \end{macrocode}
+% The two poles passed here each have four values (as dimensions),
+% ($a$, $b$, $c$, $d$) and
+% ($a'$, $b'$, $c'$, $d'$). These are arguments
+% $1$--$4$ and $5$--$8$, respectively. In both
+% cases $a$ and $b$ are the co-ordinates of a point on the
+% pole and $c$ and $d$ define the direction of the pole. Finding
+% the intersection depends on the directions of the poles, which are
+% given by $d / c$ and $d' / c'$. However, if one of the poles
+% is either horizontal or vertical then one or more of $c$, $d$,
+% $c'$ and $d'$ are zero and a special case is needed.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_calculate_intersection:nnnnnnnn
+ #1#2#3#4#5#6#7#8
+ {
+ \dim_compare:nNnTF {#3} = \c_zero_dim
+% \end{macrocode}
+% The case where the first pole is vertical. So the $x$-component
+% of the interaction is at $a$. There is then a test on the
+% second pole: if it is also vertical then there is an error.
+% \begin{macrocode}
+ {
+ \dim_set:Nn \l_@@_x_dim {#1}
+ \dim_compare:nNnTF {#7} = \c_zero_dim
+ { \bool_set_true:N \l_@@_error_bool }
+% \end{macrocode}
+% The second pole may still be horizontal, in which case the
+% $y$-component of the intersection is $b'$. If not,
+% \[
+% y = \frac{d'}{c'} \left ( a - a' \right ) + b'
+% \]
+% with the $x$-component already known to be |#1|.
+% \begin{macrocode}
+ {
+ \dim_set:Nn \l_@@_y_dim
+ {
+ \dim_compare:nNnTF {#8} = \c_zero_dim
+ {#6}
+ {
+ \fp_to_dim:n
+ {
+ ( \dim_to_fp:n {#8} / \dim_to_fp:n {#7} )
+ * ( \dim_to_fp:n {#1} - \dim_to_fp:n {#5} )
+ + \dim_to_fp:n {#6}
+ }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% If the first pole is not vertical then it may be horizontal. If so,
+% then the procedure is essentially the same as that already done but
+% with the $x$- and $y$-components interchanged.
+% \begin{macrocode}
+ {
+ \dim_compare:nNnTF {#4} = \c_zero_dim
+ {
+ \dim_set:Nn \l_@@_y_dim {#2}
+ \dim_compare:nNnTF {#8} = { \c_zero_dim }
+ { \bool_set_true:N \l_@@_error_bool }
+ {
+% \end{macrocode}
+% Now we deal with the case where the second pole may be vertical, or
+% if not we have
+% \[
+% x = \frac{c'}{d'} \left ( b - b' \right ) + a'
+% \]
+% which is again handled by the same auxiliary.
+% \begin{macrocode}
+ \dim_set:Nn \l_@@_x_dim
+ {
+ \dim_compare:nNnTF {#7} = \c_zero_dim
+ {#5}
+ {
+ \fp_to_dim:n
+ {
+ ( \dim_to_fp:n {#7} / \dim_to_fp:n {#8} )
+ * ( \dim_to_fp:n {#4} - \dim_to_fp:n {#6} )
+ + \dim_to_fp:n {#5}
+ }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% The first pole is neither horizontal nor vertical. To avoid even
+% more complexity, we now work out both slopes and pass to an auxiliary.
+% \begin{macrocode}
+ {
+ \use:e
+ {
+ \@@_calculate_intersection:nnnnnn
+ { \dim_to_fp:n {#4} / \dim_to_fp:n {#3} }
+ { \dim_to_fp:n {#8} / \dim_to_fp:n {#7} }
+ }
+ {#1} {#2} {#5} {#6}
+ }
+ }
+ }
+% \end{macrocode}
+% Assuming the two poles are not parallel, then the intersection point is
+% found in two steps. First we find the $x$-value with
+% \[
+% x = \frac { sa - s'a' - b + b' }{ s - s' }
+% \]
+% and then finding the $y$-value with
+% \[
+% y = s ( x - a ) + b
+% \]
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_calculate_intersection:nnnnnn #1#2#3#4#5#6
+ {
+ \fp_compare:nNnTF {#1} = {#2}
+ { \bool_set_true:N \l_@@_error_bool }
+ {
+ \dim_set:Nn \l_@@_x_dim
+ {
+ \fp_to_dim:n
+ {
+ (
+ #1 * \dim_to_fp:n {#3}
+ - #2 * \dim_to_fp:n {#5}
+ - \dim_to_fp:n {#4}
+ + \dim_to_fp:n {#6}
+ )
+ /
+ ( #1 - #2 )
+ }
+ }
+ \dim_set:Nn \l_@@_y_dim
+ {
+ \fp_to_dim:n
+ {
+ #1 * ( \l_@@_x_dim - \dim_to_fp:n {#3} )
+ + \dim_to_fp:n {#4}
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Affine transformations}
+%
+% \begin{variable}{\l_@@_sin_fp}
+% \begin{variable}{\l_@@_cos_fp}
+% Used for rotations to get the sine and cosine values.
+% \begin{macrocode}
+\fp_new:N \l_@@_sin_fp
+\fp_new:N \l_@@_cos_fp
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_bounding_prop}
+% A property list for the bounding box of a coffin. This is only needed
+% during the rotation, so there is just the one.
+% \begin{macrocode}
+\prop_new:N \l_@@_bounding_prop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_corners_prop, \l_@@_poles_prop}
+% Used to avoid needing to track scope for intermediate steps.
+% \begin{macrocode}
+\prop_new:N \l_@@_corners_prop
+\prop_new:N \l_@@_poles_prop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_bounding_shift_dim}
+% The shift of the bounding box of a coffin from the real content.
+% \begin{macrocode}
+\dim_new:N \l_@@_bounding_shift_dim
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_left_corner_dim}
+% \begin{variable}{\l_@@_right_corner_dim}
+% \begin{variable}{\l_@@_bottom_corner_dim}
+% \begin{variable}{\l_@@_top_corner_dim}
+% These are used to hold maxima for the various corner values: these
+% thus define the minimum size of the bounding box after rotation.
+% \begin{macrocode}
+\dim_new:N \l_@@_left_corner_dim
+\dim_new:N \l_@@_right_corner_dim
+\dim_new:N \l_@@_bottom_corner_dim
+\dim_new:N \l_@@_top_corner_dim
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+% \end{variable}
+% \end{variable}
+%
+% \begin{macro}
+% {
+% \coffin_rotate:Nn, \coffin_rotate:cn,
+% \coffin_grotate:Nn, \coffin_grotate:cn
+% }
+% \begin{macro}{\@@_rotate:NnNNN}
+% Rotating a coffin requires several steps which can be conveniently
+% run together. The sine and cosine of the angle in degrees are
+% computed. This is then used to set \cs{l_@@_sin_fp} and
+% \cs{l_@@_cos_fp}, which are carried through unchanged for the rest
+% of the procedure.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_rotate:Nn #1#2
+ { \@@_rotate:NnNNN #1 {#2} \box_rotate:Nn \prop_set_eq:cN \hbox_set:Nn }
+\cs_generate_variant:Nn \coffin_rotate:Nn { c }
+\cs_new_protected:Npn \coffin_grotate:Nn #1#2
+ { \@@_rotate:NnNNN #1 {#2} \box_grotate:Nn \prop_gset_eq:cN \hbox_gset:Nn }
+\cs_generate_variant:Nn \coffin_grotate:Nn { c }
+\cs_new_protected:Npn \@@_rotate:NnNNN #1#2#3#4#5
+ {
+ \fp_set:Nn \l_@@_sin_fp { sind ( #2 ) }
+ \fp_set:Nn \l_@@_cos_fp { cosd ( #2 ) }
+% \end{macrocode}
+% Use a local copy of the property lists to avoid needing to pass the
+% name and scope around.
+% \begin{macrocode}
+ \prop_set_eq:Nc \l_@@_corners_prop
+ { coffin ~ \@@_to_value:N #1 ~ corners }
+ \prop_set_eq:Nc \l_@@_poles_prop
+ { coffin ~ \@@_to_value:N #1 ~ poles }
+% \end{macrocode}
+% The corners and poles of the coffin can now be rotated around the
+% origin. This is best achieved using mapping functions.
+% \begin{macrocode}
+ \prop_map_inline:Nn \l_@@_corners_prop
+ { \@@_rotate_corner:Nnnn #1 {##1} ##2 }
+ \prop_map_inline:Nn \l_@@_poles_prop
+ { \@@_rotate_pole:Nnnnnn #1 {##1} ##2 }
+% \end{macrocode}
+% The bounding box of the coffin needs to be rotated, and to do this
+% the corners have to be found first. They are then rotated in the same
+% way as the corners of the coffin material itself.
+% \begin{macrocode}
+ \@@_set_bounding:N #1
+ \prop_map_inline:Nn \l_@@_bounding_prop
+ { \@@_rotate_bounding:nnn {##1} ##2 }
+% \end{macrocode}
+% At this stage, there needs to be a calculation to find where the
+% corners of the content and the box itself will end up.
+% \begin{macrocode}
+ \@@_find_corner_maxima:N #1
+ \@@_find_bounding_shift:
+ #3 #1 {#2}
+% \end{macrocode}
+% The correction of the box position itself takes place here. The idea
+% is that the bounding box for a coffin is tight up to the content, and
+% has the reference point at the bottom-left. The $x$-direction is
+% handled by moving the content by the difference in the positions of
+% the bounding box and the content left edge. The $y$-direction is
+% dealt with by moving the box down by any depth it has acquired. The
+% internal box is used here to allow for the next step.
+% \begin{macrocode}
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \__kernel_kern:n
+ { \l_@@_bounding_shift_dim - \l_@@_left_corner_dim }
+ \box_move_down:nn { \l_@@_bottom_corner_dim }
+ { \box_use:N #1 }
+ }
+% \end{macrocode}
+% If there have been any previous rotations then the size of the
+% bounding box will be bigger than the contents. This can be corrected
+% easily by setting the size of the box to the height and width of the
+% content. As this operation requires setting box dimensions and these
+% transcend grouping, the safe way to do this is to use the internal box
+% and to reset the result into the target box.
+% \begin{macrocode}
+ \box_set_ht:Nn \l_@@_internal_box
+ { \l_@@_top_corner_dim - \l_@@_bottom_corner_dim }
+ \box_set_dp:Nn \l_@@_internal_box { 0pt }
+ \box_set_wd:Nn \l_@@_internal_box
+ { \l_@@_right_corner_dim - \l_@@_left_corner_dim }
+ #5 #1 { \box_use_drop:N \l_@@_internal_box }
+% \end{macrocode}
+% The final task is to move the poles and corners such that they are
+% back in alignment with the box reference point.
+% \begin{macrocode}
+ \prop_map_inline:Nn \l_@@_corners_prop
+ { \@@_shift_corner:Nnnn #1 {##1} ##2 }
+ \prop_map_inline:Nn \l_@@_poles_prop
+ { \@@_shift_pole:Nnnnnn #1 {##1} ##2 }
+% \end{macrocode}
+% Update the coffin data.
+% \begin{macrocode}
+ #4 { coffin ~ \@@_to_value:N #1 ~ corners }
+ \l_@@_corners_prop
+ #4 { coffin ~ \@@_to_value:N #1 ~ poles }
+ \l_@@_poles_prop
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_set_bounding:N}
+% The bounding box corners for a coffin are easy enough to find: this
+% is the same code as for the corners of the material itself, but
+% using a dedicated property list.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_set_bounding:N #1
+ {
+ \prop_put:Nne \l_@@_bounding_prop { tl }
+ { { 0pt } { \dim_eval:n { \box_ht:N #1 } } }
+ \prop_put:Nne \l_@@_bounding_prop { tr }
+ {
+ { \dim_eval:n { \box_wd:N #1 } }
+ { \dim_eval:n { \box_ht:N #1 } }
+ }
+ \dim_set:Nn \l_@@_internal_dim { -\box_dp:N #1 }
+ \prop_put:Nne \l_@@_bounding_prop { bl }
+ { { 0pt } { \dim_use:N \l_@@_internal_dim } }
+ \prop_put:Nne \l_@@_bounding_prop { br }
+ {
+ { \dim_eval:n { \box_wd:N #1 } }
+ { \dim_use:N \l_@@_internal_dim }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_rotate_bounding:nnn}
+% \begin{macro}{\@@_rotate_corner:Nnnn}
+% Rotating the position of the corner of the coffin is just a case
+% of treating this as a vector from the reference point. The same
+% treatment is used for the corners of the material itself and the
+% bounding box.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_rotate_bounding:nnn #1#2#3
+ {
+ \@@_rotate_vector:nnNN {#2} {#3} \l_@@_x_dim \l_@@_y_dim
+ \prop_put:Nne \l_@@_bounding_prop {#1}
+ { { \dim_use:N \l_@@_x_dim } { \dim_use:N \l_@@_y_dim } }
+ }
+\cs_new_protected:Npn \@@_rotate_corner:Nnnn #1#2#3#4
+ {
+ \@@_rotate_vector:nnNN {#3} {#4} \l_@@_x_dim \l_@@_y_dim
+ \prop_put:Nne \l_@@_corners_prop {#2}
+ { { \dim_use:N \l_@@_x_dim } { \dim_use:N \l_@@_y_dim } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_rotate_pole:Nnnnnn}
+% Rotating a single pole simply means shifting the co-ordinate of
+% the pole and its direction. The rotation here is about the bottom-left
+% corner of the coffin.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_rotate_pole:Nnnnnn #1#2#3#4#5#6
+ {
+ \@@_rotate_vector:nnNN {#3} {#4} \l_@@_x_dim \l_@@_y_dim
+ \@@_rotate_vector:nnNN {#5} {#6}
+ \l_@@_x_prime_dim \l_@@_y_prime_dim
+ \prop_put:Nne \l_@@_poles_prop {#2}
+ {
+ { \dim_use:N \l_@@_x_dim } { \dim_use:N \l_@@_y_dim }
+ { \dim_use:N \l_@@_x_prime_dim }
+ { \dim_use:N \l_@@_y_prime_dim }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_rotate_vector:nnNN}
+% A rotation function, which needs only an input vector (as dimensions)
+% and an output space. The values \cs{l_@@_cos_fp} and
+% \cs{l_@@_sin_fp} should previously have been set up correctly.
+% Working this way means that the floating point work is kept to a
+% minimum: for any given rotation the sin and cosine values do no
+% change, after all.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_rotate_vector:nnNN #1#2#3#4
+ {
+ \dim_set:Nn #3
+ {
+ \fp_to_dim:n
+ {
+ \dim_to_fp:n {#1} * \l_@@_cos_fp
+ - \dim_to_fp:n {#2} * \l_@@_sin_fp
+ }
+ }
+ \dim_set:Nn #4
+ {
+ \fp_to_dim:n
+ {
+ \dim_to_fp:n {#1} * \l_@@_sin_fp
+ + \dim_to_fp:n {#2} * \l_@@_cos_fp
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_find_corner_maxima:N}
+% \begin{macro}{\@@_find_corner_maxima_aux:nn}
+% The idea here is to find the extremities of the content of the
+% coffin. This is done by looking for the smallest values for the bottom
+% and left corners, and the largest values for the top and right
+% corners. The values start at the maximum dimensions so that the
+% case where all are positive or all are negative works out correctly.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_find_corner_maxima:N #1
+ {
+ \dim_set:Nn \l_@@_top_corner_dim { -\c_max_dim }
+ \dim_set:Nn \l_@@_right_corner_dim { -\c_max_dim }
+ \dim_set:Nn \l_@@_bottom_corner_dim { \c_max_dim }
+ \dim_set:Nn \l_@@_left_corner_dim { \c_max_dim }
+ \prop_map_inline:Nn \l_@@_corners_prop
+ { \@@_find_corner_maxima_aux:nn ##2 }
+ }
+\cs_new_protected:Npn \@@_find_corner_maxima_aux:nn #1#2
+ {
+ \dim_set:Nn \l_@@_left_corner_dim
+ { \dim_min:nn { \l_@@_left_corner_dim } {#1} }
+ \dim_set:Nn \l_@@_right_corner_dim
+ { \dim_max:nn { \l_@@_right_corner_dim } {#1} }
+ \dim_set:Nn \l_@@_bottom_corner_dim
+ { \dim_min:nn { \l_@@_bottom_corner_dim } {#2} }
+ \dim_set:Nn \l_@@_top_corner_dim
+ { \dim_max:nn { \l_@@_top_corner_dim } {#2} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_find_bounding_shift:}
+% \begin{macro}{\@@_find_bounding_shift_aux:nn}
+% The approach to finding the shift for the bounding box is similar to
+% that for the corners. However, there is only one value needed here and
+% a fixed input property list, so things are a bit clearer.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_find_bounding_shift:
+ {
+ \dim_set:Nn \l_@@_bounding_shift_dim { \c_max_dim }
+ \prop_map_inline:Nn \l_@@_bounding_prop
+ { \@@_find_bounding_shift_aux:nn ##2 }
+ }
+\cs_new_protected:Npn \@@_find_bounding_shift_aux:nn #1#2
+ {
+ \dim_set:Nn \l_@@_bounding_shift_dim
+ { \dim_min:nn { \l_@@_bounding_shift_dim } {#1} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_shift_corner:Nnnn}
+% \begin{macro}{\@@_shift_pole:Nnnnnn}
+% Shifting the corners and poles of a coffin means subtracting the
+% appropriate values from the $x$- and $y$-components. For
+% the poles, this means that the direction vector is unchanged.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_shift_corner:Nnnn #1#2#3#4
+ {
+ \prop_put:Nne \l_@@_corners_prop {#2}
+ {
+ { \dim_eval:n { #3 - \l_@@_left_corner_dim } }
+ { \dim_eval:n { #4 - \l_@@_bottom_corner_dim } }
+ }
+ }
+\cs_new_protected:Npn \@@_shift_pole:Nnnnnn #1#2#3#4#5#6
+ {
+ \prop_put:Nne \l_@@_poles_prop {#2}
+ {
+ { \dim_eval:n { #3 - \l_@@_left_corner_dim } }
+ { \dim_eval:n { #4 - \l_@@_bottom_corner_dim } }
+ {#5} {#6}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_scale_x_fp}
+% \begin{variable}{\l_@@_scale_y_fp}
+% Storage for the scaling factors in $x$ and $y$, respectively.
+% \begin{macrocode}
+\fp_new:N \l_@@_scale_x_fp
+\fp_new:N \l_@@_scale_y_fp
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_scaled_total_height_dim}
+% \begin{variable}{\l_@@_scaled_width_dim}
+% When scaling, the values given have to be turned into absolute values.
+% \begin{macrocode}
+\dim_new:N \l_@@_scaled_total_height_dim
+\dim_new:N \l_@@_scaled_width_dim
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{macro}
+% {
+% \coffin_resize:Nnn, \coffin_resize:cnn,
+% \coffin_gresize:Nnn, \coffin_gresize:cnn
+% }
+% \begin{macro}{\@@_resize:NnnNN}
+% Resizing a coffin begins by setting up the user-friendly names for
+% the dimensions of the coffin box. The new sizes are then turned into
+% scale factor. This is the same operation as takes place for the
+% underlying box, but that operation is grouped and so the same
+% calculation is done here.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_resize:Nnn #1#2#3
+ {
+ \@@_resize:NnnNN #1 {#2} {#3}
+ \box_resize_to_wd_and_ht_plus_dp:Nnn
+ \prop_set_eq:cN
+ }
+\cs_generate_variant:Nn \coffin_resize:Nnn { c }
+\cs_new_protected:Npn \coffin_gresize:Nnn #1#2#3
+ {
+ \@@_resize:NnnNN #1 {#2} {#3}
+ \box_gresize_to_wd_and_ht_plus_dp:Nnn
+ \prop_gset_eq:cN
+ }
+\cs_generate_variant:Nn \coffin_gresize:Nnn { c }
+\cs_new_protected:Npn \@@_resize:NnnNN #1#2#3#4#5
+ {
+ \fp_set:Nn \l_@@_scale_x_fp
+ { \dim_to_fp:n {#2} / \dim_to_fp:n { \coffin_wd:N #1 } }
+ \fp_set:Nn \l_@@_scale_y_fp
+ {
+ \dim_to_fp:n {#3}
+ / \dim_to_fp:n { \coffin_ht:N #1 + \coffin_dp:N #1 }
+ }
+ #4 #1 {#2} {#3}
+ \@@_resize_common:NnnN #1 {#2} {#3} #5
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_resize_common:NnnN}
+% The poles and corners of the coffin are scaled to the appropriate
+% places before actually resizing the underlying box.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_resize_common:NnnN #1#2#3#4
+ {
+ \prop_set_eq:Nc \l_@@_corners_prop
+ { coffin ~ \@@_to_value:N #1 ~ corners }
+ \prop_set_eq:Nc \l_@@_poles_prop
+ { coffin ~ \@@_to_value:N #1 ~ poles }
+ \prop_map_inline:Nn \l_@@_corners_prop
+ { \@@_scale_corner:Nnnn #1 {##1} ##2 }
+ \prop_map_inline:Nn \l_@@_poles_prop
+ { \@@_scale_pole:Nnnnnn #1 {##1} ##2 }
+% \end{macrocode}
+% Negative $x$-scaling values place the poles in the wrong
+% location: this is corrected here.
+% \begin{macrocode}
+ \fp_compare:nNnT \l_@@_scale_x_fp < \c_zero_fp
+ {
+ \prop_map_inline:Nn \l_@@_corners_prop
+ { \@@_x_shift_corner:Nnnn #1 {##1} ##2 }
+ \prop_map_inline:Nn \l_@@_poles_prop
+ { \@@_x_shift_pole:Nnnnnn #1 {##1} ##2 }
+ }
+ #4 { coffin ~ \@@_to_value:N #1 ~ corners }
+ \l_@@_corners_prop
+ #4 { coffin ~ \@@_to_value:N #1 ~ poles }
+ \l_@@_poles_prop
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \coffin_scale:Nnn, \coffin_scale:cnn,
+% \coffin_gscale:Nnn, \coffin_gscale:cnn
+% }
+% \begin{macro}{\@@_scale:NnnNN}
+% For scaling, the opposite calculation is done to find the new
+% dimensions for the coffin. Only the total height is needed, as this
+% is the shift required for corners and poles. The scaling is done
+% the \TeX{} way as this works properly with floating point values
+% without needing to use the \texttt{fp} module.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_scale:Nnn #1#2#3
+ { \@@_scale:NnnNN #1 {#2} {#3} \box_scale:Nnn \prop_set_eq:cN }
+\cs_generate_variant:Nn \coffin_scale:Nnn { c }
+\cs_new_protected:Npn \coffin_gscale:Nnn #1#2#3
+ { \@@_scale:NnnNN #1 {#2} {#3} \box_gscale:Nnn \prop_gset_eq:cN }
+\cs_generate_variant:Nn \coffin_gscale:Nnn { c }
+\cs_new_protected:Npn \@@_scale:NnnNN #1#2#3#4#5
+ {
+ \fp_set:Nn \l_@@_scale_x_fp {#2}
+ \fp_set:Nn \l_@@_scale_y_fp {#3}
+ #4 #1 { \l_@@_scale_x_fp } { \l_@@_scale_y_fp }
+ \dim_set:Nn \l_@@_internal_dim
+ { \coffin_ht:N #1 + \coffin_dp:N #1 }
+ \dim_set:Nn \l_@@_scaled_total_height_dim
+ { \fp_abs:n { \l_@@_scale_y_fp } \l_@@_internal_dim }
+ \dim_set:Nn \l_@@_scaled_width_dim
+ { -\fp_abs:n { \l_@@_scale_x_fp } \coffin_wd:N #1 }
+ \@@_resize_common:NnnN #1
+ { \l_@@_scaled_width_dim } { \l_@@_scaled_total_height_dim }
+ #5
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_scale_vector:nnNN}
+% This functions scales a vector from the origin using the pre-set scale
+% factors in $x$ and $y$. This is a much less complex operation
+% than rotation, and as a result the code is a lot clearer.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_scale_vector:nnNN #1#2#3#4
+ {
+ \dim_set:Nn #3
+ { \fp_to_dim:n { \dim_to_fp:n {#1} * \l_@@_scale_x_fp } }
+ \dim_set:Nn #4
+ { \fp_to_dim:n { \dim_to_fp:n {#2} * \l_@@_scale_y_fp } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_scale_corner:Nnnn}
+% \begin{macro}{\@@_scale_pole:Nnnnnn}
+% Scaling both corners and poles is a simple calculation using the
+% preceding vector scaling.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_scale_corner:Nnnn #1#2#3#4
+ {
+ \@@_scale_vector:nnNN {#3} {#4} \l_@@_x_dim \l_@@_y_dim
+ \prop_put:Nne \l_@@_corners_prop {#2}
+ { { \dim_use:N \l_@@_x_dim } { \dim_use:N \l_@@_y_dim } }
+ }
+\cs_new_protected:Npn \@@_scale_pole:Nnnnnn #1#2#3#4#5#6
+ {
+ \@@_scale_vector:nnNN {#3} {#4} \l_@@_x_dim \l_@@_y_dim
+ \prop_put:Nne \l_@@_poles_prop {#2}
+ {
+ { \dim_use:N \l_@@_x_dim } { \dim_use:N \l_@@_y_dim }
+ {#5} {#6}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_x_shift_corner:Nnnn}
+% \begin{macro}{\@@_x_shift_pole:Nnnnnn}
+% These functions correct for the $x$ displacement that takes
+% place with a negative horizontal scaling.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_x_shift_corner:Nnnn #1#2#3#4
+ {
+ \prop_put:Nne \l_@@_corners_prop {#2}
+ {
+ { \dim_eval:n { #3 + \box_wd:N #1 } } {#4}
+ }
+ }
+\cs_new_protected:Npn \@@_x_shift_pole:Nnnnnn #1#2#3#4#5#6
+ {
+ \prop_put:Nne \l_@@_poles_prop {#2}
+ {
+ { \dim_eval:n { #3 + \box_wd:N #1 } } {#4}
+ {#5} {#6}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Aligning and typesetting of coffins}
+%
+% \begin{macro}
+% {
+% \coffin_join:NnnNnnnn, \coffin_join:cnnNnnnn,
+% \coffin_join:Nnncnnnn, \coffin_join:cnncnnnn,
+% \coffin_gjoin:NnnNnnnn, \coffin_gjoin:cnnNnnnn,
+% \coffin_gjoin:Nnncnnnn, \coffin_gjoin:cnncnnnn
+% }
+% \begin{macro}{\@@_join:NnnNnnnnN}
+% This command joins two coffins, using a horizontal and vertical pole
+% from each coffin and making an offset between the two. The result
+% is stored as the as a third coffin, which has all of its handles
+% reset to standard values. First, the more basic alignment function is
+% used to get things started.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_join:NnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \@@_join:NnnNnnnnN #1 {#2} {#3} #4 {#5} {#6} {#7} {#8}
+ \coffin_set_eq:NN
+ }
+\cs_generate_variant:Nn \coffin_join:NnnNnnnn { c , Nnnc , cnnc }
+\cs_new_protected:Npn \coffin_gjoin:NnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \@@_join:NnnNnnnnN #1 {#2} {#3} #4 {#5} {#6} {#7} {#8}
+ \coffin_gset_eq:NN
+ }
+\cs_generate_variant:Nn \coffin_gjoin:NnnNnnnn { c , Nnnc , cnnc }
+\cs_new_protected:Npn \@@_join:NnnNnnnnN #1#2#3#4#5#6#7#8#9
+ {
+ \@@_align:NnnNnnnnN
+ #1 {#2} {#3} #4 {#5} {#6} {#7} {#8} \l_@@_aligned_coffin
+% \end{macrocode}
+% Correct the placement of the reference point. If the $x$-offset
+% is negative then the reference point of the second box is to the left
+% of that of the first, which is corrected using a kern. On the right
+% side the first box might stick out, which would show up if it is wider
+% than the sum of the $x$-offset and the width of the second box.
+% So a second kern may be needed.
+% \begin{macrocode}
+ \hbox_set:Nn \l_@@_aligned_coffin
+ {
+ \dim_compare:nNnT { \l_@@_offset_x_dim } < \c_zero_dim
+ { \__kernel_kern:n { -\l_@@_offset_x_dim } }
+ \hbox_unpack:N \l_@@_aligned_coffin
+ \dim_set:Nn \l_@@_internal_dim
+ { \l_@@_offset_x_dim - \box_wd:N #1 + \box_wd:N #4 }
+ \dim_compare:nNnT \l_@@_internal_dim < \c_zero_dim
+ { \__kernel_kern:n { -\l_@@_internal_dim } }
+ }
+% \end{macrocode}
+% The coffin structure is reset, and the corners are cleared: only
+% those from the two parent coffins are needed.
+% \begin{macrocode}
+ \@@_reset_structure:N \l_@@_aligned_coffin
+ \prop_clear:c
+ {
+ coffin ~ \@@_to_value:N \l_@@_aligned_coffin
+ \c_space_tl corners
+ }
+ \@@_update_poles:N \l_@@_aligned_coffin
+% \end{macrocode}
+% The structures of the parent coffins are now transferred to the new
+% coffin, which requires that the appropriate offsets are applied. That
+% then depends on whether any shift was needed.
+% \begin{macrocode}
+ \dim_compare:nNnTF \l_@@_offset_x_dim < \c_zero_dim
+ {
+ \@@_offset_poles:Nnn #1 { -\l_@@_offset_x_dim } { 0pt }
+ \@@_offset_poles:Nnn #4 { 0pt } { \l_@@_offset_y_dim }
+ \@@_offset_corners:Nnn #1 { -\l_@@_offset_x_dim } { 0pt }
+ \@@_offset_corners:Nnn #4 { 0pt } { \l_@@_offset_y_dim }
+ }
+ {
+ \@@_offset_poles:Nnn #1 { 0pt } { 0pt }
+ \@@_offset_poles:Nnn #4
+ { \l_@@_offset_x_dim } { \l_@@_offset_y_dim }
+ \@@_offset_corners:Nnn #1 { 0pt } { 0pt }
+ \@@_offset_corners:Nnn #4
+ { \l_@@_offset_x_dim } { \l_@@_offset_y_dim }
+ }
+ \@@_update_vertical_poles:NNN #1 #4 \l_@@_aligned_coffin
+ #9 #1 \l_@@_aligned_coffin
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \coffin_attach:NnnNnnnn, \coffin_attach:cnnNnnnn,
+% \coffin_attach:Nnncnnnn, \coffin_attach:cnncnnnn,
+% \coffin_gattach:NnnNnnnn, \coffin_gattach:cnnNnnnn,
+% \coffin_gattach:Nnncnnnn, \coffin_gattach:cnncnnnn
+% }
+% \begin{macro}{\@@_attach:NnnNnnnnN}
+% \begin{macro}{\@@_attach_mark:NnnNnnnn}
+% A more simple version of the above, as it simply uses the size of the
+% first coffin for the new one. This means that the work here is rather
+% simplified compared to the above code. The function used when marking
+% a position is hear also as it is similar but without the structure
+% updates.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_attach:NnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \@@_attach:NnnNnnnnN #1 {#2} {#3} #4 {#5} {#6} {#7} {#8}
+ \coffin_set_eq:NN
+ }
+\cs_generate_variant:Nn \coffin_attach:NnnNnnnn { c , Nnnc , cnnc }
+\cs_new_protected:Npn \coffin_gattach:NnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \@@_attach:NnnNnnnnN #1 {#2} {#3} #4 {#5} {#6} {#7} {#8}
+ \coffin_gset_eq:NN
+ }
+\cs_generate_variant:Nn \coffin_gattach:NnnNnnnn { c , Nnnc , cnnc }
+\cs_new_protected:Npn \@@_attach:NnnNnnnnN #1#2#3#4#5#6#7#8#9
+ {
+ \@@_align:NnnNnnnnN
+ #1 {#2} {#3} #4 {#5} {#6} {#7} {#8} \l_@@_aligned_coffin
+ \box_set_ht:Nn \l_@@_aligned_coffin { \box_ht:N #1 }
+ \box_set_dp:Nn \l_@@_aligned_coffin { \box_dp:N #1 }
+ \box_set_wd:Nn \l_@@_aligned_coffin { \box_wd:N #1 }
+ \@@_reset_structure:N \l_@@_aligned_coffin
+ \prop_set_eq:cc
+ {
+ coffin ~ \@@_to_value:N \l_@@_aligned_coffin
+ \c_space_tl corners
+ }
+ { coffin ~ \@@_to_value:N #1 ~ corners }
+ \@@_update_poles:N \l_@@_aligned_coffin
+ \@@_offset_poles:Nnn #1 { 0pt } { 0pt }
+ \@@_offset_poles:Nnn #4
+ { \l_@@_offset_x_dim } { \l_@@_offset_y_dim }
+ \@@_update_vertical_poles:NNN #1 #4 \l_@@_aligned_coffin
+ #9 #1 \l_@@_aligned_coffin
+ }
+\cs_new_protected:Npn \@@_attach_mark:NnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \@@_align:NnnNnnnnN
+ #1 {#2} {#3} #4 {#5} {#6} {#7} {#8} \l_@@_aligned_coffin
+ \box_set_ht:Nn \l_@@_aligned_coffin { \box_ht:N #1 }
+ \box_set_dp:Nn \l_@@_aligned_coffin { \box_dp:N #1 }
+ \box_set_wd:Nn \l_@@_aligned_coffin { \box_wd:N #1 }
+ \box_set_eq:NN #1 \l_@@_aligned_coffin
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_align:NnnNnnnnN}
+% The internal function aligns the two coffins into a third one, but
+% performs no corrections on the resulting coffin poles. The process
+% begins by finding the points of intersection for the poles for each
+% of the input coffins. Those for the first coffin are worked out after
+% those for the second coffin, as this allows the `primed'
+% storage area to be used for the second coffin. The `real' box
+% offsets are then calculated, before using these to re-box the
+% input coffins. The default poles are then set up, but the final result
+% depends on how the bounding box is being handled.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_align:NnnNnnnnN #1#2#3#4#5#6#7#8#9
+ {
+ \@@_calculate_intersection:Nnn #4 {#5} {#6}
+ \dim_set:Nn \l_@@_x_prime_dim { \l_@@_x_dim }
+ \dim_set:Nn \l_@@_y_prime_dim { \l_@@_y_dim }
+ \@@_calculate_intersection:Nnn #1 {#2} {#3}
+ \dim_set:Nn \l_@@_offset_x_dim
+ { \l_@@_x_dim - \l_@@_x_prime_dim + #7 }
+ \dim_set:Nn \l_@@_offset_y_dim
+ { \l_@@_y_dim - \l_@@_y_prime_dim + #8 }
+ \hbox_set:Nn \l_@@_aligned_internal_coffin
+ {
+ \box_use:N #1
+ \__kernel_kern:n { -\box_wd:N #1 }
+ \__kernel_kern:n { \l_@@_offset_x_dim }
+ \box_move_up:nn { \l_@@_offset_y_dim } { \box_use:N #4 }
+ }
+ \coffin_set_eq:NN #9 \l_@@_aligned_internal_coffin
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_offset_poles:Nnn}
+% \begin{macro}{\@@_offset_pole:Nnnnnnn}
+% Transferring structures from one coffin to another requires that the
+% positions are updated by the offset between the two coffins. This is
+% done by mapping over the property list of the source coffins, moving
+% as appropriate and saving to the new coffin data structures. The
+% test for a |-| means that the structures from the parent coffins
+% are uniquely labelled and do not depend on the order of alignment.
+% The pay off for this is that |-| should not be used in coffin pole
+% or handle names, and that multiple alignments do not result in a
+% whole set of values.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_offset_poles:Nnn #1#2#3
+ {
+ \prop_map_inline:cn { coffin ~ \@@_to_value:N #1 ~ poles }
+ { \@@_offset_pole:Nnnnnnn #1 {##1} ##2 {#2} {#3} }
+ }
+\cs_new_protected:Npn \@@_offset_pole:Nnnnnnn #1#2#3#4#5#6#7#8
+ {
+ \dim_set:Nn \l_@@_x_dim { #3 + #7 }
+ \dim_set:Nn \l_@@_y_dim { #4 + #8 }
+ \tl_if_in:nnTF {#2} { - }
+ { \tl_set:Nn \l_@@_internal_tl { {#2} } }
+ { \tl_set:Nn \l_@@_internal_tl { { #1 - #2 } } }
+ \exp_last_unbraced:NNo \@@_set_pole:Nnn \l_@@_aligned_coffin
+ { \l_@@_internal_tl }
+ {
+ { \dim_use:N \l_@@_x_dim } { \dim_use:N \l_@@_y_dim }
+ {#5} {#6}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_offset_corners:Nnn}
+% \begin{macro}{\@@_offset_corner:Nnnnn}
+% Saving the offset corners of a coffin is very similar, except that
+% there is no need to worry about naming: every corner can be saved
+% here as order is unimportant.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_offset_corners:Nnn #1#2#3
+ {
+ \prop_map_inline:cn { coffin ~ \@@_to_value:N #1 ~ corners }
+ { \@@_offset_corner:Nnnnn #1 {##1} ##2 {#2} {#3} }
+ }
+\cs_new_protected:Npn \@@_offset_corner:Nnnnn #1#2#3#4#5#6
+ {
+ \prop_put:cne
+ {
+ coffin ~ \@@_to_value:N \l_@@_aligned_coffin
+ \c_space_tl corners
+ }
+ { #1 - #2 }
+ {
+ { \dim_eval:n { #3 + #5 } }
+ { \dim_eval:n { #4 + #6 } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_update_vertical_poles:NNN}
+% \begin{macro}{\@@_update_T:nnnnnnnnN}
+% \begin{macro}{\@@_update_B:nnnnnnnnN}
+% The \texttt{T} and \texttt{B} poles need to be recalculated
+% after alignment. These functions find the larger absolute value for
+% the poles, but this is of course only logical when the poles are
+% horizontal.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_update_vertical_poles:NNN #1#2#3
+ {
+ \@@_get_pole:NnN #3 { #1 -T } \l_@@_pole_a_tl
+ \@@_get_pole:NnN #3 { #2 -T } \l_@@_pole_b_tl
+ \exp_last_two_unbraced:Noo \@@_update_T:nnnnnnnnN
+ \l_@@_pole_a_tl \l_@@_pole_b_tl #3
+ \@@_get_pole:NnN #3 { #1 -B } \l_@@_pole_a_tl
+ \@@_get_pole:NnN #3 { #2 -B } \l_@@_pole_b_tl
+ \exp_last_two_unbraced:Noo \@@_update_B:nnnnnnnnN
+ \l_@@_pole_a_tl \l_@@_pole_b_tl #3
+ }
+\cs_new_protected:Npn \@@_update_T:nnnnnnnnN #1#2#3#4#5#6#7#8#9
+ {
+ \dim_compare:nNnTF {#2} < {#6}
+ {
+ \@@_set_pole:Nnn #9 { T }
+ { { 0pt } {#6} { 1000pt } { 0pt } }
+ }
+ {
+ \@@_set_pole:Nnn #9 { T }
+ { { 0pt } {#2} { 1000pt } { 0pt } }
+ }
+ }
+\cs_new_protected:Npn \@@_update_B:nnnnnnnnN #1#2#3#4#5#6#7#8#9
+ {
+ \dim_compare:nNnTF {#2} < {#6}
+ {
+ \@@_set_pole:Nnn #9 { B }
+ { { 0pt } {#2} { 1000pt } { 0pt } }
+ }
+ {
+ \@@_set_pole:Nnn #9 { B }
+ { { 0pt } {#6} { 1000pt } { 0pt } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\c_@@_empty_coffin}
+% An empty-but-horizontal coffin.
+% \begin{macrocode}
+\coffin_new:N \c_@@_empty_coffin
+\tex_setbox:D \c_@@_empty_coffin = \tex_hbox:D { }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\coffin_typeset:Nnnnn, \coffin_typeset:cnnnn}
+% Typesetting a coffin means aligning it with the current position,
+% which is done using a coffin with no content at all. As well as aligning to
+% the empty coffin, there is also a need to leave vertical mode, if necessary.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_typeset:Nnnnn #1#2#3#4#5
+ {
+ \mode_leave_vertical:
+ \@@_align:NnnNnnnnN \c_@@_empty_coffin { H } { l }
+ #1 {#2} {#3} {#4} {#5} \l_@@_aligned_coffin
+ \box_use_drop:N \l_@@_aligned_coffin
+ }
+\cs_generate_variant:Nn \coffin_typeset:Nnnnn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Coffin diagnostics}
+%
+% \begin{variable}{\l_@@_display_coffin}
+% \begin{variable}{\l_@@_display_coord_coffin}
+% \begin{variable}{\l_@@_display_pole_coffin}
+% Used for printing coffins with data structures attached.
+% \begin{macrocode}
+\coffin_new:N \l_@@_display_coffin
+\coffin_new:N \l_@@_display_coord_coffin
+\coffin_new:N \l_@@_display_pole_coffin
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_display_handles_prop}
+% This property list is used to print coffin handles at suitable
+% positions. The offsets are expressed as multiples of the basic offset
+% value, which therefore acts as a scale-factor.
+% \begin{macrocode}
+\prop_new:N \l_@@_display_handles_prop
+\prop_put:Nnn \l_@@_display_handles_prop { tl }
+ { { b } { r } { -1 } { 1 } }
+\prop_put:Nnn \l_@@_display_handles_prop { thc }
+ { { b } { hc } { 0 } { 1 } }
+\prop_put:Nnn \l_@@_display_handles_prop { tr }
+ { { b } { l } { 1 } { 1 } }
+\prop_put:Nnn \l_@@_display_handles_prop { vcl }
+ { { vc } { r } { -1 } { 0 } }
+\prop_put:Nnn \l_@@_display_handles_prop { vchc }
+ { { vc } { hc } { 0 } { 0 } }
+\prop_put:Nnn \l_@@_display_handles_prop { vcr }
+ { { vc } { l } { 1 } { 0 } }
+\prop_put:Nnn \l_@@_display_handles_prop { bl }
+ { { t } { r } { -1 } { -1 } }
+\prop_put:Nnn \l_@@_display_handles_prop { bhc }
+ { { t } { hc } { 0 } { -1 } }
+\prop_put:Nnn \l_@@_display_handles_prop { br }
+ { { t } { l } { 1 } { -1 } }
+\prop_put:Nnn \l_@@_display_handles_prop { Tl }
+ { { t } { r } { -1 } { -1 } }
+\prop_put:Nnn \l_@@_display_handles_prop { Thc }
+ { { t } { hc } { 0 } { -1 } }
+\prop_put:Nnn \l_@@_display_handles_prop { Tr }
+ { { t } { l } { 1 } { -1 } }
+\prop_put:Nnn \l_@@_display_handles_prop { Hl }
+ { { vc } { r } { -1 } { 1 } }
+\prop_put:Nnn \l_@@_display_handles_prop { Hhc }
+ { { vc } { hc } { 0 } { 1 } }
+\prop_put:Nnn \l_@@_display_handles_prop { Hr }
+ { { vc } { l } { 1 } { 1 } }
+\prop_put:Nnn \l_@@_display_handles_prop { Bl }
+ { { b } { r } { -1 } { -1 } }
+\prop_put:Nnn \l_@@_display_handles_prop { Bhc }
+ { { b } { hc } { 0 } { -1 } }
+\prop_put:Nnn \l_@@_display_handles_prop { Br }
+ { { b } { l } { 1 } { -1 } }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_display_offset_dim}
+% The standard offset for the label from the handle position when
+% displaying handles.
+% \begin{macrocode}
+\dim_new:N \l_@@_display_offset_dim
+\dim_set:Nn \l_@@_display_offset_dim { 2pt }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_display_x_dim}
+% \begin{variable}{\l_@@_display_y_dim}
+% As the intersections of poles have to be calculated to find which
+% ones to print, there is a need to avoid repetition. This is done
+% by saving the intersection into two dedicated values.
+% \begin{macrocode}
+\dim_new:N \l_@@_display_x_dim
+\dim_new:N \l_@@_display_y_dim
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_display_poles_prop}
+% A property list for printing poles: various things need to be deleted
+% from this to get a \enquote{nice} output.
+% \begin{macrocode}
+\prop_new:N \l_@@_display_poles_prop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_display_font_tl}
+% Stores the settings used to print coffin data: this keeps things
+% flexible.
+% \begin{macrocode}
+\tl_new:N \l_@@_display_font_tl
+\bool_lazy_and:nnT
+ { \cs_if_exist_p:N \fmtname }
+ { \str_if_eq_p:Vn \fmtname { LaTeX2e } }
+ {
+ \tl_set:Nn \l_@@_display_font_tl
+ { \sffamily \tiny }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_rule:nn}
+% Abstract out creation of rules here until there is a higher-level interface.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_rule:nn #1#2
+ {
+ \mode_leave_vertical:
+ \hbox:n { \tex_vrule:D width #1 height #2 \scan_stop: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\coffin_mark_handle:Nnnn, \coffin_mark_handle:cnnn}
+% \begin{macro}{\@@_mark_handle_aux:nnnnNnn}
+% Marking a single handle is relatively easy. The standard attachment
+% function is used, meaning that there are two calculations for the
+% location. However, this is likely to be okay given the load expected.
+% Contrast with the more optimised version for showing all handles which
+% comes next.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_mark_handle:Nnnn #1#2#3#4
+ {
+ \hcoffin_set:Nn \l_@@_display_pole_coffin
+ {
+ \color_select:n {#4}
+ \@@_rule:nn { 1pt } { 1pt }
+ }
+ \@@_attach_mark:NnnNnnnn #1 {#2} {#3}
+ \l_@@_display_pole_coffin { hc } { vc } { 0pt } { 0pt }
+ \hcoffin_set:Nn \l_@@_display_coord_coffin
+ {
+ \color_select:n {#4}
+ \l_@@_display_font_tl
+ ( \tl_to_str:n { #2 , #3 } )
+ }
+ \prop_get:NnN \l_@@_display_handles_prop
+ { #2 #3 } \l_@@_internal_tl
+ \quark_if_no_value:NTF \l_@@_internal_tl
+ {
+ \prop_get:NnN \l_@@_display_handles_prop
+ { #3 #2 } \l_@@_internal_tl
+ \quark_if_no_value:NTF \l_@@_internal_tl
+ {
+ \@@_attach_mark:NnnNnnnn #1 {#2} {#3}
+ \l_@@_display_coord_coffin { l } { vc }
+ { 1pt } { 0pt }
+ }
+ {
+ \exp_last_unbraced:No \@@_mark_handle_aux:nnnnNnn
+ \l_@@_internal_tl #1 {#2} {#3}
+ }
+ }
+ {
+ \exp_last_unbraced:No \@@_mark_handle_aux:nnnnNnn
+ \l_@@_internal_tl #1 {#2} {#3}
+ }
+ }
+\cs_new_protected:Npn \@@_mark_handle_aux:nnnnNnn #1#2#3#4#5#6#7
+ {
+ \@@_attach_mark:NnnNnnnn #5 {#6} {#7}
+ \l_@@_display_coord_coffin {#1} {#2}
+ { #3 \l_@@_display_offset_dim }
+ { #4 \l_@@_display_offset_dim }
+ }
+\cs_generate_variant:Nn \coffin_mark_handle:Nnnn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\coffin_display_handles:Nn, \coffin_display_handles:cn}
+% \begin{macro}{\@@_display_handles_aux:nnnnnn}
+% \begin{macro}{\@@_display_handles_aux:nnnn}
+% \begin{macro}{\@@_display_attach:Nnnnn}
+% Printing the poles starts by removing any duplicates, for which the
+% \texttt{H} poles is used as the definitive version for the baseline
+% and bottom. Two loops are then used to find the combinations of
+% handles for all of these poles. This is done such that poles are
+% removed during the loops to avoid duplication.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_display_handles:Nn #1#2
+ {
+ \hcoffin_set:Nn \l_@@_display_pole_coffin
+ {
+ \color_select:n {#2}
+ \@@_rule:nn { 1pt } { 1pt }
+ }
+ \prop_set_eq:Nc \l_@@_display_poles_prop
+ { coffin ~ \@@_to_value:N #1 ~ poles }
+ \@@_get_pole:NnN #1 { H } \l_@@_pole_a_tl
+ \@@_get_pole:NnN #1 { T } \l_@@_pole_b_tl
+ \tl_if_eq:NNT \l_@@_pole_a_tl \l_@@_pole_b_tl
+ { \prop_remove:Nn \l_@@_display_poles_prop { T } }
+ \@@_get_pole:NnN #1 { B } \l_@@_pole_b_tl
+ \tl_if_eq:NNT \l_@@_pole_a_tl \l_@@_pole_b_tl
+ { \prop_remove:Nn \l_@@_display_poles_prop { B } }
+ \coffin_set_eq:NN \l_@@_display_coffin #1
+ \prop_map_inline:Nn \l_@@_display_poles_prop
+ {
+ \prop_remove:Nn \l_@@_display_poles_prop {##1}
+ \@@_display_handles_aux:nnnnnn {##1} ##2 {#2}
+ }
+ \box_use_drop:N \l_@@_display_coffin
+ }
+% \end{macrocode}
+% For each pole there is a check for an intersection, which here does
+% not give an error if none is found. The successful values are stored
+% and used to align the pole coffin with the main coffin for output.
+% The positions are recovered from the preset list if available.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_display_handles_aux:nnnnnn #1#2#3#4#5#6
+ {
+ \prop_map_inline:Nn \l_@@_display_poles_prop
+ {
+ \bool_set_false:N \l_@@_error_bool
+ \@@_calculate_intersection:nnnnnnnn {#2} {#3} {#4} {#5} ##2
+ \bool_if:NF \l_@@_error_bool
+ {
+ \dim_set:Nn \l_@@_display_x_dim { \l_@@_x_dim }
+ \dim_set:Nn \l_@@_display_y_dim { \l_@@_y_dim }
+ \@@_display_attach:Nnnnn
+ \l_@@_display_pole_coffin { hc } { vc }
+ { 0pt } { 0pt }
+ \hcoffin_set:Nn \l_@@_display_coord_coffin
+ {
+ \color_select:n {#6}
+ \l_@@_display_font_tl
+ ( \tl_to_str:n { #1 , ##1 } )
+ }
+ \prop_get:NnN \l_@@_display_handles_prop
+ { #1 ##1 } \l_@@_internal_tl
+ \quark_if_no_value:NTF \l_@@_internal_tl
+ {
+ \prop_get:NnN \l_@@_display_handles_prop
+ { ##1 #1 } \l_@@_internal_tl
+ \quark_if_no_value:NTF \l_@@_internal_tl
+ {
+ \@@_display_attach:Nnnnn
+ \l_@@_display_coord_coffin { l } { vc }
+ { 1pt } { 0pt }
+ }
+ {
+ \exp_last_unbraced:No
+ \@@_display_handles_aux:nnnn
+ \l_@@_internal_tl
+ }
+ }
+ {
+ \exp_last_unbraced:No \@@_display_handles_aux:nnnn
+ \l_@@_internal_tl
+ }
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_display_handles_aux:nnnn #1#2#3#4
+ {
+ \@@_display_attach:Nnnnn
+ \l_@@_display_coord_coffin {#1} {#2}
+ { #3 \l_@@_display_offset_dim }
+ { #4 \l_@@_display_offset_dim }
+ }
+\cs_generate_variant:Nn \coffin_display_handles:Nn { c }
+% \end{macrocode}
+% This is a dedicated version of \cs{coffin_attach:NnnNnnnn} with
+% a hard-wired first coffin. As the intersection is already known
+% and stored for the display coffin the code simply uses it directly,
+% with no calculation.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_display_attach:Nnnnn #1#2#3#4#5
+ {
+ \@@_calculate_intersection:Nnn #1 {#2} {#3}
+ \dim_set:Nn \l_@@_x_prime_dim { \l_@@_x_dim }
+ \dim_set:Nn \l_@@_y_prime_dim { \l_@@_y_dim }
+ \dim_set:Nn \l_@@_offset_x_dim
+ { \l_@@_display_x_dim - \l_@@_x_prime_dim + #4 }
+ \dim_set:Nn \l_@@_offset_y_dim
+ { \l_@@_display_y_dim - \l_@@_y_prime_dim + #5 }
+ \hbox_set:Nn \l_@@_aligned_coffin
+ {
+ \box_use:N \l_@@_display_coffin
+ \__kernel_kern:n { -\box_wd:N \l_@@_display_coffin }
+ \__kernel_kern:n { \l_@@_offset_x_dim }
+ \box_move_up:nn { \l_@@_offset_y_dim } { \box_use:N #1 }
+ }
+ \box_set_ht:Nn \l_@@_aligned_coffin
+ { \box_ht:N \l_@@_display_coffin }
+ \box_set_dp:Nn \l_@@_aligned_coffin
+ { \box_dp:N \l_@@_display_coffin }
+ \box_set_wd:Nn \l_@@_aligned_coffin
+ { \box_wd:N \l_@@_display_coffin }
+ \box_set_eq:NN \l_@@_display_coffin \l_@@_aligned_coffin
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \coffin_show_structure:N, \coffin_show_structure:c,
+% \coffin_log_structure:N, \coffin_log_structure:c,
+% \@@_show_structure:NN
+% }
+% For showing the various internal structures attached to a coffin in
+% a way that keeps things relatively readable. If there is no apparent
+% structure then the code complains.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_show_structure:N
+ { \@@_show_structure:NN \msg_show:nneeee }
+\cs_generate_variant:Nn \coffin_show_structure:N { c }
+\cs_new_protected:Npn \coffin_log_structure:N
+ { \@@_show_structure:NN \msg_log:nneeee }
+\cs_generate_variant:Nn \coffin_log_structure:N { c }
+\cs_new_protected:Npn \@@_show_structure:NN #1#2
+ {
+ \@@_if_exist:NT #2
+ {
+ #1 { coffin } { show }
+ { \token_to_str:N #2 }
+ {
+ \iow_newline: >~ ht ~=~ \dim_eval:n { \coffin_ht:N #2 }
+ \iow_newline: >~ dp ~=~ \dim_eval:n { \coffin_dp:N #2 }
+ \iow_newline: >~ wd ~=~ \dim_eval:n { \coffin_wd:N #2 }
+ }
+ {
+ \prop_map_function:cN
+ { coffin ~ \@@_to_value:N #2 ~ poles }
+ \msg_show_item_unbraced:nn
+ }
+ { }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \coffin_show:N, \coffin_show:c, \coffin_log:N, \coffin_log:c,
+% \coffin_show:Nnn, \coffin_show:cnn, \coffin_log:Nnn, \coffin_log:cnn,
+% \@@_show:NNNnn
+% }
+% Essentially a combination of \cs{coffin_show_structure:N} and
+% \cs{box_show:Nnn}, but we need to avoid having two prompts, so we
+% use \cs{msg_term:nneeee} instead of
+% \cs{msg_show:nneeee} in the |show| case.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_show:N #1
+ { \coffin_show:Nnn #1 \c_max_int \c_max_int }
+\cs_generate_variant:Nn \coffin_show:N { c }
+\cs_new_protected:Npn \coffin_log:N #1
+ { \coffin_log:Nnn #1 \c_max_int \c_max_int }
+\cs_generate_variant:Nn \coffin_log:N { c }
+\cs_new_protected:Npn \coffin_show:Nnn
+ { \@@_show:NNNnn \msg_term:nneeee \box_show:Nnn }
+\cs_generate_variant:Nn \coffin_show:Nnn { c }
+\cs_new_protected:Npn \coffin_log:Nnn
+ { \@@_show:NNNnn \msg_log:nneeee \box_show:Nnn }
+\cs_generate_variant:Nn \coffin_log:Nnn { c }
+\cs_new_protected:Npn \@@_show:NNNnn #1#2#3#4#5
+ {
+ \@@_if_exist:NT #3
+ {
+ \@@_show_structure:NN #1 #3
+ #2 #3 {#4} {#5}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+% \begin{macrocode}
+\msg_new:nnnn { coffin } { no-pole-intersection }
+ { No~intersection~between~coffin~poles. }
+ {
+ LaTeX~was~asked~to~find~the~intersection~between~two~poles,~
+ but~they~do~not~have~a~unique~meeting~point:~
+ the~value~(0pt,~0pt)~will~be~used.
+ }
+\msg_new:nnnn { coffin } { unknown }
+ { Unknown~coffin~'#1'. }
+ { The~coffin~'#1'~was~never~defined. }
+\msg_new:nnnn { coffin } { unknown-pole }
+ { Pole~'#1'~unknown~for~coffin~'#2'. }
+ {
+ LaTeX~was~asked~to~find~a~typesetting~pole~for~a~coffin,~
+ but~either~the~coffin~does~not~exist~or~the~pole~name~is~wrong.
+ }
+\msg_new:nnn { coffin } { show }
+ {
+ Size~of~coffin~#1 : #2 \\
+ Poles~of~coffin~#1 : #3 .
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3color.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3color.dtx
new file mode 100644
index 00000000000..0edc225c526
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3color.dtx
@@ -0,0 +1,3246 @@
+% \iffalse meta-comment
+%
+%% File: l3color.dtx
+%
+% Copyright (C) 2017-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% http://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3color} module\\ Color support^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{Color in boxes}
+%
+% Controlling the color of text in boxes requires a small number of control
+% functions, so that the boxed material uses the color at the point where
+% it is set, rather than where it is used.
+%
+% \begin{function}[added = 2011-09-03]{\color_group_begin:, \color_group_end:}
+% \begin{syntax}
+% \cs{color_group_begin:}
+% \ldots
+% \cs{color_group_end:}
+% \end{syntax}
+% Creates a color group: one used to \enquote{trap} color settings.
+% This grouping is built in to for example \cs{hbox_set:Nn}.
+% \end{function}
+%
+% \begin{function}[added = 2011-09-03]{\color_ensure_current:}
+% \begin{syntax}
+% \cs{color_ensure_current:}
+% \end{syntax}
+% Ensures that material inside a box uses the foreground color
+% at the point where the box is set, rather than that in force when the
+% box is used. This function should usually be used within a
+% \cs{color_group_begin:} \ldots \cs{color_group_end:} group.
+% \end{function}
+%
+% \section{Color models}
+%
+% A color \emph{model} is a way to represent sets of colors. Different models
+% are particularly suitable for different output methods, \emph{e.g.}~screen
+% or print. Parameter-based models can describe a very large number of unique
+% colors, and have a varying number of \emph{axes} which define a color
+% space. In contrast, various proprietary models are available which define
+% \emph{spot} colors (more formally separations).
+%
+% Core models are used to pass color information to output; these are
+% \enquote{native} to \pkg{l3color}. Core models use real numbers in the range
+% $[0,1]$ to represent values. The core models supported here are
+% \begin{itemize}
+% \item \texttt{gray} Grayscale color, with a single axis running from
+% $0$ (fully black) to $1$ (fully white)
+% \item \texttt{rgb} Red-green-blue color, with three axes, one for each of
+% the components
+% \item \texttt{cmyk} Cyan-magenta-yellow-black color, with four axes, one for
+% each of the components
+% \end{itemize}
+%
+% There are also interface models: these are convenient for users but have
+% to be manipulated before storing/passing to the backend. Interface models
+% are primarily integer-based: see below for more detail. The supported
+% interface models are
+% \begin{itemize}
+% \item \texttt{Gray} Grayscale color, with a single axis running from
+% $0$ (fully black) to $15$ (fully white)
+% \item \texttt{hsb} Hue-saturation-brightness color, with three axes,all
+% real values in the range $[0,1]$ for hue saturation and brightness
+% \item \texttt{Hsb} Hue-saturation-brightness color, with three axes, integer
+% in the range $[0,360]$ for hue, real values in the range $[0,1]$ for
+% saturation and brightness
+% \item \texttt{HSB} Hue-saturation-brightness color, with three axes, integers
+% in the range $[0,240]$ for hue, saturation and brightness
+% \item \texttt{HTML} HTML format representation of RGB color given as a
+% single six-digit hexadecimal number
+% \item \texttt{RGB} Red-green-blue color, with three axes, one for each of
+% the components, values as integers from $0$ to $255$
+% \item \texttt{wave} Light wavelength, a real number in the range
+% $380$ to $780$ (nanometres)
+% \end{itemize}
+% All interface models are internally stored as |rgb|.
+%
+% Finally, there are a small number of models which are parsed to allow
+% data transfer from \pkg{xcolor} but which should not be used by end-users.
+% These are
+% \begin{itemize}
+% \item \texttt{cmy} Cyan-magenta-yellow color with three axes, one for
+% each of the components; converted to |cmyk|
+% \item \texttt{tHsb} \enquote{Tuned} hue-saturation-brightness color with three
+% axes, integer in the range $[0,360]$ for hue, real values in the range
+% $[0,1]$ for saturation and brightness; converted to |rgb| using the
+% standard tuning map defined by \pkg{xcolor}
+% \item \texttt{\&spot} Spot color tint with one value; treated as a gray
+% tint as spot color data is not available for extraction
+% \end{itemize}
+%
+% To allow parsing of data from \pkg{xcolor}, any leading model up the first
+% \texttt{:} will be discarded; the approach of selecting an internal form
+% for data is \emph{not} used in \pkg{l3color}.
+%
+% Additional models may be created to allow mixing of separation colors
+% with each other or with those from other models. See
+% Section~\ref{l3color:sec:new-models} for more detail of color support
+% for additional models.
+%
+% When color is selected by model, the \meta{values} given are specified as
+% a comma-separated list. The length of the list will therefore be determined
+% by the detail of the model involved.
+%
+% Color models (and interconversion) are complex, and more details are given
+% in the manual to the \LaTeXe{} \pkg{xcolor} package and in the
+% \emph{PostScript Language Reference Manual}, published by Addison--Wesley.
+%
+% \section{Color expressions}
+%
+% In addition to allowing specification of color by model and values,
+% \pkg{l3color} also supports color expressions. These are created
+% by combining one or more color names, with the amount of each specified
+% as a value in the range $0$--$100$. The value should be given between
+% |!| symbols in the expression. Thus for example
+% \begin{verbatim}
+% red!50!green
+% \end{verbatim}
+% is a mixture of $50\,\%$ red and $50\,\%$ green. A trailing value is
+% interpreted as implicitly followed by |!white|, and so
+% \begin{verbatim}
+% red!25
+% \end{verbatim}
+% specifies $25\,\%$ red mixed with $75\,\%$ white.
+%
+% Where the models for the mixed colors are different, the model of the first
+% color is used. Thus
+% \begin{verbatim}
+% red!50!cyan
+% \end{verbatim}
+% will result in a color specification using the |rgb| model, made up of
+% $50\,\%$ red and $50\,\%$ of cyan \emph{expressed in \texttt{rgb}}.
+% This may be important as color model interconversion is not exact.
+%
+% The one exception to the above is where the first model in an expression is
+% |gray|. In this case, the order of mixing is \enquote{swapped} internally, so
+% that for example
+% \begin{verbatim}
+% black!50!red
+% \end{verbatim}
+% has the same result as
+% \begin{verbatim}
+% red!50!black
+% \end{verbatim}
+% (the predefined colors |black| and |white| use the |gray| model).
+%
+% Where more than two colors are mixed in an expression, evaluation takes place
+% in a stepwise fashion. Thus in
+% \begin{verbatim}
+% cyan!50!magenta!10!yellow
+% \end{verbatim}
+% the sub-expression
+% \begin{verbatim}
+% cyan!50!magenta
+% \end{verbatim}
+% is first evaluated to give an intermediate color specification, before
+% the second step
+% \begin{verbatim}
+% <intermediate>!10!yellow
+% \end{verbatim}
+% where |<intermediate>| represents this transitory calculated value.
+%
+% Within a color expression, |.| may be used to represent the color active
+% for typesetting (the current color). This allows for example
+% \begin{verbatim}
+% .!50
+% \end{verbatim}
+% to mean a mixture of $50\,\%$ of current color with white.
+%
+% (Color expressions supported here are a subset of those provided by
+% the \LaTeXe{} \pkg{xcolor} package. At present, only such features as are
+% clearly useful have been added here.)
+%
+% \section{Named colors}
+%
+% Color names are stored in a single namespace, which makes them accessible
+% as part of color expressions. Whilst they are not reserved in a technical
+% sense, the names |black|, |white|, |red|, |green|, |blue|, |cyan|, |magenta|
+% and |yellow| have special meaning and should not be redefined. Color names
+% should be made up of letters, numbers and spaces only: other characters are
+% reserved for use in color expressions. In particular, |.| represents the
+% current color at the start of a color expression.
+%
+% \begin{function}{\color_set:nn}
+% \begin{syntax}
+% \cs{color_set:nn} \Arg{name} \Arg{color expression}
+% \end{syntax}
+% Evaluates the \meta{color expression} and stores the resulting
+% color specification as the \meta{name}.
+% \end{function}
+%
+% \begin{function}{\color_set:nnn}
+% \begin{syntax}
+% \cs{color_set:nnn} \Arg{name} \Arg{model(s)} \Arg{value(s)}
+% \end{syntax}
+% Stores the color specification equivalent to the \meta{model(s)} and
+% \meta{values} as the \meta{name}.
+% \end{function}
+%
+% \begin{function}{\color_set_eq:nn}
+% \begin{syntax}
+% \cs{color_set_eq:nn} \Arg{name1} \Arg{name2}
+% \end{syntax}
+% Copies the color specification in \meta{name2} to \meta{name1}. The
+% special name |.| may be used to represent the current color, allowing
+% it to be saved to a name.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added = 2022-08-12]{\color_if_exist:n}
+% \begin{syntax}
+% \cs{color_if_exist_p:n} \Arg{name}
+% \cs{color_if_exist:nTF} \Arg{name} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether \meta{name} is currently defined to provide a color
+% specification.
+% \end{function}
+%
+% \begin{function}[added = 2021-05-11]{\color_show:n, \color_log:n}
+% \begin{syntax}
+% \cs{color_show:n} \Arg{name}
+% \cs{color_log:n} \Arg{name}
+% \end{syntax}
+% Displays the color specification stored in the \meta{name} on the
+% terminal or log file.
+% \end{function}
+%
+% \section{Selecting colors}
+%
+% General selection of color is safe when split across pages: a stack is
+% used to ensure that the correct color is re-selected on the new page.
+%
+% These commands set the current color (|.|): other more specialised functions
+% such as fill and stroke selectors do \emph{not} adjust this value.
+%
+% \begin{function}{\color_select:n}
+% \begin{syntax}
+% \cs{color_select:n} \Arg{color expression}
+% \end{syntax}
+% Parses the \meta{color expression} and then activates the resulting
+% color specification for typeset material.
+% \end{function}
+%
+% \begin{function}{\color_select:nn}
+% \begin{syntax}
+% \cs{color_select:nn} \Arg{model(s)} \Arg{value(s)}
+% \end{syntax}
+% Activates the color specification equivalent to the \meta{model(s)} and
+% \meta{value(s)} for typeset material.
+% \end{function}
+%
+% \begin{variable}{\l_color_fixed_model_tl}
+% When this is set to a non-empty value, colors will be converted to
+% the specified model when they are selected. Note that included images
+% and similar are not influenced by this setting.
+% \end{variable}
+%
+% \section{Colors for fills and strokes}
+%
+% Colors for drawing operations and so forth are split into strokes and fills
+% (the latter may also be referred to as non-stroke color). The fill color is
+% used for text under normal circumstances. Depending on the backend, stroke
+% color may use a \emph{stack}, in which case it exhibits the same page breaking
+% behavior as general color. However, \texttt{dvips}/\texttt{dvisvgm} do not
+% support this, and so color will need to be contained within a scope, such
+% as \cs{draw_begin:}/\cs{draw_end:}.
+%
+% \begin{function}{\color_fill:n, \color_stroke:n}
+% \begin{syntax}
+% \cs{color_fill:n} \Arg{color expression}
+% \end{syntax}
+% Parses the \meta{color expression} and then activates the resulting
+% color specification for filling or stroking.
+% \end{function}
+%
+% \begin{function}{\color_fill:nn, \color_stroke:nn}
+% \begin{syntax}
+% \cs{color_fill:nn} \Arg{model(s)} \Arg{value(s)}
+% \end{syntax}
+% Activates the color specification equivalent to the \meta{model(s)} and
+% \meta{value(s)} for filling or stroking.
+% \end{function}
+%
+% \begin{variable}[module = color]{color.sc}
+% When using \texttt{dvips}, this PostScript variables hold the stroke color.
+% \end{variable}
+%
+% \subsection{Coloring math mode material}
+%
+% Coloring math mode material using \cs[no-index]{color_select:nn(n)} has some restrictions
+% and often leads to spacing issues and/or poor input syntax. Avoiding generating
+% \tn{mathord} atoms whilst coloring only those parts of the input which are
+% required needs careful handling. The functionality here covers this important
+% use case.
+%
+% \begin{function}[added = 2022-01-26]{\color_math:nn, \color_math:nnn}
+% \begin{syntax}
+% \cs{color_math:nn} \Arg{color expression}\Arg{content}
+% \cs{color_math:nnn} \Arg{model(s)} \Arg{value(s)} \Arg{content}
+% \end{syntax}
+% Works as for \cs[no-index]{color_select:n(n)} but applies color only to the math mode
+% \meta{content}. The function does not generate a group and the \meta{content}
+% therefore retains its math atom states. Sub/superscripts are also properly
+% handled.
+% \end{function}
+%
+% \begin{variable}[added = 2022-01-26]{\l_color_math_active_tl}
+% This list controls which tokens are considered as math active and
+% should therefore be replaced by their definition during searching for
+% sub/superscripts.
+% \end{variable}
+%
+% \section{Multiple color models}
+%
+% When selecting or setting a color with an explicit model, it is possible
+% to give values for more than one model at one time. This is particularly
+% useful where automated conversion between models does not give the desired
+% outcome. To do this, the list of models and list of values are both subdivided
+% using |/| characters (as for the similar function in \pkg{xcolor}). For
+% example, to save a color with explicit |cmyk| and |rgb| values, one could
+% use
+% \begin{verbatim}
+% \color_set:nnn { foo } { cmyk / rgb }
+% { 0.1 , 0.2 , 0.3 , 0.4 / 0.1, 0.2 , 0.3 }
+% \end{verbatim}
+% The manually-specified conversion will be used in preference to automated
+% calculation whenever the model(s) listed are used: both in expressions and
+% when a fixed model is active.
+%
+% Similarly, the same syntax can be applied to directly selecting a color.
+% \begin{verbatim}
+% \color_select:nn { cmyk / rgb }
+% { 0.1 , 0.2 , 0.3 , 0.4 / 0.1, 0.2 , 0.3 }
+% \end{verbatim}
+% Again, this list is used when a fixed model is active: the first entry is used
+% unless there is a fixed model matching one of the other entries.
+%
+% \section{Exporting color specifications}
+%
+% The major use of color expressions is in setting typesetting output, but there
+% are other places in which some form of color information is required. These
+% may need data in a different format or using a different model to the internal
+% representation. Thus a set of functions are available to export colors in
+% different formats.
+%
+% Valid export targets are
+% \begin{itemize}
+% \item \texttt{backend} Two brace groups: the first containing the
+% model, the second containing space-separated values appropriate
+% for the model; this is the format required by backend functions
+% of \pkg{expl3}
+% \item \texttt{comma-sep-cmyk} Comma-separated cyan-magenta-yellow-black
+% values
+% \item \texttt{comma-sep-rgb} Comma-separated red-green-blue values
+% suitable for use as a PDF annotation color
+% \item \texttt{HTML} Uppercase two-digit hexadecimal values, expressing
+% a red-green-blue color; the digits are \emph{not} separated
+% \item \texttt{space-sep-cmyk} Space-separated cyan-magenta-yellow-black
+% values
+% \item \texttt{space-sep-rgb} Space-separated red-green-blue values
+% suitable for use as a PDF annotation color
+% \end{itemize}
+%
+% \begin{function}{\color_export:nnN}
+% \begin{syntax}
+% \cs{color_export:nnN} \Arg{color expression} \Arg{format} \Arg{tl}
+% \end{syntax}
+% Parses the \meta{color expression} as described earlier,
+% then converts to the \meta{format} specified and assigns the data to the
+% \meta{tl}.
+% \end{function}
+%
+% \begin{function}{\color_export:nnnN}
+% \begin{syntax}
+% \cs{color_export:nnnN} \Arg{model} \Arg{value(s)} \Arg{format} \Arg{tl}
+% \end{syntax}
+% Expresses the combination of \meta{model} and \meta{value(s)} in an
+% internal representation, then converts to the \meta{format} specified and
+% assigns the data to the \meta{tl}.
+% \end{function}
+%
+% \section{Creating new color models}
+% \label{l3color:sec:new-models}
+%
+% Additional color models are required to support specialist workflows, for
+% example those involving separations (see
+% \url{https://helpx.adobe.com/indesign/using/spot-process-colors.html}
+% for details of the use of separations in print). Color models may be split
+% into families; for the standard device-based color models (\texttt{DeviceCMYK},
+% \texttt{DeviceRGB}, \texttt{DeviceGray}), these are synonymous. This
+% is not generally the case: see the PDF reference for more details. (Note that
+% \pkg{l3color} uses the shorter names \texttt{cmyk}, etc.)
+%
+% \begin{function}{\color_model_new:nnn}
+% \begin{syntax}
+% \cs{color_model_new:nnn} \Arg{model} \Arg{family} \Arg{params}
+% \end{syntax}
+% Creates a new \meta{model} which is derived from the color model \meta{family}.
+% The latter should be one of
+% \begin{itemize}
+% \item \texttt{DeviceN}
+% \item \texttt{ICCBased}
+% \item \texttt{Separation}
+% \end{itemize}
+% (The \meta{family} may be given in mixed case as-in the PDF reference:
+% internally, case of these strings is folded.)
+% Depending on the \meta{family}, one or more \meta{params} are mandatory or
+% optional.
+% \end{function}
+%
+% For a \texttt{Separation} space, there are three \emph{compulsory} keys.
+% \begin{itemize}
+% \item \texttt{name} The name of the Separation, for example the formal
+% name of a spot color ink. Such a \meta{name} may contain spaces, etc.,
+% which are not permitted in the \meta{model}.
+% \item \texttt{alternative-model} An alternative device colorspace, one of
+% \texttt{cmyk}, \texttt{rgb}, \texttt{gray} or \texttt{CIELAB}. The three
+% parameter-based models work as described above; see below for
+% details of CIELAB colors.
+% \item \texttt{alternative-values} A comma-separated list of values
+% appropriate to the \texttt{alternative-model}. This information is used by
+% the PDF application if the \texttt{Separation} is not available.
+% \end{itemize}
+%
+% CIELAB color separations are created using the
+% \texttt{alternative-model = CIELAB} setting. These colors must also have an
+% \texttt{illuminant} key, one of \texttt{a}, \texttt{c}, \texttt{e},
+% \texttt{d50}, \texttt{d55}, \texttt{d65} or \texttt{d75}. The
+% \texttt{alternative-values} in this case are the three parameters $L*$, $a*$
+% and $b*$ of the CIELAB model. Full details of this device-independent color
+% approach are given in the documentation to the \pkg{colorspace} package.
+%
+% CIELAB colors \emph{cannot} be converted into other device-dependent color
+% spaces, and as such, mixing can only occur if colors set up using the CIELAB
+% model are also given with an alternative parameter-based model. If that is
+% not the case, \pkg{l3color} will fallback to using black as the colorant in
+% any mixing.
+%
+% For a \texttt{DeviceN} space, there is one \emph{compulsory} key.
+% \begin{itemize}
+% \item \texttt{names} The names of the components of the \texttt{DeviceN}
+% space. Each should be either the \meta{name} of a \texttt{Separation} model,
+% a process color name (\texttt{cyan}, etc.) or the special name \texttt{none}.
+% \end{itemize}
+%
+% For a \texttt{ICCBased} space, there is one \emph{compulsory} key.
+% \begin{itemize}
+% \item \texttt{file} The name of the file containing the profile.
+% \end{itemize}
+%
+% \subsection{Color profiles}
+%
+% Color profiles are used to ensure color accuracy by linking to collaboration.
+% Applying a profile can be used to standardise color which is otherwise
+% device-dependence.
+%
+% \begin{function}[added = 2021-02-23]{\color_profile_apply:nn}
+% \begin{syntax}
+% \cs{color_profile_apply:nn} \Arg{profile} \Arg{model}
+% \end{syntax}
+% This function applies a \meta{profile} to one of the device \meta{models}.
+% The profile will then apply to all color of the selected \meta{model}. The
+% \meta{profile} should specify an ICC profile file. The \meta{model} has to
+% be one the standard device models: \texttt{cmyk}, \texttt{gray} or
+% \texttt{rgb}.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3color} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=color>
+% \end{macrocode}
+%
+% \subsection{Basics}
+%
+% \begin{variable}
+% {\l_@@_current_tl}
+% The color currently active for foreground (text, \emph{etc.}) material.
+% This is stored in the form of a color model followed by one or more
+% values. There are four pre-defined models, three of which take numerical
+% values in the range $[0,1]$:
+% \begin{itemize}
+% \item \texttt{gray \meta{gray}} Grayscale color with the \meta{gray}
+% value running from $0$ (fully black) to $1$ (fully white)
+% \item \texttt{cmyk \meta{cyan} \meta{magenta} \meta{yellow} \meta{black}}
+% \item \texttt{rgb \meta{red} \meta{green} \meta{blue}}
+% \end{itemize}
+% Notice that the value are separated by spaces. There is a fourth pre-defined
+% model using a string value and a numerical one:
+% \begin{itemize}
+% \item \texttt{spot \meta{name} \meta{tint}} A pre-defined spot color,
+% where the \meta{name} should be a pre-defined string color name and the
+% \meta{tint} should be in the range $[0,1]$.
+% \end{itemize}
+%
+% Additional models may be created to allow mixing of spot colors. The
+% number of data entries these require will depend on the number of
+% colors to be mixed.
+% \begin{texnote}
+% The content of \cs{l_@@_current_tl} comprises two brace groups, the
+% first containing the color model and the second containing the value(s)
+% applicable in that model.
+% \end{texnote}
+% \end{variable}
+%
+% \begin{macro}{\color_group_begin:, \color_group_end:}
+% Grouping for color is the same as using the basic \cs{group_begin:}
+% and \cs{group_end:} functions. However, for semantic reasons, they
+% are renamed here.
+% \begin{macrocode}
+\cs_new_eq:NN \color_group_begin: \group_begin:
+\cs_new_eq:NN \color_group_end: \group_end:
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\color_ensure_current:}
+% A driver-independent wrapper for setting the foreground color to the
+% current color \enquote{now}.
+% \begin{macrocode}
+\cs_new_protected:Npn \color_ensure_current:
+ { \@@_select:N \l_@@_current_tl }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_select:N, \@@_select_math:N}
+% \begin{macro}{\@@_select:nn}
+% Take an internal color specification and pass it to the driver. This code
+% is needed to ensure the current color but will also be used by the
+% higher-level material.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_select:N #1
+ {
+ \exp_after:wN \@@_select:nn #1
+ \group_insert_after:N \@@_backend_reset:
+ }
+\cs_new_protected:Npn \@@_select_math:N #1
+ { \exp_after:wN \@@_select:nn #1 }
+\cs_new_protected:Npn \@@_select:nn #1#2
+ { \use:c { @@_backend_select_ #1 :n } {#2} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_current_tl}
+% The current color, with the model and
+% \begin{macrocode}
+\tl_new:N \l_@@_current_tl
+\tl_set:Nn \l_@@_current_tl { { gray } { 0 } }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Predefined color names}
+%
+% The ability to predefine colors with a name is a key part of this module and
+% means there has to be a method for storing the results. At first sight, it
+% seems natural to follow the usual \pkg{expl3} model and create a
+% \texttt{color} variable type for the process. That would then allow both
+% local and global colors, constant colors and the like. However, these names
+% need to be accessible in some form at the user level, for selection of colors
+% either simply by name or as part of a more complex expression. This does not
+% require that the full name is exposed but does require that they can be
+% looked up in a predictable way. As such, it is more useful to expose just the
+% color names as part of the interface, with the result that only local color
+% names can be created. (This is also seen for example in key creation in
+% \pkg{l3keys}.) As a result, color names are declarative (no \texttt{new}
+% functions).
+%
+% Since there is no need to manipulate colors \emph{en masse}, each is stored
+% in a two-part structure: a \texttt{prop} for the colors themselves, and a
+% \texttt{tl} for the default model for each color.
+%
+% \subsection{Setup}
+%
+% \begin{variable}{\l_@@_internal_int, \l_@@_internal_tl}
+% \begin{macrocode}
+\int_new:N \l_@@_internal_int
+\tl_new:N \l_@@_internal_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\s_@@_mark}
+% Internal scan marks. \cs{s_@@_stop} is already defined in \pkg{l3color-base}.
+% \begin{macrocode}
+\scan_new:N \s_@@_mark
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_ignore_error_bool}
+% Used to avoid issuing multiple errors if there is a change-of-model with
+% input container an error.
+% \begin{macrocode}
+\bool_new:N \l_@@_ignore_error_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Utility functions}
+%
+% \begin{macro}[pTF, EXP]{\color_if_exist:n}
+% A simple wrapper to avoid needing to have the lookup repeated in too many
+% places.To guard against a color created in a group, we need to test for
+% entries in the |prop|.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \color_if_exist:n #1 { p , T, F, TF }
+ {
+ \prop_if_exist:cTF { l_@@_named_ #1 _prop }
+ {
+ \prop_if_empty:cTF { l_@@_named_ #1 _prop }
+ \prg_return_false:
+ \prg_return_true:
+ }
+ \prg_return_false:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_model:N, \@@_values:N}
+% Simple abstractions.
+% \begin{macrocode}
+\cs_new:Npn \@@_model:N #1 { \exp_after:wN \use_i:nn #1 }
+\cs_new:Npn \@@_values:N #1 { \exp_after:wN \use_ii:nn #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_extract:nNN, \@@_extract:VNN}
+% Recover the values for the standard model for a color.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_extract:nNN #1#2#3
+ {
+ \tl_set_eq:Nc #2 { l_@@_named_ #1 _tl }
+ \prop_get:cVN { l_@@_named_ #1 _prop } #2 #3
+ }
+\cs_generate_variant:Nn \@@_extract:nNN { V }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Model conversion}
+%
+% \begin{macro}{\@@_convert:nnN, \@@_convert:VVN}
+% \begin{macro}{\@@_convert:nnnN, \@@_convert:nVnN, \@@_convert:nnVN}
+% \begin{macro}[EXP]
+% {
+% \@@_convert_gray_gray:w
+% \@@_convert_gray_rgb:w
+% \@@_convert_gray_cmyk:w
+% \@@_convert_cmyk_gray:w
+% \@@_convert_cmyk_rgb:w
+% \@@_convert_cmyk_cmyk:w
+% \@@_convert_rgb_gray:w
+% \@@_convert_rgb_rgb:w
+% \@@_convert_rgb_cmyk:w
+% }
+% \begin{macro}[EXP]{\@@_convert_rgb_cmyk:nnn}
+% \begin{macro}[EXP]{\@@_convert_rgb_cmyk:nnnn}
+% Model conversion is carried out using standard formulae for base models,
+% as described in the manual for \pkg{xcolor} (see also the \emph{PostScript
+% Language Reference Manual}). For other models direct conversion might not
+% be defined, so we go through the fallback models if necessary.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_convert:nnN #1#2#3
+ { \@@_convert:nnVN {#1} {#2} #3 #3 }
+\cs_generate_variant:Nn \@@_convert:nnN { VV }
+\cs_generate_variant:Nn \exp_last_unbraced:Nf { c }
+\cs_new_protected:Npn \@@_convert:nnnN #1#2#3#4
+ {
+ \tl_set:Ne #4
+ {
+ \cs_if_exist_use:cTF { @@_convert_ #1 _ #2 :w }
+ { #3 \s_@@_stop }
+ {
+ \cs_if_exist:cTF { @@_convert_ \use:c { c_@@_fallback_ #1 _tl } _ #2 :w }
+ {
+ \exp_last_unbraced:cf
+ { @@_convert_ \use:c { c_@@_fallback_ #1 _tl } _ #2 :w }
+ { \use:c { @@_convert_ #1 _ \use:c { c_@@_fallback_ #1 _tl } :w } #3 \s_@@_stop }
+ \s_@@_stop
+ }
+ {
+ \exp_last_unbraced:cf
+ { @@_convert_ \use:c { c_@@_fallback_ #2 _tl } _ #2 :w }
+ {
+ \cs_if_exist_use:cTF { @@_convert_ #1 _ \use:c { c_@@_fallback_ #2 _tl } :w }
+ { #3 \s_@@_stop }
+ {
+ \exp_last_unbraced:cf
+ { @@_convert_ \use:c { c_@@_fallback_ #1 _tl } _ \use:c { c_@@_fallback_ #2 _tl } :w }
+ { \use:c { @@_convert_ #1 _ \use:c { c_@@_fallback_ #1 _tl } :w } #3 \s_@@_stop }
+ \s_@@_stop
+ }
+ }
+ \s_@@_stop
+ }
+ }
+ }
+ }
+\cs_generate_variant:Nn \@@_convert:nnnN { nV , nnV }
+\cs_new:Npn \@@_convert_gray_gray:w #1 \s_@@_stop
+ { #1 }
+\cs_new:Npn \@@_convert_gray_rgb:w #1 \s_@@_stop
+ { #1 ~ #1 ~ #1 }
+\cs_new:Npn \@@_convert_gray_cmyk:w #1 \s_@@_stop
+ { 0 ~ 0 ~ 0 ~ \fp_eval:n { 1 - #1 } }
+% \end{macrocode}
+% These rather odd values are based on \textsc{ntsc} television: the set are
+% used for the |cmyk| conversion.
+% \begin{macrocode}
+\cs_new:Npn \@@_convert_rgb_gray:w #1 ~ #2 ~ #3 \s_@@_stop
+ { \fp_eval:n { 0.3 * #1 + 0.59 * #2 + 0.11 * #3 } }
+\cs_new:Npn \@@_convert_rgb_rgb:w #1 \s_@@_stop
+ { #1 }
+% \end{macrocode}
+% The conversion from |rgb| to |cmyk| is the most complex: a two-step
+% procedure which requires \emph{black generation} and \emph{undercolor
+% removal} functions. The PostScript reference describes them as
+% device-dependent, but following \pkg{xcolor} we assume they are linear.
+% Moreover, as the likelihood of anyone using a non-unitary matrix here is
+% tiny, we simplify and treat those two concepts as no-ops. To allow code
+% sharing with parsing of |cmy| values, we have an intermediate function
+% here (\cs{@@_convert_rgb_cmyk:nnn}) which actually takes |cmy| values
+% as input.
+% \begin{macrocode}
+\cs_new:Npn \@@_convert_rgb_cmyk:w #1 ~ #2 ~ #3 \s_@@_stop
+ {
+ \exp_args:Neee \@@_convert_rgb_cmyk:nnn
+ { \fp_eval:n { 1 - #1 } }
+ { \fp_eval:n { 1 - #2 } }
+ { \fp_eval:n { 1 - #3 } }
+ }
+\cs_new:Npn \@@_convert_rgb_cmyk:nnn #1#2#3
+ {
+ \exp_args:Ne \@@_convert_rgb_cmyk:nnnn
+ { \fp_eval:n { min( #1, #2 , #3 ) } } {#1} {#2} {#3}
+ }
+\cs_new:Npn \@@_convert_rgb_cmyk:nnnn #1#2#3#4
+ {
+ \fp_eval:n { min ( 1 , max ( 0 , #2 - #1 ) ) } \c_space_tl
+ \fp_eval:n { min ( 1 , max ( 0 , #3 - #1 ) ) } \c_space_tl
+ \fp_eval:n { min ( 1 , max ( 0 , #4 - #1 ) ) } \c_space_tl
+ #1
+ }
+\cs_new:Npn \@@_convert_cmyk_gray:w #1 ~ #2 ~ #3 ~ #4 \s_@@_stop
+ { \fp_eval:n { 1 - min ( 1 , 0.3 * #1 + 0.59 * #2 + 0.11 * #3 + #4 ) } }
+\cs_new:Npn \@@_convert_cmyk_rgb:w #1 ~ #2 ~ #3 ~ #4 \s_@@_stop
+ {
+ \fp_eval:n { 1 - min ( 1 , #1 + #4 ) } \c_space_tl
+ \fp_eval:n { 1 - min ( 1 , #2 + #4 ) } \c_space_tl
+ \fp_eval:n { 1 - min ( 1 , #3 + #4 ) }
+ }
+\cs_new:Npn \@@_convert_cmyk_cmyk:w #1 \s_@@_stop
+ { #1 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Color expressions}
+%
+% \begin{variable}
+% {\l_@@_model_tl, \l_@@_value_tl, \l_@@_next_model_tl, \l_@@_next_value_tl}
+% Working space to store the color data whilst doing calculations: keeping
+% it on the stack is attractive but gets tricky (return is non-trivial).
+% \begin{macrocode}
+\tl_new:N \l_@@_model_tl
+\tl_new:N \l_@@_value_tl
+\tl_new:N \l_@@_next_model_tl
+\tl_new:N \l_@@_next_value_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_parse:nN}
+% \begin{macro}{\@@_parse_aux:nN}
+% \begin{macro}{\@@_parse_eq:Nn}
+% \begin{macro}{\@@_parse_eq:nNn}
+% \begin{macro}{\@@_parse:Nw}
+% \begin{macro}{\@@_parse_loop_init:Nnn}
+% \begin{macro}{\@@_parse_loop:w}
+% \begin{macro}{\@@_parse_loop_check:nn}
+% \begin{macro}{\@@_parse_loop:nn}
+% \begin{macro}{\@@_parse_gray:n, \@@_parse_std:n}
+% \begin{macro}{\@@_parse_break:w}
+% \begin{macro}{\@@_parse_end:}
+% \begin{macro}[EXP]{\@@_parse_mix:Nnnn, \@@_parse_mix:NVVn}
+% \begin{macro}[EXP]{\@@_parse_mix:nNnn}
+% \begin{macro}[EXP]
+% {
+% \@@_parse_mix_gray:nw ,
+% \@@_parse_mix_rgb:nw ,
+% \@@_parse_mix_cmyk:nw
+% }
+% The main function for parsing color expressions removes actives but
+% otherwise expands, then starts working through the expression itself.
+% At the end, we apply the payload.
+% \begin{macrocode}
+\cs_new_protected:Npe \@@_parse:nN #1#2
+ {
+ \tl_set:Ne \exp_not:c { l_@@_named_ . _tl }
+ { \exp_not:N \@@_model:N \exp_not:N \l_@@_current_tl }
+ \prop_put:NVe \exp_not:c { l_@@_named_ . _prop }
+ \exp_not:c { l_@@_named_ . _tl }
+ { \exp_not:N \@@_values:N \exp_not:N \l_@@_current_tl }
+ \exp_not:N \exp_args:Ne \exp_not:N \@@_parse_aux:nN
+ { \exp_not:N \tl_to_str:n {#1} } #2
+ }
+% \end{macrocode}
+% Before going to all of the effort of parsing an expression, these two
+% precursor functions look for a pre-defined name, either on its own or
+% with a trailing |!| (which is the same thing).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_parse_aux:nN #1#2
+ {
+ \color_if_exist:nTF {#1}
+ { \@@_parse_set_eq:Nn #2 {#1} }
+ { \@@_parse:Nw #2#1 ! \s_@@_stop }
+ \@@_check_model:N #2
+ }
+\cs_new_protected:Npn \@@_parse_set_eq:Nn #1#2
+ {
+ \tl_if_empty:NTF \l_color_fixed_model_tl
+ { \exp_args:Nv \@@_parse_set_eq:nNn { l_@@_named_ #2 _tl } }
+ { \exp_args:NV \@@_parse_set_eq:nNn \l_color_fixed_model_tl }
+ #1 {#2}
+ }
+% \end{macrocode}
+% Here, we have to allow for the case where there is a fixed model:
+% that can't be swept up by generic conversion as we are dealing with a
+% named color.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_parse_set_eq:nNn #1#2#3
+ {
+ \prop_get:cnNTF
+ { l_@@_named_ #3 _prop } {#1}
+ \l_@@_value_tl
+ { \tl_set:Ne #2 { {#1} { \l_@@_value_tl } } }
+ {
+ \tl_set_eq:Nc \l_@@_model_tl { l_@@_named_ #3 _tl }
+ \prop_get:cVN { l_@@_named_ #3 _prop } \l_@@_model_tl
+ \l_@@_value_tl
+ \@@_convert:nnN
+ \l_@@_model_tl {#1} \l_@@_value_tl
+ \tl_set:Ne #2
+ {
+ {#1}
+ { \l_@@_value_tl }
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_parse:Nw #1#2 ! #3 \s_@@_stop
+ {
+ \color_if_exist:nTF {#2}
+ {
+ \tl_if_blank:nTF {#3}
+ { \@@_parse_set_eq:Nn #1 {#2} }
+ { \@@_parse_loop_init:Nnn #1 {#2} {#3} }
+ }
+ {
+ \msg_error:nnn { color } { unknown-color } {#2}
+ \tl_set:Nn \l_@@_current_tl { { gray } { 0 } }
+ }
+ }
+% \end{macrocode}
+% Once we establish that a full parse is needed, the next job is to get the
+% detail of the first color. That will determine the model we use for the
+% calculation: splitting here makes checking that a bit easier.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_parse_loop_init:Nnn #1#2#3
+ {
+ \group_begin:
+ \@@_extract:nNN {#2} \l_@@_model_tl \l_@@_value_tl
+ \@@_parse_loop:w #3 ! ! ! ! \s_@@_stop
+ \tl_set:Ne \l_@@_internal_tl
+ { { \l_@@_model_tl } { \l_@@_value_tl } }
+ \exp_args:NNNV \group_end:
+ \tl_set:Nn #1 \l_@@_internal_tl
+ }
+% \end{macrocode}
+% This is the loop proper: there can be an open-ended set of colors to parse,
+% separated by |!| tokens. There are a few cases to look out for. At the end
+% of the expression and with we find a mix of $100$ then we simply skip the
+% next color entirely (we can't stop the loop as there might be a further
+% valid color to mix in). On the other hand, if we get a mix of $0$ then
+% drop everything so far and start again. There is also a trailing
+% |white| to \enquote{read in} if the final explicit data is a mix.
+% Those conditions are separate from actually looping, which is therefore
+% sorted out by checking if we have further data to process: in contrast
+% to \pkg{xcolor}, we don't allow |!!| so the test can be simplified.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_parse_loop:w #1 ! #2 ! #3 ! #4 ! #5 \s_@@_stop
+ {
+ \tl_if_blank:nF {#1}
+ {
+ \bool_lazy_and:nnTF
+ { \fp_compare_p:nNn {#1} > { 0 } }
+ { \fp_compare_p:nNn {#1} < { 100 } }
+ {
+ \use:e
+ {
+ \@@_parse_loop:nn {#1}
+ { \tl_if_blank:nTF {#2} { white } {#2} }
+ }
+ }
+ { \@@_parse_loop_check:nn {#1} {#2} }
+ }
+ \tl_if_blank:nF {#3}
+ { \@@_parse_loop:w #3 ! #4 ! #5 \s_@@_stop }
+ \@@_parse_end:
+ }
+% \end{macrocode}
+% As these are unusual cases, we accept slower performance here for clearer
+% code: check for the error conditions, handle the boundary cases after
+% that.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_parse_loop_check:nn #1#2
+ {
+ \bool_if:NF \l_@@_ignore_error_bool
+ {
+ \bool_lazy_or:nnT
+ { \fp_compare_p:nNn {#1} < { 0 } }
+ { \fp_compare_p:nNn {#1} > { 100 } }
+ { \msg_error:nnnnn { color } { out-of-range } {#1} { 0 } { 100 } }
+ }
+ \fp_compare:nNnF {#1} > \c_zero_fp
+ {
+ \tl_if_blank:nTF {#2}
+ { \@@_extract:nNN { white } }
+ { \@@_extract:nNN {#2} }
+ \l_@@_model_tl \l_@@_value_tl
+ }
+ }
+% \end{macrocode}
+% The \enquote{payload} of calculation in the loop first. If the model for
+% the upcoming color is different from that of the existing (partial) color,
+% convert the model. For |gray| the two are flipped round so that the outcome
+% is something with \enquote{real} color. We are then in a position to do the
+% actual calculation itself. The two auxiliaries here give us a way to break
+% the loop should an invalid name be found.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_parse_loop:nn #1#2
+ {
+ \color_if_exist:nTF {#2}
+ {
+ \@@_extract:nNN {#2} \l_@@_next_model_tl \l_@@_next_value_tl
+ \tl_if_eq:NNF \l_@@_model_tl \l_@@_next_model_tl
+ {
+ \str_if_eq:VnTF \l_@@_model_tl { gray }
+ { \@@_parse_gray:n {#2} }
+ { \@@_parse_std:n {#2} }
+ }
+ \tl_set:Ne \l_@@_value_tl
+ {
+ \@@_parse_mix:NVVn
+ \l_@@_model_tl \l_@@_value_tl \l_@@_next_value_tl {#1}
+ }
+ }
+ {
+ \msg_error:nnn { color } { unknown-color } {#2}
+ \@@_extract:nNN { black } \l_@@_model_tl \l_@@_value_tl
+ \@@_parse_break:w
+ }
+ }
+% \end{macrocode}
+% The \texttt{gray} model needs special handling: the models need to be
+% swapped: we do that using a dedicated function.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_parse_gray:n #1
+ {
+ \tl_set_eq:NN \l_@@_model_tl \l_@@_next_model_tl
+ \tl_set:Nn \l_@@_next_model_tl { gray }
+ \exp_args:NnV \@@_convert:nnN { gray } \l_@@_model_tl
+ \l_@@_value_tl
+ \prop_get:cVN { l_@@_named_ #1 _prop } \l_@@_model_tl
+ \l_@@_next_value_tl
+ }
+\cs_new_protected:Npn \@@_parse_std:n #1
+ {
+ \prop_get:cVNF { l_@@_named_ #1 _prop }
+ \l_@@_model_tl
+ \l_@@_next_value_tl
+ {
+ \@@_convert:VVN
+ \l_@@_next_model_tl
+ \l_@@_model_tl
+ \l_@@_next_value_tl
+ }
+ }
+\cs_new_protected:Npn \@@_parse_break:w #1 \@@_parse_end: { }
+\cs_new_protected:Npn \@@_parse_end: { }
+% \end{macrocode}
+% Do the vector arithmetic: mainly a question of shuffling input, along
+% with one pre-calculation to keep down the use of division.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_mix:Nnnn #1#2#3#4
+ {
+ \exp_args:Nf \@@_parse_mix:nNnn
+ { \fp_eval:n { #4 / 100 } }
+ #1 {#2} {#3}
+ }
+\cs_generate_variant:Nn \@@_parse_mix:Nnnn { NVV }
+\cs_new:Npn \@@_parse_mix:nNnn #1#2#3#4
+ {
+ \use:c { @@_parse_mix_ #2 :nw } {#1}
+ #3 \s_@@_mark #4 \s_@@_stop
+ }
+\cs_new:Npn \@@_parse_mix_gray:nw #1#2 \s_@@_mark #3 \s_@@_stop
+ { \fp_eval:n { #2 * #1 + #3 * ( 1 - #1 ) } }
+\cs_new:Npn \@@_parse_mix_rgb:nw
+ #1#2 ~ #3 ~ #4 \s_@@_mark #5 ~ #6 ~ #7 \s_@@_stop
+ {
+ \fp_eval:n { #2 * #1 + #5 * ( 1 - #1 ) } \c_space_tl
+ \fp_eval:n { #3 * #1 + #6 * ( 1 - #1 ) } \c_space_tl
+ \fp_eval:n { #4 * #1 + #7 * ( 1 - #1 ) }
+ }
+\cs_new:Npn \@@_parse_mix_cmyk:nw
+ #1#2 ~ #3 ~ #4 ~ #5 \s_@@_mark #6 ~ #7 ~ #8 ~ #9 \s_@@_stop
+ {
+ \fp_eval:n { #2 * #1 + #6 * ( 1 - #1 ) } \c_space_tl
+ \fp_eval:n { #3 * #1 + #7 * ( 1 - #1 ) } \c_space_tl
+ \fp_eval:n { #4 * #1 + #8 * ( 1 - #1 ) } \c_space_tl
+ \fp_eval:n { #5 * #1 + #9 * ( 1 - #1 ) }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_model_gray:w, \@@_parse_model_rgb:w,
+% \@@_parse_model_cmyk:w
+% }
+% \begin{macro}[EXP]{\@@_parse_number:n}
+% \begin{macro}[EXP]{\@@_parse_number:w}
+% Turn the input into internal form, also tidying up the number quickly.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_model_gray:w #1 , #2 \s_@@_stop
+ { { gray } { \@@_parse_number:n {#1} } }
+\cs_new:Npn \@@_parse_model_rgb:w #1 , #2 , #3 , #4 \s_@@_stop
+ {
+ { rgb }
+ {
+ \@@_parse_number:n {#1} ~
+ \@@_parse_number:n {#2} ~
+ \@@_parse_number:n {#3}
+ }
+ }
+\cs_new:Npn \@@_parse_model_cmyk:w #1 , #2 , #3 , #4 , #5 \s_@@_stop
+ {
+ { cmyk }
+ {
+ \@@_parse_number:n {#1} ~
+ \@@_parse_number:n {#2} ~
+ \@@_parse_number:n {#3} ~
+ \@@_parse_number:n {#4}
+ }
+ }
+\cs_new:Npn \@@_parse_number:n #1
+ { \@@_parse_number:w #1 . 0 . \s_@@_stop }
+\cs_new:Npn \@@_parse_number:w #1 . #2 . #3 \s_@@_stop
+ { \tl_if_blank:nTF {#1} { 0 } {#1} . #2 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_model_Gray:w, \@@_parse_model_hsb:w,
+% \@@_parse_model_Hsb:w, \@@_parse_model_HSB:w,
+% \@@_parse_model_HTML:w, \@@_parse_model_RGB:w
+% }
+% \begin{macro}[EXP]{\@@_parse_model_hsb:nnn, \@@_parse_model_hsb_aux:nnn}
+% \begin{macro}[EXP]{\@@_parse_model_hsb:nnnn}
+% \begin{macro}[EXP]{\@@_parse_model_hsb:nnnnn}
+% \begin{macro}[EXP]
+% {
+% \@@_parse_model_hsb_0:nnnn ,
+% \@@_parse_model_hsb_1:nnnn ,
+% \@@_parse_model_hsb_2:nnnn ,
+% \@@_parse_model_hsb_3:nnnn ,
+% \@@_parse_model_hsb_4:nnnn ,
+% \@@_parse_model_hsb_5:nnnn
+% }
+% \begin{macro}[EXP]{\@@_parse_model_wave:w}
+% \begin{macro}[EXP]
+% {\@@_parse_model_wave_auxi:nn, \@@_parse_model_wave_auxii:nn}
+% \begin{macro}[EXP]{\@@_parse_model_wave_rho:n}
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_model_Gray:w #1 , #2 \s_@@_stop
+ { { gray } { \fp_eval:n { #1 / 15 } } }
+\cs_new:Npn \@@_parse_model_hsb:w #1 , #2 , #3 , #4 \s_@@_stop
+ { \@@_parse_model_hsb:nnn {#1} {#2} {#3} }
+\cs_new:Npn \@@_parse_model_Hsb:w #1 , #2 , #3 , #4 \s_@@_stop
+ {
+ \exp_args:Ne \@@_parse_model_hsb:nnn { \fp_eval:n { #1 / 360 } }
+ {#2} {#3}
+ }
+% \end{macrocode}
+% The conversion here is non-trivial but is described at length
+% in the \pkg{xcolor} manual. For ease, we calculate the integer
+% and fractional parts of the hue first, then use them to work out the
+% possible values for $r$, $g$ and $b$ before putting them in the correct
+% places.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_model_hsb:nnn #1#2#3
+ {
+ { rgb }
+ {
+ \exp_args:Ne \@@_parse_model_hsb_aux:nnn
+ { \fp_eval:n { 6 * (#1) } } {#2} {#3}
+ }
+ }
+\cs_new:Npn \@@_parse_model_hsb_aux:nnn #1#2#3
+ {
+ \exp_args:Nee \@@_parse_model_hsb_aux:nnnn
+ { \fp_eval:n { floor(#1) } } { \fp_eval:n { #1 - floor(#1) } }
+ {#2} {#3}
+ }
+\cs_new:Npn \@@_parse_model_hsb_aux:nnnn #1#2#3#4
+ {
+ \use:e
+ {
+ \exp_not:N \@@_parse_model_hsb_aux:nnnnn
+ { \@@_parse_number:n {#4} }
+ { \fp_eval:n { round(#4 * (1 - #3) ,5) } }
+ { \fp_eval:n { round(#4 * ( 1 - #3 * #2 ) ,5) } }
+ { \fp_eval:n { round(#4 * ( 1 - #3 * (1 - #2) ) ,5) } }
+ {#1}
+ }
+ }
+\cs_new:Npn \@@_parse_model_hsb_aux:nnnnn #1#2#3#4#5
+ { \use:c { @@_parse_model_hsb_ #5 :nnnn } {#1} {#2} {#3} {#4} }
+\cs_new:cpn { @@_parse_model_hsb_0:nnnn } #1#2#3#4 { #1 ~ #4 ~ #2 }
+\cs_new:cpn { @@_parse_model_hsb_1:nnnn } #1#2#3#4 { #3 ~ #1 ~ #2 }
+\cs_new:cpn { @@_parse_model_hsb_2:nnnn } #1#2#3#4 { #2 ~ #1 ~ #4 }
+\cs_new:cpn { @@_parse_model_hsb_3:nnnn } #1#2#3#4 { #2 ~ #3 ~ #1 }
+\cs_new:cpn { @@_parse_model_hsb_4:nnnn } #1#2#3#4 { #4 ~ #2 ~ #1 }
+\cs_new:cpn { @@_parse_model_hsb_5:nnnn } #1#2#3#4 { #1 ~ #2 ~ #3 }
+\cs_new:cpn { @@_parse_model_hsb_6:nnnn } #1#2#3#4 { #1 ~ #2 ~ #2 }
+\cs_new:Npn \@@_parse_model_HSB:w #1 , #2 , #3 , #4 \s_@@_stop
+ {
+ \exp_args:Neee \@@_parse_model_hsb:nnn
+ { \fp_eval:n { round((#1) / 240,5) } }
+ { \fp_eval:n { round((#2) / 240,5) } }
+ { \fp_eval:n { round((#3) / 240,5) } }
+ }
+\cs_new:Npn \@@_parse_model_HTML:w #1 , #2 \s_@@_stop
+ { \@@_parse_model_HTML_aux:w #1 0 0 0 0 0 0 \s_@@_stop }
+\cs_new:Npn \@@_parse_model_HTML_aux:w #1#2#3#4#5#6#7 \s_@@_stop
+ {
+ { rgb }
+ {
+ \fp_eval:n { round(\int_from_hex:n {#1#2} / 255,5) } ~
+ \fp_eval:n { round(\int_from_hex:n {#3#4} / 255,5) } ~
+ \fp_eval:n { round(\int_from_hex:n {#5#6} / 255,5) }
+ }
+ }
+\cs_new:Npn \@@_parse_model_RGB:w #1 , #2 , #3 , #4 \s_@@_stop
+ {
+ { rgb }
+ {
+ \fp_eval:n { round((#1) / 255,5) } ~
+ \fp_eval:n { round((#2) / 255,5) } ~
+ \fp_eval:n { round((#3) / 255,5) }
+ }
+ }
+% \end{macrocode}
+% Following the description in the \pkg{xcolor} manual. As we always use |rgb|,
+% there is no need to find the sixth, we just pas the information straight
+% to the |hsb| auxiliary defined earlier.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_model_wave:w #1 , #2 \s_@@_stop
+ {
+ { rgb }
+ {
+ \fp_compare:nNnTF {#1} < { 420 }
+ { \@@_parse_model_wave_auxi:nn {#1} { 0.3 + 0.7 * (#1 - 380) / 40 }
+ }
+ {
+ \fp_compare:nNnTF {#1} > { 700 }
+ { \@@_parse_model_wave_auxi:nn {#1} { 0.3 + 0.7 * (#1 - 780) / -80 } }
+ { \@@_parse_model_wave_auxi:nn {#1} { 1 } }
+ }
+ }
+ }
+\cs_new:Npn \@@_parse_model_wave_auxi:nn #1#2
+ {
+ \fp_compare:nNnTF {#1} < { 440 }
+ {
+ \@@_parse_model_wave_auxii:nn
+ { 4 + \@@_parse_model_wave_rho:n { (#1 - 440) / -60 } }
+ {#2}
+ }
+ {
+ \fp_compare:nNnTF {#1} < { 490 }
+ {
+ \@@_parse_model_wave_auxii:nn
+ { 4 - \@@_parse_model_wave_rho:n { (#1 - 440) / 50 } }
+ {#2}
+ }
+ {
+ \fp_compare:nNnTF {#1} < { 510 }
+ {
+ \@@_parse_model_wave_auxii:nn
+ { 2 + \@@_parse_model_wave_rho:n { (#1 - 510) / -20 } }
+ {#2}
+ }
+ {
+ \fp_compare:nNnTF {#1} < { 580 }
+ {
+ \@@_parse_model_wave_auxii:nn
+ { 2 - \@@_parse_model_wave_rho:n { (#1 - 510) / 70 } }
+ {#2}
+ }
+ {
+ \fp_compare:nNnTF {#1} < { 645 }
+ {
+ \@@_parse_model_wave_auxii:nn
+ { \@@_parse_model_wave_rho:n { (#1 - 645) / -65 } }
+ {#2}
+ }
+ { \@@_parse_model_wave_auxii:nn { 0 } {#2} }
+ }
+ }
+ }
+ }
+ }
+\cs_new:Npn \@@_parse_model_wave_auxii:nn #1#2
+ {
+ \exp_args:Neee \@@_parse_model_hsb_aux:nnn
+ { \fp_eval:n {#1} }
+ { 1 }
+ { \@@_parse_model_wave_rho:n {#2} }
+ }
+\cs_new:Npn \@@_parse_model_wave_rho:n #1
+ { \fp_eval:n { min(1, max(0,#1) ) } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_parse_model_cmy:w}
+% Simply pass data to the conversion functions.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_model_cmy:w #1 , #2 , #3 , #4 \s_@@_stop
+ {
+ { cmyk }
+ { \@@_convert_rgb_cmyk:nnn {#1} {#2} {#3} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_parse_model_tHsb:w}
+% \begin{macro}{\@@_parse_model_tHsb:n}
+% \begin{macro}{\@@_parse_model_tHsb:nw}
+% There are three stages to the process here: bring the |tH| argument into
+% the normal range, divide through to get to |hsb| and finally convert that
+% to |rgb|. The final stage can be delegated to the parsing function for
+% |hsb|, and the conversion from |Hsb| to |hsb| is trivial, so the main focus
+% here is the first stage. We use a simple expandable loop to do the work,
+% and we implement the equation given in the \pkg{xcolor} manual
+% (number~85 there) as a simple expression.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_model_tHsb:w #1 , #2 , #3 , #4 \s_@@_stop
+ {
+ \exp_args:Ne \@@_parse_model_hsb:nnn
+ { \@@_parse_model_tHsb:n {#1} } {#2} {#3}
+ }
+\cs_new:Npn \@@_parse_model_tHsb:n #1
+ {
+ \@@_parse_model_tHsb:nw {#1}
+ 0 , 0 ;
+ 60 , 30 ;
+ 120 , 60 ;
+ 180 , 120 ;
+ 210 , 180 ;
+ 240 , 240 ;
+ 360 , 360 ;
+ \q_recursion_tail , ;
+ \q_recursion_stop
+ }
+\cs_new:Npn \@@_parse_model_tHsb:nw #1 #2 , #3 ; #4 , #5 ;
+ {
+ \quark_if_recursion_tail_stop_do:nn {#4} { 0 }
+ \fp_compare:nNnTF {#1} > {#4}
+ { \@@_parse_model_tHsb:nw {#1} #4 , #5 ; }
+ {
+ \use_i_delimit_by_q_recursion_stop:nw
+ { \fp_eval:n { ((#1 - #2) / (#4 - #2) * (#5 - #3) + #3) / 360 } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_parse_model_&spot:w}
+% We cannot extract data here from that passed by \pkg{xcolor}, so
+% we fall back on a black tint.
+% \begin{macrocode}
+\cs_new:cpn { @@_parse_model_&spot:w } #1 , #2 \s_@@_stop
+ { { gray } { #1 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Selecting colors (and color models)}
+%
+% \begin{variable}{\l_color_fixed_model_tl}
+% For selecting a single fixed model.
+% \begin{macrocode}
+\tl_new:N \l_color_fixed_model_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_check_model:N}
+% \begin{macro}{\@@_check_model:nn}
+% Check that the model in use is the one required.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_check_model:N #1
+ {
+ \tl_if_empty:NF \l_color_fixed_model_tl
+ {
+ \exp_after:wN \@@_check_model:nn #1
+ \tl_if_eq:NNF \l_@@_model_tl \l_color_fixed_model_tl
+ {
+ \@@_convert:VVN \l_@@_model_tl \l_color_fixed_model_tl
+ \l_@@_value_tl
+ }
+ \tl_set:Ne #1
+ { { \l_color_fixed_model_tl } { \l_@@_value_tl } }
+ }
+ }
+\cs_new_protected:Npn \@@_check_model:nn #1#2
+ {
+ \tl_set:Nn \l_@@_model_tl {#1}
+ \tl_set:Nn \l_@@_value_tl {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_finalise_current:}
+% A backend-neutral location for \enquote{last minute} manipulations before
+% handing off to the backend code. We set the special |.| syntax here: this
+% will therefore always be available. The finalisation is separate from the
+% main function so it can also be applied to \emph{e.g.}~page color.
+% \begin{macrocode}
+\cs_new_protected:Npe \@@_finalise_current:
+ {
+ \tl_set:Ne \exp_not:c { l_@@_named_ . _tl }
+ { \exp_not:N \@@_model:N \exp_not:N \l_@@_current_tl }
+ \prop_clear:N \exp_not:c { l_@@_named_ . _prop }
+ \prop_put:NVe \exp_not:c { l_@@_named_ . _prop }
+ \exp_not:c { l_@@_named_ . _tl }
+ { \exp_not:N \@@_values:N \exp_not:N \l_@@_current_tl }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\color_select:n}
+% \begin{macro}{\color_select:nn}
+% \begin{macro}{\@@_select_main:Nw, \@@_select_loop:Nw}
+% \begin{macro}{\@@_select:nnN}
+% \begin{macro}{\@@_select_swap:Nnn}
+% Parse the input expressions then get the backend to actually activate
+% them. The main complexity here is the need to check through multiple models.
+% That is done \enquote{locally} here as the approach is subtly different to
+% when different models are being stored.
+% \begin{macrocode}
+\cs_new_protected:Npn \color_select:n #1
+ {
+ \@@_parse:nN {#1} \l_@@_current_tl
+ \@@_finalise_current:
+ \@@_select:N \l_@@_current_tl
+ }
+\cs_new_protected:Npn \color_select:nn #1#2
+ {
+ \@@_select_main:Nw \l_@@_current_tl
+ #1 / / \s_@@_mark #2 / / \s_@@_stop
+ \@@_finalise_current:
+ \@@_select:N \l_@@_current_tl
+ }
+% \end{macrocode}
+% If the first color model is the fixed one, or if there is no fixed
+% model, we don't need most of the data: just set up and apply the backend
+% function.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_select_main:Nw
+ #1 #2 / #3 / #4 \s_@@_mark #5 / #6 / #7 \s_@@_stop
+ {
+ \@@_select:nnN {#2} {#5} #1
+ \bool_lazy_or:nnF
+ { \tl_if_empty_p:N \l_color_fixed_model_tl }
+ { \str_if_eq_p:nV {#2} \l_color_fixed_model_tl }
+ { \@@_select_loop:Nw #1 #3 / #4 \s_@@_mark #6 / #7 \s_@@_stop }
+ }
+% \end{macrocode}
+% If a fixed model applies, we need to check each possible value in order.
+% If there is no hit at all, fall back on the generic formula-based
+% interchange.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_select_loop:Nw
+ #1 #2 / #3 \s_@@_mark #4 / #5 \s_@@_stop
+ {
+ \str_if_eq:nVTF {#2} \l_color_fixed_model_tl
+ { \@@_select:nnN {#2} {#4} #1 }
+ {
+ \tl_if_blank:nTF {#2}
+ { \exp_after:wN \@@_select_swap:Nnn \exp_after:wN #1 #1 }
+ { \@@_select_loop:Nw #1 #3 \s_@@_mark #5 \s_@@_stop }
+ }
+ }
+\cs_new_protected:Npn \@@_select:nnN #1#2#3
+ {
+ \cs_if_exist:cTF { @@_parse_model_ #1 :w }
+ {
+ \tl_set:Ne #3
+ { \use:c { @@_parse_model_ #1 :w } #2 , 0 , 0 , 0 , 0 \s_@@_stop }
+ }
+ { \msg_error:nnn { color } { unknown-model } {#1} }
+ }
+\cs_new_protected:Npn \@@_select_swap:Nnn #1#2#3
+ {
+ \@@_convert:nVnN {#2} \l_color_fixed_model_tl {#3} \l_@@_value_tl
+ \tl_set:Ne #1
+ { { \l_color_fixed_model_tl } { \l_@@_value_tl } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Math color}
+%
+% The approach here is the same as for the \LaTeXe{} \cs{mathcolor} command,
+% but as we are working at the \pkg{expl3} level we can make some minor
+% changes.
+%
+% \begin{macro}{\l_color_math_active_tl}
+% Tokens representing active sub/superscripts.
+% \begin{macrocode}
+\tl_new:N \l_color_math_active_tl
+\tl_set:Nn \l_color_math_active_tl { ' }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\g_@@_math_seq}
+% Not all engines have multiple color stacks, and at the same time we are
+% not expecting breaking within a colored math fragment. So we track the
+% color stack ourselves.
+% \begin{macrocode}
+\seq_new:N \g_@@_math_seq
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\color_math:nn}
+% \begin{macro}{\color_math:nnn}
+% \begin{macro}{\@@_math:nn}
+% The basic set up here is relatively simple: store the current color,
+% parse the new color as-normal, then switch color before inserting the
+% tokens we are asked to change. The tricky part is right at the end,
+% handling the reset.
+% \begin{macrocode}
+\cs_new_protected:Npn \color_math:nn #1#2
+ {
+ \@@_math:nn {#2}
+ { \@@_parse:nN {#1} \l_@@_current_tl }
+ }
+\cs_new_protected:Npn \color_math:nnn #1#2#3
+ {
+ \@@_math:nn {#3}
+ {
+ \@@_select_main:Nw \l_@@_current_tl
+ #1 / / \s_@@_mark #2 / / \s_@@_stop
+ }
+ }
+\cs_new_protected:Npn \@@_math:nn #1#2
+ {
+ \seq_gpush:NV \g_@@_math_seq \l_@@_current_tl
+ #2
+ \@@_select_math:N \l_@@_current_tl
+ #1
+ \@@_math_scan:w
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_math_scan:w ,
+% \@@_math_scan_auxi: ,
+% \@@_math_scan_auxii: ,
+% \@@_math_scan_end:
+% }
+% The complication when changing the color back is due to the fact
+% that the \cs{color_math:nn(n)} may be followed by \verb=^= or \verb=_=
+% or the hidden superscript (for example \texttt{'}) and its argument may
+% end in a \tn{mathop} in which case the sub- and superscripts may be
+% attached as \cs{limits} instead of after the material. All cases
+% need separate treatment. To avoid repeatedly collecting the same
+% token, we first check for an alignment tab: assuming we don't have
+% one of those, we can \enquote{recycle} \cs{l_peek_token} safely.
+% As we have an explicit \cs{c_alignment_token}, there needs to be
+% an align-safe group present.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_math_scan:w
+ {
+ \peek_remove_filler:n
+ {
+ \group_align_safe_begin:
+ \peek_catcode:NTF \c_alignment_token
+ {
+ \group_align_safe_end:
+ \@@_math_scan_end:
+ }
+ {
+ \group_align_safe_end:
+ \@@_math_scan_auxi:
+ }
+ }
+ }
+% \end{macrocode}
+% Dealing with literal |_| and |^| is easy, and as we have exactly two cases,
+% we can hard-code this. We use a hard-coded list for limits: these are all
+% primitives. The \cs{use_none:n} here also removes the test token so it is
+% left just in the right place.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_math_scan_auxi:
+ {
+ \token_case_catcode:NnTF \l_peek_token
+ {
+ \c_math_subscript_token { }
+ \c_math_superscript_token { }
+ }
+ { \@@_math_scripts:Nw }
+ {
+ \token_case_meaning:NnTF \l_peek_token
+ {
+ \tex_limits:D { \tex_limits:D }
+ \tex_nolimits:D { \tex_nolimits:D }
+ \tex_displaylimits:D { \tex_displaylimits:D }
+ }
+ { \@@_math_scan:w \use_none:n }
+ { \@@_math_scan_auxii: }
+ }
+ }
+% \end{macrocode}
+% The one final case to handle is math-active tokens, most obviously
+% \texttt{'}, as these won't be covered earlier.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_math_scan_auxii:
+ {
+ \tl_map_inline:Nn \l_color_math_active_tl
+ {
+ \token_if_eq_meaning:NNT \l_peek_token ##1
+ {
+ \tl_map_break:n
+ {
+ \use_i:nn
+ { \@@_math_scan_auxiii:N ##1 }
+ }
+ }
+ \@@_math_scan_end:
+ }
+ }
+\cs_new_protected:Npn \@@_math_scan_auxiii:N #1
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_math_scan:w
+ \char_generate:nn { `#1 } { 13 }
+ }
+\cs_new_protected:Npn \@@_math_scan_end:
+ {
+ \@@_backend_reset:
+ \seq_gpop:NN \g_@@_math_seq \l_@@_current_tl
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_math_scripts:Nw}
+% \begin{macro}{\@@_math_script_aux:N}
+% The tricky part of handling sub and superscripts is that we have
+% to reset color to the one that is on the stack but reset it back
+% to what it was before to allow for cases like
+% \begin{verbatim}
+% \[ \color_math:n { red } { a + \sum } _ { i = 1 } ^ { n } \]
+% \end{verbatim}
+% Here, \TeX{} constructs a \cs{vbox} stacking subscript, summation
+% sign, and superscript. So technically the superscript comes first
+% and the \cs{sum} that should get colored red is the middle.
+%
+% The approach here is to set up a brace group immediately after the
+% script token, then to set the color appropriately in that argument.
+% We need an extra group to keep the color contained, and as we
+% need to allow for an explicit closing brace in the source, the
+% inner group also is a brace one rather than \cs{group_begin:}-based.
+% At the end of the outer group we need to insert \cs{@@_math_scan:w}
+% to continue the search for a second script token.
+%
+% Notice that here we \emph{don't} need to use the math-specific
+% color selector as we can allow the
+% |\group_insert_after:N \@@_backend_reset:| to operate normally.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_math_scripts:Nw #1
+ {
+ #1
+ \c_group_begin_token
+ \c_group_begin_token
+ \seq_get:NN \g_@@_math_seq \l_@@_current_tl
+ \@@_select:N \l_@@_current_tl
+ \group_insert_after:N \c_group_end_token
+ \group_insert_after:N \@@_math_scan:w
+ \peek_remove_filler:n
+ {
+ \peek_catcode_remove:NF \c_group_begin_token
+ { \@@_math_script_aux:N }
+ }
+ }
+% \end{macrocode}
+% Deal with the case where we do not have an explicit brace pair in the
+% source.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_math_script_aux:N #1 { #1 \c_group_end_token }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Fill and stroke color}
+%
+% \begin{macro}{\color_fill:n, \color_stroke:n}
+% \begin{macro}{\color_fill:nn, \color_stroke:nn}
+% \begin{macro}{\@@_draw:nnn}
+% \begin{macrocode}
+\cs_new_protected:Npn \color_fill:n #1
+ {
+ \@@_parse:nN {#1} \l_@@_current_tl
+ \exp_after:wN \@@_draw:nnn \l_@@_current_tl { fill }
+ }
+\cs_new_protected:Npn \color_stroke:n #1
+ {
+ \@@_parse:nN {#1} \l_@@_current_tl
+ \exp_after:wN \@@_draw:nnn \l_@@_current_tl { stroke }
+ }
+\cs_new_protected:Npn \color_fill:nn #1#2
+ {
+ \@@_select_main:Nw \l_@@_current_tl
+ #1 / / \s_@@_mark #2 / / \s_@@_stop
+ \exp_after:wN \@@_draw:nnn \l_@@_current_tl { fill }
+ }
+\cs_new_protected:Npn \color_stroke:nn #1#2
+ {
+ \@@_select_main:Nw \l_@@_current_tl
+ #1 / / \s_@@_mark #2 / / \s_@@_stop
+ \exp_after:wN \@@_draw:nnn \l_@@_current_tl { stroke }
+ }
+\cs_new_protected:Npn \@@_draw:nnn #1#2#3
+ {
+ \use:c { @@_backend_ #3 _ #1 :n } {#2}
+ \exp_args:Nc \group_insert_after:N { @@_backend_ #3 _ reset: }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Defining named colors}
+%
+% \begin{variable}{\l_@@_named_tl}
+% Space to store the detail of the named color.
+% \begin{macrocode}
+\tl_new:N \l_@@_named_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\color_set:nn}
+% \begin{macro}{\@@_set:nnn}
+% \begin{macro}{\@@_set:nn}
+% \begin{macro}{\@@_set:nnw}
+% \begin{macro}{\color_set:nnn, \@@_set_aux:nnn}
+% \begin{macro}{\@@_set_colon:nnw}
+% \begin{macro}{\@@_set_loop:nw}
+% \begin{macro}{\color_set_eq:nn}
+% Defining named colors means working through the model list and saving
+% both the \enquote{main} color and any equivalents in other models. Even
+% if there is only one model, we store a |prop| as well as a |tl|, as there
+% could be grouping weirdness, etc. When setting using an expression,
+% we need to avoid any fixed model issues, which is done without a group as
+% in \pkg{l3keys}.
+% \begin{macrocode}
+\cs_new_protected:Npn \color_set:nn #1#2
+ {
+ \exp_args:NV \@@_set:nnn
+ \l_color_fixed_model_tl {#1} {#2}
+ }
+\cs_new_protected:Npn \@@_set:nnn #1#2#3
+ {
+ \tl_clear:N \l_color_fixed_model_tl
+ \@@_set:nn {#2} {#3}
+ \tl_set:Nn \l_color_fixed_model_tl {#1}
+ }
+\cs_new_protected:Npn \@@_set:nn #1#2
+ {
+ \str_if_eq:nnF {#1} { . }
+ {
+ \@@_parse:nN {#2} \l_@@_named_tl
+ \tl_clear_new:c { l_@@_named_ #1 _tl }
+ \tl_set:ce { l_@@_named_ #1 _tl }
+ { \@@_model:N \l_@@_named_tl }
+ \prop_clear_new:c { l_@@_named_ #1 _prop }
+ \prop_put:cve { l_@@_named_ #1 _prop } { l_@@_named_ #1 _tl }
+ { \@@_values:N \l_@@_named_tl }
+ \@@_set:nnw {#1} {#2} #2 ! \s_@@_stop
+ }
+ }
+% \end{macrocode}
+% When setting an expression-based color, there could be multiple model
+% data available for one or more of the input colors. Where that is true for
+% the \emph{first} named color in an expression, we re-parse the expression
+% when they are also parameter-based: only |cmyk|, |gray| and |rgb| make
+% any sense here. There is a bit of a performance hit but this should be
+% rare and taking place during set-up.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_set:nnw #1#2#3 ! #4 \s_@@_stop
+ {
+ \clist_map_inline:nn { cmyk , gray , rgb }
+ {
+ \prop_get:cnNT { l_@@_named_ #3 _prop } {##1} \l_@@_internal_tl
+ {
+ \prop_if_in:cnF { l_@@_named_ #1 _prop } {##1}
+ {
+ \group_begin:
+ \bool_set_true:N \l_@@_ignore_error_bool
+ \tl_set:cn { l_@@_named_ #3 _tl } {##1}
+ \@@_parse:nN {#2} \l_@@_internal_tl
+ \exp_args:NNNV \group_end:
+ \tl_set:Nn \l_@@_internal_tl \l_@@_internal_tl
+ \prop_put:cee { l_@@_named_ #1 _prop }
+ { \@@_model:N \l_@@_internal_tl }
+ { \@@_values:N \l_@@_internal_tl }
+ }
+ }
+ }
+ }
+\cs_new_protected:Npn \color_set:nnn #1#2#3
+ {
+ \str_if_eq:nnF {#1} { . }
+ {
+ \tl_clear_new:c { l_@@_named_ #1 _tl }
+ \prop_clear_new:c { l_@@_named_ #1 _prop }
+ \exp_args:Ne \@@_set_aux:nnn { \tl_to_str:n {#2} }
+ {#1} {#3}
+ }
+ }
+\cs_new_protected:Npe \@@_set_aux:nnn #1#2#3
+ {
+ \exp_not:N \@@_set_colon:nnw {#2} {#3}
+ #1 \c_colon_str \c_colon_str \exp_not:N \s_@@_stop
+ }
+\use:e
+ {
+ \cs_new_protected:Npn \exp_not:N \@@_set_colon:nnw
+ #1#2 #3 \c_colon_str #4 \c_colon_str
+ #5 \exp_not:N \s_@@_stop
+ }
+ {
+ \tl_if_blank:nTF {#4}
+ { \@@_set_loop:nw {#1} #3 }
+ { \@@_set_loop:nw {#1} #4 }
+ / / \s_@@_mark #2 / / \s_@@_stop
+ }
+\cs_new_protected:Npn \@@_set_loop:nw
+ #1#2 / #3 \s_@@_mark #4 / #5 \s_@@_stop
+ {
+ \tl_if_blank:nF {#2}
+ {
+ \@@_select:nnN {#2} {#4} \l_@@_named_tl
+ \tl_set:Ne \l_@@_internal_tl { \@@_model:N \l_@@_named_tl }
+ \tl_if_empty:cT { l_@@_named_ #1 _tl }
+ { \tl_set_eq:cN { l_@@_named_ #1 _tl } \l_@@_internal_tl }
+ \prop_put:cVe { l_@@_named_ #1 _prop } \l_@@_internal_tl
+ { \@@_values:N \l_@@_named_tl }
+ \@@_set_loop:nw {#1} #3 \s_@@_mark #5 \s_@@_stop
+ }
+ }
+\cs_new_protected:Npn \color_set_eq:nn #1#2
+ {
+ \color_if_exist:nTF {#2}
+ {
+ \tl_clear_new:c { l_@@_named_ #1 _tl }
+ \prop_clear_new:c { l_@@_named_ #1 _prop }
+ \str_if_eq:nnTF {#2} { . }
+ {
+ \tl_set:ce { l_@@_named_ #1 _tl }
+ { \@@_model:N \l_@@_current_tl }
+ \prop_put:cve { l_@@_named_ #1 _prop } { l_@@_named_ #1 _tl }
+ { \@@_values:N \l_@@_current_tl }
+ }
+ {
+ \tl_set_eq:cc { l_@@_named_ #1 _tl } { l_@@_named_ #2 _tl }
+ \prop_set_eq:cc { l_@@_named_ #1 _prop } { l_@@_named_ #2 _prop }
+ }
+ }
+ {
+ \msg_error:nnn { color } { unknown-color } {#2}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% A small set of colors are always defined.
+% \begin{macrocode}
+\color_set:nnn { black } { gray } { 0 }
+\color_set:nnn { white } { gray } { 1 }
+\color_set:nnn { cyan } { cmyk } { 1 , 0 , 0 , 0 }
+\color_set:nnn { magenta } { cmyk } { 0 , 1 , 0 , 0 }
+\color_set:nnn { yellow } { cmyk } { 0 , 0 , 1 , 0 }
+\color_set:nnn { red } { rgb } { 1 , 0 , 0 }
+\color_set:nnn { green } { rgb } { 0 , 1 , 0 }
+\color_set:nnn { blue } { rgb } { 0 , 0 , 1 }
+% \end{macrocode}
+%
+% \begin{variable}{\l_@@_named_._prop, \l_@@_named_._tl}
+% A special named color: this is always defined though not fixed in
+% definition.
+% \begin{macrocode}
+\prop_new:c { l_@@_named_._prop }
+\tl_new:c { l_@@_named_._tl }
+\tl_set:ce { l_@@_named_._tl } { \@@_model:N \l_@@_current_tl }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Exporting colors}
+%
+% \begin{macro}{\color_export:nnN}
+% \begin{macro}{\color_export:nnnN}
+% \begin{macro}{\@@_export:nN}
+% \begin{macro}{\@@_export:nnnN}
+% \begin{macrocode}
+\cs_new_protected:Npn \color_export:nnN #1#2#3
+ {
+ \group_begin:
+ \tl_if_exist:cT { c_@@_export_ #2 _tl }
+ { \tl_set_eq:Nc \l_color_fixed_model_tl { c_@@_export_ #2 _tl } }
+ \@@_parse:nN {#1} #3
+ \@@_export:nN {#2} #3
+ \exp_args:NNNV \group_end:
+ \tl_set:Nn #3 #3
+ }
+\cs_new_protected:Npn \color_export:nnnN #1#2#3#4
+ {
+ \@@_select_main:Nw #4
+ #1 / / \s_@@_mark #2 / / \s_@@_stop
+ \@@_export:nN {#3} #4
+ }
+\cs_new_protected:Npn \@@_export:nN #1#2
+ { \exp_after:wN \@@_export:nnnN #2 {#1} #2 }
+\cs_new:Npn \@@_export:nnnN #1#2#3#4
+ {
+ \cs_if_exist_use:cF { @@_export_format_ #3 :nnN }
+ {
+ \msg_error:nnn { color } { unknown-export-format } {#3}
+ \use_none:nnn
+ }
+ {#1} {#2} #4
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_export_format_backend:nnN}
+% Simple.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_export_format_backend:nnN #1#2#3
+ { \tl_set:Nn #3 { {#1} {#2} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_export:nnnNN}
+% A generic auxiliary for cases where only one model is appropriate.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_export:nnnNN #1#2#3#4#5
+ {
+ \str_if_eq:nnTF {#2} {#1}
+ { #5 #4 #3 \s_@@_stop }
+ {
+ \@@_convert:nnnN {#2} {#1} {#3} #4
+ \exp_after:wN #5 \exp_after:wN #4
+ #4 \s_@@_stop
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}
+% {
+% \c_@@_export_comma-sep-cmyk_tl ,
+% \c_@@_export_comma-sep-rgb_tl ,
+% \c_@@_export_HTML_tl ,
+% \c_@@_export_space-sep-cmyk_tl ,
+% \c_@@_export_space-sep-rgb_tl
+% }
+% \begin{macrocode}
+\tl_const:cn { c_@@_export_comma-sep-cmyk_tl } { cmyk }
+\tl_const:cn { c_@@_export_comma-sep-rgb_tl } { rgb }
+\tl_const:Nn \c_@@_export_HTML_tl { rgb }
+\tl_const:cn { c_@@_export_space-sep-cmyk_tl } { cmyk }
+\tl_const:cn { c_@@_export_space-sep-rgb_tl } { rgb }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}
+% {
+% \@@_export_format_comma-sep-cmyk:nnN ,
+% \@@_export_format_comma-sep-rgb:nnN ,
+% \@@_export_format_space-sep-cmyk:nnN ,
+% \@@_export_format_space-sep-rgb:nnN
+% }
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \@@_tmp:w #1#2
+ {
+ \cs_new_protected:cpe { @@_export_format_ #1 :nnN } ##1##2##3
+ {
+ \exp_not:N \@@_export:nnnNN {#2} {##1} {##2} ##3
+ \exp_not:c { @@_export_ #1 :Nw }
+ }
+ }
+ \@@_tmp:w { comma-sep-cmyk } { cmyk }
+ \@@_tmp:w { comma-sep-rgb } { rgb }
+ \@@_tmp:w { HTML } { rgb }
+ \@@_tmp:w { space-sep-cmyk } { cmyk }
+ \@@_tmp:w { space-sep-rgb } { rgb }
+
+\group_end:
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_export_space-sep-cmyk:Nw, \@@_export_comma-sep-cmyk:Nw}
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_export_comma-sep-cmyk:Nw }
+ #1#2 ~ #3 ~ #4 ~ #5 \s_@@_stop
+ { \tl_set:Nn #1 { #2 , #3 , #4 , #5 } }
+\cs_new_protected:cpn { @@_export_space-sep-cmyk:Nw } #1#2 \s_@@_stop
+ { \tl_set:Nn #1 {#2} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_export_comma-sep-rgb:Nw ,
+% \@@_export_HTML:Nw ,
+% \@@_export_space-sep-rgb:Nw
+% }
+% \begin{macro}[EXP]{\@@_export_HTML:n}
+% \textsc{html} values must be given in |rgb|: we force conversion if
+% required, then do some simple maths.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_export_comma-sep-rgb:Nw } #1#2 ~ #3 ~ #4 \s_@@_stop
+ { \tl_set:Ne #1 { #2 , #3 , #4 } }
+\cs_new_protected:Npn \@@_export_HTML:Nw #1#2 ~ #3 ~ #4 \s_@@_stop
+ {
+ \tl_set:Ne #1
+ {
+ \@@_export_HTML:n {#2}
+ \@@_export_HTML:n {#3}
+ \@@_export_HTML:n {#4}
+ }
+ }
+\cs_new:Npn \@@_export_HTML:n #1
+ {
+ \fp_compare:nNnTF {#1} = { 0 }
+ { 00 }
+ {
+ \fp_compare:nNnT { #1 * 255 } < { 16 } { 0 }
+ \int_to_Hex:n { \fp_to_int:n { #1 * 255 } }
+ }
+ }
+\cs_new_protected:cpn { @@_export_space-sep-rgb:Nw } #1#2 \s_@@_stop
+ { \tl_set:Nn #1 {#2} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Additional color models}
+%
+% \begin{variable}{\l_@@_internal_prop}
+% \begin{macrocode}
+\prop_new:N \l_@@_internal_prop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_model_int}
+% A tracker for the total number of new models.
+% \begin{macrocode}
+\int_new:N \g_@@_model_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {\c_@@_fallback_cmyk_tl, \c_@@_fallback_gray_tl, \c_@@_fallback_rgb_tl}
+% For every colorspace, we define one of the base colorspaces as a fallback.
+% The base colorspaces themselves are their own fallback.
+% \begin{macrocode}
+\tl_const:Nn \c_@@_fallback_cmyk_tl { cmyk }
+\tl_const:Nn \c_@@_fallback_gray_tl { gray }
+\tl_const:Nn \c_@@_fallback_rgb_tl { rgb }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_colorants_prop}
+% Mapping from names to colorants.
+% \begin{macrocode}
+\prop_new:N \g_@@_colorants_prop
+\prop_gput:Nnn \g_@@_colorants_prop { black } { Black }
+\prop_gput:Nnn \g_@@_colorants_prop { blue } { Blue }
+\prop_gput:Nnn \g_@@_colorants_prop { cyan } { Cyan }
+\prop_gput:Nnn \g_@@_colorants_prop { green } { Green }
+\prop_gput:Nnn \g_@@_colorants_prop { magenta } { Magenta }
+\prop_gput:Nnn \g_@@_colorants_prop { none } { None }
+\prop_gput:Nnn \g_@@_colorants_prop { red } { Red }
+\prop_gput:Nnn \g_@@_colorants_prop { yellow } { Yellow }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \c_@@_model_whitepoint_CIELAB_a_tl ,
+% \c_@@_model_whitepoint_CIELAB_b_tl ,
+% \c_@@_model_whitepoint_CIELAB_e_tl ,
+% \c_@@_model_whitepoint_CIELAB_d50_tl ,
+% \c_@@_model_whitepoint_CIELAB_d55_tl ,
+% \c_@@_model_whitepoint_CIELAB_d65_tl ,
+% \c_@@_model_whitepoint_CIELAB_d75_tl
+% }
+% Whitepoint data for the CIELAB profiles.
+% \begin{macrocode}
+\tl_const:Nn \c_@@_model_whitepoint_CIELAB_a_tl { 1.0985 ~ 1 ~ 0.3558 }
+\tl_const:Nn \c_@@_model_whitepoint_CIELAB_b_tl { 0.9807 ~ 1 ~ 1.1822 }
+\tl_const:Nn \c_@@_model_whitepoint_CIELAB_e_tl { 1 ~ 1 ~ 1 }
+\tl_const:cn { c_@@_model_whitepoint_CIELAB_d50_tl } { 0.9642 ~ 1 ~ 0.8251 }
+\tl_const:cn { c_@@_model_whitepoint_CIELAB_d55_tl } { 0.9568 ~ 1 ~ 0.9214 }
+\tl_const:cn { c_@@_model_whitepoint_CIELAB_d65_tl } { 0.9504 ~ 1 ~ 1.0888 }
+\tl_const:cn { c_@@_model_whitepoint_CIELAB_d75_tl } { 0.9497 ~ 1 ~ 1.2261 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_model_range_CIELAB_tl}
+% The range for CIELAB color spaces.
+% \begin{macrocode}
+\tl_const:Nn \c_@@_model_range_CIELAB_tl { 0 ~ 100 ~ -128 ~ 127 ~ -128 ~ 127 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_alternative_model_prop}
+% For tracking the alternative model set up for separations, etc.
+% \begin{macrocode}
+\prop_new:N \g_@@_alternative_model_prop
+\clist_map_inline:nn { cyan , magenta , yellow , black }
+ { \prop_gput:Nnn \g_@@_alternative_model_prop {#1} { cmyk } }
+\clist_map_inline:nn { red , green , blue }
+ { \prop_gput:Nnn \g_@@_alternative_model_prop {#1} { rgb } }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_alternative_values_prop}
+% Same for the values: a bit more involved.
+% \begin{macrocode}
+\prop_new:N \g_@@_alternative_values_prop
+\prop_gput:Nnn \g_@@_alternative_values_prop { cyan } { 1 , 0 , 0 , 0 }
+\prop_gput:Nnn \g_@@_alternative_values_prop { magenta } { 0 , 1 , 0 , 0 }
+\prop_gput:Nnn \g_@@_alternative_values_prop { yellow } { 0 , 0 , 1 , 0 }
+\prop_gput:Nnn \g_@@_alternative_values_prop { black } { 0 , 0 , 0 , 1 }
+\prop_gput:Nnn \g_@@_alternative_values_prop { red } { 1 , 0 , 0 }
+\prop_gput:Nnn \g_@@_alternative_values_prop { green } { 0 , 1 , 0 }
+\prop_gput:Nnn \g_@@_alternative_values_prop { blue } { 0 , 0 , 1 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\color_model_new:nnn, \@@_model_new:nnn}
+% Set up a new model: in general this has to be handled by a family-dependent
+% function. To avoid some \enquote{interesting} questions with casing, we
+% fold the case of the family name. The key--value list should always be
+% present, so we convert it up-front to a |prop|, then deal with the detail
+% on a per-family basis.
+% \begin{macrocode}
+\cs_new_protected:Npn \color_model_new:nnn #1#2#3
+ {
+ \exp_args:Nee \@@_model_new:nnn
+ { \tl_to_str:n {#1} }
+ { \str_casefold:n {#2} } {#3}
+ }
+\cs_new_protected:Npn \@@_model_new:nnn #1#2#3
+ {
+ \cs_if_exist:cTF { @@_parse_model_ #1 :w }
+ {
+ \msg_error:nnn { color } { model-already-defined } {#1}
+ }
+ {
+ \cs_if_exist:cTF { @@_model_ #2 :n }
+ {
+ \prop_set_from_keyval:Nn \l_@@_internal_prop {#3}
+ \use:c { @@_model_ #2 :n } {#1}
+ }
+ {
+ \msg_error:nnn { color } { unknown-model-type } {#2}
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_model_init:nnn, \@@_model_init:nne}
+% A shared auxiliary to do the basics of setting up a new model: reserve a
+% number, create a white-equivalent, set up links to the backend.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_model_init:nnn #1#2#3
+ {
+ \int_gincr:N \g_@@_model_int
+ \clist_map_inline:nn { fill , stroke , select }
+ {
+ \cs_new_protected:cpe { @@_backend_ ##1 _ #1 :n } ####1
+ {
+ \exp_not:c { @@_backend_ ##1 _ #2 :nn }
+ { color \int_use:N \g_@@_model_int } {####1}
+ }
+ }
+ \cs_new_protected:cpe { @@_model_ #1 _white: }
+ {
+ \prop_put:Nnn \exp_not:N \l_@@_named_white_prop {#1}
+ { \exp_not:n {#3} }
+ \exp_not:N \int_compare:nNnF { \tex_currentgrouplevel:D } = 0
+ { \group_insert_after:N \exp_not:c { @@_model_ #1 _ white: } }
+ }
+ \use:c { @@_model_ #1 _white: }
+ }
+\cs_generate_variant:Nn \@@_model_init:nnn { nne }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_model_separation:n}
+% \begin{macro}{\@@_model_separation:nn}
+% \begin{macro}{\@@_model_separation:nnn}
+% \begin{macro}{\@@_model_separation:w}
+% \begin{macro}
+% {
+% \@@_model_separation_cmyk:nnnnnn ,
+% \@@_model_separation_gray:nnnnnn ,
+% \@@_model_separation_rgb:nnnnnn
+% }
+% \begin{macro}{\@@_model_convert:nnn}
+% \begin{macro}{\@@_model_separation_CIELAB:nnnnnn}
+% \begin{macro}{\@@_model_separation_CIELAB:nnnnnnn}
+% Separations must have a \enquote{real} name, which is pretty easy to find.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_model_separation:n #1
+ {
+ \prop_get:NnNTF \l_@@_internal_prop { name }
+ \l_@@_internal_tl
+ {
+ \exp_args:NV \@@_model_separation:nn
+ \l_@@_internal_tl {#1}
+ }
+ {
+ \msg_error:nnn { color }
+ { separation-requires-name } {#1}
+ }
+ }
+% \end{macrocode}
+% We have two keys to find at this stage: the alternative space model
+% and linked values.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_model_separation:nn #1#2
+ {
+ \prop_get:NnNTF \l_@@_internal_prop { alternative-model }
+ \l_@@_internal_tl
+ {
+ \exp_args:NV \@@_model_separation:nnn
+ \l_@@_internal_tl {#2} {#1}
+ }
+ {
+ \msg_error:nnn { color }
+ { separation-alternative-model } {#2}
+ }
+ }
+\cs_new_protected:Npn \@@_model_separation:nnn #1#2#3
+ {
+ \cs_if_exist:cTF { @@_model_separation_ #1 :nnnnnn }
+ {
+ \prop_get:NnNTF \l_@@_internal_prop { alternative-values }
+ \l_@@_internal_tl
+ {
+ \exp_after:wN \@@_model_separation:w \l_@@_internal_tl
+ , 0 , 0 , 0 , 0 \s_@@_stop {#2} {#3} {#1}
+ }
+ {
+ \msg_error:nnn { color }
+ { separation-alternative-values } {#2}
+ }
+ }
+ {
+ \msg_error:nnn { color }
+ { unknown-alternative-model } {#1}
+ }
+ }
+% \end{macrocode}
+% As each alternative space leads to a different requirement for conversion,
+% and as there are only a small number of choices, we manually split the data
+% and then set up. Notice that mixing tints is really just the same
+% as mixing \texttt{gray}. The \texttt{white} color is special, as it allows
+% tints to be adjusted without an additional color space. To make sure the
+% data is set for that at all group levels, we need to work on a per-level
+% basis. Within the output, only the set-up needs the \enquote{real} name
+% of the colorspace: we use a simple tracking number for general usage
+% as this is a clear namespace without issues of escaping chars.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_model_separation:w
+ #1 , #2 , #3 , #4 , #5 \s_@@_stop #6#7#8
+ {
+ \@@_model_init:nnn {#6} { separation } { 0 }
+ \cs_new_eq:cN { @@_parse_mix_ #6 :nw } \@@_parse_mix_gray:nw
+ \cs_new:cpn { @@_parse_model_ #6 :w } ##1 , ##2 \s_@@_stop
+ { {#6} { \@@_parse_number:n {##1} } }
+ \use:c { @@_model_separation_ #8 :nnnnnn }
+ {#6} {#7} {#1} {#2} {#3} {#4}
+ \prop_gput:Nnn \g_@@_alternative_model_prop {#6} {#8}
+ \prop_gput:Nne \g_@@_colorants_prop {#6}
+ { \str_convert_pdfname:n {#7} }
+ }
+\cs_new_protected:Npn \@@_model_separation_cmyk:nnnnnn #1#2#3#4#5#6
+ {
+ \tl_const:cn { c_@@_fallback_ #1 _tl } { cmyk }
+ \cs_new:cpn { @@_convert_ #1 _cmyk:w } ##1 \s_@@_stop
+ {
+ \fp_eval:n {##1 * #3} ~
+ \fp_eval:n {##1 * #4} ~
+ \fp_eval:n {##1 * #5} ~
+ \fp_eval:n {##1 * #6}
+ }
+ \cs_new:cpn { @@_convert_cmyk_ #1 :w } ##1 \s_@@_stop { 1 }
+ \prop_gput:Nnn \g_@@_alternative_values_prop {#1} { #3 , #4 , #5 , #6 }
+ \@@_backend_separation_init:nnnnn {#2} { /DeviceCMYK } { }
+ { 0 ~ 0 ~ 0 ~ 0 } { #3 ~ #4 ~ #5 ~ #6 }
+ }
+\cs_new_protected:Npn \@@_model_separation_rgb:nnnnnn #1#2#3#4#5#6
+ {
+ \tl_const:cn { c_@@_fallback_ #1 _tl } { rgb }
+ \cs_new:cpn { @@_convert_ #1 _rgb:w } ##1 \s_@@_stop
+ {
+ \fp_eval:n {##1 * #3} ~
+ \fp_eval:n {##1 * #4} ~
+ \fp_eval:n {##1 * #5}
+ }
+ \cs_new:cpn { @@_convert_rgb_ #1 :w } ##1 \s_@@_stop { 1 }
+ \prop_gput:Nnn \g_@@_alternative_values_prop {#1} { #3 , #4 , #5 }
+ \@@_backend_separation_init:nnnnn {#2} { /DeviceRGB } { }
+ { 0 ~ 0 ~ 0 } { #3 ~ #4 ~ #5 }
+ }
+\cs_new_protected:Npn \@@_model_separation_gray:nnnnnn #1#2#3#4#5#6
+ {
+ \tl_const:cn { c_@@_fallback_ #1 _tl } { gray }
+ \cs_new:cpn { @@_convert_ #1 _gray:w } ##1 \s_@@_stop
+ { \fp_eval:n {##1 * #3} }
+ \cs_new:cpn { @@_convert_gray_ #1 :w } ##1 \s_@@_stop { 1 }
+ \prop_gput:Nnn \g_@@_alternative_values_prop {#1} {#3}
+ \@@_backend_separation_init:nnnnn {#2} { /DeviceGray } { } { 0 } {#3}
+ }
+% \end{macrocode}
+% Generic model conversion \emph{via} an alternative intermediate.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_model_convert:nnn #1#2#3
+ {
+ \cs_new:cpe { @@_convert_ #1 _ #3 :w } ##1 \s_@@_stop
+ {
+ \exp_not:N \exp_args:NNe \exp_not:N \use:nn
+ \exp_not:c { @@_convert_ #2 _ #3 :w }
+ { \exp_not:c { @@_convert_ #1 _ #2 :w } ##1 \s_@@_stop }
+ \c_space_tl \exp_not:N \s_@@_stop
+ }
+ }
+% \end{macrocode}
+% Setting up for CIELAB needs a bit more work: there is the illuminant and
+% the need for an appropriate object.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_model_separation_CIELAB:nnnnnn #1#2#3#4#5#6
+ {
+ \prop_get:NnNF \l_@@_internal_prop { illuminant }
+ \l_@@_internal_tl
+ {
+ \msg_error:nnn { color }
+ { CIELAB-requires-illuminant } {#1}
+ \tl_set:Nn \l_@@_internal_tl { d50 }
+ }
+ \exp_args:NV \@@_model_separation_CIELAB:nnnnnnn
+ \l_@@_internal_tl {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+% \end{macrocode}
+% If a CIELAB space is being set up, we need the illuminant, then create
+% the appropriate set up. At present, this doesn't include \texttt{BlackPoint}
+% or \texttt{Range} data, but that may be added later. As CIELAB colors
+% cannot be converted to anything else, we fallback to producing black in the
+% gray colorspace: the user should set up a second model for colors set up this way.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_model_separation_CIELAB:nnnnnnn #1#2#3#4#5#6#7
+ {
+ \tl_if_exist:cTF { c_@@_model_whitepoint_CIELAB_ #1 _tl }
+ {
+ \@@_backend_separation_init_CIELAB:nnn {#1} {#3} { #4 ~ #5 ~ #6 }
+ \tl_const:cn { c_@@_fallback_ #2 _tl } { gray }
+ \cs_new:cpn { @@_convert_ #2 _gray:w } ##1 \s_@@_stop
+ { 0 }
+ \cs_new:cpn { @@_convert_gray_ #2 :w } ##1 \s_@@_stop
+ { 1 }
+ }
+ {
+ \msg_error:nnn { color }
+ { unknown-CIELAB-illuminant } {#1}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_model_devicen:n}
+% \begin{macro}{\@@_model_devicen:nn}
+% \begin{macro}{\@@_model_devicen:nnn}
+% \begin{macro}{\@@_model_devicen:nnnn}
+% \begin{macro}
+% {
+% \@@_model_devicen_parse_1:nn ,
+% \@@_model_devicen_parse_2:nn ,
+% \@@_model_devicen_parse_3:nn ,
+% \@@_model_devicen_parse_4:nn ,
+% \@@_model_devicen_parse_generic:nn
+% }
+% \begin{macro}[EXP]{\@@_model_devicen_parse:nw}
+% \begin{macro}[EXP]{\@@_model_devicen_mix:nw}
+% \begin{macro}{\@@_model_devicen_init:nnn}
+% \begin{macro}{\@@_model_devicen_init:nnnn}
+% \begin{macro}{\@@_model_devicen_tranform:w}
+% \begin{macro}
+% {
+% \@@_model_devicen_tranform_1:nnnnn ,
+% \@@_model_devicen_tranform_3:nnnnn ,
+% \@@_model_devicen_tranform_4:nnnnn ,
+% }
+% \begin{macro}{\@@_model_devicen_tranform:nnn}
+% \begin{macro}[EXP]{\@@_model_devicen_colorant:n}
+% \begin{macro}{\@@_model_devicen_convert:nnn}
+% \begin{macro}
+% {
+% \@@_model_devicen_convert_cmyk:n ,
+% \@@_model_devicen_convert_gray:n ,
+% \@@_model_devicen_convert_rgb:n
+% }
+% \begin{macro}{\@@_model_devicen_convert:nnnn}
+% \begin{macro}[EXP]{\@@_model_devicen_convert:n, \@@_model_devicen_convert_aux:n}
+% \begin{macro}[EXP]{\@@_model_devicen_convert:w}
+% \begin{macro}[EXP]{\@@_convert_devicen_cmyk:nnnnw}
+% \begin{macro}[EXP]{\@@_convert_devicen_cmyk:nnnnnnnnn}
+% \begin{macro}[EXP]{\@@_convert_devicen_cmyk_aux:nnnnw}
+% \begin{macro}[EXP]{\@@_convert_devicen_gray:nw}
+% \begin{macro}[EXP]{\@@_convert_devicen_gray:nnn}
+% \begin{macro}[EXP]{\@@_convert_devicen_gray_aux:nw}
+% \begin{macro}[EXP]{\@@_convert_devicen_rgb:nnnw}
+% \begin{macro}[EXP]{\@@_convert_devicen_rgb:nnnnnnn}
+% \begin{macro}[EXP]{\@@_convert_devicen_rgb_aux:nnnw}
+% We require a list of component names here: one might call them colorants,
+% but it's convenient to use \TeX{} names instead so we slightly adjust the
+% terminology.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_model_devicen:n #1
+ {
+ \prop_get:NnNTF \l_@@_internal_prop { names }
+ \l_@@_internal_tl
+ {
+ \exp_args:NV \@@_model_devicen:nn
+ \l_@@_internal_tl {#1}
+ }
+ {
+ \msg_error:nnn { color }
+ { DeviceN-requires-names } {#1}
+ }
+ }
+% \end{macrocode}
+% All valid models will have an alternative listed, either hard-coded for
+% the core device ones, or dynamically added for Separations, etc.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_model_devicen:nn #1#2
+ {
+ \tl_clear:N \l_@@_model_tl
+ \clist_map_inline:nn {#1}
+ {
+ \prop_get:NnNTF \g_@@_alternative_model_prop {##1}
+ \l_@@_internal_tl
+ {
+ \tl_if_empty:NTF \l_@@_model_tl
+ { \tl_set_eq:NN \l_@@_model_tl \l_@@_internal_tl }
+ {
+ \str_if_eq:VVF \l_@@_model_tl \l_@@_internal_tl
+ {
+ \msg_error:nnn { color }
+ { DeviceN-inconsistent-alternative }
+ {#2}
+ \clist_map_break:n { \use_none:nnnn }
+ }
+ }
+ }
+ {
+ \str_if_eq:nnF {##1} { none }
+ {
+ \msg_error:nnn { color }
+ { DeviceN-no-alternative }
+ {#2}
+ }
+ }
+ }
+ \tl_if_empty:NTF \l_@@_model_tl
+ {
+ \msg_error:nnn { color }
+ { DeviceN-no-alternative } {#2}
+ }
+ { \exp_args:NV \@@_model_devicen:nnn \l_@@_model_tl {#1} {#2} }
+ }
+% \end{macrocode}
+% We now complete the data we require by first finding out how many
+% colorants there are, then moving on to begin constructing the function
+% required to map to the alternative color space.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_model_devicen:nnn #1#2#3
+ {
+ \exp_args:Ne \@@_model_devicen:nnnn
+ { \clist_count:n {#2} } {#1} {#2} {#3}
+ }
+% \end{macrocode}
+% At this stage, we have checked everything is in place, so we can set up
+% the \TeX{} and backend data structures. As for separations, it's not really
+% possible in general to have a fallback, so we simply provide
+% \enquote{black} for each element.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_model_devicen:nnnn #1#2#3#4
+ {
+ \@@_model_init:nne {#4} { devicen }
+ {
+ 0 \prg_replicate:nn { #1 - 1 } { ~ 0 }
+ }
+ \cs_if_exist_use:cF { @@_model_devicen_parse_ #1 :nn }
+ { \@@_model_devicen_parse_generic:nn }
+ {#4} {#1}
+ \@@_model_devicen_init:nnn {#1} {#2} {#3}
+ \@@_model_devicen_convert:nnne {#4} {#2} {#3}
+ {
+ 1 \prg_replicate:nn { #1 - 1 } { ~ 1 }
+ }
+ }
+% \end{macrocode}
+% For short lists of DeviceN colors, we can use hand-tuned parsing. This
+% lines up with other models, where we allow for up to four components. For
+% larger spaces, rather than limit artificially, we use a somewhat slow
+% approach based on open-ended commas-lists.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_model_devicen_parse_1:nn } #1#2
+ {
+ \cs_new:cpn { @@_parse_model_ #1 :w } ##1 , ##2 \s_@@_stop
+ { {#1} { \@@_parse_number:n {##1} } }
+ \cs_new_eq:cN { @@_parse_mix_ #1 :nw } \@@_parse_mix_gray:nw
+ }
+\cs_new_protected:cpn { @@_model_devicen_parse_2:nn } #1#2
+ {
+ \cs_new:cpn { @@_parse_model_ #1 :w } ##1 , ##2 , ##3 \s_@@_stop
+ { {#1} { \@@_parse_number:n {##1} ~ \@@_parse_number:n {##2} } }
+ \cs_new:cpn { @@_parse_mix_ #1 :nw }
+ ##1##2 ~ ##3 \s_@@_mark ##4 ~ ##5 \s_@@_stop
+ {
+ \fp_eval:n { ##2 * ##1 + ##4 * ( 1 - ##1 ) } \c_space_tl
+ \fp_eval:n { ##3 * ##1 + ##5 * ( 1 - ##1 ) }
+ }
+ }
+\cs_new_protected:cpn { @@_model_devicen_parse_3:nn } #1#2
+ {
+ \cs_new:cpn { @@_parse_model_ #1 :w } ##1 , ##2 , ##3 , ##4 \s_@@_stop
+ {
+ {#1}
+ {
+ \@@_parse_number:n {##1} ~
+ \@@_parse_number:n {##2} ~
+ \@@_parse_number:n {##3}
+ }
+ }
+ \cs_new_eq:cN { @@_parse_mix_ #1 :nw } \@@_parse_mix_rgb:nw
+ }
+\cs_new_protected:cpn { @@_model_devicen_parse_4:nn } #1#2
+ {
+ \cs_new:cpn { @@_parse_model_ #1 :w }
+ ##1 , ##2 , ##3 , ##4 , ##5 \s_@@_stop
+ {
+ {#1}
+ {
+ \@@_parse_number:n {##1} ~
+ \@@_parse_number:n {##2} ~
+ \@@_parse_number:n {##3} ~
+ \@@_parse_number:n {##4}
+ }
+ }
+ \cs_new_eq:cN { @@_parse_mix_ #1 :nw } \@@_parse_mix_cmyk:nw
+ }
+\cs_new_protected:Npn \@@_model_devicen_parse_generic:nn #1#2
+ {
+ \cs_new:cpn { @@_parse_model_ #1 :w } ##1 , ##2 \s_@@_stop
+ {
+ {#1}
+ { \@@_model_devicen_parse:nw {#2} ##1 , ##2 , \q_nil , \s_@@_stop }
+ }
+ \cs_new:cpe { @@_parse_mix_ #1 :nw }
+ ##1 ##2 \s_@@_mark ##3 \s_@@_stop
+ {
+ \exp_not:N \@@_model_devicen_mix:nw {##1}
+ ##2 \c_space_tl \exp_not:N \q_nil \c_space_tl \exp_not:N \s_@@_mark
+ ##3 \c_space_tl \exp_not:N \q_nil \c_space_tl \exp_not:N \s_@@_stop
+ }
+ }
+\cs_new:Npn \@@_model_devicen_parse:nw #1#2 , #3 \s_@@_stop
+ {
+ \int_compare:nNnT {#1} > 0
+ {
+ \quark_if_nil:nTF {#2}
+ { \prg_replicate:nn {#1} { 0 ~ } }
+ {
+ \@@_parse_number:n {#2}
+ \int_compare:nNnT {#1} > 1 { ~ }
+ \exp_args:Nf \@@_model_devicen_parse:nw
+ { \int_eval:n { #1 - 1 } } #3 \s_@@_stop
+ }
+ }
+ }
+\cs_new:Npn \@@_model_devicen_mix:nw #1#2 ~ #3 \s_@@_mark #4 ~ #5 \s_@@_stop
+ {
+ \fp_eval:n { #2 * #1 + #4 * ( 1 - #1 ) }
+ \quark_if_nil:oF { \tl_head:w #3 \q_stop }
+ {
+ \c_space_tl
+ \@@_model_devicen_mix:nw {#1} #3 \s_@@_mark #5 \s_@@_stop
+ }
+ }
+% \end{macrocode}
+% To construct the tint transformation, we have to use PostScript. The
+% aim is to have the final tint for each device colorant as
+% \[
+% 1 - \prod_{n} (1 - X_{n} D_{X_{n}})
+% \]
+% where $X$ is a DeviceN colorant and $D$ is the amount of device colorant
+% that the DeviceN colorant maps to. At the start of the process, the
+% PostScript stack will contain the $X_{n}$ values, whilst we have the
+% $D$ values on a per-DeviceN colorant basis. The more convenient approach
+% for us is therefore to take each DeviceN colorant in turn and find the
+% value $1 - X_{n} D_{X_{n}}$, multiplying as we go, and finalise with the
+% subtraction. That contrasts to \pkg{colorspace}: it splits the process
+% up by process color, which works better when you have a fixed list
+% of colorants. (\pkg{colorspace} only supports up to $4$ DeviceN colors,
+% and only \texttt{cmyk} as the alternative space.) To set this up,
+% we first need to know the number of values in the target color space:
+% this is easily handled as there are a very small range of possibles.
+% Once we have that information, it's relatively easy to build the required
+% PostScript using some generic code.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_model_devicen_init:nnn #1#2#3
+ {
+ \exp_args:Ne \@@_model_devicen_init:nnnn
+ {
+ \str_case:nn {#2}
+ {
+ { cmyk } { 4 }
+ { gray } { 1 }
+ { rgb } { 3 }
+ }
+ }
+ {#1} {#2} {#3}
+ }
+% \end{macrocode}
+% As we always need to split the alternative values into parts, we use a
+% shared auxiliary and only use a minimal difference between code paths.
+% Construction of the tint transformation is as far as possible done using
+% loops, which means there are some inefficiencies for device colors in
+% the \texttt{DeviceN} space: we roll the stack one-at-a-time even if there
+% is a potential shortcut. However, that way there is nothing to special-case.
+% Once this is sorted, we can write the tint transform object, which will
+% remain as the last object until we sort out the final step: the colorant
+% list.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_model_devicen_init:nnnn #1#2#3#4
+ {
+ \tl_set:Ne \l_@@_internal_tl
+ { \prg_replicate:nn {#1} { 1.0 ~ } }
+ \int_zero:N \l_@@_internal_int
+ \clist_map_inline:nn {#4}
+ {
+ \int_incr:N \l_@@_internal_int
+ \prop_get:NnN \g_@@_alternative_values_prop {##1}
+ \l_@@_value_tl
+ \exp_after:wN \@@_model_devicen_transform:w
+ \l_@@_value_tl , 0 , 0 , 0 , \s_@@_stop {#1} {#2}
+ }
+ \tl_put_right:Ne \l_@@_internal_tl
+ {
+ \prg_replicate:nn {#1}
+ { neg ~ 1.0 ~ add ~ #1 ~ -1 ~ roll ~ }
+ \int_eval:n { #2 + #1 } ~ #1 ~ roll
+ \prg_replicate:nn {#2} { ~ pop } ~
+ #1 ~ 1 ~ roll
+ }
+ \use:e
+ {
+ \@@_backend_devicen_init:nnn
+ {
+ \clist_map_function:nN {#4}
+ \@@_model_devicen_colorant:n
+ }
+ {
+ \str_case:nn {#3}
+ {
+ { cmyk } { /DeviceCMYK }
+ { gray } { /DeviceGray }
+ { rgb } { /DeviceRGB }
+ }
+ }
+ { \exp_not:V \l_@@_internal_tl }
+ }
+ }
+\cs_new_protected:Npn \@@_model_devicen_transform:w
+ #1 , #2 , #3 , #4 , #5 \s_@@_stop #6#7
+ {
+ \use:c { @@_model_devicen_transform_ #6 :nnnnn }
+ {#1} {#2} {#3} {#4} {#7}
+ }
+\cs_new_protected:cpn { @@_model_devicen_transform_1:nnnnn } #1#2#3#4#5
+ { \@@_model_devicen_transform:nnn {#5} { 1 } {#1} }
+\cs_new_protected:cpn { @@_model_devicen_transform_3:nnnnn } #1#2#3#4#5
+ {
+ \clist_map_inline:nn { #1 , #2 , #3 }
+ { \@@_model_devicen_transform:nnn {#5} { 3 } {##1} }
+ }
+\cs_new_protected:cpn { @@_model_devicen_transform_4:nnnnn } #1#2#3#4#5
+ {
+ \clist_map_inline:nn { #1 , #2 , #3 , #4 }
+ { \@@_model_devicen_transform:nnn {#5} { 4 } {##1} }
+ }
+\cs_new_protected:Npn \@@_model_devicen_transform:nnn #1#2#3
+ {
+ \tl_put_right:Ne \l_@@_internal_tl
+ {
+ \fp_compare:nNnF {#3} = \c_zero_fp
+ {
+ \int_eval:n { #1 - \l_@@_internal_int + #2 } ~ index ~
+ -#3 ~ mul ~ 1.0 ~ add ~ mul ~
+ }
+ #2 ~ -1 ~ roll ~
+ }
+ }
+\cs_new:Npn \@@_model_devicen_colorant:n #1
+ {
+ / \prop_item:Nn \g_@@_colorants_prop {#1} ~
+ }
+% \end{macrocode}
+% Here we need to set up conversion from the DeviceN space to the alternative
+% at the \TeX{} level. This also means supplying methods for inter-converting
+% to other parameter-based spaces. Essentially the approach is exactly the same
+% as the PostScript, just expressed in \TeX{} terms.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_model_devicen_convert:nnnn #1#2#3
+ {
+ \use:c { @@_model_devicen_convert_ #2 :nnn } {#1} {#3}
+ }
+\cs_generate_variant:Nn \@@_model_devicen_convert:nnnn { nnne }
+\cs_new_protected:Npn \@@_model_devicen_convert_cmyk:nnn #1#2
+ {
+ \tl_const:cn { c_@@_fallback_ #1 _tl } { cmyk }
+ \@@_model_devicen_convert:nnnnn {#1} { cmyk } { 4 } {#2}
+ }
+\cs_new_protected:Npn \@@_model_devicen_convert_gray:nnn #1#2
+ {
+ \tl_const:cn { c_@@_fallback_ #1 _tl } { gray }
+ \@@_model_devicen_convert:nnnnn {#1} { gray } { 1 } {#2}
+ }
+\cs_new_protected:Npn \@@_model_devicen_convert_rgb:nnn #1#2
+ {
+ \tl_const:cn { c_@@_fallback_ #1 _tl } { rgb }
+ \@@_model_devicen_convert:nnnnn {#1} { rgb } { 3 } {#2}
+ }
+\cs_new_protected:Npn \@@_model_devicen_convert:nnnnn #1#2#3#4#5
+ {
+ \cs_new:cpn { @@_convert_ #2 _ #1 :w } ##1 \s_@@_stop {#5}
+ \cs_new:cpe { @@_convert_ #1 _ #2 :w } ##1 \s_@@_stop
+ {
+ \exp_not:c { @@_convert_devicen_ #2 : \prg_replicate:nn {#3} { n } w }
+ \prg_replicate:nn {#3} { { 1 } }
+ ##1 ~ \exp_not:N \s_@@_mark
+ \clist_map_function:nN {#4} \@@_model_devicen_convert:n
+ {}
+ \exp_not:N \s_@@_stop
+ }
+ }
+\cs_new:Npn \@@_model_devicen_convert:n #1
+ {
+ {
+ \exp_args:Ne \@@_model_devicen_convert_aux:n
+ { \prop_item:Nn \g_@@_alternative_values_prop {#1} }
+ }
+ }
+\cs_new:Npn \@@_model_devicen_convert_aux:n #1
+ { \@@_model_devicen_convert_aux:w #1 , , , , \s_@@_stop }
+\cs_new:Npn \@@_model_devicen_convert_aux:w #1 , #2 , #3 , #4 , #5 \s_@@_stop
+ {
+ {#1}
+ \tl_if_blank:nF {#2}
+ {
+ {#2}
+ \tl_if_blank:nF {#3}
+ {
+ {#3}
+ \tl_if_blank:nF {#4} { {#4} }
+ }
+ }
+ }
+\cs_new:Npn \@@_convert_devicen_cmyk:nnnnw
+ #1#2#3#4#5 ~ #6 \s_@@_mark #7#8 \s_@@_stop
+ {
+ \@@_convert_devicen_cmyk:nnnnnnnnn {#5} {#1} {#2} {#3} {#4} #7
+ #6 \s_@@_mark #8 \s_@@_stop
+ }
+\cs_new:Npn \@@_convert_devicen_cmyk:nnnnnnnnn #1#2#3#4#5#6#7#8#9
+ {
+ \use:e
+ {
+ \exp_not:N \@@_convert_devicen_cmyk_aux:nnnnw
+ { \fp_eval:n { #2 * (1 - (#1 * #6)) } }
+ { \fp_eval:n { #3 * (1 - (#1 * #7)) } }
+ { \fp_eval:n { #4 * (1 - (#1 * #8)) } }
+ { \fp_eval:n { #5 * (1 - (#1 * #9)) } }
+ }
+ }
+\cs_new:Npn \@@_convert_devicen_cmyk_aux:nnnnw
+ #1#2#3#4 #5 \s_@@_mark #6 \s_@@_stop
+ {
+ \tl_if_blank:nTF {#5}
+ {
+ \fp_eval:n { 1 - #1 } ~
+ \fp_eval:n { 1 - #2 } ~
+ \fp_eval:n { 1 - #3 } ~
+ \fp_eval:n { 1 - #4 }
+ }
+ {
+ \@@_convert_devicen_cmyk:nnnnw {#1} {#2} {#3} {#4}
+ #5 \s_@@_mark #6 \s_@@_stop
+ }
+ }
+\cs_new:Npn \@@_convert_devicen_gray:nw
+ #1#2 ~ #3 \s_@@_mark #4#5 \s_@@_stop
+ {
+ \@@_convert_devicen_gray:nnn {#2} {#1} #4
+ #3 \s_@@_mark #5 \s_@@_stop
+ }
+\cs_new:Npn \@@_convert_devicen_gray:nnn #1#2#3
+ {
+ \exp_arsgs:Ne \@@_convert_devicen_gray_aux:nw
+ { \fp_eval:n { #2 * (1 - (#1 * #3)) } }
+ }
+\cs_new:Npn \@@_convert_devicen_gray_aux:nw
+ #1 #2 \s_@@_mark #3 \s_@@_stop
+ {
+ \tl_if_blank:nTF {#2}
+ { \fp_eval:n { 1 - #1 } }
+ {
+ \@@_convert_devicen_gray:nw {#1}
+ #2 \s_@@_mark #3 \s_@@_stop
+ }
+ }
+\cs_new:Npn \@@_convert_devicen_rgb:nnnw
+ #1#2#3#4 ~ #5 \s_@@_mark #6#7 \s_@@_stop
+ {
+ \@@_convert_devicen_rgb:nnnnnnn {#4} {#1} {#2} {#3} #6
+ #5 \s_@@_mark #7 \s_@@_stop
+ }
+\cs_new:Npn \@@_convert_devicen_rgb:nnnnnnn #1#2#3#4#5#6#7
+ {
+ \use:e
+ {
+ \exp_not:N \@@_convert_devicen_rgb_aux:nnnw
+ { \fp_eval:n { #2 * (1 - (#1 * #5)) } }
+ { \fp_eval:n { #3 * (1 - (#1 * #6)) } }
+ { \fp_eval:n { #4 * (1 - (#1 * #7)) } }
+ }
+ }
+\cs_new:Npn \@@_convert_devicen_rgb_aux:nnnw
+ #1#2#3 #4 \s_@@_mark #5 \s_@@_stop
+ {
+ \tl_if_blank:nTF {#4}
+ {
+ \fp_eval:n { 1 - #1 } ~
+ \fp_eval:n { 1 - #2 } ~
+ \fp_eval:n { 1 - #3 }
+ }
+ {
+ \@@_convert_devicen_rgb:nnnw {#1} {#2} {#3}
+ #4 \s_@@_mark #5 \s_@@_stop
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\c_@@_icc_colorspace_signatures_prop}
+% The signatures in the ICC file header indicating the underlying
+% colorspace. We map it to three values: The number of components,
+% the values corresponding to white, and the range.
+% \begin{macrocode}
+\prop_const_from_keyval:Nn \c_@@_icc_colorspace_signatures_prop
+ {
+% Gray
+ 47524159 = {1} {1} {0} {},
+% RGB
+ 52474220 = {3} {0~0~0} {1~1~1} {},
+% CMYK
+ 434D594B = {4} {0~0~0~1} {0~0~0~0} {},
+% Lab
+ 4C616220 = {3} {0~0~0} {100~0~0} {0~100~-128~127~-128~127}
+ }
+% \end{macrocode}
+% \end{variable}
+% \begin{macro}{\@@_model_iccbased:n}
+% \begin{macro}{\@@_model_iccbased:nn}
+% \begin{macro}{\@@_model_iccbased:nnn, \@@_model_iccbased_aux:nnn}
+% For an ICC profile, we need a file name and a number of components. The
+% file name is processed here so the backend can treat it as a string.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_model_iccbased:n #1
+ {
+ \prop_get:NnNTF \l_@@_internal_prop { file }
+ \l_@@_internal_tl
+ {
+ \exp_args:NV \@@_model_iccbased:nn
+ \l_@@_internal_tl {#1}
+ }
+ {
+ \msg_error:nnn { color }
+ { ICCBased-requires-file } {#1}
+ }
+ }
+\cs_new_protected:Npn \@@_model_iccbased:nn #1#2
+ {
+ \prop_get:NeNTF \c_@@_icc_colorspace_signatures_prop
+ { \file_hex_dump:nnn { #1 } { 17 } { 20 } } \l_@@_internal_tl
+ {
+ \exp_last_unbraced:NV \@@_model_iccbased_aux:nnnnnn
+ \l_@@_internal_tl { #2 } { #1 }
+ }
+ {
+ \msg_error:nnn { color }
+ { ICCBased-unsupported-colorspace } {#2}
+ }
+ }
+% \end{macrocode}
+% Here, we can use the same internals as for DeviceN approach as we know the
+% number of components. No conversion is possible, so there is no need
+% to worry about that at all.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_model_iccbased_aux:nnnnnn #1#2#3#4#5#6
+ {
+ \@@_model_init:nnn {#5} { iccbased } {#3}
+ \tl_const:cn { c_@@_fallback_ #5 _tl } { gray }
+ \cs_new:cpn { @@_convert_ #5 _gray:w } ##1 \s_@@_stop { 0 }
+ \cs_new:cpn { @@_convert_gray_ #5 :w } ##1 \s_@@_stop { #2 }
+ \use:c { @@_model_devicen_parse_ #1 :nn } {#5} {#1}
+ \exp_args:Ne \@@_backend_iccbased_init:nnn
+ { \file_full_name:n {#6} } {#1} {#4}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Applying profiles}
+%
+% \begin{macro}{\color_profile_apply:nn, \@@_profile_apply:nn}
+% \begin{macro}
+% {
+% \@@_profile_apply_gray:n ,
+% \@@_profile_apply_rgb:n ,
+% \@@_profile_apply_cmyk:n
+% }
+% With a limited range of outcomes, this is largely about getting data to the
+% backend.
+% \begin{macrocode}
+\cs_new_protected:Npn \color_profile_apply:nn #1#2
+ {
+ \exp_args:Ne \@@_profile_apply:nn
+ { \file_full_name:n {#1} } {#2}
+ }
+\cs_new_protected:Npn \@@_profile_apply:nn #1#2
+ {
+ \cs_if_exist_use:cF { @@_profile_apply_ \tl_to_str:n {#2} :n }
+ {
+ \msg_error:nnn { color } { ICC-Device-unknown } {#2}
+ \use_none:n
+ }
+ {#1}
+ }
+\cs_new_protected:Npn \@@_profile_apply_gray:n #1
+ {
+ \int_gincr:N \g_@@_model_int
+ \@@_backend_iccbased_device:nnn {#1} { Gray } { 1 }
+ }
+\cs_new_protected:Npn \@@_profile_apply_rgb:n #1
+ {
+ \int_gincr:N \g_@@_model_int
+ \@@_backend_iccbased_device:nnn {#1} { RGB } { 3 }
+ }
+\cs_new_protected:Npn \@@_profile_apply_cmyk:n #1
+ {
+ \int_gincr:N \g_@@_model_int
+ \@@_backend_iccbased_device:nnn {#1} { CMYK } { 4 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Diagnostics}
+%
+% \begin{macro}{\color_show:n, \color_log:n, \@@_show:Nn}
+% \begin{macro}[EXP]{\@@_show:n}
+% Extract the information about a color and format for the user: the approach
+% is similar to the keys module here.
+% \begin{macrocode}
+\cs_new_protected:Npn \color_show:n
+ { \@@_show:Nn \msg_show:nneeee }
+\cs_new_protected:Npn \color_log:n
+ { \@@_show:Nn \msg_log:nneeee }
+\cs_new_protected:Npn \@@_show:Nn #1#2
+ {
+ #1 { color } { show }
+ {#2}
+ {
+ \color_if_exist:nT {#2}
+ {
+ \exp_args:Nv \@@_show:n { l_@@_named_ #2 _tl }
+ \prop_map_function:cN
+ { l_@@_named_ #2 _prop }
+ \msg_show_item_unbraced:nn
+ }
+ }
+ { }
+ { }
+ }
+\cs_new:Npn \@@_show:n #1
+ {
+ \msg_show_item_unbraced:nn { model } {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+% \begin{macrocode}
+\msg_new:nnnn { color } { CIELAB-requires-illuminant }
+ { CIELAB~color~space~'#1'~require~an~illuminant. }
+ {
+ LaTeX~has~been~asked~to~create~a~separation~color~space~using~
+ CIELAB~specifications,~but~no~\\ \\
+ \iow_indent:n { illuminant~=~<basis> }
+ \\ \\
+ key~was~given~with~the~correct~information.~LaTeX~will~use~illuminant~
+ 'd50'~for~recovery.
+ }
+\msg_new:nnnn { color } { conversion-not-available }
+ { No~model~conversion~available~from~'#1'~to~'#2'. }
+ {
+ LaTeX~has~been~asked~to~convert~a~color~from~model~'#1'~
+ to~model'#2',~but~there~is~no~method~available~to~do~that.
+ }
+\msg_new:nnnn { color } { DeviceN-inconsistent-alternative }
+ { DeviceN~color~spaces~require~a~single~alternative~space. }
+ {
+ LaTeX~has~been~asked~to~create~a~DeviceN~color~space~'#1',~
+ but~the~constituent~colors~do~not~have~a~common~alternative~
+ color.
+ }
+\msg_new:nnnn { color } { DeviceN-no-alternative }
+ { DeviceN~color~spaces~require~an~alternative~space. }
+ {
+ LaTeX~has~been~asked~to~create~a~DeviceN~color~space~'#1',~
+ but~the~constituent~colors~do~not~all~have~a~device-based~alternative.
+ }
+\msg_new:nnnn { color } { DeviceN-requires-names }
+ { DeviceN~color~space~'#1'~require~a~list~of~names. }
+ {
+ LaTeX~has~been~asked~to~create~a~DeviceN~color~space,~
+ but~no~\\ \\
+ \iow_indent:n { names~=~<names> }
+ \\ \\
+ key~was~given~with~the~correct~information.
+ }
+\msg_new:nnnn { color } { ICC-Device-unknown }
+ { Unknown~device~color~space~'#1'. }
+ {
+ LaTeX~has~been~asked~to~apply~an~ICC~profile~but~the~device~color~space~
+ '#1'~is~unknown.
+ }
+\msg_new:nnnn { color } { ICCBased-unsupported-colorspace }
+ { ICCBased~color~space~'#1'~uses~an~unsupported~data~color~space. }
+ {
+ LaTeX~has~been~asked~to~create~a~ICCBased~colorspace,~but~the~
+ used~data~colorspace~is~not~supported.~ICC~profiles~used~for~
+ defining~a~ICCBased~colorspace~should~use~a~Lab,~RGB,~or~
+ CMYK~data~colorspace.~LaTeX~will~ignore~this~request.
+ }
+\msg_new:nnnn { color } { ICCBased-requires-file }
+ { ICCBased~color~space~'#1'~require~an~file. }
+ {
+ LaTeX~has~been~asked~to~create~an~ICCBased~color~space,~but~no~\\ \\
+ \iow_indent:n { file~=~<name> }
+ \\ \\
+ key~was~given~with~the~correct~information.~LaTeX~will~ignore~this~
+ request.
+ }
+\msg_new:nnnn { color } { model-already-defined }
+ { Color~model~'#1'~already~defined. }
+ {
+ LaTeX~was~asked~to~define~a~new~color~model~called~'#1',~but~
+ this~color~model~already~exists.
+ }
+\msg_new:nnnn { color } { out-of-range }
+ { Input~value~#1~out~of~range~[#2,~#3]. }
+ {
+ LaTeX~was~expecting~a~value~in~the~range~[#2,~#3]~as~part~of~a~color,~
+ but~you~gave~#1.~LaTeX~will~assume~you~meant~the~limit~of~the~range~
+ and~continue.
+ }
+\msg_new:nnnn { color } { separation-alternative-model }
+ { Separation~color~space~'#1'~require~an~alternative~model. }
+ {
+ LaTeX~has~been~asked~to~create~a~separation~color~space,~
+ but~no~\\ \\
+ \iow_indent:n { alternative-model~=~<model> }
+ \\ \\
+ key~was~given~with~the~correct~information.
+ }
+\msg_new:nnnn { color } { separation-alternative-values }
+ { Separation~color~space~'#1'~require~values~for~the~alternative~space. }
+ {
+ LaTeX~has~been~asked~to~create~a~separation~color~space,~
+ but~no~\\ \\
+ \iow_indent:n { alternative-values~=~<model> }
+ \\ \\
+ key~was~given~with~the~correct~information.
+ }
+\msg_new:nnnn { color } { separation-requires-name }
+ { Separation~color~space~'#1'~require~a~formal~name. }
+ {
+ LaTeX~has~been~asked~to~create~a~separation~color~space,~
+ but~no~\\ \\
+ \iow_indent:n { name~=~<formal~name> }
+ \\ \\
+ key~was~given~with~the~correct~information.
+ }
+\msg_new:nnn { color } { unhandled-model }
+ {
+ Unhandled~color~model~in~LaTeX2e~value~"#1":
+ \\ \\
+ falling~back~on~grayscale.
+ }
+\msg_new:nnnn { color } { unknown-color }
+ { Unknown~color~'#1'. }
+ {
+ LaTeX~has~been~asked~to~use~a~color~named~'#1',~
+ but~this~has~never~been~defined.
+ }
+\msg_new:nnnn { color } { unknown-alternative-model }
+ { Separation~color~space~'#1'~require~an~valid~alternative~space. }
+ {
+ LaTeX~has~been~asked~to~create~a~separation~color~space,~
+ but~the~model~given~as\\ \\
+ \iow_indent:n { alternative-model~=~<model> }
+ \\ \\
+ is~unknown.
+ }
+\msg_new:nnnn { color } { unknown-export-format }
+ { Unknown~export~format~'#1'. }
+ {
+ LaTeX~has~been~asked~to~export~a~color~in~format~'#1',~
+ but~this~has~never~been~defined.
+ }
+\msg_new:nnnn { color } { unknown-CIELAB-illuminant }
+ { Unknown~illuminant~model~'#1'. }
+ {
+ LaTeX~has~been~asked~to~use~create~a~color~space~using~CIELAB~
+ illuminant~'#1',~but~this~does~not~exist.
+ }
+\msg_new:nnnn { color } { unknown-model }
+ { Unknown~color~model~'#1'. }
+ {
+ LaTeX~has~been~asked~to~use~a~color~model~called~'#1',~
+ but~this~model~is~not~set~up.
+ }
+\msg_new:nnnn { color } { unknown-model-type }
+ { Unknown~color~model~type~'#1'. }
+ {
+ LaTeX~has~been~asked~to~create~a~new~color~model~called~'#1',~
+ but~this~type~of~model~was~never~set~up.
+ }
+\prop_gput:Nnn \g_msg_module_name_prop { color } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { color } { }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\msg_new:nnn { color } { show }
+ {
+ The~color~#1~
+ \tl_if_empty:nTF {#2}
+ { is~undefined. }
+ { has~the~properties: #2 }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3debug.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3debug.dtx
new file mode 100644
index 00000000000..6dbe13f96e6
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3debug.dtx
@@ -0,0 +1,1478 @@
+% \iffalse meta-comment
+%
+%% File: l3debug.dtx
+%
+% Copyright (C) 2019-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3debug} module\\ Debugging support^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3debug} implementation}
+%
+% Internal kernel functions that are only defined here are listed in
+% \pkg{l3kernel-functions},
+% see~\ref{sec:l3kernel-functions:l3debug-internals}.
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=debug>
+% \end{macrocode}
+%
+% Standard file identification.
+% \begin{macrocode}
+\ProvidesExplFile{l3debug.def}{2024-04-11}{}{L3 Debugging support}
+% \end{macrocode}
+%
+% \begin{variable}{\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_use_i_delimit_by_s_stop:nw}
+% Functions to gobble up to a scan mark.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_i_delimit_by_s_stop:nw #1 #2 \s_@@_stop {#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\q_@@_recursion_tail,\q_@@_recursion_stop}
+% Internal quarks.
+% \begin{macrocode}
+\quark_new:N \q_@@_recursion_tail
+\quark_new:N \q_@@_recursion_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_if_recursion_tail_stop:N}
+% Functions to query recursion quarks.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_none_delimit_by_q_recursion_stop:w
+ #1 \q_@@_recursion_stop { }
+\__kernel_quark_new_test:N \@@_if_recursion_tail_stop:N
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\debug_on:n, \debug_off:n, \@@_all_on:, \@@_all_off:}
+% \begin{macrocode}
+\cs_gset_protected:Npn \debug_on:n #1
+ {
+ \exp_args:No \clist_map_inline:nn { \tl_to_str:n {#1} }
+ {
+ \cs_if_exist_use:cF { @@_ ##1 _on: }
+ { \msg_error:nnn { debug } { debug } {##1} }
+ }
+ }
+\cs_gset_protected:Npn \debug_off:n #1
+ {
+ \exp_args:No \clist_map_inline:nn { \tl_to_str:n {#1} }
+ {
+ \cs_if_exist_use:cF { @@_ ##1 _off: }
+ { \msg_error:nnn { debug } { debug } {##1} }
+ }
+ }
+\cs_new_protected:Npn \@@_all_on:
+ {
+ \debug_on:n
+ {
+ check-declarations ,
+ check-expressions ,
+ deprecation ,
+ log-functions ,
+ }
+ }
+\cs_new_protected:Npn \@@_all_off:
+ {
+ \debug_off:n
+ {
+ check-declarations ,
+ check-expressions ,
+ deprecation ,
+ log-functions ,
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\debug_suspend:, \debug_resume:}
+% \begin{macro}{\@@_suspended:T}
+% \begin{macro}{\l_@@_suspended_tl}
+% Suspend and resume locally all debug-related errors and logging
+% except deprecation errors. The \cs{debug_suspend:} and \cs{debug_resume:}
+% pairs can be nested. We keep track of nesting in a token list
+% containing a number of periods. At first begin with the
+% \enquote{non-suspended} version of \cs{@@_suspended:T}.
+% \begin{macrocode}
+\tl_new:N \l_@@_suspended_tl { }
+\cs_gset_protected:Npn \debug_suspend:
+ {
+ \tl_put_right:Nn \l_@@_suspended_tl { . }
+ \cs_set_eq:NN \@@_suspended:T \use:n
+ }
+\cs_gset_protected:Npn \debug_resume:
+ {
+ \__kernel_tl_set:Nx \l_@@_suspended_tl
+ { \tl_tail:N \l_@@_suspended_tl }
+ \tl_if_empty:NT \l_@@_suspended_tl
+ {
+ \cs_set_eq:NN \@@_suspended:T \use_none:n
+ }
+ }
+\cs_new_eq:NN \@@_suspended:T \use_none:n
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_check-declarations_on:, \@@_check-declarations_off:}
+% \begin{macro}
+% {
+% \__kernel_chk_var_exist:N,
+% \__kernel_chk_cs_exist:N,
+% \__kernel_chk_cs_exist:c
+% }
+% \begin{macro}[EXP]{\__kernel_chk_flag_exist:NN}
+% \begin{macro}{\__kernel_chk_var_local:N, \__kernel_chk_var_global:N}
+% \begin{macro}{\__kernel_chk_var_scope:NN}
+% When debugging is enabled these two functions set up functions that
+% test their argument (when \texttt{check-declarations} is active)
+% \begin{itemize}
+% \item \cs{__kernel_chk_var_exist:N} and \cs{__kernel_chk_cs_exist:N}, two
+% functions that test that their argument is defined;
+% \item \cs{__kernel_chk_var_scope:NN} that checks that its argument |#2|
+% has scope |#1|.
+% \item \cs{__kernel_chk_var_local:N} and \cs{__kernel_chk_var_global:N} that
+% perform both checks.
+% \end{itemize}
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_chk_var_exist:N #1 { }
+\cs_new_protected:Npn \__kernel_chk_cs_exist:N #1 { }
+\cs_generate_variant:Nn \__kernel_chk_cs_exist:N { c }
+\cs_new:Npn \__kernel_chk_flag_exist:NN { }
+\cs_new_protected:Npn \__kernel_chk_var_local:N #1 { }
+\cs_new_protected:Npn \__kernel_chk_var_global:N #1 { }
+\cs_new_protected:Npn \__kernel_chk_var_scope:NN #1#2 { }
+\cs_new_protected:cpn { @@_check-declarations_on: }
+ {
+ \cs_set_protected:Npn \__kernel_chk_var_exist:N ##1
+ {
+ \@@_suspended:T \use_none:nnn
+ \cs_if_exist:NF ##1
+ {
+ \msg_error:nne { debug } { non-declared-variable }
+ { \token_to_str:N ##1 }
+ }
+ }
+ \cs_set_protected:Npn \__kernel_chk_cs_exist:N ##1
+ {
+ \@@_suspended:T \use_none:nnn
+ \cs_if_exist:NF ##1
+ {
+ \msg_error:nne { kernel } { command-not-defined }
+ { \token_to_str:N ##1 }
+ }
+ }
+ \cs_set:Npn \__kernel_chk_flag_exist:NN ##1##2
+ {
+ \@@_suspended:T \use_iii:nnnn
+ \flag_if_exist:NTF ##2
+ { ##1 ##2 }
+ {
+ \msg_expandable_error:nnn { kernel } { bad-variable } {##2}
+ ##1 \l_tmpa_flag
+ }
+ }
+ \cs_set_protected:Npn \__kernel_chk_var_scope:NN
+ {
+ \@@_suspended:T \use_none:nnn
+ \@@_chk_var_scope_aux:NN
+ }
+ \cs_set_protected:Npn \__kernel_chk_var_local:N ##1
+ {
+ \@@_suspended:T \use_none:nnnnn
+ \__kernel_chk_var_exist:N ##1
+ \@@_chk_var_scope_aux:NN l ##1
+ }
+ \cs_set_protected:Npn \__kernel_chk_var_global:N ##1
+ {
+ \@@_suspended:T \use_none:nnnnn
+ \__kernel_chk_var_exist:N ##1
+ \@@_chk_var_scope_aux:NN g ##1
+ }
+ }
+\cs_new_protected:cpn { @@_check-declarations_off: }
+ {
+ \cs_set_protected:Npn \__kernel_chk_var_exist:N ##1 { }
+ \cs_set_protected:Npn \__kernel_chk_cs_exist:N ##1 { }
+ \cs_set:Npn \__kernel_chk_flag_exist:NN { }
+ \cs_set_protected:Npn \__kernel_chk_var_local:N ##1 { }
+ \cs_set_protected:Npn \__kernel_chk_var_global:N ##1 { }
+ \cs_set_protected:Npn \__kernel_chk_var_scope:NN ##1##2 { }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_chk_var_scope_aux:NN}
+% \begin{macro}{\@@_chk_var_scope_aux:Nn}
+% \begin{macro}{\@@_chk_var_scope_aux:NNn}
+% First check whether the name of the variable |#2| starts with
+% \meta{letter}|_|. If it does then pass that letter, the
+% \meta{scope}, and the variable name to
+% \cs{@@_chk_var_scope_aux:NNn}. That function compares the two
+% letters and triggers an error if they differ (the \cs{scan_stop:}
+% case is not reachable here). If the second character was not |_|
+% then pass the same data to the same auxiliary, except for its first
+% argument which is now a control sequence. That control sequence is
+% actually a token list (but to avoid triggering the checking code we
+% manipulate it using \cs{cs_set_nopar:Npn}) containing a single
+% letter \meta{scope} according to what the first assignment to the
+% given variable was.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_chk_var_scope_aux:NN #1#2
+ { \exp_args:NNf \@@_chk_var_scope_aux:Nn #1 { \cs_to_str:N #2 } }
+\cs_new_protected:Npn \@@_chk_var_scope_aux:Nn #1#2
+ {
+ \if:w _ \use_i:nn \@@_use_i_delimit_by_s_stop:nw #2 ? ? \s_@@_stop
+ \exp_after:wN \@@_chk_var_scope_aux:NNn
+ \@@_use_i_delimit_by_s_stop:nw #2 ? \s_@@_stop
+ #1 {#2}
+ \else:
+ \exp_args:Nc \@@_chk_var_scope_aux:NNn
+ { @@_chk_/ #2 }
+ #1 {#2}
+ \fi:
+ }
+\cs_new_protected:Npn \@@_chk_var_scope_aux:NNn #1#2#3
+ {
+ \if:w #1 #2
+ \else:
+ \if:w #1 \scan_stop:
+ \cs_gset_nopar:Npn #1 {#2}
+ \else:
+ \msg_error:nneee { debug } { local-global }
+ {#1} {#2} { \iow_char:N \\ #3 }
+ \fi:
+ \fi:
+ }
+\use:c { @@_check-declarations_off: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_log-functions_on:, \@@_log-functions_off:}
+% \begin{macro}{\__kernel_debug_log:e}
+% These two functions (corresponding to the \pkg{expl3} option
+% \texttt{log-functions}) control whether \cs{__kernel_debug_log:e}
+% writes to the log file or not. By default, logging is off.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_log-functions_on: }
+ {
+ \cs_set_protected:Npn \__kernel_debug_log:e
+ { \@@_suspended:T \use_none:nn \iow_log:e }
+ }
+\cs_new_protected:cpn { @@_log-functions_off: }
+ { \cs_set_protected:Npn \__kernel_debug_log:e { \use_none:n } }
+\cs_new_protected:Npn \__kernel_debug_log:e { \use_none:n }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_check-expressions_on:, \@@_check-expressions_off:}
+% \begin{macro}{\__kernel_chk_expr:nNnN}
+% \begin{macro}{\@@_chk_expr_aux:nNnN}
+% When debugging is enabled these two functions set
+% \cs{__kernel_chk_expr:nNnN} to test or not whether the given
+% expression is valid. The idea is to evaluate the expression within
+% a brace group (to catch trailing \cs{use_none:nn} or similar), then
+% test that the result is what we expect. This is done by turning it
+% to an integer and hitting that with \cs{tex_romannumeral:D} after
+% replacing the first character by |-0|. If all goes well, that
+% primitive finds a non-positive integer and gives an empty output.
+% If the original expression evaluation stopped early it leaves a
+% trailing \cs{tex_relax:D}, which stops the second evaluation (used
+% to convert to integer) before it encounters the final
+% \cs{tex_relax:D}. Since \cs{tex_romannumeral:D} does not absorb
+% \cs{tex_relax:D} the output will be nonempty. Note that |#3| is
+% empty except for mu expressions for which it is \cs{tex_mutoglue:D}
+% to avoid an \enquote{incompatible glue units} error. Note also that
+% if we had omitted the first \cs{tex_relax:D} then for instance
+% |1+2\relax+3| would incorrectly be accepted as a valid integer
+% expression.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_check-expressions_on: }
+ {
+ \cs_set:Npn \__kernel_chk_expr:nNnN ##1##2
+ {
+ \@@_suspended:T { ##1 \use_none:nnnnnnn }
+ \exp_after:wN \@@_chk_expr_aux:nNnN
+ \exp_after:wN { \tex_the:D ##2 ##1 \scan_stop: }
+ ##2
+ }
+ }
+\cs_new_protected:cpn { @@_check-expressions_off: }
+ { \cs_set:Npn \__kernel_chk_expr:nNnN ##1##2##3##4 {##1} }
+\cs_new:Npn \__kernel_chk_expr:nNnN #1#2#3#4 {#1}
+\cs_new:Npn \@@_chk_expr_aux:nNnN #1#2#3#4
+ {
+ \tl_if_empty:oF
+ {
+ \tex_romannumeral:D - 0
+ \exp_after:wN \use_none:n
+ \int_value:w #3 #2 #1 \scan_stop:
+ }
+ {
+ \msg_expandable_error:nnnn
+ { debug } { expr } {#4} {#1}
+ }
+ #1
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_deprecation_on:, \@@_deprecation_off:}
+% Make deprecated commands throw errors if the user requests it.
+% This relies on two token lists, filled up in \pkg{l3deprecation} by
+% calls to \cs{__kernel_deprecation_code:nn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_deprecation_on:
+ { \g_@@_deprecation_on_tl }
+\cs_new_protected:Npn \@@_deprecation_off:
+ { \g_@@_deprecation_off_tl }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{
+% \l_@@_internal_tl,
+% \l_@@_tmpa_tl,
+% \l_@@_tmpb_tl,
+% }
+% For patching.
+% \begin{macrocode}
+\tl_new:N \l_@@_internal_tl
+\tl_new:N \l_@@_tmpa_tl
+\tl_new:N \l_@@_tmpb_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{
+% \@@_generate_parameter_list:NNN,
+% \@@_build_parm_text:n,
+% \@@_build_arg_list:n,
+% \@@_arg_list_from_signature:nNN,
+% \@@_arg_check_invalid:N,
+% \@@_parm_terminate:w,
+% \@@_arg_if_braced:n,
+% \@@_get_base_form:N,
+% \@@_arg_return:N,
+% }
+% \begin{macro}[TF]{\@@_arg_if_braced:N}
+% Some functions don't take the arguments their signature indicates.
+% For instance, \cs{clist_concat:NNN} doesn't take (directly) any
+% argument, so patching it with something that uses |#1|, |#2|, or
+% |#3| results in ``Illegal parameter number in definition of
+% \cs{clist_concat:NNN}''.
+%
+% Instead of changing \emph{the} definition of the macros, we'll
+% create a copy of such macros, say, \cs[no-index]{@@_clist_concat:NNN} which
+% will be defined as
+% |<debug code with #1, #2 and #3>\clist_concat:NNN#1#2#3|. For that
+% we need to identify the signature of every function and build the
+% appropriate parameter list.
+%
+% \cs{@@_generate_parameter_list:NNN} takes a function in |#1| and
+% returns teo parameter lists: |#2| contains the simple |#1#2#3| as
+% would be used in the \meta{parameter~text} of the definition and
+% |#3| contains the same parameters but with braces where necessary.
+%
+% With the current implementation the resulting |#3| is, for example
+% for |\some_function:NnNn|, |#1{#2}#3{#4}|. While this is correct,
+% it might be unnecessary. Bracing everything will usually have the
+% same outcome (unless the function was misused in the first place).
+% What should be done?
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_generate_parameter_list:NNN #1#2#3
+ {
+ \__kernel_tl_set:Nx \l_@@_internal_tl
+ { \exp_last_unbraced:Nf \use_ii:nnn \cs_split_function:N #1 }
+ \__kernel_tl_set:Nx #2
+ { \exp_args:NV \@@_build_parm_text:n \l_@@_internal_tl }
+ \__kernel_tl_set:Nx #3
+ { \exp_args:NV \@@_build_arg_list:n \l_@@_internal_tl }
+ }
+\cs_new:Npn \@@_build_parm_text:n #1
+ {
+ \@@_arg_list_from_signature:nNN { 1 } \c_false_bool #1
+ \q_@@_recursion_tail \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_build_arg_list:n #1
+ {
+ \@@_arg_list_from_signature:nNN { 1 } \c_true_bool #1
+ \q_@@_recursion_tail \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_arg_list_from_signature:nNN #1 #2 #3
+ {
+ \@@_if_recursion_tail_stop:N #3
+ \@@_arg_check_invalid:N #3
+ \bool_if:NT #2 { \@@_arg_if_braced:NT #3 { \use_none:n } }
+ \use:n { \c_hash_str \int_eval:n {#1} }
+ \exp_args:Nf \@@_arg_list_from_signature:nNN
+ { \int_eval:n {#1+1} } #2
+ }
+% \end{macrocode}
+% Argument types |w|, |p|, |T|, and |F| shouldn't be included in the
+% parameter lists, so we abort the loop if either is found.
+% \begin{macrocode}
+\cs_new:Npn \@@_arg_check_invalid:N #1
+ {
+ \if:w w #1 \@@_parm_terminate:w \else:
+ \if:w p #1 \@@_parm_terminate:w \else:
+ \if:w T #1 \@@_parm_terminate:w \else:
+ \if:w F #1 \@@_parm_terminate:w \else:
+ \exp:w
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \exp_end:
+ }
+\cs_new:Npn \@@_parm_terminate:w
+ { \exp_after:wN \@@_use_none_delimit_by_q_recursion_stop:w \exp:w }
+\prg_new_conditional:Npnn \@@_arg_if_braced:N #1 { T }
+ { \exp_args:Nf \@@_arg_if_braced:n { \@@_get_base_form:N #1 } }
+\cs_new:Npn \@@_arg_if_braced:n #1
+ {
+ \if:w n #1 \prg_return_true: \else:
+ \if:w N #1 \prg_return_false: \else:
+ \msg_expandable_error:nnn
+ { debug } { bad-arg-type } {#1}
+ \fi:
+ \fi:
+ }
+\msg_new:nnn { debug } { bad-arg-type }
+ { Wrong~argument~type~#1. }
+% \end{macrocode}
+% The macro below gets the base form of an
+% argument type given a variant. It serves only to differentiate
+% arguments which should be braced from ones which shouldn't. If all
+% were to be braced this would be unnecessary. I moved the |n| and |N|
+% variants to the beginning of the test as the are much more common
+% here.
+% \begin{macrocode}
+\cs_new:Npn \@@_get_base_form:N #1
+ {
+ \if:w n #1 \@@_arg_return:N n \else:
+ \if:w N #1 \@@_arg_return:N N \else:
+ \if:w c #1 \@@_arg_return:N N \else:
+ \if:w o #1 \@@_arg_return:N n \else:
+ \if:w V #1 \@@_arg_return:N n \else:
+ \if:w v #1 \@@_arg_return:N n \else:
+ \if:w f #1 \@@_arg_return:N n \else:
+ \if:w e #1 \@@_arg_return:N n \else:
+ \if:w x #1 \@@_arg_return:N n \else:
+ \@@_arg_return:N \scan_stop:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \exp_stop_f:
+ }
+\cs_new:Npn \@@_arg_return:N #1
+ { \exp_after:wN #1 \exp:w \exp_end_continue_f:w }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{
+% \__kernel_patch:nnn,
+% \__kernel_patch_aux:nnn,
+% \@@_setup_debug_code:Nnn,
+% \@@_add_to_debug_code:Nnn,
+% \@@_insert_debug_code:Nnn,
+% \__kernel_patch_weird:nnn,
+% \__kernel_patch_weird_aux:nnn,
+% \@@_patch_weird:Nnn,
+% }
+% Simple patching by adding material at the start and end of (a
+% collection of) functions is straight-forward as we know the catcode
+% set up. The approach is essentially that in \pkg{etoolbox}. Notice
+% the need to worry about spaces: those are otherwise lost as normally
+% in \pkg{expl3} code they would be~|~|.
+%
+% As discussed above, some functions don't take arguments, so we can't
+% patch something that uses an argument in them. For these functions
+% \cs{__kernel_patch:nnn} is used. It starts by creating a copy of the
+% function (say, \cs{clist_concat:NNN}) with a |__debug_| prefix in
+% the name. This copy won't be changed. The code redefines the
+% original function to take the exact same arguments as advertised in
+% its signature (see \cs{@@_generate_parameter_list:NNN} above).
+% The redefined function also contains the debug code in the proper
+% position. If a function with the same name and the |__debug_| prefix
+% was already defined, then the macro patches that definition by
+% adding more debug code to it.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \__kernel_patch:nnn
+ {
+ \group_begin:
+ \char_set_catcode_other:N \#
+ \__kernel_patch_aux:nnn
+ }
+ \cs_set_protected:Npn \__kernel_patch_aux:nnn #1#2#3
+ {
+ \char_set_catcode_parameter:N \#
+ \char_set_catcode_space:N \ %
+ \tex_endlinechar:D -1 \scan_stop:
+ \tl_map_inline:nn {#3}
+ {
+ \cs_if_exist:cTF { @@_ \cs_to_str:N ##1 }
+ { \@@_add_to_debug_code:Nnn }
+ { \@@_setup_debug_code:Nnn }
+ ##1 {#1} {#2}
+ }
+ \group_end:
+ }
+ \cs_set_protected:Npn \@@_setup_debug_code:Nnn #1#2#3
+ {
+ \cs_gset_eq:cN { @@_ \cs_to_str:N #1 } #1
+ \@@_generate_parameter_list:NNN #1 \l_@@_tmpa_tl \l_@@_tmpb_tl
+ \exp_args:Ne \tex_scantokens:D
+ {
+ \tex_global:D \cs_prefix_spec:N #1
+ \tex_def:D \exp_not:N #1
+ \tl_use:N \l_@@_tmpa_tl
+ {
+ \tl_to_str:n {#2}
+ \exp_not:c { @@_ \cs_to_str:N #1 }
+ \tl_use:N \l_@@_tmpb_tl
+ \tl_to_str:n {#3}
+ }
+ }
+ }
+ \cs_set_protected:Npn \@@_add_to_debug_code:Nnn #1#2#3
+ {
+ \use:e
+ {
+ \cs_set:Npn \exp_not:N \@@_tmp:w
+ ##1 \tl_to_str:n { macro: }
+ ##2 \tl_to_str:n { -> }
+ ##3 \c_backslash_str \tl_to_str:n { @@_ }
+ \cs_to_str:N #1
+ ##4 \s_@@_stop
+ {
+ \exp_not:N \exp_args:Ne \exp_not:N \tex_scantokens:D
+ {
+ \tex_global:D ##1
+ \tex_def:D \exp_not:N #1 ##2
+ {
+ ##3 \tl_to_str:n {#2}
+ \c_backslash_str @@_ \cs_to_str:N #1
+ ##4 \tl_to_str:n {#3}
+ }
+ }
+ }
+ }
+ \exp_after:wN \@@_tmp:w \cs_meaning:N #1 \s_@@_stop
+ }
+% \end{macrocode}
+% Some functions, however, won't work with the signature reading setup
+% above because their signature contains |w|eird arguments. These
+% functions need to be patched using \cs{__kernel_patch_weird:nnn},
+% which won't make a copy of the function, rather it will patch the
+% debug code directly into it. This means that whatever argument the
+% debug code uses must be actually used by the patched function.
+% \begin{macrocode}
+ \cs_set_protected:Npn \__kernel_patch_weird:nnn
+ {
+ \group_begin:
+ \char_set_catcode_other:N \#
+ \__kernel_patch_weird_aux:nnn
+ }
+ \cs_set_protected:Npn \__kernel_patch_weird_aux:nnn #1#2#3
+ {
+ \char_set_catcode_parameter:N \#
+ \char_set_catcode_space:N \ %
+ \tex_endlinechar:D -1 \scan_stop:
+ \tl_map_inline:nn {#3}
+ { \@@_patch_weird:Nnn ##1 {#1} {#2} }
+ \group_end:
+ }
+ \cs_set_protected:Npn \@@_patch_weird:Nnn #1#2#3
+ {
+ \use:e
+ {
+ \tex_endlinechar:D -1 \scan_stop:
+ \exp_not:N \tex_scantokens:D
+ {
+ \tex_global:D \cs_prefix_spec:N #1
+ \tex_def:D \exp_not:N #1
+ \cs_parameter_spec:N #1
+ {
+ \tl_to_str:n {#2}
+ \cs_replacement_spec:N #1
+ \tl_to_str:n {#3}
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% Patching the second argument to ensure it exists. This happens before
+% we alter |#1| so the ordering is correct. For many variable types
+% such as \texttt{int} a low-level error occurs when |#2| is unknown, so
+% adding a check is not needed.
+% \begin{macrocode}
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_exist:N #2 }
+ { }
+ {
+ \bool_set_eq:NN
+ \bool_gset_eq:NN
+ \clist_set_eq:NN
+ \clist_gset_eq:NN
+ \fp_set_eq:NN
+ \fp_gset_eq:NN
+ \prop_set_eq:NN
+ \prop_gset_eq:NN
+ \seq_set_eq:NN
+ \seq_gset_eq:NN
+ \str_set_eq:NN
+ \str_gset_eq:NN
+ \tl_set_eq:NN
+ \tl_gset_eq:NN
+ }
+% \end{macrocode}
+%
+% Patching both second and third arguments.
+% \begin{macrocode}
+ \__kernel_patch:nnn
+ {
+ \__kernel_chk_var_exist:N #2
+ \__kernel_chk_var_exist:N #3
+ }
+ { }
+ {
+ \clist_concat:NNN
+ \clist_gconcat:NNN
+ \prop_concat:NNN
+ \prop_gconcat:NNN
+ \seq_concat:NNN
+ \seq_gconcat:NNN
+ \str_concat:NNN
+ \str_gconcat:NNN
+ \tl_concat:NNN
+ \tl_gconcat:NNN
+ }
+% \end{macrocode}
+%
+%
+% \begin{macrocode}
+\cs_gset_protected:Npn \__kernel_tl_set:Nx { \cs_set_nopar:Npe }
+\cs_gset_protected:Npn \__kernel_tl_gset:Nx { \cs_gset_nopar:Npe }
+% \end{macrocode}
+%
+% Patching where the first argument to a function needs scope-checking:
+% either local or global (so two lists).
+% \begin{macrocode}
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_local:N #1 }
+ { }
+ {
+ \bool_set:Nn
+ \bool_set_eq:NN
+ \bool_set_true:N
+ \bool_set_false:N
+ \box_set_eq:NN
+ \box_set_eq_drop:NN
+ \box_set_to_last:N
+ \clist_clear:N
+ \clist_set_eq:NN
+ \dim_zero:N
+ \dim_set:Nn
+ \dim_set_eq:NN
+ \dim_add:Nn
+ \dim_sub:Nn
+ \fp_set_eq:NN
+ \int_zero:N
+ \int_set_eq:NN
+ \int_add:Nn
+ \int_sub:Nn
+ \int_incr:N
+ \int_decr:N
+ \int_set:Nn
+ \hbox_set:Nn
+ \hbox_set_to_wd:Nnn
+ \hbox_set:Nw
+ \hbox_set_to_wd:Nnw
+ \muskip_zero:N
+ \muskip_set:Nn
+ \muskip_add:Nn
+ \muskip_sub:Nn
+ \muskip_set_eq:NN
+ \prop_clear:N
+ \prop_concat:NNN
+ \prop_pop:NnN
+ \prop_pop:NnNT
+ \prop_pop:NnNF
+ \prop_pop:NnNTF
+ \prop_put:Nnn
+ \prop_put_if_not_in:Nnn
+ \prop_put_from_keyval:Nn
+ \prop_remove:Nn
+ \prop_set_eq:NN
+ \prop_set_from_keyval:Nn
+ \seq_set_eq:NN
+ \skip_zero:N
+ \skip_set:Nn
+ \skip_set_eq:NN
+ \skip_add:Nn
+ \skip_sub:Nn
+ \str_clear:N
+ \str_set_eq:NN
+ \str_put_left:Nn
+ \str_put_right:Nn
+ \__kernel_tl_set:Nx
+ \tl_clear:N
+ \tl_set_eq:NN
+ \tl_put_left:Nn
+ \tl_put_left:NV
+ \tl_put_left:Nv
+ \tl_put_left:Ne
+ \tl_put_left:No
+ \tl_put_right:Nn
+ \tl_put_right:NV
+ \tl_put_right:Nv
+ \tl_put_right:Ne
+ \tl_put_right:No
+ \tl_build_begin:N
+ \tl_build_put_right:Nn
+ \tl_build_put_left:Nn
+ \vbox_set:Nn
+ \vbox_set_top:Nn
+ \vbox_set_to_ht:Nnn
+ \vbox_set:Nw
+ \vbox_set_to_ht:Nnw
+ \vbox_set_split_to_ht:NNn
+ }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_global:N #1 }
+ { }
+ {
+ \bool_gset:Nn
+ \bool_gset_eq:NN
+ \bool_gset_true:N
+ \bool_gset_false:N
+ \box_gset_eq:NN
+ \box_gset_eq_drop:NN
+ \box_gset_to_last:N
+ \cctab_gset:Nn
+ \clist_gclear:N
+ \clist_gset_eq:NN
+ \dim_gset_eq:NN
+ \dim_gzero:N
+ \dim_gset:Nn
+ \dim_gadd:Nn
+ \dim_gsub:Nn
+ \fp_gset_eq:NN
+ \int_gzero:N
+ \int_gset_eq:NN
+ \int_gadd:Nn
+ \int_gsub:Nn
+ \int_gincr:N
+ \int_gdecr:N
+ \int_gset:Nn
+ \hbox_gset:Nn
+ \hbox_gset_to_wd:Nnn
+ \hbox_gset:Nw
+ \hbox_gset_to_wd:Nnw
+ \muskip_gzero:N
+ \muskip_gset:Nn
+ \muskip_gadd:Nn
+ \muskip_gsub:Nn
+ \muskip_gset_eq:NN
+ \prop_gclear:N
+ \prop_gconcat:NNN
+ \prop_gpop:NnN
+ \prop_gpop:NnNT
+ \prop_gpop:NnNF
+ \prop_gpop:NnNTF
+ \prop_gput:Nnn
+ \prop_gput_if_not_in:Nnn
+ \prop_gput_from_keyval:Nn
+ \prop_gremove:Nn
+ \prop_gset_eq:NN
+ \prop_gset_from_keyval:Nn
+ \seq_gset_eq:NN
+ \skip_gzero:N
+ \skip_gset:Nn
+ \skip_gset_eq:NN
+ \skip_gadd:Nn
+ \skip_gsub:Nn
+ \str_gclear:N
+ \str_gset_eq:NN
+ \str_gput_left:Nn
+ \str_gput_right:Nn
+ \__kernel_tl_gset:Nx
+ \tl_gclear:N
+ \tl_gset_eq:NN
+ \tl_gput_left:Nn
+ \tl_gput_left:NV
+ \tl_gput_left:Nv
+ \tl_gput_left:Ne
+ \tl_gput_left:No
+ \tl_gput_right:Nn
+ \tl_gput_right:NV
+ \tl_gput_right:Nv
+ \tl_gput_right:Ne
+ \tl_gput_right:No
+ \tl_build_gbegin:N
+ \tl_build_gput_right:Nn
+ \tl_build_gput_left:Nn
+ \vbox_gset:Nn
+ \vbox_gset_top:Nn
+ \vbox_gset_to_ht:Nnn
+ \vbox_gset:Nw
+ \vbox_gset_to_ht:Nnw
+ \vbox_gset_split_to_ht:NNn
+ }
+% \end{macrocode}
+%
+% Scoping for constants.
+% \begin{macrocode}
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_scope:NN c #1 }
+ { }
+ {
+ \bool_const:Nn
+ \cctab_const:Nn
+ \dim_const:Nn
+ \int_const:Nn
+ \intarray_const_from_clist:Nn
+ \muskip_const:Nn
+ \prop_const_from_keyval:Nn
+ \prop_const_linked_from_keyval:Nn
+ \skip_const:Nn
+ \str_const:Nn
+ \tl_const:Nn
+ }
+% \end{macrocode}
+%
+% Flag functions.
+% \begin{macrocode}
+ \__kernel_patch:nnn
+ { \__kernel_chk_flag_exist:NN }
+ { }
+ {
+ \flag_ensure_raised:N
+ \flag_height:N
+ \flag_if_raised:NT
+ \flag_if_raised:NF
+ \flag_if_raised:NTF
+ \flag_if_raised_p:N
+ \flag_raise:N
+ }
+% \end{macrocode}
+%
+% Various one-offs.
+% \begin{macrocode}
+ \__kernel_patch:nnn
+ { \__kernel_chk_cs_exist:N #1 }
+ { }
+ { \cs_generate_variant:Nn }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_scope:NN g #1 }
+ { }
+ { \cctab_new:N }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_scope:NN l #1 }
+ { }
+ { \flag_new:N }
+ \__kernel_patch:nnn
+ {
+ \__kernel_chk_var_scope:NN l #1
+ \__kernel_chk_flag_exist:NN
+ }
+ { }
+ { \flag_clear:N }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_scope:NN g #1 }
+ { }
+ { \intarray_new:Nn }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_scope:NN q #1 }
+ { }
+ { \quark_new:N }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_scope:NN s #1 }
+ { }
+ { \scan_new:N }
+% \end{macrocode}
+%
+% Patch various internal commands to log definitions of functions.
+% First, a kernel internal. Then internals from the \pkg{cs},
+% \pkg{keys} and \pkg{msg} modules.
+% \begin{macrocode}
+ \__kernel_patch:nnn
+ { }
+ {
+ \__kernel_debug_log:e
+ { Defining~\token_to_str:N #1~ \msg_line_context: }
+ }
+ { \__kernel_chk_if_free_cs:N }
+%<@@=cs>
+ \__kernel_patch_weird:nnn
+ {
+ \cs_if_free:NF #4
+ {
+ \__kernel_debug_log:e
+ {
+ Variant~\token_to_str:N #4~%
+ already~defined;~ not~ changing~ it~ \msg_line_context:
+ }
+ }
+ }
+ { }
+ { \@@_generate_variant:wwNN }
+%<@@=keys>
+ \__kernel_patch:nnn
+ {
+ \cs_if_exist:cF { \c_@@_code_root_str #1 }
+ { \__kernel_debug_log:e { Defining~key~#1~\msg_line_context: } }
+ }
+ { }
+ { \@@_cmd_set_direct:nn }
+%<@@=msg>
+ \__kernel_patch:nnn
+ { }
+ {
+ \__kernel_debug_log:e
+ { Defining~message~ #1 / #2 ~\msg_line_context: }
+ }
+ { \@@_chk_free:nn }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=prg>
+% \end{macrocode}
+% Internal functions from \pkg{prg} module.
+% \begin{macrocode}
+ \__kernel_patch_weird:nnn
+ { \__kernel_chk_cs_exist:c { #5 _p : #6 } }
+ { }
+ { \@@_set_eq_conditional_p_form:wNnnnn }
+ \__kernel_patch_weird:nnn
+ { \__kernel_chk_cs_exist:c { #5 : #6 TF } }
+ { }
+ { \@@_set_eq_conditional_TF_form:wNnnnn }
+ \__kernel_patch_weird:nnn
+ { \__kernel_chk_cs_exist:c { #5 : #6 T } }
+ { }
+ { \@@_set_eq_conditional_T_form:wNnnnn }
+ \__kernel_patch_weird:nnn
+ { \__kernel_chk_cs_exist:c { #5 : #6 F } }
+ { }
+ { \@@_set_eq_conditional_F_form:wNnnnn }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=regex>
+% \end{macrocode}
+% Internal functions from \pkg{regex} module.
+% \begin{macrocode}
+ \__kernel_patch:nnn
+ {
+ \@@_trace_push:nnN { regex } { 1 } \@@_escape_use:nnnn
+ \group_begin:
+ \__kernel_tl_set:Nx \l_@@_internal_a_tl
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_escape_use:nnnn }
+ \use_none:nnn
+ }
+ { }
+ { \@@_escape_use:nnn }
+ \__kernel_patch:nnn
+ { \@@_trace_push:nnN { regex } { 1 } \@@_build:N }
+ {
+ \@@_trace_states:n { 2 }
+ \@@_trace_pop:nnN { regex } { 1 } \@@_build:N
+ }
+ { \@@_build:N }
+ \__kernel_patch:nnn
+ { \@@_trace_push:nnN { regex } { 1 } \@@_build_for_cs:n }
+ {
+ \@@_trace_states:n { 2 }
+ \@@_trace_pop:nnN { regex } { 1 } \@@_build_for_cs:n
+ }
+ { \@@_build_for_cs:n }
+ \__kernel_patch:nnn
+ {
+ \@@_trace:nne { regex } { 2 }
+ {
+ regex~new~state~
+ L=\int_use:N \l_@@_left_state_int ~ -> ~
+ R=\int_use:N \l_@@_right_state_int ~ -> ~
+ M=\int_use:N \l_@@_max_state_int ~ -> ~
+ \int_eval:n { \l_@@_max_state_int + 1 }
+ }
+ }
+ { }
+ { \@@_build_new_state: }
+ \__kernel_patch:nnn
+ { \@@_trace_push:nnN { regex } { 1 } \@@_group_aux:nnnnN }
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_group_aux:nnnnN }
+ { \@@_group_aux:nnnnN }
+ \__kernel_patch:nnn
+ { \@@_trace_push:nnN { regex } { 1 } \@@_branch:n }
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_branch:n }
+ { \@@_branch:n }
+ \__kernel_patch:nnn
+ {
+ \@@_trace_push:nnN { regex } { 1 } \@@_match:n
+ \@@_trace:nne { regex } { 1 } { analyzing~query~token~list }
+ }
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_match:n }
+ { \@@_match:n }
+ \__kernel_patch:nnn
+ {
+ \@@_trace_push:nnN { regex } { 1 } \@@_match_cs:n
+ \@@_trace:nne { regex } { 1 } { analyzing~query~token~list }
+ }
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_match_cs:n }
+ { \@@_match_cs:n }
+ \__kernel_patch:nnn
+ { \@@_trace:nne { regex } { 1 } { initializing } }
+ { }
+ { \@@_match_init: }
+ \__kernel_patch:nnn
+ {
+ \@@_trace:nne { regex } { 2 }
+ { state~\int_use:N \l_@@_curr_state_int }
+ }
+ { }
+ { \@@_use_state: }
+ \__kernel_patch:nnn
+ { \@@_trace_push:nnN { regex } { 1 } \@@_replacement:n }
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_replacement:n }
+ { \@@_replacement:n }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\group_end:
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=debug>
+% \end{macrocode}
+%
+% Patching arguments is a bit more involved: we do these one at a time.
+% The basic idea is the same, using a |#| token that is a string.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \__kernel_patch:Nn #1
+ {
+ \group_begin:
+ \char_set_catcode_other:N \#
+ \__kernel_patch_aux:Nn #1
+ }
+ \cs_set_protected:Npn \__kernel_patch_aux:Nn #1#2
+ {
+ \char_set_catcode_parameter:N \#
+ \tex_endlinechar:D -1 \scan_stop:
+ \exp_args:Ne \tex_scantokens:D
+ {
+ \tex_global:D \cs_prefix_spec:N #1 \tex_def:D \exp_not:N #1
+ \cs_parameter_spec:N #1
+ { \exp_args:No \tl_to_str:n { #1 #2 } }
+ }
+ \group_end:
+ }
+% \end{macrocode}
+%
+% The functions here can get a bit repetitive, so we define a helper
+% which can re-use the same patch code repeatedly. The main part of the
+% patch is the same, so we just have to deal with the part which varies
+% depending on the type of expression.
+% \begin{macrocode}
+ \cs_set_protected:Npn \__kernel_patch_eval:nn #1#2
+ {
+ \tl_map_inline:nn {#1}
+ {
+ \exp_args:NNe \__kernel_patch:Nn ##1
+ {
+ { \c_hash_str 1 }
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 2 }
+ \exp_not:n {#2}
+ \exp_not:N ##1
+ }
+ }
+ }
+ }
+%<@@=dim>
+ \__kernel_patch_eval:nn
+ {
+ \dim_set:Nn
+ \dim_gset:Nn
+ \dim_add:Nn
+ \dim_gadd:Nn
+ \dim_sub:Nn
+ \dim_gsub:Nn
+ \dim_const:Nn
+ }
+ { \@@_eval:w { } }
+%<@@=int>
+ \__kernel_patch_eval:nn
+ {
+ \int_set:Nn
+ \int_gset:Nn
+ \int_add:Nn
+ \int_gadd:Nn
+ \int_sub:Nn
+ \int_gsub:Nn
+ \int_const:Nn
+ }
+ { \@@_eval:w { } }
+ \__kernel_patch_eval:nn
+ {
+ \muskip_set:Nn
+ \muskip_gset:Nn
+ \muskip_add:Nn
+ \muskip_gadd:Nn
+ \muskip_sub:Nn
+ \muskip_gsub:Nn
+ \muskip_const:Nn
+ }
+ { \tex_muexpr:D { \tex_mutoglue:D } }
+ \__kernel_patch_eval:nn
+ {
+ \skip_set:Nn
+ \skip_gset:Nn
+ \skip_add:Nn
+ \skip_gadd:Nn
+ \skip_sub:Nn
+ \skip_gsub:Nn
+ \skip_const:Nn
+ }
+ { \tex_glueexpr:D { } }
+% \end{macrocode}
+%
+% Patching expandable expressions, first the one-argument versions,
+% then the two-argument ones.
+% \begin{macrocode}
+ \cs_set_protected:Npn \__kernel_patch_eval:nn #1#2
+ {
+ \tl_map_inline:nn {#1}
+ {
+ \exp_args:NNe \__kernel_patch:Nn ##1
+ {
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 1 }
+ \exp_not:n {#2}
+ \exp_not:N ##1
+ }
+ }
+ }
+ }
+%<@@=box>
+ \__kernel_patch_eval:nn
+ { \@@_dim_eval:n }
+ { \@@_dim_eval:w { } }
+%<@@=dim>
+ \__kernel_patch_eval:nn
+ {
+ \dim_eval:n
+ \dim_to_decimal:n
+ \dim_to_decimal_in_sp:n
+ \dim_abs:n
+ \dim_sign:n
+ }
+ { \@@_eval:w { } }
+%<@@=int>
+ \__kernel_patch_eval:nn
+ {
+ \int_eval:n
+ \int_abs:n
+ \int_sign:n
+ }
+ { \@@_eval:w { } }
+ \__kernel_patch_eval:nn
+ {
+ \skip_eval:n
+ \skip_horizontal:n
+ \skip_vertical:n
+ }
+ { \tex_glueexpr:D { } }
+ \__kernel_patch_eval:nn
+ {
+ \muskip_eval:n
+ }
+ { \tex_muexpr:D { \tex_mutoglue:D } }
+ \cs_set_protected:Npn \__kernel_patch_eval:nn #1#2
+ {
+ \tl_map_inline:nn {#1}
+ {
+ \exp_args:NNe \__kernel_patch:Nn ##1
+ {
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 1 }
+ \exp_not:n {#2}
+ \exp_not:N ##1
+ }
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 2 }
+ \exp_not:n {#2}
+ \exp_not:N ##1
+ }
+ }
+ }
+ }
+%<@@=dim>
+ \__kernel_patch_eval:nn
+ {
+ \dim_max:nn
+ \dim_min:nn
+ }
+ { \@@_eval:w { } }
+%<@@=int>
+ \__kernel_patch_eval:nn
+ {
+ \int_max:nn
+ \int_min:nn
+ \int_div_truncate:nn
+ \int_mod:nn
+ }
+ { \@@_eval:w { } }
+% \end{macrocode}
+%
+% Conditionals: three argument ones then one argument ones
+% \begin{macrocode}
+ \cs_set_protected:Npn \__kernel_patch_cond:nn #1#2
+ {
+ \clist_map_inline:nn { :nNnT , :nNnF , :nNnTF , _p:nNn }
+ {
+ \exp_args:Nce \__kernel_patch:Nn { #1 ##1 }
+ {
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 1 }
+ \exp_not:n {#2}
+ \exp_not:c { #1 ##1 }
+ }
+ { \c_hash_str 2 }
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 3 }
+ \exp_not:n {#2}
+ \exp_not:c { #1 ##1 }
+ }
+ }
+ }
+ }
+%<@@=dim>
+ \__kernel_patch_cond:nn { dim_compare } { \@@_eval:w { } }
+%<@@=int>
+ \__kernel_patch_cond:nn { int_compare } { \@@_eval:w { } }
+ \cs_set_protected:Npn \__kernel_patch_cond:nn #1#2
+ {
+ \clist_map_inline:nn { :nT , :nF , :nTF , _p:n }
+ {
+ \exp_args:Nce \__kernel_patch:Nn { #1 ##1 }
+ {
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 1 }
+ \exp_not:n {#2}
+ \exp_not:c { #1 ##1 }
+ }
+ }
+ }
+ }
+%<@@=int>
+ \__kernel_patch_cond:nn { int_if_even } { \@@_eval:w { } }
+ \__kernel_patch_cond:nn { int_if_odd } { \@@_eval:w { } }
+% \end{macrocode}
+%
+% Step functions.
+% \begin{macrocode}
+%<@@=dim>
+ \__kernel_patch:Nn \dim_step_function:nnnN
+ {
+ {
+ \__kernel_chk_expr:nNnN {#1} \@@_eval:w { }
+ \dim_step_function:nnnN
+ }
+ {
+ \__kernel_chk_expr:nNnN {#2} \@@_eval:w { }
+ \dim_step_function:nnnN
+ }
+ {
+ \__kernel_chk_expr:nNnN {#3} \@@_eval:w { }
+ \dim_step_function:nnnN
+ }
+ }
+%<@@=int>
+ \__kernel_patch:Nn \int_step_function:nnnN
+ {
+ {
+ \__kernel_chk_expr:nNnN {#1} \@@_eval:w { }
+ \int_step_function:nnnN
+ }
+ {
+ \__kernel_chk_expr:nNnN {#2} \@@_eval:w { }
+ \int_step_function:nnnN
+ }
+ {
+ \__kernel_chk_expr:nNnN {#3} \@@_eval:w { }
+ \int_step_function:nnnN
+ }
+ }
+% \end{macrocode}
+%
+% Odds and ends
+% \begin{macrocode}
+ \__kernel_patch:Nn \dim_to_fp:n { { (#1) } }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\group_end:
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=skip>
+% \end{macrocode}
+% This one has catcode changes so must be done by hand.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \prg_set_conditional:Npnn \skip_if_finite:n ##1 { p , T , F , TF }
+ {
+ \exp_after:wN \@@_if_finite:wwNw
+ \skip_use:N \tex_glueexpr:D
+ \__kernel_chk_expr:nNnN
+ {##1} \tex_glueexpr:D { } \skip_if_finite:n
+ ; \prg_return_false:
+ #1 ; \prg_return_true: \s_@@_stop
+ }
+ }
+\exp_args:No \@@_tmp:w { \tl_to_str:n { fil } }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=msg>
+% \end{macrocode}
+%
+% Messages.
+% \begin{macrocode}
+\msg_new:nnnn { debug } { debug }
+ { The~debugging~option~'#1'~does~not~exist~\msg_line_context:. }
+ {
+ The~functions~'\iow_char:N\\debug_on:n'~and~
+ '\iow_char:N\\debug_off:n'~only~accept~the~arguments~
+ 'all',~'check-declarations',~'check-expressions',~
+ 'deprecation',~'log-functions',~not~'#1'.
+ }
+\msg_new:nnn { debug } { expr } { '#2'~in~#1 }
+\msg_new:nnnn { debug } { local-global }
+ { Inconsistent~local/global~assignment }
+ {
+ \c_@@_coding_error_text_tl
+ \if:w l #2 Local
+ \else:
+ \if:w g #2 Global \else: Constant \fi:
+ \fi:
+ \ %
+ assignment~to~a~
+ \if:w l #1 local
+ \else:
+ \if:w g #1 global \else: constant \fi:
+ \fi:
+ \ %
+ variable~'#3'.
+ }
+\msg_new:nnnn { debug } { non-declared-variable }
+ { The~variable~#1~has~not~been~declared~\msg_line_context:. }
+ {
+ \c_@@_coding_error_text_tl
+ Checking~is~active,~and~you~have~tried~do~so~something~like: \\
+ \ \ \tl_set:Nn ~ #1 ~ \{ ~ ... ~ \} \\
+ without~first~having: \\
+ \ \ \tl_new:N ~ #1 \\
+ \\
+ LaTeX~will~continue,~creating~the~variable~where~it~is~the~one~being~set.
+ }
+% \end{macrocode}
+%
+% \begin{macro}{\__kernel_if_debug:TF}
+% Flip the switch for deprecated code.
+% \begin{macrocode}
+\cs_set_protected:Npn \__kernel_if_debug:TF #1#2 {#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3deprecation.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3deprecation.dtx
new file mode 100644
index 00000000000..7af8700df76
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3deprecation.dtx
@@ -0,0 +1,691 @@
+% \iffalse meta-comment
+%
+%% File: l3deprecation.dtx
+%
+% Copyright (C) 2017-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3deprecation} module\\ Deprecation errors^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{\pkg{l3deprecation} documentation}
+%
+% A few commands have had to be deprecated over the years.
+% All commands deprecated before release 2022-01-12 were already
+% removed; commands deprecated since then will no longer be removed.
+% Instead, this module defines them to produce errors.
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3deprecation} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=deprecation>
+% \end{macrocode}
+%
+% \subsection{Patching definitions to deprecate}
+%
+% \begin{quote}
+% \cs{__kernel_patch_deprecation:nnNNpn} \Arg{date} \Arg{replacement}
+% \meta{definition} \meta{function} \meta{parameters} \Arg{code}
+% \end{quote}
+% defines the \meta{function} to produce an error and run its
+% \meta{code}.
+%
+% We make \cs{debug_on:n} |{deprecation}| turn the \meta{function} into
+% an \tn{outer} error, and \cs{debug_off:n} |{deprecation}| restore
+% whatever the behaviour was without \cs{debug_on:n} |{deprecation}|.
+%
+% In the explanations below, \meta{definition} \meta{function}
+% \meta{parameters} \Arg{code} or assignments that only differ in the
+% scope of the \meta{definition} will be called \enquote{the standard
+% definition}.
+%
+% \begin{macro}
+% {
+% \__kernel_patch_deprecation:nnNNpn, \@@_patch_aux:nnNNnn,
+% \@@_warn_once:nnNnn,
+% \@@_patch_aux:Nn,
+% \@@_just_error:nnNN
+% }
+% (The parameter text is grabbed using |#5#|.) The arguments of
+% \cs{__kernel_deprecation_code:nn} are run upon \cs{debug_on:n}
+% |{deprecation}| and \cs{debug_off:n} |{deprecation}|, respectively.
+% In both scenarios we the \meta{function} may be \tn{outer} so we
+% undefine it with \cs{tex_let:D} before redefining it, with
+% \cs{__kernel_deprecation_error:Nnn} or with some code added shortly.
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_patch_deprecation:nnNNpn #1#2#3#4#5#
+ { \@@_patch_aux:nnNNnn {#1} {#2} #3 #4 {#5} }
+\cs_new_protected:Npn \@@_patch_aux:nnNNnn #1#2#3#4#5#6
+ {
+ \__kernel_deprecation_code:nn
+ {
+ \tex_let:D #4 \scan_stop:
+ \__kernel_deprecation_error:Nnn #4 {#2} {#1}
+ }
+ { \tex_let:D #4 \scan_stop: }
+ \cs_if_eq:NNTF #3 \cs_gset_protected:Npn
+ { \@@_warn_once:nnNnn {#1} {#2} #4 {#5} {#6} }
+ { \@@_patch_aux:Nn #3 { #4 #5 {#6} } }
+ }
+% \end{macrocode}
+% In case we want a warning, the \meta{function} is defined to produce
+% such a warning without grabbing any argument, then redefine itself
+% to the standard definition that the \meta{function} should have,
+% with arguments, and call that definition. The \texttt{e}-type
+% expansion and \cs{exp_not:n} avoid needing to double the~|#|, which
+% we could not do anyways. We then deal with the code for
+% \cs{debug_off:n} |{deprecation}|: presumably someone doing that does
+% not need the warning so we simply do the standard definition.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_warn_once:nnNnn #1#2#3#4#5
+ {
+ \cs_gset_protected:Npe #3
+ {
+ \__kernel_if_debug:TF
+ {
+ \exp_not:N \msg_warning:nneee
+ { deprecation } { deprecated-command }
+ {#1}
+ { \token_to_str:N #3 }
+ { \tl_to_str:n {#2} }
+ }
+ { }
+ \exp_not:n { \cs_gset_protected:Npn #3 #4 {#5} }
+ \exp_not:N #3
+ }
+ \__kernel_deprecation_code:nn { }
+ { \cs_set_protected:Npn #3 #4 {#5} }
+ }
+% \end{macrocode}
+% In case we want neither warning nor error, the \meta{function} is
+% given its standard definition. Here |#1| is \cs{cs_new:Npn} or
+% \cs{cs_new_protected:Npn}) and |#2| is \meta{function}
+% \meta{parameters} \Arg{code}, so |#1#2| performs the assignment.
+% For \cs{debug_off:n} |{deprecation}| we want to use the same
+% assignment but with a different scope, hence the \cs{cs_if_eq:NNTF}
+% test.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_patch_aux:Nn #1#2
+ {
+ #1 #2
+ \cs_if_eq:NNTF #1 \cs_gset_protected:Npn
+ { \__kernel_deprecation_code:nn { } { \cs_set_protected:Npn #2 } }
+ { \__kernel_deprecation_code:nn { } { \cs_set:Npn #2 } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__kernel_deprecation_error:Nnn}
+% The \tn{outer} definition here ensures the command cannot appear
+% in an argument.
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_deprecation_error:Nnn #1#2#3
+ {
+ \tex_protected:D \tex_outer:D \tex_edef:D #1
+ {
+ \exp_not:N \msg_expandable_error:nnnnn
+ { deprecation } { deprecated-command }
+ { \tl_to_str:n {#3} } { \token_to_str:N #1 } { \tl_to_str:n {#2} }
+ \exp_not:N \msg_error:nneee
+ { deprecation } { deprecated-command }
+ { \tl_to_str:n {#3} } { \token_to_str:N #1 } { \tl_to_str:n {#2} }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+\msg_new:nnn { deprecation } { deprecated-command }
+ {
+ \tl_if_blank:nF {#3} { Use~ \tl_trim_spaces:n {#3} ~not~ }
+ #2~deprecated~on~#1.
+ }
+% \end{macrocode}
+%
+% \subsection{Deprecated \pkg{l3basics} functions}
+%
+% \begin{macrocode}
+%<@@=cs>
+% \end{macrocode}
+%
+% \begin{macro}[EXP, deprecated]{\cs_argument_spec:N}
+% For the present, do not deprecate fully as \LaTeXe{} will need to catch
+% up: one for Fall 2022.
+% \begin{macrocode}
+%\__kernel_patch_deprecation:nnNNpn { 2022-06-24 } { \cs_parameter_spec:N }
+\cs_new:Npn \cs_argument_spec:N { \cs_parameter_spec:N }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3file} functions}
+%
+% \begin{macrocode}
+%<@@=file>
+% \end{macrocode}
+%
+% \begin{macro}[deprecated]
+% {
+% \iow_shipout_x:Nn, \iow_shipout_x:Nx,
+% \iow_shipout_x:cn, \iow_shipout_x:cx
+% }
+% Previously described as \texttt{x}-type, but the hash behaviour is really
+% \texttt{e}-type. Currently not \enquote{live} as we need to have a transition.
+% \begin{macrocode}
+% \__kernel_patch_deprecation:nnNNpn { 2023-10-10 } { \iow_shipout_e:Nn }
+\cs_new_protected:Npn \iow_shipout_x:Nn { \iow_shipout_e:Nn }
+\cs_generate_variant:Nn \iow_shipout_x:Nn { Nx , c, cx }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3keys} functions}
+%
+% \begin{macrocode}
+%<@@=keys>
+% \end{macrocode}
+%
+% \begin{macro}[deprecated]{.str_set_x:N, .str_set_x:c}
+% \begin{macro}[deprecated]{.str_gset_x:N, .str_gset_x:c}
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .str_set_x:N } #1
+ { \@@_variable_set:NnnN #1 { str } { } x }
+\cs_new_protected:cpn { \c_@@_props_root_str .str_set_x:c } #1
+ { \@@_variable_set:cnnN {#1} { str } { } x }
+\cs_new_protected:cpn { \c_@@_props_root_str .str_gset_x:N } #1
+ { \@@_variable_set:NnnN #1 { str } { g } x }
+\cs_new_protected:cpn { \c_@@_props_root_str .str_gset_x:c } #1
+ { \@@_variable_set:cnnN {#1} { str } { g } x }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[deprecated]{.tl_set_x:N, .tl_set_x:c}
+% \begin{macro}[deprecated]{.tl_gset_x:N, .tl_gset_x:c}
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .tl_set_x:N } #1
+ { \@@_variable_set:NnnN #1 { tl } { } x }
+\cs_new_protected:cpn { \c_@@_props_root_str .tl_set_x:c } #1
+ { \@@_variable_set:cnnN {#1} { tl } { } x }
+\cs_new_protected:cpn { \c_@@_props_root_str .tl_gset_x:N } #1
+ { \@@_variable_set:NnnN #1 { tl } { g } x }
+\cs_new_protected:cpn { \c_@@_props_root_str .tl_gset_x:c } #1
+ { \@@_variable_set:cnnN {#1} { tl } { g } x }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[deprecated]
+% {
+% \keys_set_filter:nnnN, \keys_set_filter:nnVN,
+% \keys_set_filter:nnvN, \keys_set_filter:nnoN
+% }
+% \begin{macro}[deprecated]
+% {
+% \keys_set_filter:nnnnN, \keys_set_filter:nnVnN,
+% \keys_set_filter:nnvnN, \keys_set_filter:nnonN
+% }
+% \begin{macro}[deprecated]
+% {
+% \keys_set_filter:nnn, \keys_set_filter:nnV,
+% \keys_set_filter:nnv, \keys_set_filter:nno
+% }
+% We need a transition here so for the present this is commented out:
+% only needed for \texttt{latex-lab} code so this should not last for
+% too long.
+% \begin{macrocode}
+%\__kernel_patch_deprecation:nnNNpn { 2024-01-10 } { \keys_set_exclude_groups:nnn }
+\cs_new_protected:Npn \keys_set_filter:nnn { \keys_set_exclude_groups:nnn }
+\cs_generate_variant:Nn \keys_set_filter:nnn { nnV , nnv , nno }
+%\__kernel_patch_deprecation:nnNNpn { 2024-01-10 } { \keys_set_exclude_groups:nnnN }
+\cs_new_protected:Npn \keys_set_filter:nnnN { \keys_set_exclude_groups:nnnN }
+\cs_generate_variant:Nn \keys_set_filter:nnnN { nnV , nnv , nno }
+%\__kernel_patch_deprecation:nnNNpn { 2024-01-10 } { \keys_set_exclude_groups:nnnnN }
+\cs_new_protected:Npn \keys_set_filter:nnnnN { \keys_set_exclude_groups:nnnnN }
+\cs_generate_variant:Nn \keys_set_filter:nnnnN { nnV , nnv , nno }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3msg} functions}
+%
+% \begin{macrocode}
+%<@@=msg>
+% \end{macrocode}
+%
+% \begin{macro}[deprecated]{\msg_gset:nnnn, \msg_gset:nnn}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2024-02-13 } { \msg_set:nnnn }
+\cs_new_protected:Npn \msg_gset:nnnn { \msg_set:nnnn }
+\__kernel_patch_deprecation:nnNNpn { 2024-02-13 } { \msg_set:nnn }
+\cs_new_protected:Npn \msg_gset:nnn { \msg_set:nnn }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3pdf} functions}
+%
+% \begin{macrocode}
+%<@@=pdf>
+% \end{macrocode}
+%
+% \begin{variable}[deprecated]{\g_@@_object_prop}
+% For tracking objects.
+% \begin{macrocode}
+\prop_new:N \g_@@_object_prop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[deprecated]
+% {\pdf_object_new:nn, \pdf_object_write:nn, \pdf_object_write:nx}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2022-08-30 } { [\pdf_object_new:n] }
+\cs_new_protected:Npn \pdf_object_new:nn #1#2
+ {
+ \prop_gput:Nnn \g_@@_object_prop {#1} {#2}
+ \@@_backend_object_new:n {#1}
+ }
+\__kernel_patch_deprecation:nnNNpn { 2022-08-30 } { [\pdf_object_write:n] }
+\cs_new_protected:Npn \pdf_object_write:nn #1#2
+ {
+ \exp_args:Nne \@@_backend_object_write:nnn
+ {#1} { \prop_item:Nn \g_@@_object_prop {#1} } {#2}
+ \bool_gset_true:N \g_@@_init_bool
+ }
+\cs_generate_variant:Nn \pdf_object_write:nn { nx }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3prg} functions}
+%
+% \begin{macrocode}
+%<@@=cs>
+% \end{macrocode}
+%
+% \begin{macro}[EXP, noTF, deprecated]{\bool_case_true:n}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2023-05-03 } { \bool_case:n }
+\cs_new:Npn \bool_case_true:n { \bool_case:n }
+\__kernel_patch_deprecation:nnNNpn { 2023-05-03 } { \bool_case:nT }
+\cs_new:Npn \bool_case_true:nT { \bool_case:nT }
+\__kernel_patch_deprecation:nnNNpn { 2023-05-03 } { \bool_case:nF }
+\cs_new:Npn \bool_case_true:nF { \bool_case:nF }
+\__kernel_patch_deprecation:nnNNpn { 2023-05-03 } { \bool_case:nTF }
+\cs_new:Npn \bool_case_true:nTF { \bool_case:nTF }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3str} functions}
+%
+% \begin{macrocode}
+%<@@=str>
+% \end{macrocode}
+%
+% \begin{macro}[EXP, deprecated]
+% {
+% \str_lower_case:n, \str_lower_case:f,
+% \str_upper_case:n, \str_upper_case:f,
+% \str_fold_case:n, \str_fold_case:V
+% }
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_lowercase:n }
+\cs_new:Npn \str_lower_case:n { \str_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_lowercase:f }
+\cs_new:Npn \str_lower_case:f { \str_lowercase:f }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_uppercase:n }
+\cs_new:Npn \str_upper_case:n { \str_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_uppercase:f }
+\cs_new:Npn \str_upper_case:f { \str_uppercase:f }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_casefold:n }
+\cs_new:Npn \str_fold_case:n { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_casefold:V }
+\cs_new:Npn \str_fold_case:V { \str_casefold:V }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, deprecated]
+% {\str_foldcase:n, \str_foldcase:V}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2020-10-17 } { \str_casefold:n }
+\cs_new:Npn \str_foldcase:n { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_casefold:V }
+\cs_new:Npn \str_foldcase:V { \str_casefold:V }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, deprecated]
+% {\str_declare_eight_bit_encoding:nnn}
+% This command was made internal, with one more argument. There is no
+% easy way to compute a reasonable value for that extra argument so we
+% take a value that is big enough to accommodate all of Unicode.
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2020-08-20 } { }
+\cs_new_protected:Npn \str_declare_eight_bit_encoding:nnn #1
+ { \@@_declare_eight_bit_encoding:nnnn {#1} { 1114112 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3seq} functions}
+%
+% \begin{macrocode}
+%<@@=seq>
+% \end{macrocode}
+%
+% \begin{macro}[deprecated]{\seq_indexed_map_inline:Nn}
+% \begin{macro}[EXP, deprecated]{ \seq_indexed_map_function:NN}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2020-06-18 } { \seq_map_indexed_inline:Nn }
+\cs_new_protected:Npn \seq_indexed_map_inline:Nn { \seq_map_indexed_inline:Nn }
+\__kernel_patch_deprecation:nnNNpn { 2020-06-18 } { \seq_map_indexed_function:NN }
+\cs_new:Npn \seq_indexed_map_function:NN { \seq_map_indexed_function:NN }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[deprecated]{\seq_mapthread_function:NNN}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2023-05-10 } { \seq_map_pairwise_function:NNN }
+\cs_new:Npn \seq_mapthread_function:NNN { \seq_map_pairwise_function:NNN }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[deprecated]{\seq_set_map_x:NNn, \seq_gset_map_x:NNn}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2023-10-26 } { \seq_set_map_e:NNn }
+\cs_new_protected:Npn \seq_set_map_x:NNn { \seq_set_map_e:NNn }
+\__kernel_patch_deprecation:nnNNpn { 2023-10-26 } { \seq_gset_map_e:NNn }
+\cs_new_protected:Npn \seq_gset_map_x:NNn { \seq_gset_map_e:NNn }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3sys} functions}
+%
+% \begin{macrocode}
+%<@@=sys>
+% \end{macrocode}
+%
+% \begin{macro}[deprecated]{\sys_load_deprecation:}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2021-01-11 } { (no~longer~required) }
+\cs_new_protected:Npn \sys_load_deprecation: { }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3text} functions}
+%
+% \begin{macrocode}
+%<@@=text>
+% \end{macrocode}
+%
+% \begin{macro}[EXP, deprecated]{\text_titlecase:n}
+% \begin{macro}[EXP, deprecated]{\text_titlecase:nn}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2023-07-08 } { \text_titlecase_first:n }
+\cs_new:Npn \text_titlecase:n #1
+ { \text_titlecase_first:n { \text_lowercase:n {#1} } }
+\__kernel_patch_deprecation:nnNNpn { 2023-07-08 } { \text_titlecase_first:nn }
+\cs_new:Npn \text_titlecase:nn #1#2
+ { \text_titlecase_first:nn {#1} { \text_lowercase:n {#2} } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3tl} functions}
+%
+% \begin{macrocode}
+%<@@=tl>
+% \end{macrocode}
+%
+% \begin{macro}[deprecated]
+% {
+% \tl_lower_case:n, \tl_lower_case:nn,
+% \tl_upper_case:n, \tl_upper_case:nn,
+% \tl_mixed_case:n, \tl_mixed_case:nn,
+% }
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_lowercase:n }
+\cs_new:Npn \tl_lower_case:n #1
+ { \text_lowercase:n {#1} }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_lowercase:nn }
+\cs_new:Npn \tl_lower_case:nn #1#2
+ { \text_lowercase:nn {#1} {#2} }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_uppercase:n }
+\cs_new:Npn \tl_upper_case:n #1
+ { \text_uppercase:n {#1} }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_uppercase:nn }
+\cs_new:Npn \tl_upper_case:nn #1#2
+ { \text_uppercase:nn {#1} {#2} }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase_first:n }
+\cs_new:Npn \tl_mixed_case:n #1
+ { \text_titlecase_first:n {#1} }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase_first:nn }
+\cs_new:Npn \tl_mixed_case:nn #1#2
+ { \text_titlecase_first:nn {#1} {#2} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[noTF, deprecated]{\tl_case:Nn, \tl_case:cn}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2022-05-23 } { \token_case_meaning:Nn }
+\cs_new:Npn \tl_case:Nn { \token_case_meaning:Nn }
+\__kernel_patch_deprecation:nnNNpn { 2022-05-23 } { \token_case_meaning:NnT }
+\cs_new:Npn \tl_case:NnT { \token_case_meaning:NnT }
+\__kernel_patch_deprecation:nnNNpn { 2022-05-23 } { \token_case_meaning:NnF }
+\cs_new:Npn \tl_case:NnF { \token_case_meaning:NnF }
+\__kernel_patch_deprecation:nnNNpn { 2022-05-23 } { \token_case_meaning:NnTF }
+\cs_new:Npn \tl_case:NnTF { \token_case_meaning:NnTF }
+\cs_generate_variant:Nn \tl_case:Nn { c }
+\prg_generate_conditional_variant:Nnn \tl_case:Nn
+ { c } { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[deprecated]{\tl_build_clear:N, \tl_build_gclear:N}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2023-10-18 } { \tl_build_begin:N }
+\cs_new_protected:Npn \tl_build_clear:N { \tl_build_begin:N }
+\__kernel_patch_deprecation:nnNNpn { 2023-10-18 } { \tl_build_gbegin:N }
+\cs_new_protected:Npn \tl_build_gclear:N { \tl_build_gbegin:N }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[deprecated]{\tl_build_get:NN}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2023-10-25 } { \tl_build_get_intermediate:NN }
+\cs_new_protected:Npn \tl_build_get:NN { \tl_build_get_intermediate:NN }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3token} functions}
+%
+% \begin{macrocode}
+%<@@=char>
+% \end{macrocode}
+%
+% \begin{macro}[EXP, deprecated]{\char_to_utfviii_bytes:n}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2022-10-09 } { [ \codepoint_generate:nn ] }
+\cs_new:Npn \char_to_utfviii_bytes:n { \__kernel_codepoint_to_bytes:n }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, deprecated]{\char_to_nfd:N, \char_to_nfd:n}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2022-10-09 } { \codepoint_to_nfd:n }
+\cs_new:Npn \char_to_nfd:N #1 { \codepoint_to_nfd:n {`#1} }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-09 } { \codepoint_to_nfd:n }
+\cs_new:Npn \char_to_nfd:n { \codepoint_to_nfd:n }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, deprecated]
+% {
+% \char_lower_case:N, \char_upper_case:N,
+% \char_mixed_case:Nn, \char_fold_case:N,
+% \char_str_lower_case:N, \char_str_upper_case:N,
+% \char_str_mixed_case:Nn, \char_str_fold_case:N,
+% }
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_lowercase:n }
+\cs_new:Npn \char_lower_case:N { \text_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_uppercase:n }
+\cs_new:Npn \char_upper_case:N { \text_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase_first:n }
+\cs_new:Npn \char_mixed_case:N { \text_titlecase_first:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_casefold:n }
+\cs_new:Npn \char_fold_case:N { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_lowercase:n }
+\cs_new:Npn \char_str_lower_case:N { \str_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_uppercase:n }
+\cs_new:Npn \char_str_upper_case:N { \str_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_titlecase:n }
+\cs_new:Npn \char_str_mixed_case:N { \str_titlecase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_casefold:n }
+\cs_new:Npn \char_str_fold_case:N { \str_casefold:n }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, deprecated]
+% {
+% \char_lowercase:N, \char_titlecase:N, \char_uppercase:N,
+% \char_foldcase:N,
+% \char_str_lowercase:N, \char_str_titlecase:N, \char_str_uppercase:N,
+% \char_str_foldcase:N,
+% }
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \text_lowercase:n }
+\cs_new:Npn \char_lowercase:N { \text_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \text_uppercase:n }
+\cs_new:Npn \char_uppercase:N { \text_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \text_titlecase_first:n }
+\cs_new:Npn \char_titlecase:N { \text_titlecase_first:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_casefold:n }
+\cs_new:Npn \char_foldcase:N { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_lowercase:n }
+\cs_new:Npn \char_str_lowercase:N { \str_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 }
+ { \tl_to_str:e { \text_titlecase_first:n } }
+\cs_new:Npn \char_str_titlecase:N #1
+ { \tl_to_str:e { \text_titlecase_first:n {#1} } }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_uppercase:n }
+\cs_new:Npn \char_str_uppercase:N { \str_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_casefold:n }
+\cs_new:Npn \char_str_foldcase:N { \str_casefold:n }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[TF, deprecated]
+% {
+% \peek_catcode_ignore_spaces:N, \peek_catcode_remove_ignore_spaces:N,
+% \peek_charcode_ignore_spaces:N, \peek_charcode_remove_ignore_spaces:N,
+% \peek_meaning_ignore_spaces:N, \peek_meaning_remove_ignore_spaces:N
+% }
+% A little extra fun here to deal with the expansion.
+% \begin{macrocode}
+\tl_map_inline:nn
+ {
+ { catcode } { catcode_remove }
+ { charcode } { charcode_remove }
+ { meaning } { meaning_remove }
+ }
+ {
+ \use:e
+ {
+ \__kernel_patch_deprecation:nnNNpn { 2022-01-11 } { \peek_remove_spaces:n }
+ \cs_gset_protected:Npn \exp_not:c { peek_ #1 _ignore_spaces:NTF } ##1##2##3
+ {
+ \peek_remove_spaces:n
+ { \exp_not:c { peek_ #1 :NTF } ##1 {##2} {##3} }
+ }
+ \__kernel_patch_deprecation:nnNNpn { 2022-01-11 } { \peek_remove_spaces:n }
+ \cs_gset_protected:Npn \exp_not:c { peek_ #1 _ignore_spaces:NT } ##1##2
+ {
+ \peek_remove_spaces:n
+ { \exp_not:c { peek_ #1 :NT } ##1 {##2} }
+ }
+ \__kernel_patch_deprecation:nnNNpn { 2022-01-11 } { \peek_remove_spaces:n }
+ \cs_gset_protected:Npn \exp_not:c { peek_ #1 _ignore_spaces:NF } ##1##2
+ {
+ \peek_remove_spaces:n
+ { \exp_not:c { peek_ #1 :NF } ##1 {##2} }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3prop} functions}
+%
+% \begin{macro}[deprecated]
+% {
+% \prop_put_if_new:Nnn, \prop_put_if_new:NVn, \prop_put_if_new:NnV,
+% \prop_put_if_new:cnn, \prop_put_if_new:cVn, \prop_put_if_new:cnV,
+% \prop_gput_if_new:Nnn, \prop_gput_if_new:NVn, \prop_gput_if_new:NnV,
+% \prop_gput_if_new:cnn, \prop_gput_if_new:cVn, \prop_gput_if_new:cnV
+% }
+% \begin{macrocode}
+%\__kernel_patch_deprecation:nnNNpn { 2024-03-30 } { \prop_put_if_not_in:Nnn }
+\cs_new_protected:Npn \prop_put_if_new:Nnn { \prop_put_if_not_in:Nnn }
+%\__kernel_patch_deprecation:nnNNpn { 2024-03-30 } { \prop_gput_if_not_in:Nnn }
+\cs_new_protected:Npn \prop_gput_if_new:Nnn { \prop_gput_if_not_in:Nnn }
+\cs_generate_variant:Nn \prop_put_if_new:Nnn
+ { NnV , NV , c , cnV , cV }
+\cs_generate_variant:Nn \prop_gput_if_new:Nnn
+ { NnV , NV , c , cnV , cV }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3doc.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3doc.dtx
new file mode 100644
index 00000000000..9156bd385e5
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3doc.dtx
@@ -0,0 +1,4802 @@
+% \iffalse meta-comment
+%
+%% File: l3doc.dtx
+%
+% Copyright (C) 1990-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\def\nameofplainTeX{plain}
+\ifx\fmtname\nameofplainTeX\else
+ \expandafter\begingroup
+\fi
+\input docstrip %
+\askforoverwritefalse
+\preamble
+
+Copyright (C) 1990-2024 The LaTeX Project
+
+It may be distributed and/or modified under the conditions of
+the LaTeX Project Public License (LPPL), either version 1.3c of
+this license or (at your option) any later version. The latest
+version of this license is in the file:
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "l3kernel bundle" (The Work in LPPL)
+and all files in that bundle must be distributed together.
+
+\endpreamble
+% stop docstrip adding \endinput
+\postamble
+\endpostamble
+\generate{\file{l3doc.cls}{\from{l3doc.dtx}{class,cfg}}}
+%\generate{\file{l3doc.ist}{\from{l3doc.dtx}{docist}}}
+\ifx\fmtname\nameofplainTeX
+ \expandafter\endbatchfile
+\else
+ \expandafter\endgroup
+\fi
+%</driver>
+%
+%<*driver|class>
+\RequirePackage{calc}
+%</driver|class>
+%
+%<*driver>
+\documentclass{l3doc}
+\usepackage{framed}
+\begin{document}
+ \DocInput{l3doc.dtx}
+\end{document}
+%</driver>
+%
+% This isn't included in the typeset documentation because it's a bit
+% ugly:
+%<*class>
+\ProvidesExplClass{l3doc}{2024-04-11}{}
+ {L3 Experimental documentation class}
+%</class>
+% \fi
+%
+% \title{The \cls{l3doc} class -- experimental\thanks{%
+% On popular request we now distribute the document for this
+% experimental class. However, please note that it is by no means
+% in final state and is \emph{likely} to undergo modifications,
+% even \emph{incompatible ones}! Thus, using it might therefore
+% require you to do updates, if the class changes.}}
+%
+% \author{\Team}
+% \date{Released 2024-04-11}
+% \maketitle
+% \tableofcontents
+%
+% \begin{documentation}
+%
+%
+% \section{Introduction}
+%
+% Code and documentation for this class have been written prior to the
+% change of \pkg{doc} from version 2 to version 3, which already shows
+% how far behind this class currently is. So take the following
+% warning seriously please:
+%
+% \begin{quote}
+% \textbf{It is much less stable than the main \pkg{expl3} packages.\\
+% Use at own risk!}
+% \end{quote}
+%
+% This is an ad-hoc class for documenting the \pkg{expl3} bundle, a
+% collection of modules or packages that make up \LaTeX3's programming
+% environment. Eventually it will replace the \cls{ltxdoc} class for
+% \LaTeX3, but not before the good ideas in \pkg{hypdoc}, \cls{xdoc2},
+% \pkg{docmfp}, and \cls{gmdoc} are incorporated.
+%
+% It is written as a \enquote{self-contained} docstrip file: executing
+% |latex l3doc.dtx| generates the \file{l3doc.cls} file and typesets
+% this documentation; execute |tex l3doc.dtx| to only generate
+% \file{l3doc.cls}.
+%
+% \section{Features of other packages}
+%
+% This class builds on the \pkg{ltxdoc} class and the \pkg{doc} package,
+% but in the time since they were originally written some improvements
+% and replacements have appeared that we would like to use as
+% inspiration.
+%
+% These packages or classes are \pkg{hypdoc}, \pkg{docmfp}, \pkg{gmdoc},
+% and \pkg{xdoc}. I have summarised them below in order to work out
+% what sort of features we should aim at a minimum for \pkg{l3doc}.
+%
+% \subsection{The \pkg{hypdoc} package}
+%
+% This package provides hyperlink support for the \pkg{doc} package. I
+% have included it in this list to remind me that cross-referencing
+% between documentation and implementation of methods is not very
+% good. (\emph{E.g.}, it would be nice to be able to automatically
+% hyperlink the documentation for a function from its implementation and
+% vice-versa.)
+%
+% \subsection{The \pkg{docmfp} package}
+%
+% \begin{itemize}
+% \item Provides \cs{DescribeRoutine} and the \env{routine}
+% environment (\emph{etc.}) for MetaFont and MetaPost code.
+% \item Provides \cs{DescribeVariable} and the \env{variable}
+% environment (\emph{etc.}) for more general code.
+% \item Provides \cs{Describe} and the \env{Code} environment
+% (\emph{etc.}) as a generalisation of the above two
+% instantiations.
+% \item Small tweaks to the DocStrip system to aid non-\LaTeX{} use.
+% \end{itemize}
+%
+% \subsection{The \pkg{xdoc2} package}
+%
+% \begin{itemize}
+% \item Two-sided printing.
+% \item \cs{NewMacroEnvironment}, \cs{NewDescribeEnvironment}; similar
+% idea to \pkg{docmfp} but more comprehensive.
+% \item Tons of small improvements.
+% \end{itemize}
+%
+% \subsection{The \pkg{gmdoc} package}
+%
+% Radical re-implementation of \pkg{doc} as a package or class.
+% \begin{itemize}
+% \item Requires no |\begin{macrocode}| blocks!
+% \item Automatically inserts |\begin{macro}| blocks!
+% \item And a whole bunch of other little things.
+% \end{itemize}
+%
+% \section{Problems \& Todo}
+%
+% Problems at the moment:
+% (1)~not flexible in the types of things that can be documented;
+% (2)~no obvious link between the |\begin{function}| environment for
+% documenting things to the |\begin{macro}| function that's used
+% analogously in the implementation.
+%
+% The \env{macro} should probably be renamed to \env{function} when it
+% is used within an implementation section. But they should have the
+% same syntax before that happens!
+%
+% Furthermore, we need another \enquote{layer} of documentation commands
+% to account for \enquote{user-macro} as opposed to
+% \enquote{code-functions}; the \pkg{expl3} functions should be
+% documented differently, probably, to the \pkg{ltcmd} user macros (at
+% least in terms of indexing).
+%
+% In no particular order, a list of things to do:
+% \begin{itemize}
+% \item Rename \env{function}/\env{macro} environments to better
+% describe their use.
+% \item Generalise \env{function}/\env{macro} for documenting
+% \enquote{other things}, such as environment names, package
+% options, even keyval options.
+% \item New function like \tn{part} but for files (remove awkward
+% \enquote{File} as \tn{partname}).
+% \item Something better to replace \cs{StopEventually}; I'm thinking
+% two environments \env{documentation} and \env{implementation} that
+% can conditionally typeset/ignore their material. (This has been
+% implemented but needs further consideration.)
+% \item Hyperlink documentation and implementation of macros (see the
+% \textsc{dtx} file of \pkg{svn-multi} v2 as an example). This is
+% partially done, now, but should be improved.
+% \end{itemize}
+%
+% \section{Documentation}
+%
+% \subsection{Configuration}
+%
+% Before class options are processed, \pkg{l3doc} loads a configuration
+% file \file{l3doc.cfg} if it exists, allowing you to customise the
+% behaviour of the class without having to change the documentation
+% source files.
+%
+% For example, to produce documentation on letter-sized paper instead of
+% the default A4 size, create \file{l3doc.cfg} and include the line
+% \begin{verbatim}
+% \PassOptionsToClass{letterpaper}{l3doc}
+% \end{verbatim}
+%
+% By default, \pkg{l3doc} selects the |T1| font encoding and loads the
+% Latin Modern fonts. To prevent this, use the class option
+% |cm-default|.
+%
+% \subsection{Class options}
+%
+% The class recognises a number of options, some of which are generally
+% useful and some of which are aimed squarely at use by the kernel team only.
+%
+% \DescribeOption{full}
+% \DescribeOption{onlydoc}
+% When the \texttt{full} option is set (the standard setting), both the
+% documentation and implementation parts of the source are typeset. If on the
+% other hand the \texttt{onlydoc} option is set, only the documentation part
+% is typeset.
+%
+% \DescribeOption{lm-default}
+% Selects whether the standard font set up is Latin Modern in the \texttt{T1}
+% encoding (the standard setting) or leaves the font setup unchanged.
+%
+% \DescribeOption{kernel}
+% Determines whether \pkg{l3doc} treats internal functions and variables
+% belonging to |kernel| module as allowable in code, for instance
+% \cs{__kernel_tl_to_str:w}, \cs{c__kernel_expl_date_tl}, and
+% \cs{l__kernel_expl_bool}. In general,
+% \emph{no} internal material from outside the current module is allowed.
+% However, for bootstrapping the \pkg{expl3} kernel, a small number of
+% cross-module functions are needed. To suppress the error message that
+% would otherwise arise, the class option \texttt{kernel} may be given.
+%
+% \DescribeOption{check}
+% When the \texttt{check} option is given, the class will record all commands
+% defined and documented in a \texttt{\meta{name}.cmds} file. This will show
+% which are both documented and defined, which are only documented and which
+% are only defined. (Here, \enquote{defined} means listed using a
+% \texttt{macro} or \texttt{variable} environment in the implementation part of
+% the source file).
+%
+% \DescribeOption{checktest}
+% When \texttt{checktest} is given as an option, the class will check that each
+% function entry in the implementation part of the source is marked using
+% \cs{UnitTest}.
+%
+% \DescribeOption{show-notes}
+% \DescribeOption{hide-notes}
+% These complementary options determine if the information given using the
+% \cs{NB} and \cs{NOTE} commands is printed.
+%
+% \DescribeOption{cs-break}
+% \DescribeOption{cs-break-nohyphen}
+% The commands \cs{cmd} and \cs{cs} allow hyphenation of control sequences
+% after (most) underscores. By default, a hyphen is used to mark the
+% hyphenation, but this can be changed with the \texttt{cs-break-nohyphen}
+% class option. To disable hyphenation of control sequences entirely, use
+% \texttt{cs-break = false}.
+%
+% By default, class options
+% \begin{verbatim}
+% full , check = false , checktest = false , lm-default
+% \end{verbatim}
+% are set.
+%
+% \subsection{Partitioning documentation and implementation}
+%
+% \pkg{doc} uses the \cs{OnlyDocumentation}/\cs{AlsoImplementation}
+% macros to guide the use of \cs{StopEventually}|{}|, which is intended
+% to be placed to partition the documentation and implementation within
+% a single \file{.dtx} file.
+%
+% This isn't very flexible, since it assumes that we \emph{always} want
+% to print the documentation. For the \pkg{expl3} sources, I wanted to
+% be able to input \file{.dtx} files in two modes: only displaying
+% the documentation, and only displaying the implementation. For
+% example:
+% \begin{verbatim}
+% \DisableImplementation
+% \DocInput{l3basics,l3prg,...}
+% \EnableImplementation
+% \DisableDocumentation
+% \DocInputAgain
+% \end{verbatim}
+%
+% The idea being that the entire \pkg{expl3} bundle can be documented,
+% with the implementation included at the back. Now, this isn't
+% perfect, but it's a start.
+%
+% Use |\begin{documentation}...\end{documentation}| around the
+% documentation, and |\begin{implementation}...\end{implementation}|
+% around the implementation. The
+% \cs{EnableDocumentation}/\cs{EnableImplementation} causes them to
+% be typeset when the \file{.dtx} file is \cs{DocInput}; use
+% \cs{DisableDocumentation}/\cs{DisableImplementation} to omit the
+% contents of those environments.
+%
+% Note that \cs{DocInput} now takes comma-separated arguments, and
+% \cs{DocInputAgain} can be used to re-input all \file{.dtx} files
+% previously input in this way.
+%
+% \subsection{General text markup}
+%
+% Many of the commands in this section come from \pkg{ltxdoc} with some
+% improvements.
+%
+% \begin{function}{\cmd, \cs}
+% \begin{syntax}
+% \cmd{\cmd} \oarg{options} \meta{control sequence}\\
+% \cs{cs} \oarg{options} \marg{csname}
+% \end{syntax}
+% These commands are provided to typeset control sequences.
+% |\cmd\foo| produces \enquote{\cmd\foo} and |\cs{foo}| produces the
+% same. In general, \cs{cs} is more robust since
+% it doesn't rely on catcodes being \enquote{correct} and is therefore
+% recommended.
+%
+% These commands are aware of the |@@| \pkg{DocStrip} syntax and
+% replace such instances correctly in the typeset documentation.
+% This only happens after a |%<@@=|\meta{module}|>| declaration.
+%
+% Additionally, commands can be used in the argument of \cs{cs}. For
+% instance, |\cs{\meta{name}:\meta{signature}}| produces
+% \cs[no-index]{\meta{name}:\meta{signature}}.
+%
+% The \meta{options} are a key--value list which can contain the
+% following keys:
+% \begin{itemize}
+% \item |index=|\meta{name}: the \meta{csname} is indexed as if
+% one had written \cs{cs}\Arg{name}.
+% \item |no-index|: the \meta{csname} is not indexed.
+% \item |module=|\meta{module}: the \meta{csname} is indexed in
+% the list of commands from the \meta{module}; the \meta{module}
+% can in particular be |TeX| for \enquote{\TeX{} and \LaTeXe{}}
+% commands, or empty for commands which should be placed in the
+% main index. By default, the \meta{module} is deduced
+% automatically from the command name.
+% \item |replace| is a boolean key (\texttt{true} by default) which
+% indicates whether to replace |@@| as \pkg{DocStrip} does.
+% \end{itemize}
+% These commands allow hyphenation of control sequences after (most) underscores.
+% By default, a hyphen is used to mark the hyphenation, but this can be changed with
+% the \texttt{cs-break-nohyphen} class option.
+% To disable hyphenation of control sequences entirely, use
+% \texttt{cs-break = false}.
+% \end{function}
+%
+%
+% \begin{function}{\tn}
+% \begin{syntax}
+% \cs{tn} \oarg{options} \marg{csname}
+% \end{syntax}
+% Analoguous to \cs{cs} but intended for \enquote{traditional} \TeX{}
+% or \LaTeXe{} commands; they are indexed accordingly. This is in
+% fact equivalent to \cs{cs} |[module=TeX, replace=false,|
+% \meta{options}|]| \Arg{csname}.
+% \end{function}
+%
+% \begin{function}{\meta}
+% \begin{syntax}
+% \cs{meta} \Arg{name}
+% \end{syntax}
+% \cs{meta} typesets the \meta{name} italicised in \meta{angle
+% brackets}. Within a \env{function} environment or similar, angle
+% brackets |<...>| are set up to be a shorthand for |\meta{...}|.
+%
+% This function has additional functionality over its \pkg{ltxdoc}
+% versions; underscores can be used to subscript material as in math
+% mode. For example, |\meta{arg_{xy}}| produces
+% \enquote{\meta{arg_{xy}}}.
+% \end{function}
+%
+% \begin{function}{\Arg, \marg, \oarg, \parg}
+% \begin{syntax}
+% |\Arg| \Arg{name}
+% \end{syntax}
+% Typesets the \meta{name} as for \cs{meta} and wraps it in braces.
+%
+% The \cs{marg}/\cs{oarg}/\cs{parg} versions follow from \pkg{ltxdoc}
+% in being used for \enquote{mandatory} or \enquote{optional} or
+% \enquote{picture} brackets as per \LaTeXe{} syntax.
+% \end{function}
+%
+% \begin{function}{\file, \env, \pkg, \cls}
+% \begin{syntax}
+% \cs{pkg} \Arg{name}
+% \end{syntax}
+% These all take one argument and are intended to be used as semantic
+% commands for representing files, environments, package names, and
+% class names, respectively.
+% \end{function}
+%
+% \begin{function}{\NB, \NOTE}
+% \begin{syntax}
+% \cs{NB} \marg{tag} \marg{comments}
+% \verb|\begin{NOTE}| \marg{tag}
+% \qquad\meta{comments}
+% \verb|\end{NOTE}|
+% \end{syntax}
+% Make notes in the source that are not typeset by default. When the \verb|show-notes|
+% class option is active, the comments are typeset in a detokenized and verbatim mode, respectively.
+% \end{function}
+%
+% \subsection{Describing functions in the documentation}
+%
+% \DescribeEnv{function}
+% \DescribeEnv{variable}
+% Two heavily-used environments are defined to describe \pkg{expl3} functions
+% and variables. If describing a variable, use the latter environment; it
+% behaves identically to the \env{function} environment.
+% \DescribeEnv{syntax}
+% Both of the above environments are typically combined with the \env{syntax}
+% environment, to describe their syntax.
+% \begin{framed}
+% \vspace{-\baselineskip}
+% \begin{verbatim}
+% \begin{function}{\package_function_one:N, \package_function_two:n}
+% \begin{syntax}
+% \cs{package_function_one:N} \meta{cs}
+% \cs{package_function_two:n} \marg{Argument}
+% \end{syntax}
+% Descriptive text here ...
+% \end{function}
+% \end{verbatim}
+% \hrulefill
+% \par
+% \hspace*{0.25\textwidth}
+% \begin{minipage}{0.5\textwidth}
+% \begin{function}{\package_function_one:N, \package_function_two:n}
+% \begin{syntax}
+% \cs[no-index]{package_function_one:N} \meta{cs}
+% \cs[no-index]{package_function_two:n} \marg{Argument}
+% \end{syntax}
+% \emph{Descriptive text here …}
+% \end{function}
+% \end{minipage}
+% \end{framed}
+%
+% Function environments take an optional argument to indicate whether
+% the function(s) it describes are expandable (use |EXP|) or
+% restricted-expandable (use |rEXP|) or defined in conditional forms
+% (use |TF|, |pTF|, or |noTF|). Note that |pTF| implies |EXP| since
+% predicates must always be expandable, and that |noTF| means that the
+% function without |TF| should be documented in addition to |TF|. For
+% the conditional forms |TF| and |pTF|, the argument of the
+% \env{function} environment is \emph{not} in fact a command that
+% exists: in the example below, \cs[no-index]{tl_if_empty:N} does not
+% exist, but its conditional forms \cs{tl_if_empty:NT},
+% \cs{tl_if_empty:NF}, \cs{tl_if_empty:NTF} and predicate form
+% \cs{tl_if_empty_p:N} exist:
+% \begin{framed}
+% \vspace{-\baselineskip}
+% \begin{verbatim}
+% \begin{function}[pTF]{\tl_if_empty:N, \tl_if_empty:c}
+% \begin{syntax}
+% \cs{tl_if_empty_p:N} \meta{tl~var}
+% \cs{tl_if_empty:NTF} \meta{tl~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token list variable} is entirely empty
+% (\emph{i.e.}~contains no tokens at all).
+% \end{function}
+% \end{verbatim}
+% \hrulefill
+% \par
+% \hspace*{0.25\textwidth}
+% \begin{minipage}{0.5\textwidth}
+% \begin{function}[pTF]{\tl_if_empty:N, \tl_if_empty:c}
+% \begin{syntax}
+% \cs{tl_if_empty_p:N} \meta{tl~var}
+% \cs{tl_if_empty:NTF} \meta{tl~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token list variable} is entirely empty
+% (\emph{i.e.}~contains no tokens at all).
+% \end{function}
+% \end{minipage}
+% \end{framed}
+%
+% \DescribeEnv{texnote}
+% This environment is used to call out sections within \env{function}
+% and similar environments that are only of interest to seasoned
+% \TeX{} developers.
+%
+% \subsection{Describing functions in the implementation}
+%
+% \DescribeEnv{macro}
+% The well-used environment from \LaTeXe{} for marking up the
+% implementation of macros/functions remains the \env{macro}
+% environment. Some changes in \pkg{l3doc}: it now accepts
+% comma-separated lists of functions, to avoid a very large number of
+% consecutive |\end{macro}| statements.
+% Spaces and new lines are ignored (the option |[verb]| prevents this).
+% \begin{verbatim}
+% % \begin{macro}{\foo:N, \foo:c}
+% % \begin{macrocode}
+% ... code for \foo:N and \foo:c ...
+% % \end{macrocode}
+% % \end{macro}
+% \end{verbatim}
+% If you are documenting an auxiliary macro, it's generally not
+% necessary to highlight it as much and you also don't need to check it
+% for, say, having a test function and having a documentation chunk
+% earlier in a \env{function} environment. \pkg{l3doc} will pick up these
+% cases from the presence of |__| in the name, or you may force marking
+% as internal by using |\begin{macro}[int]| to mark it as such. The margin
+% call-out is then printed in grey for such cases.
+%
+% For documenting \pkg{expl3}-type conditionals, you may also pass this
+% environment a |TF| option (and omit it from the function name) to
+% denote that the function is provided with |T|, |F|, and |TF| suffixes.
+% A similar |pTF| option prints both |TF| and |_p| predicate forms.
+% An option |noTF| prints both the |TF| forms and a form with neither
+% |T| nor |F|, to document functions such as \cs[no-index]{prop_get:NN}
+% which also have conditional forms (\cs[no-index]{prop_get:NNTF}).
+%
+% In a very small number of cases, there is no user documentation for
+% a \enquote{public} function. In these rare cases, the option
+% |no-user-doc| may be added to suppress the undefined reference that
+% would otherwise then arises.
+%
+% \DescribeMacro{\TestFiles}
+% \cs{TestFiles}\marg{list of files} is used to indicate which test
+% files are used for the current code; they are printed in the
+% documentation.
+%
+% \DescribeMacro{\UnitTested}
+% Within a \env{macro} environment, it is a good idea to mark whether a
+% unit test has been created for the commands it defines. This is
+% indicated by writing \cs{UnitTested} anywhere within |\begin{macro}|
+% \dots |\end{macro}|.
+%
+% If the class option |checktest| is enabled, then it is an \emph{error}
+% to have a \env{macro} environment without a call to
+% \file{Testfiles}. This is intended for large packages such as
+% \pkg{expl3} that should have absolutely comprehensive tests suites and
+% whose authors may not always be as sharp at adding new tests with new
+% code as they should be.
+%
+% \DescribeMacro{\TestMissing}
+% If a function is missing a test, this may be flagged by writing (as
+% many times as needed) \cs{TestMissing} \marg{explanation of test
+% required}. These missing tests are summarised in the listing
+% printed at the end of the compilation run.
+%
+% \DescribeEnv{variable}
+% When documenting variable definitions, use the \env{variable}
+% environment instead. Here it behaves identically to the
+% \env{macro} environment, except that if the class option |checktest|
+% is enabled, variables are not required to have a test file.
+%
+% \DescribeEnv{arguments}
+% Within a \env{macro} environment, you may use the \env{arguments}
+% environment to describe the arguments taken by the function(s). It
+% behaves like a modified enumerate environment.
+% \begin{verbatim}
+% % \begin{macro}{\foo:nn, \foo:VV}
+% % \begin{arguments}
+% % \item Name of froozle to be frazzled
+% % \item Name of muble to be jubled
+% % \end{arguments}
+% % \begin{macrocode}
+% ... code for \foo:nn and \foo:VV ...
+% % \end{macrocode}
+% % \end{macro}
+% \end{verbatim}
+%
+%
+% \subsection{Keeping things consistent}
+%
+% Whenever a function is either documented or defined with
+% \env{function} and \env{macro} respectively, its name is stored in a
+% sequence for later processing.
+%
+% At the end of the document (\emph{i.e.}, after the \file{.dtx} file
+% has finished processing), the list of names is analysed to check
+% whether all defined functions have been documented and vice versa. The
+% results are printed in the console output.
+%
+% If you need to do more serious work with these lists of names, take a
+% look at the implementation for the data structures and methods used to
+% store and access them directly.
+%
+% \subsection{Documenting templates}
+%
+% The following macros are provided for documenting templates; might end
+% up being something completely different but who knows.
+% \begin{quote}\parskip=0pt\obeylines
+% |\begin{TemplateInterfaceDescription}| \Arg{template type name}
+% | \TemplateArgument{none}{---}|
+% \textsc{or one or more of these:}
+% | \TemplateArgument| \Arg{arg no} \Arg{meaning}
+% \textsc{and}
+% |\TemplateSemantics|
+% | | \meta{text describing the template type semantics}
+% |\end{TemplateInterfaceDescription}|
+% \end{quote}
+%
+% \begin{quote}\parskip=0pt\obeylines
+% |\begin{TemplateDescription}| \Arg{template type name} \Arg{name}
+% \textsc{one or more of these:}
+% | \TemplateKey| \marg{key name} \marg{type of key}
+% | |\marg{textual description of meaning}
+% | |\marg{default value if any}
+% \textsc{and}
+% |\TemplateSemantics|
+% | | \meta{text describing special additional semantics of the template}
+% |\end{TemplateDescription}|
+% \end{quote}
+%
+% \begin{quote}\parskip=0pt\obeylines
+% |\begin{InstanceDescription}| \oarg{text to specify key column width (optional)}
+% \hfill\marg{template type name}\marg{instance name}\marg{template name}
+% \textsc{one or more of these:}
+% | \InstanceKey| \marg{key name} \marg{value}
+% \textsc{and}
+% |\InstanceSemantics|
+% | | \meta{text describing the result of this instance}
+% |\end{InstanceDescription}|
+% \end{quote}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3doc} implementation}
+%
+% \begin{macrocode}
+%<*class>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=codedoc>
+% \end{macrocode}
+%
+% \subsection{Variables}
+%
+% \begin{variable}{\g_docinput_clist}
+% The list of files which have been input through \cs{DocInput}.
+% \begin{macrocode}
+\clist_new:N \g_docinput_clist
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_doc_functions_seq, \g_doc_macros_seq}
+% All functions documented through \env{function}, and all macros
+% introduced through \env{macro}. They can be compared to see what
+% documentation or code is missing.
+% \begin{macrocode}
+\seq_new:N \g_doc_functions_seq
+\seq_new:N \g_doc_macros_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_detect_internals_bool, \l_@@_detect_internals_tl}
+% If \texttt{true}, \pkg{l3doc} will check for use of internal
+% commands \cs[no-index]{__\meta{pkg}_\ldots{}} from other packages in
+% the argument of the \texttt{macro} environment, and in the code typeset in
+% \texttt{macrocode} environments, but not in~\cs{cs}. Also a token list
+% to store temporary data for this purpose.
+% \begin{macrocode}
+\bool_new:N \l_@@_detect_internals_bool
+\bool_set_true:N \l_@@_detect_internals_bool
+\tl_new:N \l_@@_detect_internals_tl
+\tl_new:N \l_@@_detect_internals_cs_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_output_coffin}
+% The \env{function} environment is typeset by combining coffins
+% containing various pieces (function names, description, \emph{etc.})
+% into this coffin.
+% \begin{macrocode}
+\coffin_new:N \l_@@_output_coffin
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {\l_@@_functions_coffin, \l_@@_descr_coffin, \l_@@_syntax_coffin}
+% These coffins contain respectively the list of function names
+% (argument of the \env{function} environment), the text between
+% |\begin{function}| and |\end{function}|, and the syntax given in the
+% \env{syntax} environment.
+% \begin{macrocode}
+\coffin_new:N \l_@@_functions_coffin
+\coffin_new:N \l_@@_descr_coffin
+\coffin_new:N \l_@@_syntax_coffin
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_syntax_box}
+% The contents of the \env{syntax} environment are typeset in this box
+% before being transferred to \cs{l_@@_syntax_coffin}.
+% \begin{macrocode}
+\box_new:N \g_@@_syntax_box
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_in_function_bool}
+% True when inside a \texttt{function} or \texttt{variable}
+% environment. Used by the \texttt{syntax} environment to determine
+% its behaviour.
+% \begin{macrocode}
+\bool_new:N \l_@@_in_function_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_long_name_bool, \l_@@_trial_width_dim}
+% The boolean \cs{l_@@_long_name_bool} is \texttt{true} if the width
+% \cs{l_@@_trial_width_dim} of the coffin \cs{l_@@_functions_coffin}
+% (containing the current function names) is bigger than the space
+% available in the margin.
+% \begin{macrocode}
+\bool_new:N \l_@@_long_name_bool
+\dim_new:N \l_@@_trial_width_dim
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_nested_macro_int}
+% The nesting of \env{macro} environments (this is now~$0$ outside a
+% \env{macro} environment).
+% \begin{macrocode}
+\int_new:N \l_@@_nested_macro_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \l_@@_macro_tested_bool,
+% \g_@@_missing_tests_prop,
+% \g_@@_not_tested_seq,
+% \g_@@_testfiles_seq,
+% }
+% A boolean describing whether the current macro has tests, and some
+% global structures which contain information about test files and
+% which tests are missing.
+% \begin{macrocode}
+\bool_new:N \l_@@_macro_tested_bool
+\prop_new:N \g_@@_missing_tests_prop
+\seq_new:N \g_@@_not_tested_seq
+\seq_new:N \g_@@_testfiles_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \l_@@_macro_deprecated_bool ,
+% \l_@@_macro_internal_bool,
+% \l_@@_macro_nodoc_bool ,
+% \l_@@_macro_TF_bool,
+% \l_@@_macro_pTF_bool,
+% \l_@@_macro_noTF_bool,
+% \l_@@_macro_EXP_bool,
+% \l_@@_macro_rEXP_bool,
+% \l_@@_macro_var_bool,
+% \l_@@_override_module_tl,
+% \l_@@_macro_documented_tl,
+% }
+% Contain information about some options of function/macro
+% environments. We initialize \cs{l_@@_override_module_tl} to avoid
+% overriding module names by an empty name (meaning no module).
+% \begin{macrocode}
+\bool_new:N \l_@@_macro_deprecated_bool
+\bool_new:N \l_@@_macro_internal_bool
+\bool_new:N \l_@@_macro_nodoc_bool
+\bool_new:N \l_@@_macro_TF_bool
+\bool_new:N \l_@@_macro_pTF_bool
+\bool_new:N \l_@@_macro_noTF_bool
+\bool_new:N \l_@@_macro_EXP_bool
+\bool_new:N \l_@@_macro_rEXP_bool
+\bool_new:N \l_@@_macro_var_bool
+\tl_new:N \l_@@_override_module_tl
+\tl_set:Nn \l_@@_override_module_tl { \q_no_value }
+\tl_new:N \l_@@_macro_documented_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \g_@@_lmodern_bool,
+% \g_@@_checkfunc_bool,
+% \g_@@_checktest_bool,
+% \g_@@_cs_break_bool,
+% \g_@@_show_notes_bool,
+% \g_@@_kernel_bool
+% }
+% Information about package options.
+% \begin{macrocode}
+\bool_new:N \g_@@_lmodern_bool
+\bool_new:N \g_@@_checkfunc_bool
+\bool_new:N \g_@@_checktest_bool
+\bool_new:N \g_@@_kernel_bool
+\bool_new:N \g_@@_cs_break_bool
+\bool_new:N \g_@@_show_notes_bool
+\bool_gset_true:N \g_@@_cs_break_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_tmpa_tl, \l_@@_tmpb_tl, \l_@@_tmpa_int, \l_@@_tmpa_seq}
+% Some temporary variables.
+% \begin{macrocode}
+\tl_new:N \l_@@_tmpa_tl
+\tl_new:N \l_@@_tmpb_tl
+\int_new:N \l_@@_tmpa_int
+\int_new:N \l_@@_tmpa_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_names_block_tl}
+% List of local sequence variables (produced through
+% \cs{@@_lseq_name:n}), one for each set of variants in a
+% \env{function} or \env{macro} environment. More precisely these
+% sequences are named after the base forms, such as \cs{clist_count:n}
+% or \cs{clist_count:N} (which are not variants). Each of these
+% sequences have the base name (without any signature) as their first
+% item, followed by the list of variant's signatures, or
+% \cs{scan_stop:} to denote the absence of signature (no colon).
+% \begin{macrocode}
+\tl_new:N \l_@@_names_block_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_variants_seq}
+% Stores rather temporarily the list of variants (signatures only) of
+% a function/macro that is being documented. It is global because we
+% need it to keep its value throughout cells of an alignment.
+% \begin{macrocode}
+\seq_new:N \g_@@_variants_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_names_verb_bool}
+% Set to |true| if the main argument of a macro/function environment
+% should be used as is, without removing any comma or space.
+% \begin{macrocode}
+\bool_new:N \l_@@_names_verb_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_names_seq}
+% List of functions/environments/\ldots{} appearing as arguments of a
+% given \env{function} or \env{macro} environment. These are the
+% names after conversion of |_@@| and |@@| to |__|\meta{module name}
+% and other sanitizing.
+% \begin{macrocode}
+\seq_new:N \l_@@_names_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_nested_names_seq}
+% Collects all macros in nested \env{macro} environments, to use them
+% in the \enquote{End definition} text.
+% \begin{macrocode}
+\seq_new:N \g_@@_nested_names_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \l_@@_index_macro_tl, \l_@@_index_key_tl,
+% \l_@@_index_module_tl, \l_@@_index_internal_bool,
+% \l_@@_macro_do_not_index_tl
+% }
+% When analyzing a control sequence found within a \env{macrocode}
+% environment, \cs{l_@@_index_macro_tl} holds the control sequence
+% (partially a string), \cs{l_@@_index_key_tl} holds the future
+% sort key in the index, and \cs{l_@@_index_module_tl} is the
+% subindex in which the control sequence should be listed.
+% \cs{l_@@_index_internal_bool} indicates when the control sequence is
+% internal and should be indexed in a slightly different subindex.
+% Finally, \cs{l_@@_macro_do_not_index_tl} indicates control sequences
+% which should not be indexed in a specifiv \env{macro} envronment.
+% \begin{macrocode}
+\tl_new:N \l_@@_index_macro_tl
+\tl_new:N \l_@@_index_key_tl
+\tl_new:N \l_@@_index_module_tl
+\tl_new:N \l_@@_macro_do_not_index_tl
+\bool_new:N \l_@@_index_internal_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_module_name_tl}
+% The module name, set when reading a line |<@@=|\meta{module}|>|.
+% \begin{macrocode}
+\tl_new:N \g_@@_module_name_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_iow_rule_tl, \c_@@_iow_midrule_tl}
+% $40$~equal signs.
+% \begin{macrocode}
+\tl_const:Nn \c_@@_iow_rule_tl
+ { ======================================== }
+\tl_const:Nn \c_@@_iow_mid_rule_tl
+ { -------------------------------------- }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {\l_@@_macro_box, \l_@@_macro_index_box, \l_@@_macro_int}
+% A vertical box in which the names given to the macro environment are
+% typeset, a horizontal box in which we store the targets created by
+% indexing commands, and the number of macros so far (including those
+% from surrounding \env{macro} environments).
+% \begin{macrocode}
+\box_new:N \l_@@_macro_box
+\box_new:N \l_@@_macro_index_box
+\int_new:N \l_@@_macro_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \l_@@_cmd_tl,
+% \l_@@_cmd_index_tl,
+% \l_@@_cmd_module_tl,
+% \l_@@_cmd_noindex_bool,
+% \l_@@_cmd_replace_bool,
+% }
+% Variables used to control the behaviour of \cs{cmd}, \cs{cs} and
+% \cs{tn}.
+% \begin{macrocode}
+\tl_new:N \l_@@_cmd_tl
+\tl_new:N \l_@@_cmd_index_tl
+\tl_new:N \l_@@_cmd_module_tl
+\bool_new:N \l_@@_cmd_noindex_bool
+\bool_new:N \l_@@_cmd_replace_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_in_implementation_bool}
+% This boolean is \texttt{true} within the \env{implementation}
+% environment, and \texttt{false} anywhere else.
+% \begin{macrocode}
+\bool_new:N \l_@@_in_implementation_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \g_@@_typeset_documentation_bool,
+% \g_@@_typeset_implementation_bool
+% }
+% These booleans control whether the documentation/implementation
+% should be typeset. By default both should be.
+% \begin{macrocode}
+\bool_new:N \g_@@_typeset_documentation_bool
+\bool_new:N \g_@@_typeset_implementation_bool
+\bool_set_true:N \g_@@_typeset_documentation_bool
+\bool_set_true:N \g_@@_typeset_implementation_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_base_name_tl, \l_@@_variants_prop}
+% The name of the macro which is being documented (without its
+% signature), and a property list mapping base forms of variants to
+% all variants which have the same base form.
+% \begin{macrocode}
+\tl_new:N \g_@@_base_name_tl
+\prop_new:N \l_@@_variants_prop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_function_label_clist, \l_@@_no_label_bool}
+% Option of a \env{function} environment which replaces the label that
+% would normally be inserted by labels for the given list of control
+% sequences. This is only useful to avoid duplicate labels when a
+% function's documentation appears multiple times.
+% \begin{macrocode}
+\clist_new:N \l_@@_function_label_clist
+\bool_new:N \l_@@_no_label_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_date_added_tl, \l_@@_date_updated_tl}
+% Values of some options of the \env{function} environment.
+% \begin{macrocode}
+\tl_new:N \l_@@_date_added_tl
+\tl_new:N \l_@@_date_updated_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_macro_argument_tl}
+% Save the argument of a \env{macro} or \env{function} environment for
+% use in error messages.
+% \begin{macrocode}
+\tl_new:N \l_@@_macro_argument_tl
+% \end{macrocode}
+% \end{variable}
+%
+% ^^A Bruno: what does the next line do?
+% \begin{macrocode}
+% \int_new:N \c@CodelineNo
+% \end{macrocode}
+%
+% \subsection{Variants and helpers}
+%
+% \begin{macro}{\@@_tmpa:w, \@@_tmpb:w}
+% Auxiliary macros for temporary use.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_tmpa:w ?
+\cs_new_eq:NN \@@_tmpb:w ?
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \seq_set_split:NoV,
+% \tl_to_str:f
+% }
+% A few missing variants.
+% \begin{macrocode}
+\cs_generate_variant:Nn \seq_set_split:Nnn { NoV }
+\cs_generate_variant:Nn \tl_to_str:n { f }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[TF]{\@@_if_almost_str:n}
+% Used to test if the argument of |\cmd| or other macros to be indexed
+% is almost a string or not: for instance this is \texttt{false} if |#1|
+% contains |\meta{...}|. The surprising |f|-expansion is there to
+% cope with the case of |#1| starting with \cs{c_backslash_str}
+% which should be expanded and considered to be \enquote{normal}.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \@@_if_almost_str:n #1 { TF , T , F }
+ {
+ \int_compare:nNnTF
+ { \tl_count:n {#1} }
+ < { \tl_count:e { \tl_to_str:f {#1} } }
+ { \prg_return_false: }
+ { \prg_return_true: }
+ }
+\prg_generate_conditional_variant:Nnn \@@_if_almost_str:n { V } { T }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_trim_right:Nn, \@@_trim_right:No}
+% Removes all material after |#2| in the token list variable~|#1|.
+% Perhaps combine with \cs{@@_key_trim_module:n}?
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_trim_right:Nn #1#2
+ {
+ \cs_set:Npn \@@_tmp:w ##1 #2 ##2 \q_stop { \exp_not:n {##1} }
+ \__kernel_tl_set:Nx #1 { \exp_after:wN \@@_tmp:w #1 #2 \q_stop }
+ }
+\cs_generate_variant:Nn \@@_trim_right:Nn { No }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[TF]{\@@_str_if_begin:nn, \@@_str_if_begin:oo}
+% True if the first string starts with the second.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \@@_str_if_begin:nn #1#2 { TF , T , F }
+ {
+ \tl_if_in:ooTF
+ { \exp_after:wN \scan_stop: \tl_to_str:n {#1} }
+ { \exp_after:wN \scan_stop: \tl_to_str:n {#2} }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\prg_generate_conditional_variant:Nnn \@@_str_if_begin:nn
+ { oo } { TF , T , F }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replace_at_at:N}
+% \begin{macro}{\@@_replace_at_at_aux:Nn}
+% The goal is to replace |@@| by the current module name. We take
+% advantage of this function to also detect internal macros. If there is
+% no \meta{module~name}, do nothing. Otherwise, sanitize the catcodes
+% of |@| and~|_|, temporarily change |@@@@| to |aa| with different catcodes and later to |@@|, and replace |__@@| and |_@@| and |@@| by
+% |__|\meta{module~name}. The result contains |_| with category
+% code letter because this is what the |macrocode| environment
+% expects. Other use cases can apply \cs{tl_to_str:n} if needed.
+% Note that we include spaces between the
+% |@| in the code below, since it is also processed through the same
+% replacement rules.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replace_at_at:N #1
+ {
+ \tl_if_empty:NF \g_@@_module_name_tl
+ {
+ \exp_args:NNo \@@_replace_at_at_aux:Nn
+ #1 \g_@@_module_name_tl
+ }
+ }
+\cs_new_protected:Npe \@@_replace_at_at_aux:Nn #1#2
+ {
+ \tl_replace_all:Nnn #1 { \token_to_str:N @ } { @ }
+ \tl_replace_all:Nnn #1 { \token_to_str:N _ } { _ }
+ \tl_replace_all:Nnn #1 { @ @ @ @ } { \token_to_str:N a a }
+ \tl_replace_all:Nnn #1 { _ _ @ @ } { _ _ #2 }
+ \tl_replace_all:Nnn #1 { _ @ @ } { _ _ #2 }
+ \tl_replace_all:Nnn #1 { @ @ } { _ _ #2 }
+ \tl_replace_all:Nnn #1 { \token_to_str:N a a } { @ @ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_detect_internals:N,
+% \@@_detect_internals_aux:N,
+% \@@_if_detect_internals_ok:NF
+% }
+% After splitting at each |__| and removing the leading item from the
+% sequence (since it does not follow |__|), remove everything after
+% any space or end-of-line to get a good approximation of the control
+% sequence (for the warning message). Then check if that starts with
+% something allowed: |@@| module name and |:| or |_|, or if the
+% relevant boolean is set |kernel_| (it seems safe to assume we will
+% not define a |\__kernel:...| command). For the message itself
+% remove anything after any |_| or |:| (with either catcode) to get a
+% guess of the module name.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_detect_internals:N #1
+ {
+ \bool_if:NT \l_@@_detect_internals_bool
+ { \@@_detect_internals_aux:N #1 }
+ }
+\group_begin:
+ \char_set_catcode_active:N \^^M
+ \cs_new_protected:Npn \@@_detect_internals_aux:N #1
+ {
+ \tl_set_eq:NN \l_@@_detect_internals_tl #1
+ \tl_replace_all:NVn \l_@@_detect_internals_tl \c_underscore_str { _ }
+ \seq_set_split:NnV \l_@@_tmpa_seq { _ _ } \l_@@_detect_internals_tl
+ \seq_pop_left:NN \l_@@_tmpa_seq \l_@@_detect_internals_tl
+ \seq_map_variable:NNn \l_@@_tmpa_seq \l_@@_detect_internals_tl
+ {
+ \@@_trim_right:No \l_@@_detect_internals_tl
+ \c_catcode_active_space_tl
+ \@@_trim_right:Nn \l_@@_detect_internals_tl ^^M
+ \@@_if_detect_internals_ok:NF \l_@@_detect_internals_tl
+ {
+ \tl_set_eq:NN \l_@@_detect_internals_cs_tl \l_@@_detect_internals_tl
+ \@@_trim_right:Nn \l_@@_detect_internals_tl _
+ \@@_trim_right:Nn \l_@@_detect_internals_tl :
+ \@@_trim_right:No \l_@@_detect_internals_tl { \token_to_str:N : }
+ \msg_warning:nneee { l3doc } { foreign-internal }
+ { \tl_to_str:N \l_@@_detect_internals_cs_tl }
+ { \tl_to_str:N \l_@@_detect_internals_tl }
+ { \tl_to_str:N \g_@@_module_name_tl }
+ }
+ }
+ }
+\group_end:
+\prg_new_protected_conditional:Npnn \@@_if_detect_internals_ok:N #1 { F }
+ {
+ \@@_str_if_begin:ooTF {#1} { \g_@@_module_name_tl _ }
+ { \prg_return_true: }
+ {
+ \@@_str_if_begin:ooTF {#1} { \g_@@_module_name_tl : }
+ { \prg_return_true: }
+ {
+ \bool_if:NTF \g_@@_kernel_bool
+ {
+ \@@_str_if_begin:ooTF {#1} { kernel _ }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ { \prg_return_false: }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_signature_base_form:n}
+% \begin{macro}
+% {\@@_signature_base_form_aux:n, \@@_signature_base_form_aux:w}
+% Expands to the \enquote{base form} of the signature. For instance,
+% given |noxcfvV| it would obtain |nnnNnnn|, or given |ow| it would
+% obtain |nw|. The loop stops at the first token that is not
+% recognized; the rest is enclosed in \cs{exp_not:n}.
+% \begin{macrocode}
+\cs_new:Npn \@@_signature_base_form:n #1
+ { \@@_signature_base_form_aux:n #1 \q_stop }
+\cs_new:Npn \@@_signature_base_form_aux:n #1
+ {
+ \str_case:nnTF {#1}
+ {
+ { N } { N }
+ { c } { N }
+ { n } { n }
+ { o } { n }
+ { f } { n }
+ { e } { n }
+ { x } { n }
+ { V } { n }
+ { v } { n }
+ }
+ { \@@_signature_base_form_aux:n }
+ { \@@_signature_base_form_aux:w #1 }
+ }
+\cs_new:Npn \@@_signature_base_form_aux:w #1 \q_stop
+ { \exp_not:n {#1} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_predicate_from_base:n}
+% Get predicate from a function's base name. The code is not broken
+% by functions with no signature. The |n|-type version can be used
+% for keys and other non-control sequences. The output after
+% |e|-expansion is a string.
+% \begin{macrocode}
+\cs_new:Npn \@@_predicate_from_base:n #1
+ {
+ \@@_get_function_name:n {#1}
+ \tl_to_str:n { _p: }
+ \@@_get_function_signature:n {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_split_function_do:nn, \@@_split_function_do:on}
+% \begin{macro}{\@@_get_function_name:n, \@@_get_function_signature:n}
+% \begin{macro}{\@@_split_function_auxi:w, \@@_split_function_auxii:w}
+% Similar to internal functions defined in \pkg{l3basics}, but here we
+% operate on strings directly rather than control sequences.
+% \begin{macrocode}
+\cs_new:Npn \@@_get_function_name:n #1
+ { \@@_split_function_do:nn {#1} { \use_i:nnn } }
+\cs_new:Npn \@@_get_function_signature:n #1
+ { \@@_split_function_do:nn {#1} { \use_ii:nnn } }
+\cs_set_protected:Npn \@@_tmpa:w #1
+ {
+ \cs_new:Npn \@@_split_function_do:nn ##1
+ {
+ \exp_after:wN \@@_split_function_auxi:w
+ \tl_to_str:n {##1} \q_mark \c_true_bool
+ #1 \q_mark \c_false_bool
+ \q_stop
+ }
+ \cs_new:Npn \@@_split_function_auxi:w
+ ##1 #1 ##2 \q_mark ##3##4 \q_stop ##5
+ { \@@_split_function_auxii:w {##5} ##1 \q_mark \q_stop {##2} ##3 }
+ \cs_new:Npn \@@_split_function_auxii:w
+ ##1##2 \q_mark ##3 \q_stop
+ { ##1 {##2} }
+ }
+\exp_args:No \@@_tmpa:w { \token_to_str:N : }
+\cs_generate_variant:Nn \@@_split_function_do:nn { o }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_key_get_base:nN}
+% Get the base form of a function and store it. As part of getting
+% the base form, change trailing |T| or |F| to |TF|, skipping that
+% change if the function contains no colon to avoid changing for
+% instance some names ending in \texttt{PDF} or similar. The various
+% letters |z| serve as end-delimiters different from any outcome of
+% \cs{tl_to_str:n}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_key_get_base:nN #1#2
+ {
+ \@@_if_almost_str:nTF {#1}
+ {
+ \@@_key_get_base_TF:nN {#1} \l_@@_tmpa_tl
+ \__kernel_tl_set:Nx #2
+ { \@@_split_function_do:on \l_@@_tmpa_tl { \@@_base_form_aux:nnN } }
+ }
+ { \tl_set:Nn #2 {#1} }
+ }
+\cs_new:Npe \@@_key_get_base_TF:nN #1#2
+ {
+ \__kernel_tl_set:Nx #2 { \exp_not:N \tl_to_str:n {#1} }
+ \tl_if_in:NoF #2 { \tl_to_str:n {:} }
+ { \exp_not:N \prg_break: }
+ \tl_if_in:onT { #2 z } { \tl_to_str:n {TF} z }
+ { \exp_not:N \prg_break: }
+ \tl_if_in:onT { #2 z } { \tl_to_str:n {T} z }
+ {
+ \tl_put_right:Nn #2 { \tl_to_str:n {F} }
+ \exp_not:N \prg_break:
+ }
+ \tl_if_in:onT { #2 z } { \tl_to_str:n {F} z }
+ {
+ \tl_put_right:Nn #2 { z }
+ \tl_replace_once:Nnn #2 { \tl_to_str:n {F} z } { \tl_to_str:n {TF} }
+ \exp_not:N \prg_break:
+ }
+ \exp_not:N \prg_break_point:
+ }
+\cs_new:Npn \@@_base_form_aux:nnN #1#2#3
+ {
+ \exp_not:n {#1}
+ \bool_if:NT #3
+ {
+ \token_to_str:N :
+ \bool_lazy_or:nnTF
+ { \str_if_eq_p:nn { #1 ~ } { \exp_args } }
+ { \str_if_eq_p:nn { #1 ~ } { \exp_last_unbraced } }
+ { \exp_not:n {#2} }
+ { \@@_signature_base_form:n {#2} }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_base_form_signature_do:nnn}
+% Do |#2{#1}| if there is no signature, or if |#1| contains two colons
+% in a row (this covers the weird function |\::N| and so on).
+% Otherwise apply |#3| with the following two arguments: the base form
+% of |#1|, and the original signature with an extra pair of braces.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_base_form_signature_do:nnn #1#2#3
+ {
+ \@@_split_function_do:nn {#1}
+ { \@@_base_form_aux:nnnnnN {#1} {#2} {#3} }
+ }
+\cs_new_protected:Npn \@@_base_form_aux:nnnnnN #1#2#3#4#5#6
+ {
+ \bool_if:NTF #6
+ {
+ \tl_if_head_eq_charcode:nNTF {#4} :
+ { #2 {#1} }
+ {
+ \use:e
+ {
+ \exp_not:n {#3}
+ { \@@_base_form_aux:nnN {#4} {#5} #6 }
+ }
+ {#4} {#5}
+ }
+ }
+ { #2 {#1} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\@@_date_compare:nNn}
+% \begin{macro}{\@@_date_compare_aux:nnnNnnn, \@@_date_compare_aux:w}
+% Expects |#1| and |#3| to be dates in the format YYYY-MM-DD (but
+% accepts YYYY or YYYY-MM too). Compares them using |#2| (one of |<|,
+% |=|, |>|), filling in zeros for missing data.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_date_compare:nNn #1#2#3 { TF , T , F , p }
+ { \@@_date_compare_aux:w #1--- \q_mark #2 #3--- \q_stop }
+\cs_new:Npn \@@_date_compare_aux:w
+ #1 - #2 - #3 - #4 \q_mark #5 #6 - #7 - #8 - #9 \q_stop
+ {
+ \@@_date_compare_aux:nnnNnnn
+ { \tl_if_empty:nTF {#1} { 0 } {#1} }
+ { \tl_if_empty:nTF {#2} { 0 } {#2} }
+ { \tl_if_empty:nTF {#3} { 0 } {#3} }
+ #5
+ { \tl_if_empty:nTF {#6} { 0 } {#6} }
+ { \tl_if_empty:nTF {#7} { 0 } {#7} }
+ { \tl_if_empty:nTF {#8} { 0 } {#8} }
+ }
+\cs_new:Npn \@@_date_compare_aux:nnnNnnn #1#2#3#4#5#6#7
+ {
+ \int_compare:nNnTF {#1} = {#5}
+ {
+ \int_compare:nNnTF {#2} = {#6}
+ {
+ \int_compare:nNnTF {#3} #4 {#7}
+ { \prg_return_true: } { \prg_return_false: }
+ }
+ {
+ \int_compare:nNnTF {#2} #4 {#6}
+ { \prg_return_true: } { \prg_return_false: }
+ }
+ }
+ {
+ \int_compare:nNnTF {#1} #4 {#5}
+ { \prg_return_true: } { \prg_return_false: }
+ }
+ \use_none:n
+ \q_stop
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_gprop_name:n, \@@_lseq_name:n}
+% We need to keep track of some information about control sequences
+% (and other strings) that are being (or have been) documented. Some
+% is stored into global props and some into local seqs, whose name
+% does not follow conventions: it is \cs[no-index]{g_@@} or
+% \cs[no-index]{l_@@} followed by a space and by the string, which can
+% be arbitrary. We cannot reasonably use a single big |prop| for
+% speed reasons.
+% \begin{macrocode}
+\cs_new:Npn \@@_gprop_name:n #1 { g_@@ ~ \tl_to_str:n {#1} }
+\cs_new:Npn \@@_lseq_name:n #1 { l_@@ ~ \tl_to_str:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+% \begin{macrocode}
+\msg_new:nnnn { l3doc } { no-signature-TF }
+ { Function/macro~'#1'~cannot~be~turned~into~a~conditional. }
+ {
+ A~function~or~macro~environment~with~option~pTF,~TF~or~noTF~
+ received~the~argument~'#1'.~This~function's~name~has~no~
+ ':'~hence~it~is~not~clear~where~to~add~'_p'~or~'TF'.~
+ Please~follow~expl3~naming~conventions.
+ }
+\msg_new:nnn { l3doc } { date-format }
+ { The~date~'#1'~should~be~given~in~YYYY-MM-DD~format. }
+\msg_new:nnn { l3doc } { future-date }
+ { The~added/updated~date~'#2'~of~'#1'~is~in~the~future. }
+\msg_new:nnn { l3doc } { syntax-nested-function }
+ {
+ The~'syntax'~environment~should~be~used~in~the~
+ innermost~'function'~environment.
+ }
+\msg_new:nnn { l3doc } { multiple-syntax }
+ {
+ The~'syntax'~environment~should~only~be~used~once~in~
+ a~'function'~environment.
+ }
+\msg_new:nnn { l3doc } { deprecated-option }
+ { The~option~'#1'~has~been~deprecated~for~'#2'. }
+\msg_new:nnn { l3doc } { foreign-internal }
+ {
+ A~control~sequence~of~the~form~'...__#1'~was~used.~
+ It~should~only~be~used~in~the~module~'#2'
+ \tl_if_empty:nF {#3} { ,~not~in~'#3' } .
+ }
+% \end{macrocode}
+%
+% \subsection{Options and configuration}
+%
+% \begin{macrocode}
+\DeclareKeys [ l3doc / options ]
+ {
+ a5paper .code:n = \@latexerr { Option~not~supported } { } ,
+ full .code:n =
+ {
+ \bool_gset_true:N \g_@@_typeset_documentation_bool
+ \bool_gset_true:N \g_@@_typeset_implementation_bool
+ } ,
+ onlydoc .code:n =
+ {
+ \bool_gset_true:N \g_@@_typeset_documentation_bool
+ \bool_gset_false:N \g_@@_typeset_implementation_bool
+ } ,
+ check .bool_gset:N = \g_@@_checkfunc_bool ,
+ checktest .bool_gset:N = \g_@@_checktest_bool ,
+ kernel .bool_gset:N = \g_@@_kernel_bool ,
+ stdmodule .bool_gset_inverse:N = \g_@@_kernel_bool ,
+ lm-default .bool_gset:N = \g_@@_lmodern_bool ,
+ cs-break .bool_gset_inverse:N = \g_@@_cs_break_bool ,
+ cs-break-nohyphen .code:n = \PassOptionsToPackage{nohyphen}{underscore} ,
+ show-notes .bool_gset:N = \g_@@_show_notes_bool,
+ hide-notes .bool_gset_inverse:N = \g_@@_show_notes_bool
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\DeclareUnknownKeyHandler [ l3doc / options ]
+ { \PassOptionsToClass { \CurrentOption } { article } }
+\SetKeys [ l3doc / options ]
+ { full , check = false , checktest = false , lm-default }
+\PassOptionsToClass { a4paper } { article }
+% \end{macrocode}
+%
+% Input a local configuration file, if it exists, with a message to the
+% console that this has happened. Since we distribute a \file{.cfg} file
+% with the class, this should usually always be true. Therefore, check
+% for \cs{ExplMakeTitle} (defined in \enquote{our} \file{.cfg} file) and
+% only output the informational message if it's not found.
+%
+% \begin{macrocode}
+\msg_new:nnn { l3doc } { input-cfg }
+ { Local~config~file~l3doc.cfg~loaded. }
+\file_if_exist:nT { l3doc.cfg }
+ {
+ \file_input:n { l3doc.cfg }
+ \cs_if_exist:cF { ExplMakeTitle }
+ { \msg_info:nn { l3doc } { input-cfg } }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\ProcessKeyOptions [ l3doc / options ]
+% \end{macrocode}
+%
+%
+% \subsection{Class and package loading}
+%
+% \begin{macrocode}
+\LoadClass{article}
+\RequirePackage{doc}
+\RequirePackage
+ {
+ array,
+ alphalph,
+ amsmath,
+ amssymb,
+ booktabs,
+ color,
+ colortbl,
+ hologo,
+ enumitem,
+ pifont,
+ textcomp,
+ trace,
+ csquotes,
+ fancyvrb,
+ underscore,
+ verbatim
+ }
+\raggedbottom
+% \end{macrocode}
+%
+% Depending on the option, load the package \pkg{lmodern} to set the
+% font. Then replace the italic typewriter font with the oblique shape
+% instead; the former makes my skin crawl. (Will, Aug 2011)
+% \begin{macrocode}
+\bool_if:NT \g_@@_lmodern_bool
+ {
+ \RequirePackage[T1]{fontenc}
+ \RequirePackage{lmodern}
+ \group_begin:
+ \ttfamily
+ \DeclareFontShape{T1}{lmtt}{m}{it}{<->ec-lmtto10}{}
+ \group_end:
+ }
+% \end{macrocode}
+%
+% Must be last, as usual.
+% \begin{macrocode}
+\RequirePackage{hypdoc}
+% \end{macrocode}
+%
+% \subsection{Configuration and tweaks}
+%
+% \begin{macro}{\MakePrivateLetters}
+% A few more letters are \enquote{private} in a \LaTeX3 programming
+% environment.
+% \begin{macrocode}
+\cs_gset:Npn \MakePrivateLetters
+ {
+ \char_set_catcode_letter:N \@
+ \char_set_catcode_letter:N \_
+ \char_set_catcode_letter:N \:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{CodelineNo}
+% Some configurations which have to do with line numbering.
+% \begin{macrocode}
+\setcounter{StandardModuleDepth}{1}
+\@addtoreset{CodelineNo}{part}
+\tl_replace_once:Nnn \theCodelineNo
+ { \HDorg@theCodelineNo }
+ { \textcolor[gray]{0.5} { \sffamily\tiny\arabic{CodelineNo} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\verbatim, \endverbatim}
+% In \file{.dtx} documents, the \env{verbatim} environment adds extra
+% space because it only removes the first \enquote{\%} sign, and not
+% the indentation (typically a space). Fix it with \pkg{fancyvrb}:
+% \begin{macrocode}
+\fvset{gobble=2}
+\cs_gset_eq:NN \verbatim \Verbatim
+\cs_gset_eq:NN \endverbatim \endVerbatim
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ifnot@excluded}
+% This function tests whether a macro name stored in
+% \tn{macro@namepart} was excluded from indexing by \tn{DoNotIndex}.
+% Rather than trying to fix catcodes that come into here, turn
+% everything to string catcodes. This is slightly inefficient as we
+% could have ensured that \tn{index@excludelist} has string catcodes
+% in the first place.
+% \begin{macrocode}
+\cs_set_protected:Npn \ifnot@excluded
+ {
+ \exp_args:Nee \expanded@notin
+ { \c_backslash_str \tl_to_str:N \macro@namepart , }
+ { \exp_args:NV \tl_to_str:n \index@excludelist }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\pdfstringnewline}
+% \begin{macro}{\@@_pdfstring_newline:w}
+% We avoid some hyperref warnings by making |\\| (almost) trivial in
+% bookmarks: more precisely it might be used with a star and an
+% optional argument, which we thus remove using an \pkg{ltcmd}
+% expandable command. Since there cannot be trailing optional
+% arguments, pick up an extra mandatory one and put it back.
+% \begin{macrocode}
+\cs_new:Npn \pdfstringnewline { : ~ }
+\DeclareExpandableDocumentCommand
+ { \@@_pdfstring_newline:w } { s o m } { \pdfstringnewline #3 }
+\pdfstringdefDisableCommands
+ { \cs_set_eq:NN \\ \@@_pdfstring_newline:w }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Design}
+%
+% Increase the text width slightly so that width the standard fonts
+% 72~columns of code may appear in a \env{macrocode} environment.
+% Increase the marginpar width slightly, for long command names. And
+% increase the left margin by a similar amount.
+% \begin{macrocode}
+\setlength \textwidth { 385pt }
+\addtolength \marginparwidth { 30pt }
+\addtolength \oddsidemargin { 20pt }
+\addtolength \evensidemargin { 20pt }
+% \end{macrocode}
+% (These were introduced when \cls{article} was the documentclass, but
+% I've left them here for now to remind me to do something about them
+% later.)
+%
+% \begin{macro}{\list}
+% \begin{macro}{\@@_oldlist:nn}
+% Customise lists.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_oldlist:nn \list
+\cs_gset:Npn \list #1 #2
+ { \@@_oldlist:nn {#1} { #2 \dim_zero:N \listparindent } }
+\setlength \parindent { 2em }
+\setlength \itemindent { 0pt }
+\setlength \parskip { 0pt plus 3pt minus 0pt }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\partname}
+% Use \enquote{File} as a name in Part titles.
+% \begin{macrocode}
+\tl_gset:Nn \partname {File}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\l@section, \l@subsection}
+% Customise the table of contents (as we have so many sections).
+% Different design and/or structure is called for.
+% \begin{macrocode}
+\@addtoreset{section}{part}
+\cs_gset:Npn \l@section #1#2
+ {
+ \ifnum \c@tocdepth >\z@
+ \addpenalty\@secpenalty
+ \addvspace{1.0em \@plus\p@}
+ \setlength\@tempdima{2.5em} % was 1.5em
+ \begingroup
+ \parindent \z@ \rightskip \@pnumwidth
+ \parfillskip -\@pnumwidth
+ \leavevmode \bfseries
+ \advance\leftskip\@tempdima
+ \hskip -\leftskip
+ #1\nobreak\hfil \nobreak\hb@xt@\@pnumwidth{\hss #2}\par
+ \endgroup
+ \fi
+ }
+\cs_gset:Npn \l@subsection
+ { \@dottedtocline{2}{2.5em}{2.3em} } % #2 = 1.5em
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Text markup}
+%
+% Make "|" and |"| be \enquote{short verb} characters, but not in the
+% document preamble, where an active character may interfere with
+% packages that are loaded. Remove these short-hands at the end of the
+% document before reading the \file{.aux} file, as they may appear in
+% labels (for instance, \pkg{l3fp} documents an operation "||").
+% \begin{macrocode}
+\AtBeginDocument
+ {
+ \MakeShortVerb \"
+ \MakeShortVerb \|
+ }
+\AtEndDocument
+ {
+ \DeleteShortVerb \"
+ \DeleteShortVerb \|
+ }
+% \end{macrocode}
+%
+% \begin{macro}{\eTeX, \IniTeX, \Lua, \LuaTeX, \pdfTeX, \XeTeX,
+% \pTeX, \upTeX, \epTeX, \eupTeX}
+% Some commands for logos.
+% \begin{macrocode}
+\providecommand*\eTeX{\hologo{eTeX}}
+\providecommand*\IniTeX{\hologo{iniTeX}}
+\providecommand*\Lua{Lua}
+\providecommand*\LuaTeX{\hologo{LuaTeX}}
+\providecommand*\pdfTeX{\hologo{pdfTeX}}
+\providecommand*\XeTeX{\hologo{XeTeX}}
+\providecommand*\pTeX{p\kern-.2em\hologo{TeX}}
+\providecommand*\upTeX{up\kern-.2em\hologo{TeX}}
+\providecommand*\epTeX{$\varepsilon$-\pTeX}
+\providecommand*\eupTeX{$\varepsilon$-\upTeX}
+\providecommand*\ConTeXt{\hologo{ConTeXt}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\cmd, \cs, \tn}
+% They rely on a common auxiliary \cs{@@_cmd:nn} which receives as
+% arguments the options and some tokens whose string representation
+% starts with a backslash (to support cases such as |\cs{pkg_\ldots}|,
+% we do not turn the whole argument into a string).
+% \begin{macrocode}
+\DeclareDocumentCommand \cmd { O{} m }
+ { \@@_cmd:no {#1} { \token_to_str:N #2 } }
+\DeclareDocumentCommand \cs { O{} m }
+ { \@@_cmd:no {#1} { \c_backslash_str #2 } }
+\DeclareDocumentCommand \tn { O{} m }
+ {
+ \@@_cmd:no
+ { module = TeX , replace = false , #1 }
+ { \c_backslash_str #2 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\meta}
+% A document-level command.
+% \begin{macrocode}
+\DeclareDocumentCommand \meta { m }
+ { \texttt{ \@@_meta:n {#1} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_pdfstring_cmd:w,
+% \@@_pdfstring_cs:w,
+% \@@_pdfstring_meta:w
+% }
+% To work within a bookmark, these commands must be expandable.
+% \begin{macrocode}
+\DeclareExpandableDocumentCommand
+ { \@@_pdfstring_cmd:w } { o m } { \token_to_str:N #2 }
+\DeclareExpandableDocumentCommand
+ { \@@_pdfstring_cs:w } { o m } { \textbackslash \tl_to_str:n {#2} }
+\cs_new:Npn \@@_pdfstring_meta:w #1
+ { < \tl_to_str:n {#1} > }
+\pdfstringdefDisableCommands
+ {
+ \cs_set_eq:NN \cmd \@@_pdfstring_cmd:w
+ \cs_set_eq:NN \cs \@@_pdfstring_cs:w
+ \cs_set_eq:NN \tn \@@_pdfstring_cs:w
+ \cs_set_eq:NN \meta \@@_pdfstring_meta:w
+ \cs_set_eq:NN \@@_meta:n \@@_pdfstring_meta:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\Arg, \marg, \oarg, \parg}
+% |\marg{text}| prints \marg{text}, \enquote{mandatory argument}.\\
+% |\oarg{text}| prints \oarg{text}, \enquote{optional argument}.\\
+% |\parg{te,xt}| prints \parg{te,xt}, \enquote{picture mode argument}.
+% Finally, \cs{Arg} is the same as \cs{marg}.
+% \begin{macrocode}
+\newcommand\Arg[1]
+ { \texttt{\char`\{} \@@_meta:n {#1} \texttt{\char`\}} }
+\providecommand\marg[1]{ \Arg{#1} }
+\providecommand\oarg[1]{ \texttt[ \@@_meta:n {#1} \texttt] }
+\providecommand\parg[1]{ \texttt( \@@_meta:n {#1} \texttt) }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\file, \env, \pkg, \cls}
+% This list may change\dots this is just my preference for markup.
+% \begin{macrocode}
+\DeclareRobustCommand \file {\nolinkurl}
+\DeclareRobustCommand \env {\texttt}
+\DeclareRobustCommand \pkg {\textsf}
+\DeclareRobustCommand \cls {\textsf}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\EnableDocumentation, \EnableImplementation}
+% \begin{macro}{\DisableDocumentation, \DisableImplementation}
+% Control whether to typeset the documentation/implementation or not.
+% These simply set two switches.
+% \begin{macrocode}
+\NewDocumentCommand \EnableDocumentation { }
+ { \bool_gset_true:N \g_@@_typeset_documentation_bool }
+\NewDocumentCommand \EnableImplementation { }
+ { \bool_gset_true:N \g_@@_typeset_implementation_bool }
+\NewDocumentCommand \DisableDocumentation { }
+ { \bool_gset_false:N \g_@@_typeset_documentation_bool }
+\NewDocumentCommand \DisableImplementation { }
+ { \bool_gset_false:N \g_@@_typeset_implementation_bool }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{environment}{documentation}
+% \begin{environment}{implementation}
+% If the documentation/implementation should be typeset, then simply
+% set the boolean \cs{l_@@_in_implementation_bool} which indicates
+% whether we are within the implementation section. Otherwise use
+% \cs{comment} (and a paired \cs{endcomment}).
+% \begin{macrocode}
+\NewDocumentEnvironment { documentation } { }
+ {
+ \bool_if:NTF \g_@@_typeset_documentation_bool
+ { \bool_set_false:N \l_@@_in_implementation_bool }
+ { \comment }
+ }
+ { \bool_if:NF \g_@@_typeset_documentation_bool { \endcomment } }
+\NewDocumentEnvironment { implementation } { }
+ {
+ \bool_if:NTF \g_@@_typeset_implementation_bool
+ { \bool_set_true:N \l_@@_in_implementation_bool }
+ { \comment }
+ }
+ { \bool_if:NF \g_@@_typeset_implementation_bool { \endcomment } }
+% \end{macrocode}
+% \end{environment}
+% \end{environment}
+%
+% \begin{environment}{variable}
+% The \env{variable} environment behaves as a \env{function} or
+% \env{macro} environment depending on the part of the document.
+% \begin{macrocode}
+\DeclareDocumentEnvironment { variable } { O{} +v }
+ {
+ \bool_if:NTF \l_@@_in_implementation_bool
+ { \@@_macro:nnw { var , #1 } {#2} }
+ { \@@_function:nnw {#1} {#2} }
+ }
+ {
+ \bool_if:NTF \l_@@_in_implementation_bool
+ { \@@_macro_end: }
+ { \@@_function_end: }
+ }
+% \end{macrocode}
+% \end{environment}
+%
+% \begin{environment}{function}
+% \begin{environment}{macro}
+% Environment for documenting function(s), and environment for
+% documenting the implementation of a macro.
+% \begin{macrocode}
+\DeclareDocumentEnvironment { function } { O{} +v }
+ { \@@_function:nnw {#1} {#2} }
+ { \@@_function_end: }
+\DeclareDocumentEnvironment { macro } { O{} +v }
+ { \@@_macro:nnw {#1} {#2} }
+ { \@@_macro_end: }
+% \end{macrocode}
+% \end{environment}
+% \end{environment}
+%
+% \begin{environment}{syntax}
+% Syntax block placed next to the list of functions to illustrate
+% their use. TODO: test that the \env{syntax} environment is only
+% used inside the \env{function} environment, and that it only appears
+% once.
+% \begin{macrocode}
+\NewDocumentEnvironment { syntax } { }
+ { \@@_syntax:w }
+ {
+ \@@_syntax_end:
+ \ignorespacesafterend
+ }
+% \end{macrocode}
+% \end{environment}
+%
+% \begin{environment}{texnote}
+% Used to describe information destined to \TeX{} experts only.
+% \begin{macrocode}
+\NewDocumentEnvironment { texnote } { }
+ {
+ \endgraf
+ \vspace{3mm}
+ \small\textbf{\TeX~hackers~note:}
+ }
+ {
+ \vspace{3mm}
+ }
+% \end{macrocode}
+% \end{environment}
+%
+% \begin{environment}{arguments}
+% This environment is designed to be used within a \env{macro}
+% environment to describe the arguments of the macro/function.
+% \begin{macrocode}
+\NewDocumentEnvironment { arguments } { }
+ {
+ \enumerate [
+ nolistsep ,
+ label = \texttt{\#\arabic*} ~ : ,
+ labelsep = * ,
+ ]
+ }
+ {
+ \endenumerate
+ }
+% \end{macrocode}
+% \end{environment}
+%
+% \begin{macro}{\CodedocExplain, \CodedocExplainEXP, \CodedocExplainREXP, \CodedocExplainTF}
+% Explanation of stars and |TF| notations, for use in third-party
+% packages.
+% \begin{macrocode}
+\NewDocumentCommand { \CodedocExplain } { }
+ { \CodedocExplainEXP \ \CodedocExplainREXP \ \CodedocExplainTF }
+\NewDocumentCommand { \CodedocExplainEXP } { }
+ {
+ \raisebox{\baselineskip}[0pt][0pt]{\hypertarget{expstar}{}}%
+ \write \@auxout { \def \string \Codedoc@expstar { } }
+ \@@_typeset_exp:\ indicates~fully~expandable~functions,~which~
+ can~be~used~within~an~\texttt{e}-type~argument~(inside~an~\tn{expanded}),~
+ \texttt{x}-type~argument~(in~plain~\TeX{}~terms,~inside~an~\tn{edef}),~
+ as~well~as~within~an~\texttt{f}-type~argument.
+ }
+\NewDocumentCommand { \CodedocExplainREXP } { }
+ {
+ \raisebox{\baselineskip}[0pt][0pt]{\hypertarget{rexpstar}{}}%
+ \write \@auxout { \def \string \Codedoc@rexpstar { } }
+ \@@_typeset_rexp:\ indicates~
+ restricted~expandable~functions,~which~can~be~used~within~an~
+ \texttt{x}-type~argument~or~an~\texttt{e}-type~argument,~
+ but~cannot~be~fully~expanded~within~an~\texttt{f}-type~argument.
+ }
+\NewDocumentCommand { \CodedocExplainTF } { }
+ {
+ \raisebox{\baselineskip}[0pt][0pt]{\hypertarget{explTF}{}}%
+ \write \@auxout { \def \string \Codedoc@explTF { } }
+ \@@_typeset_TF:\ indicates~conditional~(\texttt{if})~functions~
+ whose~variants~with~\texttt{T},~\texttt{F}~and~\texttt{TF}~
+ argument~specifiers~expect~different~
+ \enquote{true}/\enquote{false}~branches.
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Implementing text markup}
+%
+% Keys for \cs{cmd}, \cs{cs} and \cs{tn}.
+% \begin{macrocode}
+\keys_define:nn { l3doc/cmd }
+ {
+ index .tl_set:N = \l_@@_cmd_index_tl ,
+ module .tl_set:N = \l_@@_cmd_module_tl ,
+ no-index .bool_set:N = \l_@@_cmd_noindex_bool ,
+ replace .bool_set:N = \l_@@_cmd_replace_bool ,
+ }
+% \end{macrocode}
+%
+% \begin{macro}[do-not-index={\\,\_,\1,\c,\2}]{\@@_cmd:nn, \@@_cmd:no}
+% Apply the key--value \meta{options}~|#1| after setting some
+% default values. Then (unless |replace=false|) replace |@@| in~|#2|,
+% which is a bit tricky: the |_| must be given the catcode expected by
+% \cs{@@_replace_at_at:N}, but should be reverted to their original
+% catcode (normally active, needed for line-breaking) without
+% rescanning the whole argument. Then typeset the command in
+% \tn{verbatim@font}, after turning it to harmless characters if
+% needed (and keeping the underscore breakable); in any case, spaces
+% must be turned into \tn{@xobeysp} and we must use \tn{@} to avoid
+% longer spaces after a control sequence that ends for instance with a
+% colon (empty signature). Finally, produce an index entry.
+% Indexing is suppressed when \cs{l_@@_cmd_noindex_bool} is true.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_cmd:nn #1#2
+ {
+ \bool_set_false:N \l_@@_cmd_noindex_bool
+ \bool_set_true:N \l_@@_cmd_replace_bool
+ \tl_set:Nn \l_@@_cmd_index_tl { \q_no_value }
+ \tl_set:Nn \l_@@_cmd_module_tl { \q_no_value }
+ \keys_set:nn { l3doc/cmd } {#1}
+ \tl_set:Nn \l_@@_cmd_tl {#2}
+ \bool_if:NT \l_@@_cmd_replace_bool
+ {
+ \tl_set_rescan:Nnn \l_@@_tmpb_tl { } { _ }
+ \tl_replace_all:NVn \l_@@_cmd_tl \l_@@_tmpb_tl { _ }
+ \@@_replace_at_at:N \l_@@_cmd_tl
+ \tl_replace_all:NnV \l_@@_cmd_tl { _ } \l_@@_tmpb_tl
+ }
+% \end{macrocode}
+% Typesetting.
+% Note the replacement for the underscore is to permit linebreaks.
+% The \texttt{underscore} package adds the linebreak,
+% and the regex results in applying the breakable underscore only to the \emph{last}
+% of a run of underscores, and not if the underscore follows a backslash.
+% \begin{macrocode}
+ \mode_if_math:T { \mbox }
+ {
+ \bool_if:NT \l_@@_allow_indexing_bool { \@@_target: }
+ \verbatim@font
+ \@@_if_almost_str:VT \l_@@_cmd_tl
+ {
+ \__kernel_tl_set:Nx \l_@@_cmd_tl { \tl_to_str:N \l_@@_cmd_tl }
+ \bool_if:NT \g_@@_cs_break_bool
+ {
+ \regex_replace_all:nnN
+ { ([^\\\_]\_*) \_ ([^\_]) }
+ { \1 \c{BreakableUnderscore} \2 }
+ \l_@@_cmd_tl
+ }
+ }
+ \tl_replace_all:Nnn \l_@@_cmd_tl { ~ } { \@xobeysp }
+ \l_@@_cmd_tl
+ \@
+ }
+% \end{macrocode}
+% Indexing.
+% \begin{macrocode}
+ \bool_if:NT \l_@@_allow_indexing_bool
+ {
+ \bool_if:NF \l_@@_cmd_noindex_bool
+ {
+ \quark_if_no_value:NF \l_@@_cmd_index_tl
+ {
+ \__kernel_tl_set:Nx \l_@@_cmd_tl
+ { \c_backslash_str \exp_not:o { \l_@@_cmd_index_tl } }
+ }
+ \exp_args:No \@@_key_get:n { \l_@@_cmd_tl }
+ \quark_if_no_value:NF \l_@@_cmd_module_tl
+ {
+ \__kernel_tl_set:Nx \l_@@_index_module_tl
+ { \tl_to_str:N \l_@@_cmd_module_tl }
+ }
+ \@@_special_index_module:ooonN
+ { \l_@@_index_key_tl }
+ { \l_@@_index_macro_tl }
+ { \l_@@_index_module_tl }
+ { usage }
+ \l_@@_index_internal_bool
+ }
+ }
+ }
+\cs_generate_variant:Nn \@@_cmd:nn { no }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_meta:n,
+% \@@_ensuremath_sb:n,
+% \@@_meta_original:n
+% }
+% Store |#1| in \cs{l_@@_tmpa_tl} and replaces every underscore,
+% regardless of its category (\enquote{math toggle},
+% \enquote{alignment}, \enquote{superscript}, \enquote{subscript},
+% \enquote{letter}, \enquote{other}, or \enquote{active}) by
+% \cs{@@_ensuremath_sb:n} (which creates math subscripts), then runs
+% the code used for \tn{meta} in \pkg{doc.sty}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_meta:n #1
+ {
+ \tl_set:Nn \l_@@_tmpa_tl {#1}
+ \tl_map_inline:nn
+ { { 3 } { 4 } { 7 } { 8 } { 11 } { 12 } { 13 } }
+ {
+ \tl_set_rescan:Nnn \l_@@_tmpb_tl
+ { \char_set_catcode:nn { `_ } {##1} } { _ }
+ \tl_replace_all:NVn \l_@@_tmpa_tl \l_@@_tmpb_tl
+ { \@@_ensuremath_sb:n }
+ }
+ \exp_args:NV \@@_meta_original:n \l_@@_tmpa_tl
+ }
+\cs_new_protected:Npn \@@_ensuremath_sb:n #1
+ { \ensuremath { \sb {#1} } }
+\cs_new_protected:Npn \@@_meta_original:n #1
+ {
+ \ensuremath \langle
+ \mode_if_math:T { \nfss@text }
+ {
+ \meta@font@select
+ \edef \meta@hyphen@restore
+ { \hyphenchar \the \font \the \hyphenchar \font }
+ \hyphenchar \font \m@ne
+ \language \l@nohyphenation
+ #1 \/
+ \meta@hyphen@restore
+ }
+ \ensuremath \rangle
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Common between \env{macro} and \env{function}}
+%
+% \begin{macro}
+% {
+% \@@_typeset_exp:, \@@_typeset_rexp:,
+% \@@_typeset_TF:, \@@_typeset_aux:n
+% }
+% Used by \cs{@@_macro_single:nNN} and in the \env{function} environment
+% to typeset conditionals and auxiliary functions.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_typeset_exp:
+ {
+ \cs_if_exist:NTF \Codedoc@expstar
+ { \hyperlink { expstar } }
+ { \mbox }
+ {$\star$}
+ }
+\cs_new_protected:Npn \@@_typeset_rexp:
+ {
+ \cs_if_exist:NTF \Codedoc@rexpstar
+ { \hyperlink { rexpstar } }
+ { \mbox }
+ { \ding { 73 } } % hollow star
+ }
+\cs_new_protected:Npn \@@_typeset_TF:
+ {
+ \cs_if_exist:NTF \Codedoc@explTF
+ { \hyperlink { explTF } }
+ { \mbox }
+ {
+ \itshape TF
+ \makebox[0pt][r]
+ {
+ \underline { \phantom{\itshape TF} \kern-0.1em }
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_typeset_aux:n #1
+ {
+ { \color[gray]{0.5} #1 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_get_hyper_target:nN, \@@_get_hyper_target:oN, \@@_get_hyper_target:eN}
+% Create a \pkg{hyperref} anchor from a macro name~|#1| and stores it
+% in the token list variable~|#2|. For instance, |\prg_replicate:nn|
+% gives |doc/function//prg/replicate:nn|.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_get_hyper_target:nN #1#2
+ {
+ \__kernel_tl_set:Nx #2 { \tl_to_str:n {#1} }
+ \tl_replace_all:NVn #2 \c_underscore_str { / }
+ \tl_remove_all:NV #2 \c_backslash_str
+ \tl_put_left:Nn #2 { doc/function// }
+ }
+\cs_generate_variant:Nn \@@_get_hyper_target:nN { o , e }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_names_get_seq:nN}
+% The argument~|#1| (argument of a |function| or |macro| environment)
+% has catcodes $10$ (space), $12$ (other) and $13$ (active). Sanitize
+% catcodes. If the |verb| option was used, output a one-item
+% sequence. Otherwise, remove any \enquote{\%} character at the
+% beginning of a line. Remove tabs and newlines. Finally, convert
+% |_@@| and |@@| to |__|\meta{module name} (if it is non-empty). At
+% this point, \cs{l_@@_tmpa_tl} contains a comma-delimited list of
+% names, where |@| and~|_| have category code letter. Turn it to a
+% string, parse it as a comma-delimited list (in particular this
+% removes spaces), and output a sequence of function/macro names.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_names_get_seq:nN #1#2
+ {
+ \__kernel_tl_set:Nx \l_@@_tmpa_tl { \tl_to_str:n {#1} }
+ \bool_if:NTF \l_@@_names_verb_bool
+ {
+ \seq_clear:N #2
+ \seq_put_right:NV #2 \l_@@_tmpa_tl
+ }
+ {
+ \tl_remove_all:Ne \l_@@_tmpa_tl
+ { \iow_char:N \^^M \c_percent_str }
+ \tl_remove_all:Ne \l_@@_tmpa_tl
+ { \token_to_str:N \obeyedline \c_space_tl \c_percent_str }
+ \tl_remove_all:Ne \l_@@_tmpa_tl { \tl_to_str:n { ^ ^ A } }
+ \tl_remove_all:Ne \l_@@_tmpa_tl { \iow_char:N \^^I }
+ \tl_remove_all:Ne \l_@@_tmpa_tl { \iow_char:N \^^M }
+ \@@_detect_internals:N \l_@@_tmpa_tl
+ \@@_replace_at_at:N \l_@@_tmpa_tl
+ \exp_args:NNe \seq_set_from_clist:Nn #2
+ { \tl_to_str:N \l_@@_tmpa_tl }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_names_parse:, \@@_names_parse_one:n}
+% The goal is to group variants together. We populate
+% \cs{l_@@_names_block_tl} with local sequence variable named with
+% \cs{@@_lseq_name:n} after the base forms. When encountering a new
+% base form, set the corresponding local sequence to hold the
+% \meta{base name} (stripped of the signature) and add the local
+% sequence to the list \cs{l_@@_names_block_tl}. In all cases append
+% the signature to the local sequence, which thus takes the form
+% \meta{base name}, \meta{signature_1}, \meta{signature_2} and so on.
+% If the original function had no signature (no colon) then use
+% \cs{scan_stop:} as the signature (there can be no variant). We
+% special case commands |#1| starting with |\::|, namely weird
+% functions named |\::N| and the like.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_names_parse:
+ {
+ \tl_clear:N \l_@@_names_block_tl
+ \seq_map_function:NN
+ \l_@@_names_seq
+ \@@_names_parse_one:n
+ }
+\cs_new_protected:Npn \@@_names_parse_one:n #1
+ {
+ \@@_split_function_do:nn {#1}
+ { \@@_names_parse_one_aux:nnNn }
+ {#1}
+ }
+\cs_new_protected:Npn \@@_names_parse_one_aux:nnNn #1#2#3#4
+ {
+ \bool_if:NTF #3
+ {
+ \tl_if_head_eq_charcode:nNTF {#2} :
+ { \@@_names_parse_aux:nnn {#4} {#4} { \scan_stop: } }
+ {
+ \exp_args:Ne \@@_names_parse_aux:nnn
+ { \@@_base_form_aux:nnN {#1} {#2} #3 }
+ {#1} {#2}
+ }
+ }
+ {
+ \bool_if:NT \l_@@_macro_TF_bool
+ { \msg_error:nne { l3doc } { no-signature-TF } {#4} }
+ \@@_names_parse_aux:nnn {#4} {#4} { \scan_stop: }
+ }
+ }
+\cs_new_protected:Npn \@@_names_parse_aux:nnn #1
+ { \exp_args:Nc \@@_names_parse_aux:Nnn { \@@_lseq_name:n {#1} } }
+\cs_new_protected:Npn \@@_names_parse_aux:Nnn #1#2#3
+ {
+ \tl_if_in:NnF \l_@@_names_block_tl {#1}
+ {
+ \tl_put_right:Nn \l_@@_names_block_tl {#1}
+ \seq_clear_new:N #1
+ \seq_put_right:Nn #1 {#2}
+ }
+ \seq_put_right:Nn #1 {#3}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_names_typeset:}
+% \begin{macro}{\@@_names_typeset_auxi:n}
+% This code is in particular used when typesetting function names in a
+% \env{function} environment. The mapping over \cs{l_@@_names_block_tl}
+% cannot use \cs{tl_map_inline:Nn} because the code following |\\|
+% would not be expandable, thus breaking \tn{bottomrule}.
+%
+% Call \cs{@@_names_typeset_auxi:n} on each local sequence (which
+% holds a set of variants). The first step is to pop the base form
+% and change spaces to category other so
+% that they get displayed eventually. Then store the variants in
+% \cs{g_@@_variants_seq}, remove the first, which will be displayed
+% more prominently, and reconstruct the actual name, passing it to
+% \cs{@@_names_typeset_auxii:n}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_names_typeset:
+ {
+ \tl_map_function:NN \l_@@_names_block_tl
+ \@@_names_typeset_auxi:n
+ }
+\cs_new_protected:Npn \@@_names_typeset_auxi:n #1
+ {
+ \seq_pop:NN #1 \l_@@_tmpa_tl
+ \tl_gset_eq:NN \g_@@_base_name_tl \l_@@_tmpa_tl
+ \tl_greplace_all:NnV \g_@@_base_name_tl
+ { ~ } \c_catcode_other_space_tl
+ \seq_get:NN #1 \l_@@_tmpa_tl
+ \str_if_eq:VnTF \l_@@_tmpa_tl { \scan_stop: }
+ {
+ \seq_gclear:N \g_@@_variants_seq
+ \@@_names_typeset_auxii:e { \g_@@_base_name_tl }
+ }
+ {
+ \seq_gset_eq:NN \g_@@_variants_seq #1
+ \seq_gpop:NN \g_@@_variants_seq \l_@@_tmpb_tl
+ \@@_names_typeset_auxii:e
+ { \g_@@_base_name_tl : \l_@@_tmpb_tl }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_names_typeset_auxii:n, \@@_names_typeset_auxii:e}
+% In case the option |pTF| was given, typeset predicates before the
+% |TF| functions. In case the option |noTF| was given, typeset the
+% non-|TF| function as well. Pass the relevant boolean in both cases
+% to control whether to append |TF|.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_names_typeset_auxii:n #1
+ {
+ \bool_if:NT \l_@@_macro_pTF_bool
+ {
+ \@@_names_typeset_block:eN
+ { \@@_predicate_from_base:n {#1} }
+ \c_false_bool
+ }
+ \bool_if:NT \l_@@_macro_noTF_bool
+ { \@@_names_typeset_block:nN {#1} \c_false_bool }
+ \@@_names_typeset_block:nN {#1} \l_@@_macro_TF_bool
+ }
+\cs_generate_variant:Nn \@@_names_typeset_auxii:n { e }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_names_typeset_block:nN, \@@_names_typeset_block:eN}
+% Names in \env{function} and \env{macro} environments are typeset
+% differently. To distinguish the two note that
+% \cs{l_@@_nested_macro_int} is at least one when in an \env{macro}
+% environment (we assume \env{function} is not nested inside it). A
+% block is a function with all its variants.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_names_typeset_block:nN
+ {
+ \int_compare:nNnTF \l_@@_nested_macro_int = 0
+ { \@@_typeset_function_block:nN }
+ { \@@_macro_typeset_block:nN }
+ }
+\cs_generate_variant:Nn \@@_names_typeset_block:nN { e }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\@@_if_macro_internal:n}
+% \begin{macro}[EXP]{\@@_if_macro_internal_aux:w}
+% Determines whether the given macro should be considered internal or
+% public. If an option such as |int| was given then the answer is
+% \cs{l_@@_macro_internal_bool}, otherwise check for whether the macro
+% name contains~|__|.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_if_macro_internal:n #1 { p , T , F , TF }
+ {
+ \bool_if:NTF \l_@@_macro_internal_bool
+ { \prg_return_true: }
+ {
+ \tl_if_empty:eTF
+ {
+ \exp_after:wN \@@_if_macro_internal_aux:w
+ \tl_to_str:n { #1 ~ __ }
+ }
+ { \prg_return_false: } { \prg_return_true: }
+ }
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new:Npn \@@_if_macro_internal_aux:w #1 { \tl_to_str:n { __ } } { }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_names_block_base_map:N}
+% The \cs{l_@@_names_block_tl} contains sequence variables
+% corresponding to different base functions and their variants. For
+% each such sequence, put the first and second items in
+% \cs{l_@@_tmpa_tl} and \cs{l_@@_tmpb_tl} and build the base
+% function's name.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_names_block_base_map:N #1
+ {
+ \tl_map_inline:Nn \l_@@_names_block_tl
+ {
+ \group_begin:
+ \seq_set_eq:NN \l_@@_tmpa_seq ##1
+ \seq_pop:NN \l_@@_tmpa_seq \l_@@_tmpa_tl
+ \seq_get:NN \l_@@_tmpa_seq \l_@@_tmpb_tl
+ \exp_args:NNe
+ \group_end:
+ #1
+ {
+ \l_@@_tmpa_tl
+ \str_if_eq:VnF \l_@@_tmpb_tl { \scan_stop: }
+ { : \l_@@_tmpb_tl }
+ \bool_if:NT \l_@@_macro_TF_bool { TF }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{The \env{function} environment}
+%
+% \begin{macrocode}
+\keys_define:nn { l3doc/function }
+ {
+ TF .value_forbidden:n = true ,
+ TF .code:n =
+ {
+ \bool_set_true:N \l_@@_macro_TF_bool
+ } ,
+ EXP .value_forbidden:n = true ,
+ EXP .code:n =
+ {
+ \bool_set_true:N \l_@@_macro_EXP_bool
+ \bool_set_false:N \l_@@_macro_rEXP_bool
+ } ,
+ rEXP .value_forbidden:n = true ,
+ rEXP .code:n =
+ {
+ \bool_set_false:N \l_@@_macro_EXP_bool
+ \bool_set_true:N \l_@@_macro_rEXP_bool
+ } ,
+ pTF .value_forbidden:n = true ,
+ pTF .code:n =
+ {
+ \bool_set_true:N \l_@@_macro_pTF_bool
+ \bool_set_true:N \l_@@_macro_TF_bool
+ \bool_set_true:N \l_@@_macro_EXP_bool
+ \bool_set_false:N \l_@@_macro_rEXP_bool
+ } ,
+ noTF .value_forbidden:n = true ,
+ noTF .code:n =
+ {
+ \bool_set_true:N \l_@@_macro_noTF_bool
+ \bool_set_true:N \l_@@_macro_TF_bool
+ } ,
+ added .code:n = { \@@_date_set_past:Nn \l_@@_date_added_tl {#1} },
+ updated .code:n = { \@@_date_set_past:Nn \l_@@_date_updated_tl {#1} } ,
+ deprecated .bool_set:N = \l_@@_macro_deprecated_bool ,
+ no-user-doc .bool_set:N = \l_@@_macro_nodoc_bool ,
+ tested .code:n = { } ,
+ label .code:n =
+ {
+ \clist_set:Nn \l_@@_function_label_clist {#1}
+ \bool_set_true:N \l_@@_no_label_bool
+ } ,
+ verb .value_forbidden:n = true ,
+ verb .bool_set:N = \l_@@_names_verb_bool ,
+ module .tl_set:N = \l_@@_override_module_tl ,
+ }
+% \end{macrocode}
+%
+% \begin{macro}[do-not-index={\A,\Z,\d,\1,\2,\3}]
+% {\@@_date_set:Nn,\@@_date_set_past:Nn}
+% Normalize the date into the format \texttt{YYYY-MM-DD}; more
+% precisely month and day are allowed to be single digits. The
+% \cs{@@_date_set_past:Nn} function only allows dates in the past (or
+% same day).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_date_set:Nn #1#2
+ {
+ \tl_set:Nn #1 {#2}
+ \regex_replace_once:nnNF
+ { \A(\d\d\d\d)[-/](\d\d?)[-/](\d\d?)\Z } { \1-\2-\3 } #1
+ {
+ \msg_error:nnn { l3doc } { date-format } {#2}
+ \tl_set:Nn #1 { 1970-01-01 }
+ }
+ }
+\cs_new_protected:Npn \@@_date_set_past:Nn #1#2
+ {
+ \@@_date_set:Nn #1 {#2}
+ \exp_args:No \@@_date_compare:nNnT
+ {#1} > { \c_sys_year_int - \c_sys_month_int - \c_sys_day_int }
+ {
+ \msg_error:nnee { l3doc } { future-date }
+ { \tl_to_str:N \l_@@_macro_argument_tl }
+ {#1}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_function:nnw}
+% \begin{arguments}
+% \item Key--value list.
+% \item Comma-separated list of functions; input has already been
+% sanitised by catcode changes before reading the argument.
+% \end{arguments}
+% \begin{macro}{\@@_function_end:}
+% Make sure any paragraph is finished, and similar safe practices at
+% the beginning of an environment which will typeset material.
+% Initialize some variables. Parse the key--value list. Clean up the
+% list of functions, then go through them to extract some data. After
+% this, typeset the function names in the coffin
+% \cs{l_@@_functions_coffin} and measure it to know if it fits in the
+% margin. Finally, start a vertical coffin for the main part of the
+% environment. This coffin stops when the environment ends, then all
+% the pieces are assembled into a single coffin, which is typeset.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_function:nnw #1#2
+ {
+ \@@_function_typeset_start:
+ \@@_function_init:
+ \tl_set:Nn \l_@@_macro_argument_tl {#2}
+ \keys_set:nn { l3doc/function } {#1}
+ \@@_names_get_seq:nN {#2} \l_@@_names_seq
+ \@@_names_parse:
+ \@@_function_typeset:
+ \@@_function_reset:
+ \@@_function_descr_start:w
+ }
+\cs_new_protected:Npn \@@_function_end:
+ {
+ \@@_function_descr_stop:
+ \@@_function_assemble:
+ \@@_function_typeset_stop:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_function_typeset_start:, \@@_function_typeset_stop:}
+% At the start of the \env{function} environment, before performing
+% any assignment, close the last paragraph, and set up the typesetting
+% scene. Further code typesets a coffin, so we end the paragraph and
+% allow a page break.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_function_typeset_start:
+ {
+ \par \bigskip \noindent
+ }
+\cs_new_protected:Npn \@@_function_typeset_stop:
+ {
+ \par
+ \dim_set:Nn \prevdepth { \box_dp:N \l_@@_descr_coffin }
+ \allowbreak
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_function_init:}
+% Complain if \texttt{function} environments are nested. Clear
+% various variables.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_function_init:
+ {
+ \box_if_empty:NF \g_@@_syntax_box
+ { \msg_error:nn { l3doc } { syntax-nested-function } }
+ \coffin_clear:N \l_@@_descr_coffin
+ \box_gclear:N \g_@@_syntax_box
+ \coffin_clear:N \l_@@_syntax_coffin
+ \coffin_clear:N \l_@@_functions_coffin
+ \bool_set_false:N \l_@@_macro_TF_bool
+ \bool_set_false:N \l_@@_macro_pTF_bool
+ \bool_set_false:N \l_@@_macro_noTF_bool
+ \bool_set_false:N \l_@@_macro_EXP_bool
+ \bool_set_false:N \l_@@_macro_rEXP_bool
+ \bool_set_false:N \l_@@_no_label_bool
+ \bool_set_false:N \l_@@_names_verb_bool
+ \bool_set_true:N \l_@@_in_function_bool
+ \clist_clear:N \l_@@_function_label_clist
+ \tl_set:Nn \l_@@_override_module_tl { \q_no_value }
+ \char_set_active_eq:NN \< \@@_shorthand_meta:
+ \char_set_catcode_active:N \<
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_shorthand_meta:, \@@_shorthand_meta:w}
+% Allow |<...>| to be used as markup for |\meta{...}|.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_shorthand_meta:
+ { \mode_if_math:TF { < } { \@@_shorthand_meta:w } }
+\cs_new_protected_nopar:Npn \@@_shorthand_meta:w #1 > { \meta {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_function_reset:}
+% Clear some variables.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_function_reset:
+ {
+ \tl_set:Nn \l_@@_override_module_tl { \q_no_value }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_function_typeset:}
+% Typeset in the coffin \cs{l_@@_functions_coffin} the functions listed in
+% \cs{l_@@_names_block_tl} and the relevant dates, then set
+% \cs{l_@@_long_name_bool} to be \texttt{true} if this coffin is
+% larger than the available width in the margin. The function
+% \cs{@@_typeset_functions:} is quite involved hence given later.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_function_typeset:
+ {
+ \dim_zero:N \l_@@_trial_width_dim
+ \hcoffin_set:Nn \l_@@_functions_coffin { \@@_typeset_functions: }
+ \dim_set:Nn \l_@@_trial_width_dim
+ { \box_wd:N \l_@@_functions_coffin }
+ \bool_set:Nn \l_@@_long_name_bool
+ { \dim_compare_p:nNn \l_@@_trial_width_dim > \marginparwidth }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_function_descr_start:w, \@@_function_descr_stop:}
+% The last step in \cs{@@_function:nnw} (the beginning of a
+% \env{function} environment) is to open a coffin which will capture
+% the description of the function, namely the body of the
+% \env{function} environment. This is closed by \cs{@@_function_end:}
+% (the end of a \env{function} environment).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_function_descr_start:w
+ {
+ \vcoffin_set:Nnw \l_@@_descr_coffin { \textwidth }
+ \noindent \ignorespaces
+ }
+\cs_new_protected:Npn \@@_function_descr_stop:
+ { \vcoffin_set_end: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_function_assemble:}
+% The box \cs{g_@@_syntax_box} contains the contents of the syntax
+% environment if it was used. Now that we have all the pieces, join
+% together the syntax coffin, the names coffin, and the description
+% coffin. The relative positions depend on whether the names coffin
+% fits in the margin. Then typeset the combination.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_function_assemble:
+ {
+ \hcoffin_set:Nn \l_@@_syntax_coffin
+ { \box_use_drop:N \g_@@_syntax_box }
+ \bool_if:NTF \l_@@_long_name_bool
+ {
+ \coffin_join:NnnNnnnn
+ \l_@@_output_coffin {hc} {vc}
+ \l_@@_syntax_coffin {l} {T}
+ {0pt} {0pt}
+ \coffin_join:NnnNnnnn
+ \l_@@_output_coffin {l} {t}
+ \l_@@_functions_coffin {r} {t}
+ {-\marginparsep} {0pt}
+ \coffin_join:NnnNnnnn
+ \l_@@_output_coffin {l} {b}
+ \l_@@_descr_coffin {l} {t}
+ {0.75\marginparwidth + \marginparsep} {-\medskipamount}
+ \coffin_typeset:Nnnnn \l_@@_output_coffin
+ {\l_@@_descr_coffin-l} {\l_@@_descr_coffin-t}
+ {0pt} {0pt}
+ }
+ {
+ \coffin_join:NnnNnnnn
+ \l_@@_output_coffin {hc} {vc}
+ \l_@@_syntax_coffin {l} {t}
+ {0pt} {0pt}
+ \coffin_join:NnnNnnnn
+ \l_@@_output_coffin {l} {b}
+ \l_@@_descr_coffin {l} {t}
+ {0pt} {-\medskipamount}
+ \coffin_join:NnnNnnnn
+ \l_@@_output_coffin {l} {t}
+ \l_@@_functions_coffin {r} {t}
+ {-\marginparsep} {0pt}
+ \coffin_typeset:Nnnnn \l_@@_output_coffin
+ {\l_@@_syntax_coffin-l} {\l_@@_syntax_coffin-T}
+ {0pt} {0pt}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_typeset_functions:}
+% This function builds the \cs{l_@@_functions_coffin} by typesetting the
+% function names (with variants) and the relevant dates in a
+% \env{tabular} environment. The use of rules \tn{toprule},
+% \tn{midrule} and \tn{bottomrule} requires whatever lies between the
+% last |\\| and the rule to be expandable, making our lives a bit
+% complicated.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_typeset_functions:
+ {
+ \small\ttfamily
+ \@@_target:
+ \Hy@MakeCurrentHref { HD. \int_use:N \c@HD@hypercount }
+ \begin{tabular} [t] { @{} l @{} >{\hspace{\tabcolsep}} r @{} }
+ \toprule
+ \@@_function_extra_labels:
+ \@@_names_typeset:
+ \@@_typeset_dates:
+ \bottomrule
+ \end{tabular}
+ \normalfont\normalsize
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% ^^A TODO: collect all index targets from a given function environment in a box and stick it at the top.
+% \begin{macro}
+% {\@@_typeset_function_block:nN, \@@_typeset_function_block:eN}
+% \begin{macro}{\@@_function_index:n, \@@_function_index:e}
+% |#1| is a csname, |#2| a boolean indicating whether to add |TF| or not.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_typeset_function_block:nN #1#2
+ {
+ \@@_function_index:e
+ { #1 \bool_if:NT #2 { \tl_to_str:n {TF} } }
+ \@@_function_label:eN {#1} #2
+ #1
+ \bool_if:NT #2 { \@@_typeset_TF: }
+ \@@_typeset_expandability:
+ \seq_if_empty:NF \g_@@_variants_seq
+ { \@@_typeset_variant_list:nN {#1} #2 }
+ \\
+ }
+\cs_generate_variant:Nn \@@_typeset_function_block:nN { e }
+\cs_new_protected:Npn \@@_function_index:n #1
+ {
+ \seq_gput_right:Nn \g_doc_functions_seq {#1}
+ \@@_special_index:nn {#1} { usage }
+ }
+\cs_generate_variant:Nn \@@_function_index:n { e }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_typeset_expandability:
+ {
+ &
+ \bool_if:NT \l_@@_macro_EXP_bool { \@@_typeset_exp: }
+ \bool_if:NT \l_@@_macro_rEXP_bool { \@@_typeset_rexp: }
+ }
+% \end{macrocode}
+%
+% |#1| is the function, |#2| whether to add |TF|.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_typeset_variant_list:nN #1#2
+ {
+ \\
+ \@@_typeset_aux:n { \@@_get_function_name:n {#1} }
+ :
+ \int_compare:nTF { \seq_count:N \g_@@_variants_seq == 1 }
+ {
+ \seq_use:Nn \g_@@_variants_seq { }
+ \bool_if:NT #2 { \@@_typeset_TF: }
+ }
+ {
+ \hbox_set:Nn \l_tmpa_box
+ { \seq_use:Nn \g__@@_variants_seq { \textrm| \nolinebreak[2] } }
+ \textrm(
+% \end{macrocode}
+%
+% Set long variant lists in a parbox, short lists set natural length.
+% \begin{macrocode}
+ \dim_compare:nNnTF { \box_wd:N \l_tmpa_box } > { .4\columnwidth }
+ {
+ \parbox[t]{.4\columnwidth}
+ {
+ \raggedright
+ \hbox_unpack_drop:N \l_tmpa_box
+ \textrm)
+ \bool_if:NT #2 { \@@_typeset_TF: }
+ }
+ }
+ {
+ \hbox_unpack_drop:N \l_tmpa_box
+ \textrm)
+ \bool_if:NT #2 { \@@_typeset_TF: }
+ }
+ }
+ \@@_typeset_expandability:
+ }
+% \end{macrocode}
+%
+% |#1| is the function name, |#2| whether to add |TF|.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_function_extra_labels:
+ {
+ \bool_if:NT \l_@@_no_label_bool
+ {
+ \clist_map_inline:Nn \l_@@_function_label_clist
+ {
+ \@@_get_hyper_target:oN { \token_to_str:N ##1 }
+ \l_@@_tmpa_tl
+ \exp_args:No \label { \l_@@_tmpa_tl }
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_function_label:nN #1#2
+ {
+ \bool_if:NF \l_@@_no_label_bool
+ {
+ \@@_get_hyper_target:eN
+ {
+ \exp_not:n {#1}
+ \bool_if:NT #2 { \tl_to_str:n {TF} }
+ }
+ \l_@@_tmpa_tl
+ \exp_args:No \label { \l_@@_tmpa_tl }
+ }
+ }
+\cs_generate_variant:Nn \@@_function_label:nN { e }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_typeset_dates:}
+% To display metadata for when functions are added/modified.
+% This function must be expandable since it produces rules for use in
+% alignments.
+% \begin{macrocode}
+\cs_new:Npn \@@_typeset_dates:
+ {
+ \bool_lazy_and:nnF
+ { \tl_if_empty_p:N \l_@@_date_added_tl }
+ { \tl_if_empty_p:N \l_@@_date_updated_tl }
+ { \midrule }
+ \tl_if_empty:NF \l_@@_date_added_tl
+ {
+ \multicolumn { 2 } { @{} r @{} }
+ { \scriptsize New: \, \l_@@_date_added_tl } \\
+ }
+
+ \tl_if_empty:NF \l_@@_date_updated_tl
+ {
+ \multicolumn { 2 } { @{} r @{} }
+ { \scriptsize Updated: \, \l_@@_date_updated_tl } \\
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_syntax:w, \@@_syntax_end:}
+% Implement the \env{syntax} environment.
+% \begin{macrocode}
+\dim_new:N \l_@@_syntax_dim
+\cs_new_protected:Npn \@@_syntax:w
+ {
+ \box_if_empty:NF \g_@@_syntax_box
+ { \msg_error:nn { l3doc } { multiple-syntax } }
+ \dim_set:Nn \l_@@_syntax_dim
+ {
+ \textwidth
+ \bool_if:NT \l_@@_long_name_bool
+ { + 0.75 \marginparwidth - \l_@@_trial_width_dim }
+ }
+ \hbox_gset:Nw \g_@@_syntax_box
+ \small \ttfamily
+ \arrayrulecolor{white}
+ \begin{tabular} { @{} l @{} }
+ \toprule
+ \begin{minipage}[t]{\l_@@_syntax_dim}
+ \raggedright
+ \obeyspaces
+ \obeylines
+ }
+\cs_new_protected:Npn \@@_syntax_end:
+ {
+ \end{minipage}
+ \end{tabular}
+ \arrayrulecolor{black}
+ \hbox_gset_end:
+ \bool_if:NF \l_@@_in_function_bool
+ {
+ \begin{quote}
+ \mode_leave_vertical:
+ \box_use_drop:N \g_@@_syntax_box
+ \end{quote}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{The \env{macro} environment}
+%
+% Keyval for the \env{macro} environment.
+% TODO: provide document command for documenting keys.
+% \begin{macrocode}
+\keys_define:nn { l3doc/macro }
+ {
+ aux .value_forbidden:n = true ,
+ aux .code:n =
+ {
+ \msg_warning:nnnn { l3doc } { deprecated-option }
+ { aux } { function/macro }
+ } ,
+ deprecated .bool_set:N = \l_@@_macro_deprecated_bool ,
+ internal .value_forbidden:n = true ,
+ internal .code:n =
+ { \bool_set_true:N \l_@@_macro_internal_bool } ,
+ int .value_forbidden:n = true ,
+ int .code:n =
+ { \bool_set_true:N \l_@@_macro_internal_bool } ,
+ no-user-doc .bool_set:N = \l_@@_macro_nodoc_bool ,
+ var .value_forbidden:n = true ,
+ var .code:n =
+ { \bool_set_true:N \l_@@_macro_var_bool } ,
+ TF .value_forbidden:n = true ,
+ TF .code:n =
+ { \bool_set_true:N \l_@@_macro_TF_bool } ,
+ pTF .value_forbidden:n = true ,
+ pTF .code:n =
+ {
+ \bool_set_true:N \l_@@_macro_TF_bool
+ \bool_set_true:N \l_@@_macro_pTF_bool
+ \bool_set_true:N \l_@@_macro_EXP_bool
+ \bool_set_false:N \l_@@_macro_rEXP_bool
+ } ,
+ noTF .value_forbidden:n = true ,
+ noTF .code:n =
+ {
+ \bool_set_true:N \l_@@_macro_TF_bool
+ \bool_set_true:N \l_@@_macro_noTF_bool
+ } ,
+ EXP .value_forbidden:n = true ,
+ EXP .code:n =
+ {
+ \bool_set_true:N \l_@@_macro_EXP_bool
+ \bool_set_false:N \l_@@_macro_rEXP_bool
+ } ,
+ rEXP .value_forbidden:n = true ,
+ rEXP .code:n =
+ {
+ \bool_set_false:N \l_@@_macro_EXP_bool
+ \bool_set_true:N \l_@@_macro_rEXP_bool
+ } ,
+ tested .code:n =
+ {
+ \bool_set_true:N \l_@@_macro_tested_bool
+ } ,
+ added .code:n = {} , % TODO
+ updated .code:n = {} , % TODO
+ verb .bool_set:N = \l_@@_names_verb_bool ,
+ module .tl_set:N = \l_@@_override_module_tl ,
+ documented-as .tl_set:N = \l_@@_macro_documented_tl ,
+ do-not-index .value_required:n = true ,
+ do-not-index .tl_set:N = \l_@@_macro_do_not_index_tl ,
+ % do-not-index .default:n = \q_no_value ,
+ }
+% \end{macrocode}
+%
+% \begin{macro}{\@@_macro:nnw}
+% The arguments are a key--value list of \meta{options} and a
+% comma-list of \meta{names}, read verbatim by \pkg{ltcmd}. First
+% initialize some variables before applying the \meta{options}, then
+% parse the \meta{names} to get a sequence of macro names, then apply
+% \cs{@@_macro_single:nNN} to each (this step is more subtle than
+% \cs{seq_map_function:NN} because of |TF|/|pTF|/|noTF|). Finally typeset
+% the macro names in the margin.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_macro:nnw #1#2
+ {
+ \@@_macro_init:
+ \tl_set:Nn \l_@@_macro_argument_tl {#2}
+ \keys_set:nn { l3doc/macro } {#1}
+ \@@_names_get_seq:nN {#2} \l_@@_names_seq
+ \@@_names_parse:
+ \@@_macro_exclude_index:
+ \@@_macro_save_names:
+ \@@_names_typeset:
+ \@@_macro_dump:
+ \@@_macro_reset:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_macro_init:}
+% The booleans hold various key--value options,
+% \cs{l_@@_nested_macro_int} counts the number of \env{macro}
+% environments around the current point (is $0$ outside).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_macro_init:
+ {
+ \int_incr:N \l_@@_nested_macro_int
+ \bool_set_false:N \l_@@_macro_deprecated_bool
+ \bool_set_false:N \l_@@_macro_internal_bool
+ \bool_set_false:N \l_@@_macro_TF_bool
+ \bool_set_false:N \l_@@_macro_pTF_bool
+ \bool_set_false:N \l_@@_macro_noTF_bool
+ \bool_set_false:N \l_@@_macro_EXP_bool
+ \bool_set_false:N \l_@@_macro_rEXP_bool
+ \bool_set_false:N \l_@@_macro_var_bool
+ \bool_set_false:N \l_@@_macro_tested_bool
+ \bool_set_false:N \l_@@_names_verb_bool
+ \tl_set:Nn \l_@@_override_module_tl { \q_no_value }
+ \tl_clear:N \l_@@_macro_documented_tl
+ \cs_set_eq:NN \testfile \@@_print_testfile:n
+ \box_clear:N \l_@@_macro_index_box
+ \vbox_set:Nn \l_@@_macro_box
+ {
+ \hbox:n
+ {
+ \strut
+ \int_compare:nNnT \l_@@_macro_int = 0 { \@@_target: }
+ }
+ \vskip \int_eval:n { \l_@@_macro_int - 1 } \baselineskip
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_macro_reset:}
+% We ensure that \cs{cs} commands nested inside a macro whose module
+% is imposed are not affected.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_macro_reset:
+ {
+ \tl_set:Nn \l_@@_override_module_tl { \q_no_value }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_macro_save_names:}
+% \begin{macro}[EXP]{\@@_macro_save_names_aux:w}
+% The list of names defined in a set of \env{macro} environments is
+% eventually used to display on which page they are documented. If
+% the |documented-as| key is given, use that, otherwise find names in
+% \cs{l_@@_names_block_tl}. The |documented-as| needs to be detokenized
+% without adding a space, so it has to be treated in a slightly awkward
+% way.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_macro_save_names:
+ {
+ \tl_if_empty:NTF \l_@@_macro_documented_tl
+ { \@@_names_block_base_map:N \@@_macro_save_names_aux:n }
+ {
+ \seq_gput_right:Ne \g_@@_nested_names_seq
+ {
+ \exp_after:wN \@@_macro_save_names_aux:w
+ \l_@@_macro_documented_tl \q_@@_stop
+ }
+ }
+ }
+\cs_new:Npn \@@_macro_save_names_aux:w #1#2 \q_@@_stop
+ { \token_to_str:N #1 \tl_to_str:n {#2} }
+\cs_new_protected:Npn \@@_macro_save_names_aux:n #1
+ { \seq_gput_right:Nn \g_@@_nested_names_seq {#1} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_macro_exclude_index:}
+% Some control sequences in a \env{macrocode} environment shouldn't
+% be indexed, for different reasons. This macro parses the argument
+% of the |do-not-index| option and locally removes the given macros
+% from the index.
+%
+% The optional argument to \env{macro} is not scanned with verbatim
+% catcodes, so we use \cs{tl_set_rescan:NnV} to rescan the commands
+% with the same catcodes as \cs{DoNotIndex}. The scanned token list
+% contains spaces after control sequences, which are not there when
+% \cs{DoNotIndex} is used. Since \cs{seq_set_from_clist:Nn} removes
+% spaces around the items, we can abuse that and \cs{seq_use:Nn} to
+% normalise each item. After that \cs{DoNotIndex} can do its thing.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_macro_exclude_index:
+ {
+ \tl_if_empty:NF \l_@@_macro_do_not_index_tl
+ {
+ \tl_set_rescan:NnV \l_@@_macro_do_not_index_tl
+ { \MakePrivateLetters \catcode`\\12 }
+ \l_@@_macro_do_not_index_tl
+ \exp_args:NNV \seq_set_from_clist:Nn
+ \l_@@_tmpa_seq \l_@@_macro_do_not_index_tl
+ \__kernel_tl_set:Nx \l_@@_macro_do_not_index_tl
+ { \seq_use:Nn \l_@@_tmpa_seq { , } }
+ \exp_args:NV \DoNotIndex \l_@@_macro_do_not_index_tl
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_macro_dump:}
+% This calls |\makelabel{}|
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_macro_dump:
+ {
+ \topsep\MacroTopsep
+ \trivlist
+ \cs_set:Npn \makelabel ##1
+ {
+ \llap
+ {
+ \hbox_unpack_drop:N \l_@@_macro_index_box
+ \vtop to \baselineskip
+ {
+ \vbox_unpack_drop:N \l_@@_macro_box
+ \vss
+ }
+ }
+ }
+ \item [ ]
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_macro_typeset_block:nN}
+% Used to typeset a macro and its variants. |#1| is the macro name,
+% |#2| is a boolean controlling whether to add |TF|.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_macro_typeset_block:nN #1#2
+ {
+ \@@_macro_single:nNN {#1} \c_true_bool #2
+ \seq_if_empty:NF \g_@@_variants_seq
+ {
+ \@@_macro_typeset_variant_list:eN
+ { \@@_get_function_name:n {#1} } #2
+ }
+ }
+\cs_new_protected:Npn \@@_macro_typeset_variant_list:nN #1#2
+ {
+ \seq_map_inline:Nn \g_@@_variants_seq
+ { \@@_macro_single:nNN { #1 : ##1 } \c_false_bool #2 }
+ }
+\cs_generate_variant:Nn \@@_macro_typeset_variant_list:nN { e }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_macro_single:nNN}
+% The arguments are |#1| a macro name (without |TF|), |#2| a boolean
+% determining whether or not to index, and |#3| whether or not to add |TF|.
+% Let's start to mess around with \cls{doc}'s \env{macro} environment.
+% See \file{doc.dtx} for a full explanation of the original
+% environment. It's rather \emph{enthusiastically} commented.
+% \begin{arguments}
+% \item Macro/function/whatever name; input has already been
+% sanitised.
+% \end{arguments}
+% The assignments to \cs{saved@macroname} and \cs{saved@indexname}
+% are used by \pkg{doc}'s \cs{changes} mechanism.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_macro_single:nNN #1#2#3
+ {
+ \tl_set:Nn \saved@macroname {#1}
+ \@@_macro_typeset_one:nN {#1} #3
+ \bool_if:NT #3 { \DoNotIndex {#1} }
+ \exp_args:Ne \@@_macro_index:nN
+ { #1 \bool_if:NT #3 { \tl_to_str:n { TF } } }
+ #2
+ }
+\cs_new_protected:Npn \@@_macro_index:nN #1#2
+ {
+ \DoNotIndex {#1}
+ \bool_if:NT #2
+ {
+ \bool_lazy_any:nF
+ {
+ { \@@_if_macro_internal_p:n {#1} }
+ { \l_@@_macro_deprecated_bool }
+ { \l_@@_macro_nodoc_bool }
+ }
+ { \seq_gput_right:Nn \g_doc_macros_seq {#1} }
+ \hbox_set:Nw \l_@@_macro_index_box
+ \hbox_unpack_drop:N \l_@@_macro_index_box
+ \int_gincr:N \c@CodelineNo
+ \@@_special_index:nn {#1} { main }
+ \int_gdecr:N \c@CodelineNo
+ \exp_args:NNNo \hbox_set_end:
+ \tl_set:Nn \saved@indexname { \l_@@_index_key_tl }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_macro_typeset_one:nN}
+% For a long time, \cls{l3doc} collected the macro names as labels in
+% the first items of nested \tn{trivlist}, but these were not closed
+% properly with \tn{endtrivlist}. Also, it interacted in surprising
+% ways with \pkg{hyperref} targets. Now, we collect typeset macro
+% names by hand in the box \cs{l_@@_macro_box}. The fixed-size space
+% |\MacroFont\ | could be replaced by an customizable horizontal
+% space; it is important for it to be the same for all macros.
+% |#1| is the macro name, |#2| whether to add |TF|.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_macro_typeset_one:nN #1#2
+ {
+ \vbox_set:Nn \l_@@_macro_box
+ {
+ \vbox_unpack_drop:N \l_@@_macro_box
+ \hbox { \llap { \@@_print_macroname:nN {#1} #2
+ \MacroFont \
+ } }
+ }
+ \int_incr:N \l_@@_macro_int
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_print_macroname:nN}
+% In the name, spaces are replaced by other spaces to ensure they get
+% displayed in case there are any.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_print_macroname:nN #1#2
+ {
+ \strut
+ \@@_get_hyper_target:eN
+ {
+ \exp_not:n {#1}
+ \bool_if:NT #2 { \tl_to_str:n {TF} }
+ }
+ \l_@@_tmpa_tl
+ \cs_if_exist:cTF { r@ \l_@@_tmpa_tl }
+ { \exp_last_unbraced:NNo \hyperref [ \l_@@_tmpa_tl ] }
+ { \use:n }
+ {
+ \int_compare:nTF { \str_count:n {#1} <= 28 }
+ { \MacroFont } { \MacroLongFont }
+ \tl_set:Nn \l_@@_tmpa_tl {#1}
+ \tl_replace_all:NnV \l_@@_tmpa_tl
+ { ~ } \c_catcode_other_space_tl
+ \@@_print_macroname_aux:on
+ \l_@@_tmpa_tl { \bool_if:NT #2 { \@@_typeset_TF: } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_print_macroname_aux:nn}
+% |#1| is prefix, |#2| is optional |TF| suffix.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_print_macroname_aux:nn #1#2
+ {
+ \@@_if_macro_internal:nTF {#1}
+ { \@@_typeset_aux:n { #1 #2 } } { #1 #2 }
+ }
+\cs_generate_variant:Nn \@@_print_macroname_aux:nn { o }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\MacroLongFont}
+% \begin{macrocode}
+\providecommand \MacroLongFont
+ {
+ \fontfamily{lmtt}\fontseries{lc}\small
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_print_testfile:n, \@@_print_testfile_aux:n}
+% Used to show that a macro has a test, somewhere.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_print_testfile:n #1
+ {
+ \bool_set_true:N \l_@@_macro_tested_bool
+ \tl_if_eq:nnF {#1} {*}
+ {
+ \seq_if_in:NnF \g_@@_testfiles_seq {#1}
+ {
+ \seq_gput_right:Nn \g_@@_testfiles_seq {#1}
+ \par
+ \@@_print_testfile_aux:n {#1}
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_print_testfile_aux:n #1
+ {
+ \footnotesize
+ (
+ \textit
+ {
+ The~ test~ suite~ for~ this~ command,~
+ and~ others~ in~ this~ file,~ is~ \textsf{#1}
+ }.
+ )\par
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\TestFiles}
+% \begin{macrocode}
+\DeclareDocumentCommand \TestFiles {m}
+ {
+ \par
+ \textit
+ {
+ The~ following~ test~ files~ are~
+ used~ for~ this~ code:~ \textsf{#1}.
+ }
+ \par \ignorespaces
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\UnitTested}
+% \begin{macrocode}
+\DeclareDocumentCommand \UnitTested { } { \testfile* }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\TestMissing}
+% \begin{macrocode}
+\DeclareDocumentCommand \TestMissing { m }
+ { \@@_test_missing:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_test_missing:n}
+% Keys in \cs{g_@@_missing_tests_prop} are lists of macros given as
+% arguments of one \env{macro} environment. Values are pairs of a
+% file name and a comment about the missing tests.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_test_missing:n #1
+ {
+ \@@_test_missing_aux:Nen
+ \g_@@_missing_tests_prop
+ { \seq_use:Nn \l_@@_names_seq { , } }
+ { { \g_file_curr_name_str \c_space_tl (#1) } }
+ }
+\cs_new_protected:Npn \@@_test_missing_aux:Nnn #1#2#3
+ {
+ \prop_get:NnNTF #1 {#2} \l_@@_tmpa_tl
+ { \tl_put_right:Nn \l_@@_tmpa_tl { , #3 } }
+ { \tl_set:Nn \l_@@_tmpa_tl {#3} }
+ \prop_put:Nno #1 {#2} \l_@@_tmpa_tl
+ }
+\cs_generate_variant:Nn \@@_test_missing_aux:Nnn { Ne }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_macro_end:}
+% It is too late for anyone to declare a test file for this macro, so
+% we can check now whether the macro is tested. If the \env{macro}
+% environment which is being ended is the outermost one, then wrap
+% each macro in \tn{texttt} (with the addition of |TF| if relevant)
+% and typeset two informations: that this ends the definition of some
+% macros, and that they are documented on some page.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_macro_end:
+ {
+ \endtrivlist
+ \@@_macro_end_check_tested:
+ \int_compare:nNnT \l_@@_nested_macro_int = 1
+ { \@@_macro_end_style:n { \@@_print_end_definition: } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_macro_end_check_tested:}
+% If the |checktest| option was issued and the macro is not an
+% auxiliary nor a variable (and it does not have a test), then add it
+% to the sequence of non-tested macros.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_macro_end_check_tested:
+ {
+ \bool_lazy_all:nT
+ {
+ { \g_@@_checktest_bool }
+ { ! \l_@@_macro_var_bool }
+ { ! \l_@@_macro_tested_bool }
+ }
+ {
+ \seq_set_filter:NNn \l_@@_tmpa_seq \l_@@_names_seq
+ { ! \@@_if_macro_internal_p:n {##1} }
+ \seq_gput_right:Ne \g_@@_not_tested_seq
+ {
+ \seq_use:Nn \l_@@_tmpa_seq { , }
+ \bool_if:NTF \l_@@_macro_pTF_bool {~(pTF)}
+ { \bool_if:NT \l_@@_macro_TF_bool {~(TF)} }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_macro_end_style:n}
+% Style for the extra information at the end of a top-level
+% \env{macro} environment.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_macro_end_style:n #1
+ {
+ \nobreak \noindent
+ { \footnotesize ( \emph{#1} ) \par }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_print_end_definition:,
+% \@@_macro_end_wrap_item:n,
+% \@@_print_documented:
+% }
+% Surround each item by \tn{texttt}, replacing |_|
+% by \tn{_} as well. Then list the
+% macro names through \cs{seq_use:Nnnn}, unless there are too many.
+% Finally, if the macro is neither auxiliary nor internal, add a link
+% to where it is documented.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_macro_end_wrap_item:n #1
+ {
+ \tl_set:Nn \l_@@_tmpa_tl {#1}
+ \tl_replace_all:NVn \l_@@_tmpa_tl
+ \c_underscore_str { \_ }
+ \texttt { \l_@@_tmpa_tl }
+ }
+\cs_new_protected:Npn \@@_print_end_definition:
+ {
+ \seq_set_map:NNn \l_@@_tmpa_seq
+ \g_@@_nested_names_seq
+ { \@@_macro_end_wrap_item:n {##1} }
+ End~ of~ definition~ for~
+ \int_compare:nTF { \seq_count:N \l_@@_tmpa_seq <= 3 }
+ {
+ \seq_use:Nnnn \l_@@_tmpa_seq
+ { \,~and~ } { \,,~ } { \,,~and~ }
+ }
+ { \seq_item:Nn \l_@@_tmpa_seq {1}\,~and~others }
+ \@.
+ \@@_print_documented:
+ }
+\cs_new_protected:Npn \@@_print_documented:
+ {
+ \seq_gset_filter:NNn \g_@@_nested_names_seq
+ \g_@@_nested_names_seq
+ {
+ ! \bool_lazy_any_p:n
+ {
+ { \@@_if_macro_internal_p:n {##1} }
+ { \l_@@_macro_deprecated_bool }
+ { \l_@@_macro_nodoc_bool }
+ }
+ }
+ \seq_if_empty:NF \g_@@_nested_names_seq
+ {
+ \int_set:Nn \l_@@_tmpa_int
+ { \seq_count:N \g_@@_nested_names_seq }
+ \int_compare:nNnTF \l_@@_tmpa_int = 1 {~This~} {~These~}
+ \bool_if:NTF \l_@@_macro_var_bool {variable} {function}
+ \int_compare:nNnTF \l_@@_tmpa_int = 1 {~is~} {s~are~}
+ documented~on~page~
+ \@@_get_hyper_target:eN
+ { \seq_item:Nn \g_@@_nested_names_seq { 1 } }
+ \l_@@_tmpa_tl
+ \exp_args:Ne \pageref { \l_@@_tmpa_tl } .
+ }
+ \seq_gclear:N \g_@@_nested_names_seq
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Misc}
+%
+% \begin{macro}{\DescribeOption}
+% For describing package options: retained for consistency, but updated for
+% \pkg{doc}~v3.
+% \begin{macrocode}
+\NewDocElement[idxtype = option, idxgroup = options]{Option}{optionenv}
+% \end{macrocode}
+% \end{macro}
+%
+% Here are some definitions for additional markup that helps to
+% structure your documentation.
+%
+% \begin{environment}{danger}
+% \begin{environment}{ddanger}
+% \begin{syntax}
+% |\begin{[d]danger}|\\
+% dangerous code\\
+% |\end{[d]danger}|
+% \end{syntax}
+% \begin{danger}
+% Provides a danger bend, as known from the \TeX{}book.
+% \end{danger}
+% The actual character from the font |manfnt|:
+% \begin{macrocode}
+\font \manual = manfnt \scan_stop:
+\cs_gset:Npn \dbend { {\manual\char127} }
+% \end{macrocode}
+%
+% Defines the single danger bend. Use it whenever there is a feature in
+% your package that might be tricky to use. FIXME: Has to be fixed when
+% in combination with a macro-definition.
+% \begin{macrocode}
+\newenvironment {danger}
+ {
+ \begin{trivlist}\item[]\noindent
+ \begingroup\hangindent=2pc\hangafter=-2
+ \cs_set:Npn \par{\endgraf\endgroup}
+ \hbox to0pt{\hskip-\hangindent\dbend\hfill}\ignorespaces
+ }
+ {
+ \par\end{trivlist}
+ }
+% \end{macrocode}
+%
+% \begin{ddanger}
+% Use the double danger bend if there is something which could cause
+% serious problems when used in a wrong way. Better the normal user
+% does not know about such things.
+% \end{ddanger}
+% \begin{macrocode}
+\newenvironment {ddanger}
+ {
+ \begin{trivlist}\item[]\noindent
+ \begingroup\hangindent=3.5pc\hangafter=-2
+ \cs_set:Npn \par{\endgraf\endgroup}
+ \hbox to0pt{\hskip-\hangindent\dbend\kern2pt\dbend\hfill}\ignorespaces
+ }{
+ \par\end{trivlist}
+ }
+% \end{macrocode}
+% \end{environment}
+% \end{environment}
+%
+% \subsubsection{NB and NOTE}
+%
+% These macros are intended for additional notes added to the source that are not typeset.
+%
+% \begin{macro}{\NB}
+% \NB{wspr}{this is what I think about this!}
+% \begin{verbatim}
+% \NB{wspr}{this is what I think about this!}
+% \end{verbatim}
+% \begin{macrocode}
+\bool_if:NTF \g_@@_show_notes_bool
+ {
+ \NewDocumentCommand\NB{mm}
+ {
+ (\emph{Note}\footnote{\ttfamily [#1]:~\detokenize{#2}})
+ }
+ }
+ {
+ \NewDocumentCommand\NB{mm}{}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{environment}{NOTE}
+% \begin{NOTE}{wspr}
+% this is what I #$%& think about this!
+% \end{NOTE}
+% \begin{verbatim}
+% \begin{NOTE}{wspr}
+% this is what I #$%& think about this!
+% \end{NOTE}
+% \end{verbatim}
+% \begin{macrocode}
+\bool_if:NTF \g_@@_show_notes_bool
+ {
+ \NewDocumentEnvironment{NOTE}{m}
+ {
+ \par\noindent (\emph{Note}~[\texttt{#1}]:\par
+ \verbatim
+ }
+ {
+ \endverbatim
+ \par\noindent \emph{Note~end})\par
+ }
+ }
+ {
+ \NewDocumentEnvironment{NOTE}{m}{\comment}{\endcomment}
+ }
+% \end{macrocode}
+% \end{environment}
+%
+% \subsection{Footnote support}
+% The environments \env{function} and \env{variable} are boxes
+% and so looses footnotes. The following implements support.
+% It relies currently on an internal from hyperref to get the correct targets.
+%
+% \begin{macrocode}
+\providecommand\Hy@footnote@currentHref{}
+\prop_new:N\g_@@_fnmark_prop
+\cs_new_protected:Npn \@@_fn_store:
+ {
+ \prop_gput:Nee\g_@@_fnmark_prop
+ {fn\int_use:N\c@footnote}{{\Hy@footnote@currentHref}{\int_use:N\c@footnote}}
+ }
+\cs_new_protected:Npn \@@_fn_restore:n #1
+ {
+ \prop_get:NnN \g_@@_fnmark_prop {fn#1}\l_@@_tmpa_tl
+ \tl_gset:Ne\Hy@footnote@currentHref
+ {\exp_last_unbraced:NV\use_i:nn \l_@@_tmpa_tl }
+ \setcounter{footnote}{\exp_last_unbraced:NV\use_ii:nn \l_@@_tmpa_tl}
+ }
+
+\cs_generate_variant:Nn \hook_gput_next_code:nn {ne}
+\cs_new_protected:Npn \@@_fn_footnote:nn #1 #2
+ {
+ \footnotemark
+ \@@_fn_store:
+ \hook_gput_next_code:ne {env/#1/after}
+ {\exp_not:N\@@_fn_restore:n{\int_use:N\c@footnote}{\exp_not:n{\footnotetext{#2}}}}}
+
+\AddToHook{env/function/begin}{\def\footnote{\@@_fn_footnote:nn{function}}}
+\AddToHook{env/variable/begin}{\def\footnote{\@@_fn_footnote:nn{variable}}}
+% \end{macrocode}
+%
+% \subsection{Documenting templates}
+%
+% \begin{macrocode}
+\newenvironment{TemplateInterfaceDescription}[1]
+ {
+ \subsection{The~object~type~`#1'}
+ \begingroup
+ \@beginparpenalty\@M
+ \description
+ \def\TemplateArgument##1##2{\item[Arg:~##1]##2\par}
+ \def\TemplateSemantics
+ {
+ \enddescription\endgroup
+ \subsubsection*{Semantics:}
+ }
+ }
+ {
+ \par\bigskip
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\newenvironment{TemplateDescription}[2]
+ {
+ \subsection{The~template~`#2'~(object~type~#1)}
+ \subsubsection*{Attributes:}
+ \begingroup
+ \@beginparpenalty\@M
+ \description
+ \def\TemplateKey##1##2##3##4
+ {
+ \item[##1~(##2)]##3%
+ \ifx\TemplateKey##4\TemplateKey\else
+% \hskip0ptplus3em\penalty-500\hskip 0pt plus 1filll Default:~##4%
+ \hfill\penalty500\hbox{}\hfill Default:~##4%
+ \nobreak\hskip-\parfillskip\hskip0pt\relax
+ \fi
+ \par
+ }
+ \def\TemplateSemantics
+ {
+ \enddescription\endgroup
+ \subsubsection*{Semantics~\&~Comments:}
+ }
+ }
+ { \par \bigskip }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\newenvironment{InstanceDescription}[4][xxxxxxxxxxxxxxx]
+ {
+ \subsubsection{The~instance~`#3'~(template~#2/#4)}
+ \subsubsection*{Attribute~values:}
+ \begingroup
+ \@beginparpenalty\@M
+ \def\InstanceKey##1##2{\>\textbf{##1}\>##2\\}
+ \def\InstanceSemantics{\endtabbing\endgroup
+ \vskip-30pt\vskip0pt
+ \subsubsection*{Layout~description~\&~Comments:}}
+ \tabbing
+ xxxx\=#1\=\kill
+ }
+ { \par \bigskip }
+% \end{macrocode}
+%
+% \subsection{Inheriting doc}
+%
+% Code here is taken from \pkg{doc}, stripped of comments and translated
+% into \pkg{expl3} syntax. New features are added in various places.
+%
+% \begin{macro}
+% {\StopEventually, \MaybeStop, \Finale, \AlsoImplementation, \OnlyDescription}
+% \begin{variable}{\g_@@_finale_tl}
+% TODO: remove these four commands altogether, document that it is
+% better to use the \env{documentation} and \env{implementation}
+% environments.
+% \begin{macrocode}
+\DeclareDocumentCommand \OnlyDescription { }
+ { \bool_gset_false:N \g_@@_typeset_implementation_bool }
+\DeclareDocumentCommand \AlsoImplementation { }
+ { \bool_gset_true:N \g_@@_typeset_implementation_bool }
+\DeclareDocumentCommand \StopEventually { m }
+ {
+ \bool_if:NTF \g_@@_typeset_implementation_bool
+ {
+ \@bsphack
+ \tl_gset:Nn \g_@@_finale_tl { #1 \check@checksum }
+ \init@checksum
+ \@esphack
+ }
+ { #1 \endinput }
+ }
+% \end{macrocode}
+% We also need to support doc V3 \cs{MaybeStop} if it is around
+% (which may not be the case).
+% \begin{macrocode}
+\cs_if_exist:NT \MaybeStop
+ { \RenewCommandCopy \MaybeStop \StopEventually }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\DeclareDocumentCommand \Finale { }
+ { \tl_use:N \g_@@_finale_tl }
+\tl_new:N \g_@@_finale_tl
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \begin{macro}{\@@_input:n}
+% Inputting a file, with some setup: the module name should be empty
+% before the first |<@@=|\meta{module}|>| line in the file.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_input:n #1
+ {
+ \tl_gclear:N \g_@@_module_name_tl
+ \MakePercentIgnore
+ \input{#1}
+ \MakePercentComment
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\DocInput}
+% Modified from \pkg{doc} to accept comma-list input (who has commas
+% in filenames?).
+% \begin{macrocode}
+\DeclareDocumentCommand \DocInput { m }
+ {
+ \clist_map_inline:nn {#1}
+ {
+ \clist_put_right:Nn \g_docinput_clist {##1}
+ \@@_input:n {##1}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\DocInputAgain}
+% Uses \cs{g_docinput_clist} to re-input whatever's already been
+% \tn{DocInput}-ed until now. May be used multiple times.
+% \begin{macrocode}
+\DeclareDocumentCommand \DocInputAgain { }
+ { \clist_map_function:NN \g_docinput_clist \@@_input:n }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\DocInclude}
+% More or less exactly the same as \tn{include}, but uses
+% \tn{DocInput} on a \file{.fdd} or \file{.dtx} file (without file
+% extension), not \tn{input} on a \file{.tex} file.
+%
+% \begin{macrocode}
+\msg_new:nnn { l3doc } { missing-endgroup }
+ {
+ \str_if_eq:VnTF \@currenvir { document }
+ {
+ There~are~\int_use:N \tex_currentgrouplevel:D
+ \c_space_tl unclosed~groups~in~#1.
+ }
+ {
+ The~\@currenvir \c_space_tl environment~on~line~\@currenvline
+ \c_space_tl doesn't~have~a~matching~\iow_char:N\\end{\@currenvir}.
+ }
+ }
+\NewDocumentCommand \DocInclude { m }
+ {
+ \relax\clearpage
+ \docincludeaux
+ \IfFileExists{#1.fdd}
+ { \cs_set:Npn \currentfile{#1.fdd} }
+ { \cs_set:Npn \currentfile{#1.dtx} }
+ \int_compare:nNnTF \@auxout = \@partaux
+ { \@latexerr{\string\include\space cannot~be~nested}\@eha }
+ { \@docinclude {#1} }
+ % check missing \endgroup, e.g., missing "\end{macro}" in time
+ \int_compare:nNnF { \tex_currentgrouplevel:D } = { 0 }
+ {
+ \int_compare:nNnT { \tex_interactionmode:D } = { 0 }
+ { \int_gset:Nn \tex_interactionmode:D { 1 } }
+ \msg_fatal:nne { l3doc } { missing-endgroup } { \currentfile }
+ }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_gset:Npn \@docinclude #1
+ {
+ \clearpage
+ \immediate\write\@mainaux{\string\@input{#1.aux}}
+ \@tempswatrue
+ \if@partsw
+ \@tempswafalse
+ \cs_set:Npe \@tempb {#1}
+ \clist_map_inline:Nn \@partlist
+ {
+ \if_meaning:w \@tempa \@tempb
+ \@tempswatrue
+ \fi:
+ }
+ \fi
+ \if@tempswa
+ \cs_set_eq:NN \@auxout \@partaux
+ \immediate\openout\@partaux #1.aux
+ \immediate\write\@partaux{\relax}
+ \cs_set_eq:NN \@ltxdoc@PrintIndex \PrintIndex
+ \cs_set_eq:NN \PrintIndex \relax
+ \cs_set_eq:NN \@ltxdoc@PrintChanges \PrintChanges
+ \cs_set_eq:NN \PrintChanges \relax
+ \cs_set_eq:NN \@ltxdoc@theglossary \theglossary
+ \cs_set_eq:NN \@ltxdoc@endtheglossary \endtheglossary
+ \part{\currentfile}
+ {
+ \cs_set_eq:NN \ttfamily\relax
+ \cs_gset:Npe \filekey
+ { \filekey,~ \thepart = { \ttfamily \currentfile } }
+ }
+ \DocInput{\currentfile}
+ \cs_set_eq:NN \PrintIndex \@ltxdoc@PrintIndex
+ \cs_set_eq:NN \PrintChanges \@ltxdoc@PrintChanges
+ \cs_set_eq:NN \theglossary \@ltxdoc@theglossary
+ \cs_set_eq:NN \endtheglossary \@ltxdoc@endtheglossary
+ \clearpage
+ \@writeckpt{#1}
+ \immediate \closeout \@partaux
+ \else
+ \@nameuse{cp@#1}
+ \fi
+ \cs_set_eq:NN \@auxout \@mainaux
+ }
+% \end{macrocode}
+%
+% Here, |MMMMI| (for page references) and |MMMMV| (for codeline
+% references) are interpreted by |makeindex| as an uppercase Roman
+% number pages, and should be large enough to avoid collisions with
+% other uses of uppercase Roman number pages. Two subtle differences
+% between \cs{@wrindex} and \cs{codeline@wrindex} are that the first
+% must be a delayed write because the page number is not known yet, and
+% it must close a group and finish some space-hack.
+%
+% We also provide versions for our use that refer
+% \begin{macrocode}
+\cs_gset_protected:Npn \@wrindex #1
+ {
+ \protected@write \@indexfile {}
+ { \string \indexentry {#1} { MMMMI - \thepage } }
+ \endgroup \@esphack
+ }
+\cs_gset_protected:Npn \codeline@wrindex #1
+ {
+ \immediate\write\@indexfile
+ {
+ \string\indexentry{#1}
+ { MMMMV - \filesep \int_use:N \c@CodelineNo }
+ }
+ }
+\tl_gclear:N \filesep
+\cs_new_protected:Npn \@@_index_page_hc:nn #1#2
+ {
+ \protected@write \@indexfile {}
+ {
+ \string \indexentry { #1 \encapchar hdpindex{#2} }
+ { MMMMI - \thepage }
+ }
+ }
+\cs_new_protected:Npn \@@_index_codeline_hc:nn #1#2
+ {
+ \immediate\write\@indexfile
+ {
+ \string \indexentry { #1 \encapchar hdclindex{\the\c@HD@hypercount}{#2} }
+ { MMMMV - \filesep \int_use:N \c@CodelineNo - MMMD - \the\c@HD@hypercount - M }
+ }
+ }
+% \end{macrocode}
+% We already have a single |HD.xx| target per code line. It would be
+% better to have a target |CL.\the\c@CodelineNo| per code line and
+% change |hdclindex{\the\c@HD@hypercount}| to a mechanism closer to
+% |hdpindex|, but we need to understand better the different types of
+% indexings, and there are subtleties with indexing |\{| and |\}|.
+% \end{macro}
+%
+% \begin{macro}{\docincludeaux}
+% \begin{macrocode}
+\cs_gset:Npn \docincludeaux
+ {
+ \tl_set:Nn \thepart { \alphalph { part } }
+ \tl_set:Nn \filesep { \thepart - }
+ \cs_set_eq:NN \filekey \use_none:n
+ \tl_gput_right:Nn \index@prologue
+ {
+ \cs_gset:Npn \@oddfoot
+ {
+ \parbox { \textwidth }
+ {
+ \strut \footnotesize
+ \raggedright { \bfseries File~Key: } ~ \filekey
+ }
+ }
+ \cs_set_eq:NN \@evenfoot \@oddfoot
+ }
+ \cs_gset_eq:NN \docincludeaux \relax
+ \cs_gset:Npn \@oddfoot
+ {
+ \cs_if_exist:cTF { ver @ \currentfile }
+ { File~\thepart :~{\ttfamily\currentfile}~ }
+ {
+ \GetFileInfo{\currentfile}
+ File~\thepart :~{\ttfamily\filename}~
+ Date:~\ExplFileDate\ % space
+ Version~\ExplFileVersion
+ }
+ \hfill \thepage
+ }
+ \cs_set_eq:NN \@evenfoot \@oddfoot
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{The \env{macrocode} environment}
+%
+% \begin{macro}{\xmacro@code, \@@_xmacro_code:n, \@@_xmacro_code:w}
+% Hook into the \texttt{macrocode} environment in a dirty way:
+% \tn{xmacro@code} is responsible for grabbing (and tokenizing) the
+% body of the environment. Redefine it to pass what it grabs to
+% \cs{@@_xmacro_code:n}. This new macro replaces all |@@| by the
+% appropriate module name. One exceptional case is the
+% |<@@=|\meta{module}|>| lines themselves, where |@@| should not be
+% modified. Actually, we search for such lines, to set the module
+% name automatically. We need to be careful: no |<@@=| should appear
+% as such in the code below since \pkg{l3doc} is also typeset using
+% this code.
+% At each |<@@=| found, replace the \meta{module} in the code behind
+% it, update the \meta{module}, and loop to check for further
+% occurrences of |<@@=|.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_other:N \^^A
+ \char_set_catcode_active:N \^^S
+ \char_set_catcode_active:N \^^B
+ \char_set_catcode_other:N \^^L
+ \char_set_catcode_other:N \^^R
+ \char_set_lccode:nn { `\^^A } { `\% }
+ \char_set_lccode:nn { `\^^S } { `\ }
+ \char_set_lccode:nn { `\^^B } { `\\ }
+ \char_set_lccode:nn { `\^^L } { `\{ }
+ \char_set_lccode:nn { `\^^R } { `\} }
+ \tex_lowercase:D
+ {
+ \group_end:
+ \cs_set_protected:Npn \xmacro@code
+ #1 ^^A ^^S^^S^^S^^S ^^Bend ^^Lmacrocode^^R
+ { \@@_xmacro_code:n {#1} \end{macrocode} }
+ }
+\group_begin:
+ \char_set_catcode_active:N \<
+ \char_set_catcode_active:N \>
+ \cs_new_protected:Npn \@@_xmacro_code:n #1
+ {
+ \tl_clear:N \l_@@_tmpa_tl
+ \tl_if_in:nnTF {#1} { < @ @ = }
+ { \@@_xmacro_code:w #1 < @ @ = \q_recursion_tail > \q_recursion_stop }
+ {
+ \tl_set:Nn \l_@@_tmpa_tl {#1}
+ \@@_detect_internals:N \l_@@_tmpa_tl
+ \@@_replace_at_at:N \l_@@_tmpa_tl
+ \tl_use:N \l_@@_tmpa_tl
+ }
+ }
+ \cs_new_protected:Npn \@@_xmacro_code:w #1 < @ @ = #2 >
+ {
+ % Add code before <@@@@=...>
+ \tl_set:Nn \l_@@_tmpb_tl {#1}
+ \@@_detect_internals:N \l_@@_tmpb_tl
+ \@@_replace_at_at:N \l_@@_tmpb_tl
+ \tl_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl
+ % Check for \q_recursion_tail
+ \quark_if_recursion_tail_stop_do:nn {#2}
+ { \tl_use:N \l_@@_tmpa_tl }
+ % Change module name and add <@@@@=#2> to typeset output
+ \tl_gset:Nn \g_@@_module_name_tl {#2}
+ \tl_put_right:Nn \l_@@_tmpa_tl { < \text { \verbatim@font @ @ = #2 } > }
+ % Loop
+ \@@_xmacro_code:w
+ }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{At end document}
+%
+% Print all defined and documented macros/functions.
+%
+% \begin{macrocode}
+\iow_new:N \g_@@_func_iow
+% \end{macrocode}
+%
+% \begin{macrocode}
+\tl_new:N \l_@@_doc_def_tl
+\tl_new:N \l_@@_doc_undef_tl
+\tl_new:N \l_@@_undoc_def_tl
+\tl_const:Nn \c_@@_iow_separator_tl { ---- }
+\tl_const:Nn \c_@@_iow_midrule_tl { -- }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_show_functions_defined:
+ {
+ \bool_lazy_and:nnT
+ { \g_@@_typeset_implementation_bool } { \g_@@_checkfunc_bool }
+ {
+ \iow_term:e { \c_@@_iow_separator_tl \iow_newline: }
+ \iow_open:Nn \g_@@_func_iow { \c_sys_jobname_str .cmds }
+
+ \tl_clear:N \l_@@_doc_def_tl
+ \tl_clear:N \l_@@_doc_undef_tl
+ \tl_clear:N \l_@@_undoc_def_tl
+ \seq_gremove_duplicates:N \g_doc_functions_seq
+ \seq_gremove_duplicates:N \g_doc_macros_seq
+ \seq_map_inline:Nn \g_doc_functions_seq
+ {
+ \seq_if_in:NnTF \g_doc_macros_seq {##1}
+ {
+ \tl_put_right:Ne \l_@@_doc_def_tl
+ { \iow_newline: > ~ ##1 }
+ }
+ {
+ \tl_put_right:Ne \l_@@_doc_undef_tl
+ { \iow_newline: ! ~ ##1 }
+ }
+ }
+ \seq_map_inline:Nn \g_doc_macros_seq
+ {
+ \seq_if_in:NnF \g_doc_functions_seq {##1}
+ {
+ \tl_put_right:Ne \l_@@_undoc_def_tl
+ { \iow_newline: ? ~ ##1 }
+ }
+ }
+ \@@_functions_typeout:nN
+ {
+ Functions~both~documented~and~defined: \iow_newline:
+ (In~order~of~being~documented)
+ }
+ \l_@@_doc_def_tl
+ \@@_functions_typeout:nN
+ { Functions~documented~but~not~defined: }
+ \l_@@_doc_undef_tl
+ \@@_functions_typeout:nN
+ { Functions~defined~but~not~documented: }
+ \l_@@_undoc_def_tl
+
+ \iow_close:N \g_@@_func_iow
+ \iow_term:e { \c_@@_iow_separator_tl }
+ }
+ }
+\AtEndDocument { \@@_show_functions_defined: }
+% \end{macrocode}
+%
+% TODO: use \cs{iow_term:e}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_functions_typeout:nN #1#2
+ {
+ \tl_if_empty:NF #2
+ {
+ \iow_now:Ne \g_@@_func_iow
+ {
+ \c_@@_iow_midrule_tl \iow_newline:
+ #1 \iow_newline:
+ \c_@@_iow_midrule_tl
+ #2
+ }
+ \tl_clear:N #2
+ }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_show_not_tested:
+ {
+ \bool_if:NT \g_@@_checktest_bool
+ {
+ \tl_clear:N \l_@@_tmpa_tl
+ \prop_if_empty:NF \g_@@_missing_tests_prop
+ {
+ \cs_set:Npn \@@_tmpa:w ##1##2
+ {
+ \iow_newline:
+ \space\space\space\space \exp_not:n {##1}
+ \clist_map_function:nN {##2} \@@_tmpb:w
+ }
+ \cs_set:Npn \@@_tmpb:w ##1
+ {
+ \iow_newline:
+ \space\space\space\space\space\space * ~ ##1
+ }
+ \tl_put_right:Ne \l_@@_tmpa_tl
+ {
+ \iow_newline: \iow_newline:
+ The~ following~ macro(s)~ have~ incomplete~ tests:
+ \iow_newline:
+ \prop_map_function:NN
+ \g_@@_missing_tests_prop \@@_tmpa:w
+ }
+ }
+ \seq_if_empty:NF \g_@@_not_tested_seq
+ {
+ \cs_set:Npn \@@_tmpa:w ##1
+ { \clist_map_function:nN {##1} \@@_tmpb:w }
+ \cs_set:Npn \@@_tmpb:w ##1
+ {
+ \iow_newline:
+ \space\space\space\space ##1
+ }
+ \tl_put_right:Ne \l_@@_tmpa_tl
+ {
+ \iow_newline:
+ \iow_newline:
+ The~ following~ macro(s)~ do~ not~ have~ any~ tests:
+ \iow_newline:
+ \seq_map_function:NN
+ \g_@@_not_tested_seq \@@_tmpa:w
+ }
+ }
+ \tl_if_empty:NF \l_@@_tmpa_tl
+ {
+ \int_set:Nn \l_@@_tmpa_int { \tex_interactionmode:D }
+ \errorstopmode
+ \ClassError { l3doc } { \l_@@_tmpa_tl } { }
+ \int_gset:Nn \tex_interactionmode:D { \l_@@_tmpa_int }
+ }
+ }
+ }
+\AtEndDocument { \@@_show_not_tested: }
+% \end{macrocode}
+%
+% \subsection{Indexing}
+%
+% \subsubsection{Necessary patching}
+%
+% The following is useful to set up \pkg{hyperref} targets, for instance
+% for the purpose of indexing. Contrarily to \pkg{hypdoc} we do not try
+% to save pdf destinations, as this leads to too many \pdfTeX{} warnings
+% on early runs.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_target:
+ {
+ \mode_leave_vertical:
+ \group_begin:
+ \HD@savedestfalse \HD@target
+ \group_end:
+ }
+% \end{macrocode}
+% Force targets on every code line.
+% \begin{macrocode}
+\cs_set_nopar:Npe \theCodelineNo
+ {
+ \group_begin:
+ \exp_not:N \HD@savedestfalse
+ \exp_not:o \theCodelineNo
+ \group_end:
+ }
+% \end{macrocode}
+%
+% Inside the table of contents (and other similar lists introduced by
+% \cs{@starttoc}), we suppress indexing. This is because \cs{cmd},
+% \cs{cs}, or \cs{tn} appearing in titles only gets typeset in the
+% second run, and getting their indexing right would require even more
+% runs than we already need. Besides, it is not useful to index uses of
+% some command in the table of contents.
+% \begin{macrocode}
+\bool_new:N \l_@@_allow_indexing_bool
+\bool_set_true:N \l_@@_allow_indexing_bool
+\use:e
+ {
+ \exp_not:n { \cs_set_nopar:Npn \@starttoc #1 }
+ {
+ \group_begin:
+ \bool_set_false:N \l_@@_allow_indexing_bool
+ \exp_not:o { \@starttoc {#1} }
+ \group_end:
+ }
+ }
+% \end{macrocode}
+%
+% \subsubsection{Userspace commands}
+%
+% Fix index (for now):
+% \begin{macrocode}
+\g@addto@macro \theindex { \MakePrivateLetters }
+\cs_gset:Npn \verbatimchar {&}
+% \end{macrocode}
+%
+% \begin{macrocode}
+\setcounter { IndexColumns } { 2 }
+% \end{macrocode}
+%
+% Set up the Index to use \tn{part}
+% \begin{macrocode}
+\IndexPrologue
+ {
+ \part*{Index}
+ \markboth{Index}{Index}
+ \addcontentsline{toc}{part}{Index}
+ The~italic~numbers~denote~the~pages~where~the~
+ corresponding~entry~is~described,~
+ numbers~underlined~point~to~the~definition,~
+ all~others~indicate~the~places~where~it~is~used.
+ }
+% \end{macrocode}
+%
+% \begin{macro}{\SpecialIndex}
+% An attempt at affecting how commands which appear within the
+% \env{macrocode} environment are treated in the index.
+% \begin{macrocode}
+\cs_gset_protected:Npn \SpecialIndex #1
+ {
+ \@bsphack
+ \@@_special_index:nn {#1} { }
+ \@esphack
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+\msg_new:nnn { l3doc } { print-index-howto }
+ {
+ Generate~the~index~by~executing\\
+ \iow_indent:n
+ { makeindex~-s~gind.ist~-o~\c_sys_jobname_str.ind~\c_sys_jobname_str.idx }
+ }
+\tl_gput_right:Nn \PrintIndex
+ { \AtEndDocument { \msg_info:nn { l3doc } { print-index-howto } } }
+% \end{macrocode}
+%
+% \subsubsection{Internal index commands}
+%
+% \begin{macro}{\it@is@a}
+% The index of one-character commands within the \env{macrocode}
+% environment is produced using \tn{it@is@a} \meta{char}. Alter that
+% command.
+% \begin{macrocode}
+\cs_gset_protected:Npn \it@is@a #1
+ {
+ \use:e
+ {
+ \@@_special_index_module:nnnnN
+ {#1}
+ { \bslash #1 }
+ { }
+ { }
+ \c_false_bool
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_special_index:nn}
+% ^^A TODO this override is somewhat a hack
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_special_index:nn #1#2
+ {
+ \@@_key_get:n {#1}
+ \quark_if_no_value:NF \l_@@_override_module_tl
+ { \tl_set_eq:NN \l_@@_index_module_tl \l_@@_override_module_tl }
+ \@@_special_index_module:ooonN
+ { \l_@@_index_key_tl }
+ { \l_@@_index_macro_tl }
+ { \l_@@_index_module_tl }
+ {#2}
+ \l_@@_index_internal_bool
+ }
+\cs_generate_variant:Nn \@@_special_index:nn { o }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_special_index_module:nnnnN,
+% \@@_special_index_module:ooonN,
+% \@@_special_index_aux:nnnnnn,
+% \@@_special_index_set:Nn,
+% }
+% Remotely based on Heiko's replacement to play nicely with
+% \pkg{hypdoc}. We use \tn{verb} or a \tn{verbatim@font} construction
+% depending on whether the number of tokens in |#2| is equal to its
+% number of characters: if it is not then that suggests that there is
+% a construct such as |\meta{...}|.
+% \begin{macrocode}
+\tl_new:N \l_@@_index_escaped_macro_tl
+\tl_new:N \l_@@_index_escaped_key_tl
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_special_index_module:nnnnN #1#2#3#4#5
+% \end{macrocode}
+% \begin{arguments}
+% \item key
+% \item macro
+% \item module
+% \item index `type' (\texttt{main}/\texttt{usage}/\emph{etc.})
+% \item boolean whether internal command
+% \end{arguments}
+% \begin{macrocode}
+ {
+ \use:e
+ {
+ \exp_not:n { \@@_special_index_aux:nnnnnn {#1} {#2} }
+ \tl_if_empty:nTF {#3}
+ { { } { } { } }
+ {
+ \str_if_eq:nnTF {#3} { TeX }
+ {
+ { TeX~and~LaTeX2e }
+ { \string\TeX{}~and~\string\LaTeXe{} }
+ }
+ {
+ {#3}
+ { \string\pkg{#3} }
+ }
+ { \bool_if:NT #5 { ~internal } ~commands: }
+ }
+ }
+ {#4}
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_generate_variant:Nn \@@_special_index_module:nnnnN { ooo }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_special_index_aux:nnnnnn #1#2#3#4#5#6
+% \end{macrocode}
+% \begin{arguments}
+% \item key
+% \item macro
+% \item index subheading string
+% \item index subheading text
+% \item index subheading suffix (appended to both arg 3 and 4)
+% \item index `type' (\texttt{main}/\texttt{usage}/\emph{etc.})
+% \end{arguments}
+% \begin{macrocode}
+ {
+ \tl_set:Nn \l_@@_index_escaped_key_tl {#1}
+ \@@_quote_special_char:N \l_@@_index_escaped_key_tl
+ \@@_special_index_set:Nn \l_@@_index_escaped_macro_tl {#2}
+ \str_if_eq:onTF { \@currenvir } { macrocode }
+ { \@@_index_codeline_hc:nn }
+ {
+ \str_case:nnF {#6}
+ {
+ { main } { \@@_index_codeline_hc:nn }
+ { usage } { \@@_index_page_hc:nn }
+ }
+ { \@@_target: \@@_index_page_hc:nn }
+ }
+ {
+ \tl_if_empty:nF { #3 #4 #5 }
+ { #3 #5 \actualchar #4 #5 \levelchar }
+ \l_@@_index_escaped_key_tl
+ \actualchar
+ {
+ \token_to_str:N \verbatim@font \c_space_tl
+ \l_@@_index_escaped_macro_tl
+ }
+ }
+ {#6}
+ }
+% \end{macrocode}
+%
+% \begin{macro}{\hdpindex, \@@_old_hdpindex:nn, \hdclindex, \@@_old_hdclindex:nnn, \@@_hdindex:nn, \c_@@_active_minus_tl, \@@_hdindex_aux:nn, \@@_hdindex_aux:w}
+% Note that |#3| here could contain |MMMMI-| or |MMMMV-| more than once
+% if several successive code lines have been merged into a range
+% somehow. Note incidentally that the dash is active in some of our
+% sources, like |interface3.tex| or |source2e.tex|.
+% \begin{macrocode}
+\group_begin:
+\char_set_active_eq:NN - \scan_stop:
+\tl_const:Ne \c_@@_active_minus_tl { \char_generate:nn { `- } { 13 } }
+\group_end:
+\cs_new_eq:NN \@@_old_hdpindex:nn \hdpindex
+\cs_new_eq:NN \@@_old_hdclindex:nnn \hdclindex
+\cs_gset_protected:Npn \hdpindex #1
+ { \@@_hdindex:nn { \@@_old_hdpindex:nn {#1} } }
+\cs_gset_protected:Npn \hdclindex #1#2
+ { \@@_hdindex:nn { \@@_old_hdclindex:nnn {#1} {#2} } }
+\cs_new_protected:Npn \@@_hdindex:nn #1#2
+ {
+ \tl_set:Nn \l_@@_tmpa_tl {#2}
+ \tl_replace_all:Nen \l_@@_tmpa_tl
+ { \exp_not:V \c_@@_active_minus_tl \exp_not:V \c_@@_active_minus_tl }
+ { -- }
+ \seq_set_split:NnV \l_@@_tmpa_seq { -- } \l_@@_tmpa_tl
+ \seq_set_map:NNn \l_@@_tmpa_seq \l_@@_tmpa_seq
+ { \@@_hdindex_aux:nn {#1} {##1} }
+ \seq_use:Nn \l_@@_tmpa_seq { -- }
+ }
+\cs_new_protected:Npn \@@_hdindex_aux:nn #1#2
+ {
+ \tl_set:Nn \l_@@_tmpa_tl {#2}
+ \tl_replace_all:Nnn \l_@@_tmpa_tl { MMMM } { \use_none:nn }
+ \tl_if_in:NnT \l_@@_tmpa_tl { MMMD }
+ {
+ \tl_replace_all:Nen \l_@@_tmpa_tl
+ { \exp_not:V \c_@@_active_minus_tl MMMD } { - MMMD }
+ \tl_replace_all:Nnn \l_@@_tmpa_tl { - MMMD } { \@@_hdindex_aux:w }
+ }
+ \use:e { \exp_not:n {#1} { \exp_not:V \l_@@_tmpa_tl } }
+ }
+\cs_new_protected:Npn \@@_hdindex_aux:w #1 M { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_special_index_set:Nn #1#2
+ {
+ \__kernel_tl_set:Nx #1 { \tl_to_str:n {#2} }
+ \@@_if_almost_str:nTF {#2}
+ {
+ \tl_replace_all:Nen #1 { \tl_to_str:n { __ } }
+ {
+ \verbatimchar
+ \token_to_str:N \_ \token_to_str:N \_
+ \token_to_str:N \verb * \verbatimchar
+ }
+ \exp_args:Ne \tl_map_inline:nn
+ { \tl_to_str:N \verbatimchar \token_to_str:N _ }
+ {
+ \tl_replace_all:Nnn #1 {##1}
+ {
+ \verbatimchar \c_backslash_str ##1
+ \token_to_str:N \verb * \verbatimchar
+ }
+ }
+ \__kernel_tl_set:Nx #1
+ {
+ \token_to_str:N \verb * \verbatimchar
+ #1 \verbatimchar
+ }
+ }
+ {
+ \tl_set:Nn #1 {#2}
+ \tl_replace_all:NVn #1
+ \c_backslash_str
+ { \token_to_str:N \bslash \c_space_tl }
+ }
+ \@@_quote_special_char:N #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_quote_special_char:N}
+% Quote some special characters.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_quote_special_char:N #1
+ {
+ \tl_map_inline:nn { \quotechar \actualchar \encapchar \levelchar \bslash }
+ {
+ \tl_replace_all:Nen #1
+ { \tl_to_str:N ##1 } { \quotechar \tl_to_str:N ##1 }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Finding sort-key and module}
+%
+% \begin{macro}{\@@_key_get:n}
+% Sets \cs{l_@@_index_macro_tl}, \cs{l_@@_index_key_tl}, and
+% \cs{l_@@_index_module_tl} from |#1|. The base function is stored by
+% \cs{@@_key_get_base:nN} in \cs{l_@@_index_macro_tl}, falling back to
+% |#1| if it contains markup or has no signature.
+%
+% The starting point for the \meta{key} is \cs{l_@@_index_key_tl} as a
+% string. If it the first character is a backslash, remove
+% it. Then recognize |expl| functions and variables by the presence
+% of |:| or~|_| and \TeX{}/\LaTeXe{} commands by the presence of~|@|.
+% For |expl| names, we call \cs{@@_key_func:} or \cs{@@_key_var:},
+% which are responsible for removing some characters and finding the
+% module name, while for \TeX{}/\LaTeXe{} commands the module name is
+% |TeX|, and others have an empty module name.
+% \begin{macrocode}
+\cs_new_protected:Npe \@@_key_get:n #1
+ {
+ \exp_not:N \@@_key_get_base:nN {#1} \exp_not:N \l_@@_index_macro_tl
+ \__kernel_tl_set:Nx \exp_not:N \l_@@_index_key_tl
+ { \exp_not:N \tl_to_str:N \exp_not:N \l_@@_index_macro_tl }
+ \tl_clear:N \exp_not:N \l_@@_index_module_tl
+ \tl_if_in:NnTF \exp_not:N \l_@@_index_key_tl { \tl_to_str:n { __ } }
+ { \bool_set_true:N \exp_not:N \l_@@_index_internal_bool }
+ { \bool_set_false:N \exp_not:N \l_@@_index_internal_bool }
+ \exp_not:N \tl_if_head_eq_charcode:VNT
+ \exp_not:N \l_@@_index_key_tl \c_backslash_str
+ { \exp_not:N \@@_key_pop: }
+ \tl_if_in:NnTF \exp_not:N \l_@@_index_key_tl { \token_to_str:N : }
+ { \exp_not:N \@@_key_func: }
+ {
+ \tl_if_in:NnTF \exp_not:N \l_@@_index_key_tl { \token_to_str:N _ }
+ { \exp_not:N \@@_key_var: }
+ {
+ \tl_if_in:NnT \exp_not:N \l_@@_index_key_tl { \token_to_str:N @ }
+ { \tl_set:Nn \exp_not:N \l_@@_index_module_tl { TeX } }
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_key_pop:
+ {
+ \__kernel_tl_set:Nx \l_@@_index_key_tl
+ { \tl_tail:N \l_@@_index_key_tl }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_key_trim_module:n, \@@_key_drop_underscores:}
+% Helper that removes from \cs{l_@@_index_module_tl} everything after
+% the first occurence of |#1|. Helper that removes any leading
+% underscore from \cs{l_@@_index_key_tl}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_key_trim_module:n #1
+ {
+ \cs_set:Npn \@@_tmpa:w ##1 #1 ##2 \q_stop
+ { \exp_not:n {##1} }
+ \__kernel_tl_set:Nx \l_@@_index_module_tl
+ { \exp_after:wN \@@_tmpa:w \l_@@_index_module_tl #1 \q_stop }
+ }
+\cs_new_protected:Npn \@@_key_drop_underscores:
+ {
+ \tl_if_head_eq_charcode:VNT \l_@@_index_key_tl _
+ { \@@_key_pop: \@@_key_drop_underscores: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_key_func:}
+% The function \cs{@@_key_func:} is used if there is a colon, so
+% either for usual \pkg{expl3} functions or for keys from
+% \pkg{l3keys}. After removing from the key a leading dot (for the
+% latter case), and any leading underscore, the module name is the
+% part before any colon or underscore.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_key_func:
+ {
+ \tl_if_head_eq_charcode:VNT \l_@@_index_key_tl .
+ { \@@_key_pop: }
+ \@@_key_drop_underscores:
+ \tl_set_eq:NN \l_@@_index_module_tl \l_@@_index_key_tl
+ \exp_args:No \@@_key_trim_module:n { \token_to_str:N : }
+ \exp_args:No \@@_key_trim_module:n { \token_to_str:N _ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_key_var:, \@@_key_get_module:}
+% The function \cs{@@_key_var:} covers cases with no~|:| but with~|_|,
+% typically variables but occasionally non-\pkg{expl3} functions such
+% as \Lua{} function with underscores. First test the second
+% character: if that is~|_| then assume we have a proper variable,
+% otherwise use the part before any underscore as the module name.
+% For variables, distinguish quarks and scan marks (starting with |q|
+% and~|s|), then drop the first letter (local/global/constant marker)
+% and underscores to improve the index sorting.
+% Then get the module as the first (underscore-delimited) \enquote{word}.
+% In the past, we distinguished according to how many such words there
+% were, to detect commands like \cs[no-index]{c_zero}, which should be
+% sorted as |int| variables, and \cs[no-index]{l_tmpa_dim}, which should
+% be sorted in the |dim| and not the |tmpa| module.
+% Now the first case has been deprecated for some time, while |tmpa| and
+% similar are special-cased through an explicit list given below.
+% The way it works is that if the module is in a list of special names
+% that are not valid modules, then we try the last word, and if that
+% also fails (for instance in the deprecated \cs[no-index]{c_one_hundred})
+% we empty the module completely.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_key_var:
+ {
+ \exp_args:Ne \tl_if_head_eq_charcode:nNTF
+ { \exp_args:No \str_tail:n \l_@@_index_key_tl } _
+ {
+ \str_case:en { \str_head:N \l_@@_index_key_tl }
+ {
+ { q } { \tl_set:Nn \l_@@_index_module_tl { quark } }
+ { s } { \tl_set:Nn \l_@@_index_module_tl { scan } }
+ }
+ \@@_key_pop:
+ \@@_key_pop:
+ \@@_key_drop_underscores:
+ \tl_if_empty:NT \l_@@_index_module_tl
+ {
+ \seq_set_split:NoV \l_@@_tmpa_seq
+ { \token_to_str:N _ } \l_@@_index_key_tl
+ \seq_get_left:NN \l_@@_tmpa_seq \l_@@_index_module_tl
+ \clist_if_in:NoT \g_@@_non_modules_clist \l_@@_index_module_tl
+ {
+ \seq_get_right:NN \l_@@_tmpa_seq \l_@@_index_module_tl
+ \clist_if_in:NoT \g_@@_non_modules_clist \l_@@_index_module_tl
+ {
+ \tl_clear:N \l_@@_index_module_tl
+ }
+ }
+ }
+ }
+ {
+ \tl_set_eq:NN \l_@@_index_module_tl \l_@@_index_key_tl
+ \exp_args:No \@@_key_trim_module:n { \token_to_str:N _ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\g_@@_non_modules_clist}
+% List of names that appear as the first word in an \pkg{expl3}
+% command, but that are not true modules, so that they should be
+% sorted differently in an index.
+% \begin{macrocode}
+\clist_new:N \g_@@_non_modules_clist
+\clist_gset:Ne \g_@@_non_modules_clist
+ {
+ \tl_to_str:n
+ {
+
+ alignment, ampersand, atsign, backslash, catcode, circumflex,
+ code, colon, document, dollar, e, empty, false, hash, inf,
+ initex, job, left, log, math, mark, max, minus, nan, nil, no,
+ novalue, other, parameter, percent, pi, recursion, right, space,
+ stop, term, tilde, tmpa, tmpb, true, underscore, zero, one, two,
+ three, four, five, six, seven, eight, nine, ten, eleven, twelve,
+ thirteen, fourteen, fifteen, sixteen, thirty, hundred
+
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Change history}
+%
+% Set the change history to use \tn{part}.
+% Allow control names to be hyphenated in here\dots
+% \begin{macrocode}
+\GlossaryPrologue
+ {
+ \part*{Change~History}
+ {\GlossaryParms\ttfamily\hyphenchar\font=`\-}
+ \markboth{Change~History}{Change~History}
+ \addcontentsline{toc}{part}{Change~History}
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\msg_new:nnn { l3doc } { print-changes-howto }
+ {
+ Generate~the~change~list~by~executing\\
+ \iow_indent:n
+ { makeindex~-s~gglo.ist~-o~\c_sys_jobname_str.gls~\c_sys_jobname_str.glo }
+ }
+\tl_gput_right:Nn \PrintChanges
+ { \AtEndDocument { \msg_info:nn { l3doc } { print-changes-howto } } }
+% \end{macrocode}
+%
+%^^A The standard \changes command modified slightly to better cope with
+%^^A this multiple file document.
+%^^A\def\changes@#1#2#3{%
+%^^A \let\protect\@unexpandable@protect
+%^^A \edef\@tempa{\noexpand\glossary{#2\space\currentfile\space#1\levelchar
+%^^A \ifx\saved@macroname\@empty
+%^^A \space
+%^^A \actualchar
+%^^A \generalname
+%^^A \else
+%^^A \expandafter\@gobble
+%^^A \saved@macroname
+%^^A \actualchar
+%^^A \string\verb\quotechar*%
+%^^A \verbatimchar\saved@macroname
+%^^A \verbatimchar
+%^^A \fi
+%^^A :\levelchar #3}}%
+%^^A \@tempa\endgroup\@esphack}
+%
+% \subsection{Default configuration}
+%
+% \begin{macrocode}
+\bool_if:NTF \g_@@_typeset_implementation_bool
+ {
+ \RecordChanges
+ \CodelineIndex
+ \EnableCrossrefs
+ \AlsoImplementation
+ }
+ {
+ \CodelineNumbered
+ \DisableCrossrefs
+ \OnlyDescription
+ }
+% \end{macrocode}
+%
+%
+% \begin{macrocode}
+%</class>
+% \end{macrocode}
+%
+% \subsection{Internal macros for \LaTeX3 sources}
+%
+% These definitions are only used by the \LaTeX3 documentation; they are
+% not necessary for third-party users of \cls{l3doc}. In time this will
+% be broken into a separate package that is specifically loaded in the
+% various \pkg{expl3} modules, \emph{etc.}
+%
+% \begin{macrocode}
+%<*cfg>
+% \end{macrocode}
+%
+% The Guilty Parties.
+% \begin{macrocode}
+\tl_const:Nn \Team
+ {
+ The~\LaTeX3~Project\thanks
+ {\url{https://www.latex-project.org/latex3/}}
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\NewDocumentCommand{\ExplMakeTitle}{mm}
+ {
+ \title
+ {
+ The~\pkg{#1}~package \\ #2
+ }
+ \author
+ {
+ The~\LaTeX3~Project\thanks{E-mail:~
+ \href{mailto:latex-l@listserv.uni-heidelberg.de}
+ {latex-l@listserv.uni-heidelberg.de}}
+ }
+ \date{Released~\ExplFileDate}
+ \maketitle
+ }
+% \end{macrocode}
+%
+% \subsection{Math extras}
+%
+% For \pkg{l3fp}.
+%
+% \begin{macrocode}
+\AtBeginDocument
+ {
+ \clist_map_inline:nn
+ {
+ asin, acos, atan, acot,
+ asinh, acosh, atanh, acoth, round, floor, ceil
+ }
+ { \exp_args:Nc \DeclareMathOperator{#1}{#1} }
+ }
+% \end{macrocode}
+%
+% \begin{macro}{\nan}
+% \begin{macrocode}
+\NewDocumentCommand { \nan } { } { \text { \texttt { nan } } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</cfg>
+% \end{macrocode}
+%
+%
+% \subsection{Makeindex configuration}
+%
+% \begin{macrocode}
+%<*docist>
+% \end{macrocode}
+%
+% The makeindex style \file{l3doc.ist} is used in place of the usual
+% \file{gind.ist} to ensure that |I| is used in the sequence |I J K| not
+% |I II II|, which would be the default makeindex behaviour.
+%
+% Will: Do we need this?
+%
+% Frank: at the moment we do not distribute or generate this file.
+% \file{gind.ist} is used instead.
+%
+% \begin{macro}[do-not-index={\\,\n}]{}
+% \begin{macrocode}
+actual '='
+quote '!'
+level '>'
+preamble
+"\n \\begin{theindex} \n \\makeatletter\\scan@allowedfalse\n"
+postamble
+"\n\n \\end{theindex}\n"
+item_x1 "\\efill \n \\subitem "
+item_x2 "\\efill \n \\subsubitem "
+delim_0 "\\pfill "
+delim_1 "\\pfill "
+delim_2 "\\pfill "
+% The next lines will produce some warnings when
+% running Makeindex as they try to cover two different
+% versions of the program:
+lethead_prefix "{\\bfseries\\hfil "
+lethead_suffix "\\hfil}\\nopagebreak\n"
+lethead_flag 1
+heading_prefix "{\\bfseries\\hfil "
+heading_suffix "\\hfil}\\nopagebreak\n"
+headings_flag 1
+
+% and just for source3:
+% Remove R so I is treated in sequence I J K not I II III
+page_precedence "rnaA"
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</docist>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3docstrip.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3docstrip.dtx
new file mode 100644
index 00000000000..c2733e810ef
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3docstrip.dtx
@@ -0,0 +1,96 @@
+% \iffalse
+%
+%% File l3dosctrip.dtx
+%
+% Copyright (C) 2012-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver|program>
+%</driver|program>
+%<*driver>
+% The same approach as used in \textsf{DocStrip}: if \cs{documentclass}
+% is undefined then skip the driver, allowing the file to be used to extract
+% \texttt{l3docstrip.tex} from \texttt{l3docstrip.dtx} directly. This works
+% as the \cs{fi} is only seen if \LaTeX{} is not in use. The odd \cs{jobname}
+% business allows the extraction to work with \LaTeX{} provided an appropriate
+% \texttt{.ins} file is set up.
+%<*gobble>
+\ifx\jobname\relax\let\documentclass\undefined\fi
+\ifx\documentclass\undefined
+\else \csname fi\endcsname
+%</gobble>
+ \def\filename{docstrip.dtx}
+ \documentclass[full,kernel]{l3doc}
+ \ExplSyntaxOn
+ \cs_set_eq:NN \__codedoc_replace_at_at:N \use_none:n
+ \ExplSyntaxOff
+ \begin{document}
+ \DocInput{\jobname.dtx}
+ \end{document}
+%<*gobble>
+\fi
+%</gobble>
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3docstrip} module\\ Code extraction and manipulation^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% This is a stub file to allow extraction of \texttt{l3docstrip}: all
+% functionality has been moved to the main DocStrip program.
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3docstrip} implementation}
+%
+% \begin{macrocode}
+%<*program>
+% \end{macrocode}
+%
+% Simply input DocStrip.
+% \begin{macrocode}
+\input docstrip %
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</program>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3expan.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3expan.dtx
new file mode 100644
index 00000000000..e79b5f32b90
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3expan.dtx
@@ -0,0 +1,2398 @@
+% \iffalse meta-comment
+%
+%% File: l3expan.dtx
+%
+% Copyright (C) 1990-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3expan} module\\ Argument expansion^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% This module provides generic methods for expanding \TeX{} arguments in a
+% systematic manner. The functions in this module all have prefix |exp|.
+%
+% Not all possible variations are implemented for every base
+% function. Instead only those that are used within the \LaTeX3 kernel
+% or otherwise seem to be of general interest are implemented.
+% Consult the module description to find out which functions are
+% actually defined. The next section explains how to define missing
+% variants.
+%
+% \section{Defining new variants}
+% \label{sec:l3expan:defining-variants}
+%
+% The definition of variant forms for base functions may be necessary
+% when writing new functions or when applying a kernel function in a
+% situation that we haven't thought of before.
+%
+% Internally preprocessing of arguments is done with functions of the form
+% \cs[no-index]{exp_\ldots{}}. They all look alike, an example would be
+% \cs{exp_args:NNo}. This function has three arguments, the first and the
+% second are a single tokens, while the third argument should be given
+% in braces. Applying \cs{exp_args:NNo} expands the content of third
+% argument once before any expansion of the first and second arguments.
+% If \cs{seq_gpush:No} was not defined it could be coded in the following way:
+% \begin{verbatim}
+% \exp_args:NNo \seq_gpush:Nn
+% \g_file_name_stack
+% { \l_tmpa_tl }
+% \end{verbatim}
+% In other words, the first argument to \cs{exp_args:NNo} is the base
+% function and the other arguments are preprocessed and then passed to
+% this base function. In the example the first argument to the base
+% function should be a single token which is left unchanged while the
+% second argument is expanded once. From this example we can also see
+% how the variants are defined. They just expand into the appropriate
+% |\exp_| function followed by the desired base function, \emph{e.g.}
+% \begin{quote}
+% |\cs_generate_variant:Nn \seq_gpush:Nn { No } |
+% \end{quote}
+% results in the definition of |\seq_gpush:No|
+% \begin{quote}
+% |\cs_new:Npn \seq_gpush:No { \exp_args:NNo \seq_gpush:Nn }|
+% \end{quote}
+% Providing variants in this way in style files is safe as the
+% \cs{cs_generate_variant:Nn} function will only create new definitions if
+% there is not already one available. Therefore adding
+% such definition to later releases of the kernel will not make such
+% style files obsolete.
+%
+% The steps above may be automated by using the function
+% \cs{cs_generate_variant:Nn}, described next.
+%
+% \section{Methods for defining variants}
+% \label{sec:l3expan:variants-method}
+%
+% We recall the set of available argument specifiers.
+% \begin{itemize}
+% \item |N|~is used for single-token arguments while |c|~constructs a
+% control sequence from its name and passes it to a parent function as
+% an |N|-type argument.
+% \item Many argument types extract or expand some tokens and provide it
+% as an |n|-type argument, namely a braced multiple-token argument:
+% |V|~extracts the value of a variable, |v|~extracts the value from
+% the name of a variable, |n|~uses the argument as it is, |o|~expands
+% once, |f|~expands fully the front of the token list, |e| and
+% |x|~expand fully all tokens (differences are explained later).
+% \item A few odd argument types remain: |T|~and |F|~for conditional
+% processing, otherwise identical to |n|-type arguments,
+% |p|~for the parameter text
+% in definitions, |w|~for arguments with a specific syntax, and |D|~to
+% denote primitives that should not be used directly.
+% \end{itemize}
+%
+% \begin{function}[updated = 2017-11-28]
+% {\cs_generate_variant:Nn, \cs_generate_variant:cn}
+% \begin{syntax}
+% \cs{cs_generate_variant:Nn} \meta{parent control sequence} \Arg{variant argument specifiers}
+% \end{syntax}
+% This function is used to define argument-specifier variants of the
+% \meta{parent control sequence} for \LaTeX3 code-level macros. The
+% \meta{parent control sequence} is first separated into the
+% \meta{base name} and \meta{original argument specifier}. The
+% comma-separated list of \meta{variant argument specifiers} is
+% then used to define variants of the
+% \meta{original argument specifier} if these are not already
+% defined; entries which correspond to existing functions are silently
+% ingored. For each \meta{variant} given, a function is created
+% that expands its arguments as detailed and passes them
+% to the \meta{parent control sequence}. So for example
+% \begin{verbatim}
+% \cs_set:Npn \foo:Nn #1#2 { code here }
+% \cs_generate_variant:Nn \foo:Nn { c }
+% \end{verbatim}
+% creates a new function |\foo:cn| which expands its first
+% argument into a control sequence name and passes the result to
+% |\foo:Nn|. Similarly
+% \begin{verbatim}
+% \cs_generate_variant:Nn \foo:Nn { NV , cV }
+% \end{verbatim}
+% generates the functions |\foo:NV| and |\foo:cV| in the same
+% way. The \cs{cs_generate_variant:Nn} function should only be applied if
+% the \meta{parent control sequence} is already defined. (This is only
+% enforced if debugging support \texttt{check-declarations} is enabled.)
+% If the \meta{parent
+% control sequence} is protected or if the \meta{variant} involves any
+% |x|~argument, then the \meta{variant control sequence} is also
+% protected. The \meta{variant} is created globally, as is any
+% \cs[no-index]{exp_args:N\meta{variant}} function needed to carry out the
+% expansion. There is no need to re-apply \cs{cs_generate_variant:Nn} after
+% changing the definition of the parent function: the variant will always
+% use the current definition of the parent. Providing variants repeatedly is
+% safe as \cs{cs_generate_variant:Nn} will only create new definitions if
+% there is not already one available.
+%
+% Only |n|~and |N| arguments can be changed to other types. The only
+% allowed changes are
+% \begin{itemize}
+% \item |c|~variant of an |N|~parent;
+% \item |o|, |V|, |v|, |f|, |e|, or~|x| variant of an |n|~parent;
+% \item |N|, |n|, |T|, |F|, or |p| argument unchanged.
+% \end{itemize}
+% This means the \meta{parent} of a \meta{variant} form is always
+% unambiguous, even in cases where both an |n|-type parent and an
+% |N|-type parent exist, such as for \cs{tl_count:n} and
+% \cs{tl_count:N}.
+%
+% When creating variants for conditional functions,
+% \cs{prg_generate_conditional_variant:Nnn} provides a convenient way
+% of handling the related function set.
+%
+% For backward compatibility it is currently possible to make |n|,
+% |o|, |V|, |v|, |f|, |e|, or |x|-type variants of an |N|-type argument or
+% |N| or |c|-type variants of an |n|-type argument. Both are
+% deprecated. The first because passing more than one token to an
+% |N|-type argument will typically break the parent function's code.
+% The second because programmers who use that most often want to
+% access the value of a variable given its name, hence should use a
+% |V|-type or |v|-type variant instead of |c|-type. In those cases,
+% using the lower-level \cs{exp_args:No} or \cs{exp_args:Nc}
+% functions explicitly is preferred to defining confusing variants.
+% \end{function}
+%
+% \begin{function}[added = 2018-04-04, updated = 2019-02-08]
+% {\exp_args_generate:n}
+% \begin{syntax}
+% \cs{exp_args_generate:n} \Arg{variant argument specifiers}
+% \end{syntax}
+% Defines \cs[no-index]{exp_args:N\meta{variant}} functions for each
+% \meta{variant} given in the comma list \Arg{variant argument
+% specifiers}. Each \meta{variant} should consist of the letters |N|,
+% |c|, |n|, |V|, |v|, |o|, |f|, |e|, |x|, |p| and the resulting function is
+% protected if the letter |x| appears in the \meta{variant}. This is
+% only useful for cases where \cs{cs_generate_variant:Nn} is not
+% applicable.
+% \end{function}
+%
+% \section{Introducing the variants}
+%
+% The |V| type returns the value of a register, which can be one of
+% |tl|, |clist|, |int|, |skip|, |dim|, |muskip|, or built-in \TeX{}
+% registers. The |v| type is the same except it first creates a
+% control sequence out of its argument before returning the
+% value.
+%
+% In general, the programmer should not need to be concerned with
+% expansion control. When simply using the content of a variable,
+% functions with a |V| specifier should be used. For those referred to by
+% (cs)name, the |v| specifier is available for the same purpose. Only when
+% specific expansion steps are needed, such as when using delimited
+% arguments, should the lower-level functions with |o| specifiers be employed.
+%
+% The |e| type expands all tokens fully, starting from the first. More
+% precisely the expansion is identical to that of \TeX{}'s \tn{message}
+% (in particular |#| needs not be doubled). It relies on the
+% primitive \tn{expanded} hence is fast.
+%
+% The |x| type expands all tokens fully, starting from the first. In
+% contrast to |e|, all macro parameter characters |#| must be doubled,
+% and omitting this leads to low-level errors. In addition this type of
+% expansion is not expandable, namely functions that have |x| in their
+% signature do not themselves expand when appearing inside |e| or |x|
+% expansion.
+%
+% The |f| type is so special that it deserves an example. It is
+% typically used in contexts where only expandable commands are allowed.
+% Then |x|-expansion cannot be used, and |f|-expansion provides an
+% alternative that expands the front of the token list
+% as much as can be done in such contexts. For
+% instance, say that we want to evaluate the integer expression $3 + 4$
+% and pass the result $7$ as an argument to an expandable function
+% |\example:n|. For this, one should define a variant using
+% \cs{cs_generate_variant:Nn} |\example:n| |{| |f| |}|, then do
+% \begin{quote}
+% |\example:f { \int_eval:n { 3 + 4 } }|
+% \end{quote}
+% Note that |x|-expansion would also expand \cs{int_eval:n} fully to its
+% result~$7$, but the variant |\example:x| cannot be expandable. Note
+% also that |o|-expansion would not expand \cs{int_eval:n} fully to its
+% result since that function requires several expansions. Besides the
+% fact that |x|-expansion is protected rather than expandable, another
+% difference between |f|-expansion and |x|-expansion is that
+% |f|-expansion expands tokens from the beginning and stops as soon as a
+% non-expandable token is encountered, while |x|-expansion continues
+% expanding further tokens. Thus, for instance
+% \begin{quote}
+% |\example:f { \int_eval:n { 1 + 2 } , \int_eval:n { 3 + 4 } }|
+% \end{quote}
+% results in the call
+% \begin{quote}
+% |\example:n { 3 , \int_eval:n { 3 + 4 } }|
+% \end{quote}
+% while using |\example:x| or |\example:e| instead results in
+% \begin{quote}
+% |\example:n { 3 , 7 }|
+% \end{quote}
+% at the cost of being protected for |x|-type.
+% If you use |f| type expansion in conditional processing then
+% you should stick to using |TF| type functions only as the expansion
+% does not finish any |\if... \fi:| itself!
+%
+% It is important to note that both \texttt{f}- and \texttt{o}-type
+% expansion are concerned with the expansion of tokens from left to
+% right in their arguments. In particular, \texttt{o}-type expansion
+% applies to the first \emph{token} in the argument it receives: it
+% is conceptually similar to
+% \begin{verbatim}
+% \exp_after:wN <base function> \exp_after:wN { <argument> }
+% \end{verbatim}
+% At the same time, \texttt{f}-type expansion stops at the \emph{first}
+% non-expandable token. This means for example that both
+% \begin{verbatim}
+% \tl_set:No \l_tmpa_tl { { \g_tmpb_tl } }
+% \end{verbatim}
+% and
+% \begin{verbatim}
+% \tl_set:Nf \l_tmpa_tl { { \g_tmpb_tl } }
+% \end{verbatim}
+% leave |\g_tmpb_tl| unchanged: |{| is the first token in the
+% argument and is non-expandable.
+%
+% It is usually best to keep the following in mind when using variant
+% forms.
+% \begin{itemize}
+% \item
+% Variants with |x|-type arguments (that are fully expanded before
+% being passed to the |n|-type base function) are never expandable
+% even when the base function is. Such variants cannot work
+% correctly in arguments that are themselves subject to expansion.
+% Consider using |f| or |e| expansion.
+% \item
+% In contrast, |e|~expansion (full expansion, almost like~|x| except
+% for the treatment of~|#|) does not prevent variants from being
+% expandable (if the base function is).
+% \item
+% Finally |f|~expansion only expands the front of the token list,
+% stopping at the first non-expandable token. This may fail to
+% fully expand the argument.
+% \end{itemize}
+%
+% When speed is essential (for functions that do very little work and
+% whose variants are used numerous times in a document) the following
+% considerations apply because the speed of internal functions that
+% expand the arguments of a base function depend on what needs doing
+% with each argument and where this happens in the list of arguments:
+% \begin{itemize}
+% \item for fastest processing any |c|-type arguments should come first
+% followed by all other modified arguments;
+% \item unchanged |N|-type args that appear before modified ones have
+% a small performance hit;
+% \item unchanged |n|-type args that appear before modified ones have
+% a relative larger performance hit.
+% \end{itemize}
+%
+% \section{Manipulating the first argument}
+%
+% These functions are described in detail: expansion of multiple tokens follows
+% the same rules but is described in a shorter fashion.
+%
+% \begin{function}[EXP]{\exp_args:Nc, \exp_args:cc}
+% \begin{syntax}
+% \cs{exp_args:Nc} \meta{function} \Arg{tokens}
+% \end{syntax}
+% This function absorbs two arguments (the \meta{function} name and
+% the \meta{tokens}). The \meta{tokens} are expanded until only characters
+% remain, and are then turned into a control sequence.
+% The result is inserted into the input stream \emph{after} reinsertion
+% of the \meta{function}. Thus the \meta{function} may take more than
+% one argument: all others are left unchanged.
+%
+% The |:cc| variant constructs the \meta{function} name in the same
+% manner as described for the \meta{tokens}.
+% \end{function}
+%
+% \begin{function}[EXP]{\exp_args:No}
+% \begin{syntax}
+% \cs{exp_args:No} \meta{function} \Arg{tokens} ...
+% \end{syntax}
+% This function absorbs two arguments (the \meta{function} name and
+% the \meta{tokens}). The \meta{tokens} are expanded once, and the result
+% is inserted in braces into the input stream \emph{after} reinsertion
+% of the \meta{function}. Thus the \meta{function} may take more than
+% one argument: all others are left unchanged.
+% \end{function}
+%
+% \begin{function}[EXP]{\exp_args:NV}
+% \begin{syntax}
+% \cs{exp_args:NV} \meta{function} \meta{variable}
+% \end{syntax}
+% This function absorbs two arguments (the names of the \meta{function} and
+% the \meta{variable}). The content of the \meta{variable} are recovered
+% and placed inside braces into the input stream \emph{after} reinsertion
+% of the \meta{function}. Thus the \meta{function} may take more than
+% one argument: all others are left unchanged.
+% \end{function}
+%
+% \begin{function}[EXP]{\exp_args:Nv}
+% \begin{syntax}
+% \cs{exp_args:Nv} \meta{function} \Arg{tokens}
+% \end{syntax}
+% This function absorbs two arguments (the \meta{function} name and
+% the \meta{tokens}). The \meta{tokens} are expanded until only characters
+% remain, and are then turned into a control sequence.
+% This control sequence should
+% be the name of a \meta{variable}. The content of the \meta{variable} are
+% recovered and placed inside braces into the input stream \emph{after}
+% reinsertion of the \meta{function}. Thus the \meta{function} may take more
+% than one argument: all others are left unchanged.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-05-15]{\exp_args:Ne}
+% \begin{syntax}
+% \cs{exp_args:Ne} \meta{function} \Arg{tokens}
+% \end{syntax}
+% This function absorbs two arguments (the \meta{function} name and
+% the \meta{tokens}) and exhaustively expands the \meta{tokens}.
+% The result is inserted in braces into the input stream
+% \emph{after} reinsertion of the \meta{function}.
+% Thus the \meta{function} may take more
+% than one argument: all others are left unchanged.
+% \end{function}
+%
+% \begin{function}[EXP]{\exp_args:Nf}
+% \begin{syntax}
+% \cs{exp_args:Nf} \meta{function} \Arg{tokens}
+% \end{syntax}
+% This function absorbs two arguments (the \meta{function} name and
+% the \meta{tokens}). The \meta{tokens} are fully expanded until the
+% first non-expandable token is found (if that is a space it is
+% removed), and the result
+% is inserted in braces into the input stream \emph{after} reinsertion
+% of the \meta{function}. Thus the \meta{function} may take more than
+% one argument: all others are left unchanged.
+% \end{function}
+%
+% \begin{function}{\exp_args:Nx}
+% \begin{syntax}
+% \cs{exp_args:Nx} \meta{function} \Arg{tokens}
+% \end{syntax}
+% This function absorbs two arguments (the \meta{function} name and
+% the \meta{tokens}) and exhaustively expands the \meta{tokens}.
+% The result is inserted in braces into the input stream
+% \emph{after} reinsertion of the \meta{function}.
+% Thus the \meta{function} may take more
+% than one argument: all others are left unchanged.
+% \end{function}
+%
+% \section{Manipulating two arguments}
+%
+% \begin{function}[EXP, updated = 2018-05-15]
+% {
+% \exp_args:NNc,
+% \exp_args:NNo,
+% \exp_args:NNV,
+% \exp_args:NNv,
+% \exp_args:NNe,
+% \exp_args:NNf,
+% \exp_args:Ncc,
+% \exp_args:Nco,
+% \exp_args:NcV,
+% \exp_args:Ncv,
+% \exp_args:Ncf,
+% \exp_args:NVV
+% }
+% \begin{syntax}
+% \cs{exp_args:NNc} \meta{token_1} \meta{token_2} \Arg{tokens}
+% \end{syntax}
+% These optimized functions absorb three arguments and expand the second and
+% third as detailed by their argument specifier. The first argument
+% of the function is then the next item on the input stream, followed
+% by the expansion of the second and third arguments.
+% \end{function}
+%
+% \begin{function}[EXP, updated = 2018-05-15]
+% {
+% \exp_args:Nnc,
+% \exp_args:Nno,
+% \exp_args:NnV,
+% \exp_args:Nnv,
+% \exp_args:Nne,
+% \exp_args:Nnf,
+% \exp_args:Noc,
+% \exp_args:Noo,
+% \exp_args:Nof,
+% \exp_args:NVo,
+% \exp_args:Nfo,
+% \exp_args:Nff,
+% \exp_args:Nee,
+% }
+% \begin{syntax}
+% \cs{exp_args:Noo} \meta{token} \Arg{tokens_1} \Arg{tokens_2}
+% \end{syntax}
+% These functions absorb three arguments and expand the second and
+% third as detailed by their argument specifier. The first argument
+% of the function is then the next item on the input stream, followed
+% by the expansion of the second and third arguments.
+% \end{function}
+%
+% \begin{function}
+% {
+% \exp_args:NNx,
+% \exp_args:Ncx,
+% \exp_args:Nnx,
+% \exp_args:Nox,
+% \exp_args:Nxo,
+% \exp_args:Nxx
+% }
+% \begin{syntax}
+% \cs{exp_args:NNx} \meta{token_1} \meta{token_2} \Arg{tokens}
+% \end{syntax}
+% These functions absorb three arguments and expand the second and
+% third as detailed by their argument specifier. The first argument
+% of the function is then the next item on the input stream, followed
+% by the expansion of the second and third arguments. These functions
+% are not expandable due to their |x|-type argument.
+% \end{function}
+%
+% \section{Manipulating three arguments}
+%
+% \begin{function}[EXP]
+% {
+% \exp_args:NNNo,
+% \exp_args:NNNV,
+% \exp_args:NNNv,
+% \exp_args:NNNe,
+% \exp_args:Nccc,
+% \exp_args:NcNc,
+% \exp_args:NcNo,
+% \exp_args:Ncco
+% }
+% \begin{syntax}
+% \cs{exp_args:NNNo} \meta{token_1} \meta{token_2} \meta{token_3} \Arg{tokens}
+% \end{syntax}
+% These optimized functions absorb four arguments and expand the second, third
+% and fourth as detailed by their argument specifier. The first
+% argument of the function is then the next item on the input stream,
+% followed by the expansion of the second argument, \emph{etc}.
+% \end{function}
+%
+% \begin{function}[EXP]
+% {
+% \exp_args:NNcf,
+% \exp_args:NNno,
+% \exp_args:NNnV,
+% \exp_args:NNoo,
+% \exp_args:NNVV,
+% \exp_args:Ncno,
+% \exp_args:NcnV,
+% \exp_args:Ncoo,
+% \exp_args:NcVV,
+% \exp_args:Nnnc,
+% \exp_args:Nnno,
+% \exp_args:Nnnf,
+% \exp_args:Nnff,
+% \exp_args:Nooo,
+% \exp_args:Noof,
+% \exp_args:Nffo,
+% \exp_args:Neee
+% }
+% \begin{syntax}
+% \cs{exp_args:NNoo} \meta{token_1} \meta{token_2} \Arg{token_3} \Arg{tokens}
+% \end{syntax}
+% These functions absorb four arguments and expand the second, third
+% and fourth as detailed by their argument specifier. The first
+% argument of the function is then the next item on the input stream,
+% followed by the expansion of the second argument, \emph{etc}.
+% \end{function}
+%
+% \begin{function}[added = 2015-08-12]
+% {
+% \exp_args:NNNx,
+% \exp_args:NNnx,
+% \exp_args:NNox,
+% \exp_args:Nccx,
+% \exp_args:Ncnx,
+% \exp_args:Nnnx,
+% \exp_args:Nnox,
+% \exp_args:Noox,
+% }
+% \begin{syntax}
+% \cs{exp_args:NNnx} \meta{token_1} \meta{token_2} \Arg{tokens_1} \Arg{tokens_2}
+% \end{syntax}
+% These functions absorb four arguments and expand the second, third
+% and fourth as detailed by their argument specifier. The first
+% argument of the function is then the next item on the input stream,
+% followed by the expansion of the second argument, \emph{etc.}
+% \end{function}
+%
+% \section{Unbraced expansion}
+%
+% \begin{function}[EXP, updated = 2018-05-15]
+% {
+% \exp_last_unbraced:No,
+% \exp_last_unbraced:NV,
+% \exp_last_unbraced:Nv,
+% \exp_last_unbraced:Ne,
+% \exp_last_unbraced:Nf,
+% \exp_last_unbraced:NNo,
+% \exp_last_unbraced:NNV,
+% \exp_last_unbraced:NNf,
+% \exp_last_unbraced:Nco,
+% \exp_last_unbraced:NcV,
+% \exp_last_unbraced:Nno,
+% \exp_last_unbraced:Nnf,
+% \exp_last_unbraced:Noo,
+% \exp_last_unbraced:Nfo,
+% \exp_last_unbraced:NNNo,
+% \exp_last_unbraced:NNNV,
+% \exp_last_unbraced:NNNf,
+% \exp_last_unbraced:NnNo,
+% \exp_last_unbraced:NNNNo,
+% \exp_last_unbraced:NNNNf,
+% }
+% \begin{syntax}
+% \cs{exp_last_unbraced:Nno} \meta{token} \Arg{tokens_1} \Arg{tokens_2}
+% \end{syntax}
+% These functions absorb the number of arguments given by their
+% specification, carry out the expansion
+% indicated and leave the results in the input stream, with the
+% last argument not surrounded by the usual braces.
+% Of these, the |:Nno|, |:Noo|, |:Nfo| and |:NnNo|
+% variants need slower processing.
+% \begin{texnote}
+% As an optimization, the last argument is unbraced by some
+% of those functions before expansion. This can cause problems
+% if the argument is empty: for instance,
+% \cs{exp_last_unbraced:Nf} |\foo_bar:w| |{ }| \cs{q_stop}
+% leads to an infinite loop, as the quark is \texttt{f}-expanded.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}{\exp_last_unbraced:Nx}
+% \begin{syntax}
+% \cs{exp_last_unbraced:Nx} \meta{function} \Arg{tokens}
+% \end{syntax}
+% This function fully expands the \meta{tokens} and leaves the result
+% in the input stream after reinsertion of the \meta{function}.
+% This function is not expandable.
+% \end{function}
+%
+% \begin{function}[EXP]{\exp_last_two_unbraced:Noo}
+% \begin{syntax}
+% \cs{exp_last_two_unbraced:Noo} \meta{token} \Arg{tokens_1} \Arg{tokens_2}
+% \end{syntax}
+% This function absorbs three arguments and expands the second and third
+% once. The first argument of the function is then the next item on the
+% input stream, followed by the expansion of the second and third arguments,
+% which are not wrapped in braces.
+% This function needs special (slower) processing.
+% \end{function}
+%
+% \begin{function}[EXP]{\exp_after:wN}
+% \begin{syntax}
+% \cs{exp_after:wN} \meta{token_1} \meta{token_2}
+% \end{syntax}
+% Carries out a single expansion of \meta{token_2} (which may consume
+% arguments) prior to the expansion of \meta{token_1}. If \meta{token_2} has
+% no expansion (for example, if it is a character) then it is left
+% unchanged. It is important to notice that \meta{token_1} may be
+% \emph{any} single token, including group-opening and -closing
+% tokens (|{| or |}| assuming normal \TeX{} category codes). Unless
+% specifically required this should be avoided: expansion should be carried out using an
+% appropriate argument specifier variant or the appropriate
+% \cs[no-index]{exp_args:N\meta{variant}} function.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{expandafter}.
+% \end{texnote}
+% \end{function}
+%
+%
+% \section{Preventing expansion}
+%
+% Despite the fact that the following functions are all about preventing
+% expansion, they're designed to be used in an expandable context and hence
+% are all marked as being `expandable' since they themselves disappear
+% after the expansion has completed.
+%
+% \begin{function}[EXP]{\exp_not:N}
+% \begin{syntax}
+% \cs{exp_not:N} \meta{token}
+% \end{syntax}
+% Prevents expansion of the \meta{token} in a context where it would
+% otherwise be expanded, for example an |e|-type or |x|-type argument or
+% the first token in an |o|-type or |f|-type argument.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{noexpand}. It only prevents
+% expansion. At the beginning of an |f|-type argument, a space
+% \meta{token} is removed even if it appears as \cs{exp_not:N}
+% \cs{c_space_token}. In an |e|-expanding definition
+% (\cs{cs_new:Npe}), a macro parameter introduces an argument even
+% if it appears as \cs{exp_not:N} |#| |1|. This differs from
+% \cs{exp_not:n}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\exp_not:c}
+% \begin{syntax}
+% \cs{exp_not:c} \Arg{tokens}
+% \end{syntax}
+% Expands the \meta{tokens} until only characters remain, and then
+% converts this into a control sequence.
+% Further expansion of this control sequence is then inhibited using
+% \cs{exp_not:N}.
+% \end{function}
+%
+% \begin{function}[EXP]{\exp_not:n}
+% \begin{syntax}
+% \cs{exp_not:n} \Arg{tokens}
+% \end{syntax}
+% Prevents expansion of the \meta{tokens} in an |e|-type or |x|-type argument.
+% In all other cases the \meta{tokens} continue to be expanded, for
+% example in the input stream or in other types of arguments such as
+% \texttt{c}, \texttt{f}, \texttt{v}. The argument of \cs{exp_not:n}
+% \emph{must} be surrounded by braces.
+% \begin{texnote}
+% This is the \eTeX{} primitive \tn{unexpanded}. In an
+% |e|-expanding definition (\cs{cs_new:Npe}), \cs{exp_not:n}~|{#1}|
+% is equivalent to |##1| rather than to~|#1|, namely it inserts the
+% two characters |#| and~|1|, and
+% \cs{exp_not:n}~|{#}| is equivalent to |#|, namely it inserts the
+% character~|#|.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\exp_not:o}
+% \begin{syntax}
+% \cs{exp_not:o} \Arg{tokens}
+% \end{syntax}
+% Expands the \meta{tokens} once, then prevents any further expansion
+% in |e|-type or |x|-type arguments using \cs{exp_not:n}.
+% \end{function}
+%
+% \begin{function}[EXP]{\exp_not:V}
+% \begin{syntax}
+% \cs{exp_not:V} \meta{variable}
+% \end{syntax}
+% Recovers the content of the \meta{variable}, then prevents expansion
+% of this material in |e|-type or |x|-type arguments using \cs{exp_not:n}.
+% \end{function}
+%
+% \begin{function}[EXP]{\exp_not:v}
+% \begin{syntax}
+% \cs{exp_not:v} \Arg{tokens}
+% \end{syntax}
+% Expands the \meta{tokens} until only characters remains, and then
+% converts this into a control sequence which should be a \meta{variable}
+% name.
+% The content of the \meta{variable} is recovered, and further
+% expansion in |e|-type or |x|-type arguments is prevented using \cs{exp_not:n}.
+% \end{function}
+%
+% \begin{function}[EXP]{\exp_not:e}
+% \begin{syntax}
+% \cs{exp_not:e} \Arg{tokens}
+% \end{syntax}
+% Expands \meta{tokens} exhaustively, then protects the result of the
+% expansion (including any tokens which were not expanded) from
+% further expansion in |e|-type or |x|-type arguments using \cs{exp_not:n}.
+% This is very rarely useful but is provided for consistency.
+% \end{function}
+%
+% \begin{function}[EXP]{\exp_not:f}
+% \begin{syntax}
+% \cs{exp_not:f} \Arg{tokens}
+% \end{syntax}
+% Expands \meta{tokens} fully until the first unexpandable token is
+% found (if it is a space it is removed). Expansion then stops, and
+% the result of the expansion (including any tokens which were not
+% expanded) is protected from further expansion in |e|-type or |x|-type arguments
+% using \cs{exp_not:n}.
+% \end{function}
+%
+% \begin{function}[updated = 2011-06-03, EXP]{\exp_stop_f:}
+% \begin{syntax}
+% |\foo_bar:f| \{ \meta{tokens} \cs{exp_stop_f:} \meta{more tokens} \}
+% \end{syntax}
+% This function terminates an \texttt{f}-type expansion. Thus if
+% a function |\foo_bar:f| starts an \texttt{f}-type expansion
+% and all of \meta{tokens} are expandable \cs{exp_stop_f:}
+% terminates the expansion of tokens even if \meta{more tokens}
+% are also expandable. The function itself is an implicit space
+% token. Inside an \texttt{e}-type or \texttt{x}-type expansion, it retains its
+% form, but when typeset it produces the underlying space (\verb*| |).
+% \end{function}
+%
+%
+% \section{Controlled expansion}
+%
+% The \pkg{expl3} language makes all efforts to hide the complexity of
+% \TeX{} expansion from the programmer by providing concepts that
+% evaluate/expand arguments of functions prior to calling the \enquote{base}
+% functions. Thus, instead of using many \tn{expandafter} calls and
+% other trickery it is usually a matter of choosing the right variant
+% of a function to achieve a desired result.
+%
+% Of course, deep down \TeX{} is using expansion as always and there
+% are cases where a programmer needs to control that expansion
+% directly; typical situations are basic data manipulation tools. This
+% section documents the functions for that level. These
+% commands are used throughout the kernel code, but we hope that outside
+% the kernel there will be little need to resort to them. Instead the
+% argument manipulation methods document above should usually be sufficient.
+%
+% While \cs{exp_after:wN} expands one token (out of order) it is
+% sometimes necessary to expand several tokens in one go. The next set
+% of commands provide this functionality. Be aware that it is
+% absolutely required that the programmer has full control over the
+% tokens to be expanded, i.e., it is not possible to use these
+% functions to expand unknown input as part of
+% \meta{expandable-tokens} as that will break badly if unexpandable
+% tokens are encountered in that place!
+%
+% \begin{function}[added=2015-08-23,EXP]
+% {
+% \exp:w ,
+% \exp_end:
+% }
+% \begin{syntax}
+% \cs{exp:w} \meta{expandable tokens} \cs{exp_end:} \\
+% \end{syntax}
+% Expands \meta{expandable-tokens} until reaching \cs{exp_end:} at
+% which point expansion stops.
+% The full expansion of \meta{expandable tokens} has to be empty.
+% If any token in \meta{expandable tokens} or any token generated by
+% expanding the tokens therein is not expandable the expansion will end
+% prematurely and as a result \cs{exp_end:} will be misinterpreted
+% later on.\footnotemark
+%
+% In typical use cases the \cs{exp_end:} is hidden somewhere
+% in the replacement text of \meta{expandable-tokens} rather than
+% being on the same expansion level than \cs{exp:w}, e.g., you may
+% see code such as
+%\begin{verbatim}
+% \exp:w \@@_case:NnTF #1 {#2} { } { }
+%\end{verbatim}
+% where somewhere during the expansion of |\@@_case:NnTF| the
+% \cs{exp_end:} gets generated.
+% \begin{texnote}
+% The current implementation uses \tn{romannumeral} hence ignores
+% space tokens and explicit signs |+| and |-| in the expansion of the
+% \meta{expandable tokens}, but this should not be relied upon.
+% \end{texnote}
+% \end{function}
+% \footnotetext{Due to the implementation you might get the character
+% in position 0 in the current font (typically \enquote{\texttt{`}})
+% in the output without any error message!}
+%
+% \begin{function}[added=2015-08-23, EXP, label = \exp_end_continue_f:w]
+% {
+% \exp:w ,
+% \exp_end_continue_f:w
+% }
+% \begin{syntax}
+% \cs{exp:w} \meta{expandable-tokens} \cs{exp_end_continue_f:w} \meta{further-tokens}
+% \end{syntax}
+% Expands \meta{expandable-tokens} until reaching \cs{exp_end_continue_f:w} at
+% which point expansion continues as an \texttt{f}-type expansion expanding
+% \meta{further-tokens} until an unexpandable token is encountered (or
+% the \texttt{f}-type expansion is explicitly terminated by
+% \cs{exp_stop_f:}). As with all \texttt{f}-type expansions a space ending
+% the expansion gets removed.
+%
+% The full expansion of \meta{expandable-tokens} has to be empty.
+% If any token in \meta{expandable-tokens} or any token generated by
+% expanding the tokens therein is not expandable the expansion will end
+% prematurely and as a result \cs{exp_end_continue_f:w} will be misinterpreted
+% later on.\footnotemark
+%
+%
+% In typical use cases \meta{expandable-tokens} contains no tokens at all,
+% e.g., you will see code such as
+%\begin{verbatim}
+% \exp_after:wN { \exp:w \exp_end_continue_f:w #2 }
+%\end{verbatim}
+% where the \cs{exp_after:wN} triggers an \texttt{f}-expansion of the tokens
+% in |#2|. For technical reasons this has to happen using two tokens
+% (if they would be hidden inside another command \cs{exp_after:wN}
+% would only expand the command but not trigger any additional
+% |f|-expansion).
+%
+% You might wonder why there are two different approaches available,
+% after all the effect of
+% \begin{quote}
+% \cs{exp:w} \meta{expandable-tokens} \cs{exp_end:}
+% \end{quote}
+% can be alternatively achieved through an \texttt{f}-type expansion by using
+% \cs{exp_stop_f:}, i.e.
+% \begin{quote}
+% \cs{exp:w} \cs{exp_end_continue_f:w} \meta{expandable-tokens} \cs{exp_stop_f:}
+% \end{quote}
+% The reason is simply that the first approach is slightly faster
+% (one less token to parse and less expansion internally)
+% so in places where such performance really matters and where we
+% want to explicitly stop the expansion at a defined point the first
+% form is preferable.
+% \end{function}
+% \footnotetext{In this particular case you may get a character into
+% the output as well as an error message.}
+%
+% \begin{function}[added=2015-08-23, EXP, label = \exp_end_continue_f:nw]
+% {
+% \exp:w ,
+% \exp_end_continue_f:nw
+% }
+% \begin{syntax}
+% \cs{exp:w} \meta{expandable-tokens} \cs{exp_end_continue_f:nw} \meta{further-tokens}
+% \end{syntax}
+% The difference to \cs{exp_end_continue_f:w} is that we first we pick
+% up an argument which is then returned to the input stream. If
+% \meta{further-tokens} starts with space tokens then these space
+% tokens are removed while searching for the argument. If it starts
+% with a brace group then the braces are removed. Thus such spaces or
+% braces will not terminate the \texttt{f}-type expansion.
+% \end{function}
+%
+% \section{Internal functions}
+%
+% \begin{function}{\::n, \::N, \::p, \::c, \::o, \::e, \::f, \::x, \::v, \::V, \:::}
+% \begin{syntax}
+% |\cs_new:Npn \exp_args:Ncof { \::c \::o \::f \::: }|
+% \end{syntax}
+% Internal forms for the base expansion types. These names do \emph{not}
+% conform to the general \LaTeX3 approach as this makes them more readily
+% visible in the log and so forth. They should not be used outside this module.
+% \end{function}
+%
+% \begin{function}
+% {\::o_unbraced, \::e_unbraced, \::f_unbraced, \::x_unbraced, \::v_unbraced, \::V_unbraced}
+% \begin{syntax}
+% |\cs_new:Npn \exp_last_unbraced:Nno { \::n \::o_unbraced \::: }|
+% \end{syntax}
+% Internal forms for the expansion types which leave the terminal argument
+% unbraced. These names do \emph{not}
+% conform to the general \LaTeX3 approach as this makes them more readily
+% visible in the log and so forth. They should not be used outside this module.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3expan} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=exp>
+% \end{macrocode}
+%
+% \begin{variable}{\l_@@_internal_tl}
+% The |\exp_| module has its private variable to temporarily store the
+% result of |x|-type argument expansion. This is done to avoid interference
+% with other functions using temporary variables.
+% \end{variable}
+%
+% \begin{macro}{\exp_after:wN}
+% \begin{macro}{\exp_not:N}
+% \begin{macro}{\exp_not:n}
+% These are defined in \pkg{l3basics}, as they are needed
+% \enquote{early}. This is just a reminder of that fact!
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{General expansion}
+%
+% In this section a general mechanism for defining functions that handle
+% arguments is defined. These general expansion functions are
+% expandable unless |x| is used. (Any version of |x| is going to have
+% to use one of the \LaTeX3 names for \cs{cs_set:Npx} at some
+% point, and so is never going to be expandable.)
+%
+% The definition of expansion functions with this technique happens
+% in section~\ref{sec:l3expan:gendef}.
+% In section~\ref{sec:l3expan:handtune} some common cases are coded by a more direct
+% method for efficiency, typically using calls to \cs{exp_after:wN}.
+%
+% \begin{variable}{\l_@@_internal_tl}
+% This scratch token list variable is defined in \pkg{l3basics}.
+% \end{variable}
+%
+% This code uses internal functions with names that start with |\::| to
+% perform the expansions. All macros are |long| since the tokens
+% undergoing expansion may be arbitrary user input.
+%
+% An argument manipulator |\::|\meta{Z} always has signature |#1\:::#2#3|
+% where |#1| holds the remaining argument manipulations to be performed,
+% \cs{:::} serves as an end marker for the list of manipulations, |#2|
+% is the carried over result of the previous expansion steps and |#3| is
+% the argument about to be processed.
+% One exception to this rule is \cs{::p}, which has to grab an argument
+% delimited by a left brace.
+%
+% \begin{macro}[EXP]{\@@_arg_next:nnn}
+% \begin{macro}[EXP]{\@@_arg_next:Nnn}
+% |#1| is the result of an expansion step, |#2| is the remaining
+% argument manipulations and |#3| is the current result of the
+% expansion chain. This auxiliary function moves |#1| back after
+% |#3| in the input stream and checks if any expansion is left to
+% be done by calling |#2|. In by far the most cases we need
+% to add a set of braces to the result of an argument manipulation
+% so it is more effective to do it directly here. Actually, so far
+% only the |c| of the final argument manipulation variants does not
+% require a set of braces.
+% \begin{macrocode}
+\cs_new:Npn \@@_arg_next:nnn #1#2#3 { #2 \::: { #3 {#1} } }
+\cs_new:Npn \@@_arg_next:Nnn #1#2#3 { #2 \::: { #3 #1 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\:::}
+% The end marker is just another name for the identity function.
+% \begin{macrocode}
+\cs_new:Npn \::: #1 {#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\::n}
+% This function is used to skip an argument that doesn't need to
+% be expanded.
+% \begin{macrocode}
+\cs_new:Npn \::n #1 \::: #2#3 { #1 \::: { #2 {#3} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\::N}
+% This function is used to skip an argument that consists of a
+% single token and doesn't need to be expanded.
+% \begin{macrocode}
+\cs_new:Npn \::N #1 \::: #2#3 { #1 \::: {#2#3} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\::p}
+% This function is used to skip an argument that is delimited by a
+% left brace and doesn't need to be expanded. It is not
+% wrapped in braces in the result.
+% \begin{macrocode}
+\cs_new:Npn \::p #1 \::: #2#3# { #1 \::: {#2#3} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\::c}
+% This function is used to skip an argument that is turned into
+% a control sequence without expansion.
+% \begin{macrocode}
+\cs_new:Npn \::c #1 \::: #2#3
+ { \exp_after:wN \@@_arg_next:Nnn \cs:w #3 \cs_end: {#1} {#2} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\::o}
+% This function is used to expand an argument once.
+% \begin{macrocode}
+\cs_new:Npn \::o #1 \::: #2#3
+ { \exp_after:wN \@@_arg_next:nnn \exp_after:wN {#3} {#1} {#2} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\::e}
+% With the \tn{expanded} primitive available, just expand.
+% \begin{macrocode}
+\cs_new:Npn \::e #1 \::: #2#3
+ { \tex_expanded:D { \exp_not:n { #1 \::: } { \exp_not:n {#2} {#3} } } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\::f}
+% \begin{macro}{\exp_stop_f:}
+% This function is used to expand a token list until the first
+% unexpandable token is found. This is achieved through \cs{exp:w}
+% \cs{exp_end_continue_f:w} that expands everything in its way
+% following it. This scanning procedure is terminated once the
+% expansion hits something non-expandable (if that is a space it is
+% removed). We introduce \cs{exp_stop_f:} to mark such an
+% end-of-expansion marker. For example, |f|-expanding
+% |\cs_set_eq:Nc \aaa { b \l_tmpa_tl b }| where |\l_tmpa_tl| contains
+% the characters |lur| gives |\tex_let:D \aaa = \blurb| which then
+% turns out to start with the non-expandable token |\tex_let:D|.
+% Since the expansion of \cs{exp:w} \cs{exp_end_continue_f:w} is
+% empty, we wind up with a fully expanded list, only \TeX{} has not
+% tried to execute any of the non-expandable tokens. This is what
+% differentiates this function from the |e| and |x| argument type.
+% \begin{macrocode}
+\cs_new:Npn \::f #1 \::: #2#3
+ {
+ \exp_after:wN \@@_arg_next:nnn
+ \exp_after:wN { \exp:w \exp_end_continue_f:w #3 }
+ {#1} {#2}
+ }
+\use:nn { \cs_new_eq:NN \exp_stop_f: } { ~ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\::x}
+% This function is used to expand an argument fully.
+% We build in the expansion of \cs{@@_arg_next:nnn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \::x #1 \::: #2#3
+ {
+ \cs_set_nopar:Npe \l_@@_internal_tl
+ { \exp_not:n { #1 \::: } { \exp_not:n {#2} {#3} } }
+ \l_@@_internal_tl
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\::v}
+% \begin{macro}[EXP]{\::V}
+% These functions return the value of a register, i.e., one of
+% |tl|, |clist|, |int|, |skip|, |dim|, |muskip|, or built-in
+% \TeX{} register. The |V| version
+% expects a single token whereas |v| like |c| creates a csname from
+% its argument given in braces and then evaluates it as if it was a
+% |V|. The \cs{exp:w} sets off an expansion
+% similar to an |f|-type expansion, which we terminate using
+% \cs{exp_end:}. The argument is returned in braces.
+% \begin{macrocode}
+\cs_new:Npn \::V #1 \::: #2#3
+ {
+ \exp_after:wN \@@_arg_next:nnn
+ \exp_after:wN { \exp:w \@@_eval_register:N #3 }
+ {#1} {#2}
+}
+\cs_new:Npn \::v #1 \::: #2#3
+ {
+ \exp_after:wN \@@_arg_next:nnn
+ \exp_after:wN { \exp:w \@@_eval_register:c {#3} }
+ {#1} {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_eval_register:N, \@@_eval_register:c}
+% \begin{macro}[EXP]{\@@_eval_error_msg:w}
+% This function evaluates a register. Now a register might exist as
+% one of two things: A parameter-less macro or a built-in \TeX{}
+% register such as |\count|. For the \TeX{} registers we have to
+% utilize a \tn{the} whereas for the macros we merely have to
+% expand them once. The trick is to find out when to use
+% \tn{the} and when not to. What we want here is to find out
+% whether the token expands to something else when hit with
+% \cs{exp_after:wN}. The technique is to compare the meaning of the
+% token in question when it has been prefixed with \cs{exp_not:N}
+% and the token itself. If it is a macro, the prefixed
+% \cs{exp_not:N} temporarily turns it into the primitive
+% \cs{scan_stop:}.
+% \begin{macrocode}
+\cs_new:Npn \@@_eval_register:N #1
+ {
+ \exp_after:wN \if_meaning:w \exp_not:N #1 #1
+% \end{macrocode}
+% If the token was not a macro it may be a malformed variable from a
+% |c| expansion in which case it is equal to the primitive
+% \cs{scan_stop:}. In that case we throw an error. We could let \TeX{}
+% do it for us but that would result in the rather obscure
+% \begin{quote}
+% |! You can't use `\relax' after \the.|
+% \end{quote}
+% which while quite true doesn't give many hints as to what actually
+% went wrong. We provide something more sensible.
+% \begin{macrocode}
+ \if_meaning:w \scan_stop: #1
+ \@@_eval_error_msg:w
+ \fi:
+% \end{macrocode}
+% The next bit requires some explanation. The function must be
+% initiated by \cs{exp:w} and we want to
+% terminate this expansion chain by inserting the \cs{exp_end:}
+% token.
+% However, we have to expand the register |#1| before we do
+% that. If it is a \TeX{} register, we need to execute the sequence
+% |\exp_after:wN \exp_end: \tex_the:D #1| and if it is a macro we
+% need to execute |\exp_after:wN \exp_end: #1|. We therefore issue
+% the longer of the two sequences and if the register is a macro, we
+% remove the \cs{tex_the:D}.
+% \begin{macrocode}
+ \else:
+ \exp_after:wN \use_i_ii:nnn
+ \fi:
+ \exp_after:wN \exp_end: \tex_the:D #1
+ }
+\cs_new:Npn \@@_eval_register:c #1
+ { \exp_after:wN \@@_eval_register:N \cs:w #1 \cs_end: }
+% \end{macrocode}
+% Clean up nicely, then call the undefined control sequence. The
+% result is an error message looking like this:
+% \begin{verbatim}
+% ! Undefined control sequence.
+% <argument> \LaTeX3 error:
+% Erroneous variable used!
+% l.55 \tl_set:Nv \l_tmpa_tl {undefined_tl}
+% \end{verbatim}
+% \begin{macrocode}
+\cs_new:Npn \@@_eval_error_msg:w #1 \tex_the:D #2
+ {
+ \fi:
+ \fi:
+ \msg_expandable_error:nnn { kernel } { bad-variable } {#2}
+ \exp_end:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Hand-tuned definitions}
+% \label{sec:l3expan:handtune}
+%
+% One of the most important features of these functions is that they
+% are fully expandable.
+%
+% \begin{macro}[EXP]{\exp_args:Nc, \exp_args:cc}
+% In \pkg{l3basics}.
+% \end{macro}
+%
+% \begin{macro}[EXP]{\exp_args:NNc, \exp_args:Ncc, \exp_args:Nccc}
+% Here are the functions that turn their argument into csnames but are
+% expandable.
+% \begin{macrocode}
+\cs_new:Npn \exp_args:NNc #1#2#3
+ { \exp_after:wN #1 \exp_after:wN #2 \cs:w # 3\cs_end: }
+\cs_new:Npn \exp_args:Ncc #1#2#3
+ { \exp_after:wN #1 \cs:w #2 \exp_after:wN \cs_end: \cs:w #3 \cs_end: }
+\cs_new:Npn \exp_args:Nccc #1#2#3#4
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \cs:w #3 \exp_after:wN \cs_end:
+ \cs:w #4 \cs_end:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\exp_args:No}
+% \begin{macro}[EXP]{\exp_args:NNo}
+% \begin{macro}[EXP]{\exp_args:NNNo}
+% Those lovely runs of expansion!
+% \begin{macrocode}
+\cs_new:Npn \exp_args:No #1#2 { \exp_after:wN #1 \exp_after:wN {#2} }
+\cs_new:Npn \exp_args:NNo #1#2#3
+ { \exp_after:wN #1 \exp_after:wN #2 \exp_after:wN {#3} }
+\cs_new:Npn \exp_args:NNNo #1#2#3#4
+ { \exp_after:wN #1 \exp_after:wN#2 \exp_after:wN #3 \exp_after:wN {#4} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\exp_args:Ne}
+% When the \tn{expanded} primitive is available, use it.
+% \begin{macrocode}
+\cs_new:Npn \exp_args:Ne #1#2
+ { \exp_after:wN #1 \tex_expanded:D { {#2} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\exp_args:Nf, \exp_args:NV, \exp_args:Nv}
+% \begin{macrocode}
+\cs_new:Npn \exp_args:Nf #1#2
+ { \exp_after:wN #1 \exp_after:wN { \exp:w \exp_end_continue_f:w #2 } }
+\cs_new:Npn \exp_args:Nv #1#2
+ {
+ \exp_after:wN #1 \exp_after:wN
+ { \exp:w \@@_eval_register:c {#2} }
+ }
+\cs_new:Npn \exp_args:NV #1#2
+ {
+ \exp_after:wN #1 \exp_after:wN
+ { \exp:w \@@_eval_register:N #2 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \exp_args:NNV, \exp_args:NNv, \exp_args:NNe, \exp_args:NNf,
+% \exp_args:Nco, \exp_args:NcV, \exp_args:Ncv, \exp_args:Ncf,
+% \exp_args:NVV,
+% }
+% Some more hand-tuned function with three arguments.
+% If we forced that an |o| argument always has braces,
+% we could implement \cs{exp_args:Nco} with less tokens
+% and only two arguments.
+% \begin{macrocode}
+\cs_new:Npn \exp_args:NNV #1#2#3
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN { \exp:w \@@_eval_register:N #3 }
+ }
+\cs_new:Npn \exp_args:NNv #1#2#3
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN { \exp:w \@@_eval_register:c {#3} }
+ }
+\cs_new:Npn \exp_args:NNe #1#2#3
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \tex_expanded:D { {#3} }
+ }
+\cs_new:Npn \exp_args:NNf #1#2#3
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN { \exp:w \exp_end_continue_f:w #3 }
+ }
+\cs_new:Npn \exp_args:Nco #1#2#3
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \exp_after:wN {#3}
+ }
+\cs_new:Npn \exp_args:NcV #1#2#3
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \exp_after:wN { \exp:w \@@_eval_register:N #3 }
+ }
+\cs_new:Npn \exp_args:Ncv #1#2#3
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \exp_after:wN { \exp:w \@@_eval_register:c {#3} }
+ }
+\cs_new:Npn \exp_args:Ncf #1#2#3
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \exp_after:wN { \exp:w \exp_end_continue_f:w #3 }
+ }
+\cs_new:Npn \exp_args:NVV #1#2#3
+ {
+ \exp_after:wN #1
+ \exp_after:wN { \exp:w \exp_after:wN
+ \@@_eval_register:N \exp_after:wN #2 \exp_after:wN }
+ \exp_after:wN { \exp:w \@@_eval_register:N #3 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \exp_args:NNNV, \exp_args:NNNv, \exp_args:NNNe,
+% \exp_args:NcNc, \exp_args:NcNo,
+% \exp_args:Ncco,
+% }
+% A few more that we can hand-tune.
+% \begin{macrocode}
+\cs_new:Npn \exp_args:NNNV #1#2#3#4
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN { \exp:w \@@_eval_register:N #4 }
+ }
+\cs_new:Npn \exp_args:NNNv #1#2#3#4
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN { \exp:w \@@_eval_register:c {#4} }
+ }
+\cs_new:Npn \exp_args:NNNe #1#2#3#4
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \tex_expanded:D { {#4} }
+ }
+\cs_new:Npn \exp_args:NcNc #1#2#3#4
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \exp_after:wN #3
+ \cs:w #4 \cs_end:
+ }
+\cs_new:Npn \exp_args:NcNo #1#2#3#4
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \exp_after:wN #3
+ \exp_after:wN {#4}
+ }
+\cs_new:Npn \exp_args:Ncco #1#2#3#4
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \cs:w #3 \exp_after:wN \cs_end:
+ \exp_after:wN {#4}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\exp_args:Nx}
+% \begin{macrocode}
+\cs_new_protected:Npn \exp_args:Nx #1#2
+ { \use:x { \exp_not:N #1 {#2} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Last-unbraced versions}
+%
+% \begin{macro}[EXP]{\@@_arg_last_unbraced:nn}
+% \begin{macro}[EXP]{\::o_unbraced}
+% \begin{macro}[EXP]{\::V_unbraced}
+% \begin{macro}[EXP]{\::v_unbraced}
+% \begin{macro}[EXP]{\::e_unbraced}
+% \begin{macro}[EXP]{\::f_unbraced}
+% \begin{macro}[EXP]{\::x_unbraced}
+% There are a few places where the last argument needs to be available
+% unbraced. First some helper macros.
+% \begin{macrocode}
+\cs_new:Npn \@@_arg_last_unbraced:nn #1#2 { #2#1 }
+\cs_new:Npn \::o_unbraced \::: #1#2
+ { \exp_after:wN \@@_arg_last_unbraced:nn \exp_after:wN {#2} {#1} }
+\cs_new:Npn \::V_unbraced \::: #1#2
+ {
+ \exp_after:wN \@@_arg_last_unbraced:nn
+ \exp_after:wN { \exp:w \@@_eval_register:N #2 } {#1}
+ }
+\cs_new:Npn \::v_unbraced \::: #1#2
+ {
+ \exp_after:wN \@@_arg_last_unbraced:nn
+ \exp_after:wN { \exp:w \@@_eval_register:c {#2} } {#1}
+ }
+\cs_new:Npn \::e_unbraced \::: #1#2
+ { \tex_expanded:D { \exp_not:n {#1} #2 } }
+\cs_new:Npn \::f_unbraced \::: #1#2
+ {
+ \exp_after:wN \@@_arg_last_unbraced:nn
+ \exp_after:wN { \exp:w \exp_end_continue_f:w #2 } {#1}
+ }
+\cs_new_protected:Npn \::x_unbraced \::: #1#2
+ {
+ \cs_set_nopar:Npe \l_@@_internal_tl { \exp_not:n {#1} #2 }
+ \l_@@_internal_tl
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \exp_last_unbraced:No,
+% \exp_last_unbraced:NV,
+% \exp_last_unbraced:Nv,
+% \exp_last_unbraced:Ne,
+% \exp_last_unbraced:Nf,
+% \exp_last_unbraced:NNo,
+% \exp_last_unbraced:NNV,
+% \exp_last_unbraced:NNf,
+% \exp_last_unbraced:Nco,
+% \exp_last_unbraced:NcV,
+% \exp_last_unbraced:NNNo,
+% \exp_last_unbraced:NNNV,
+% \exp_last_unbraced:NNNf,
+% \exp_last_unbraced:Nno,
+% \exp_last_unbraced:Nnf,
+% \exp_last_unbraced:Noo,
+% \exp_last_unbraced:Nfo,
+% \exp_last_unbraced:NnNo,
+% \exp_last_unbraced:NNNNo,
+% \exp_last_unbraced:NNNNf,
+% }
+% \begin{macro}{\exp_last_unbraced:Nx}
+% Now the business end: most of these are hand-tuned for speed, but the
+% general system is in place.
+% \begin{macrocode}
+\cs_new:Npn \exp_last_unbraced:No #1#2 { \exp_after:wN #1 #2 }
+\cs_new:Npn \exp_last_unbraced:NV #1#2
+ { \exp_after:wN #1 \exp:w \@@_eval_register:N #2 }
+\cs_new:Npn \exp_last_unbraced:Nv #1#2
+ { \exp_after:wN #1 \exp:w \@@_eval_register:c {#2} }
+\cs_new:Npn \exp_last_unbraced:Ne #1#2
+ { \exp_after:wN #1 \tex_expanded:D {#2} }
+\cs_new:Npn \exp_last_unbraced:Nf #1#2
+ { \exp_after:wN #1 \exp:w \exp_end_continue_f:w #2 }
+\cs_new:Npn \exp_last_unbraced:NNo #1#2#3
+ { \exp_after:wN #1 \exp_after:wN #2 #3 }
+\cs_new:Npn \exp_last_unbraced:NNV #1#2#3
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp:w \@@_eval_register:N #3
+ }
+\cs_new:Npn \exp_last_unbraced:NNf #1#2#3
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp:w \exp_end_continue_f:w #3
+ }
+\cs_new:Npn \exp_last_unbraced:Nco #1#2#3
+ { \exp_after:wN #1 \cs:w #2 \exp_after:wN \cs_end: #3 }
+\cs_new:Npn \exp_last_unbraced:NcV #1#2#3
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \exp:w \@@_eval_register:N #3
+ }
+\cs_new:Npn \exp_last_unbraced:NNNo #1#2#3#4
+ { \exp_after:wN #1 \exp_after:wN #2 \exp_after:wN #3 #4 }
+\cs_new:Npn \exp_last_unbraced:NNNV #1#2#3#4
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp:w \@@_eval_register:N #4
+ }
+\cs_new:Npn \exp_last_unbraced:NNNf #1#2#3#4
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp:w \exp_end_continue_f:w #4
+ }
+\cs_new:Npn \exp_last_unbraced:Nno { \::n \::o_unbraced \::: }
+\cs_new:Npn \exp_last_unbraced:Nnf { \::n \::f_unbraced \::: }
+\cs_new:Npn \exp_last_unbraced:Noo { \::o \::o_unbraced \::: }
+\cs_new:Npn \exp_last_unbraced:Nfo { \::f \::o_unbraced \::: }
+\cs_new:Npn \exp_last_unbraced:NnNo { \::n \::N \::o_unbraced \::: }
+\cs_new:Npn \exp_last_unbraced:NNNNo #1#2#3#4#5
+ { \exp_after:wN #1 \exp_after:wN #2 \exp_after:wN #3 \exp_after:wN #4 #5 }
+\cs_new:Npn \exp_last_unbraced:NNNNf #1#2#3#4#5
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN #4
+ \exp:w \exp_end_continue_f:w #5
+ }
+\cs_new_protected:Npn \exp_last_unbraced:Nx { \::x_unbraced \::: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\exp_last_two_unbraced:Noo}
+% \begin{macro}[EXP]{\@@_last_two_unbraced:noN}
+% If |#2| is a single token then this can be implemented as
+% \begin{verbatim}
+% \cs_new:Npn \exp_last_two_unbraced:Noo #1 #2 #3
+% { \exp_after:wN \exp_after:wN \exp_after:wN #1 \exp_after:wN #2 #3 }
+% \end{verbatim}
+% However, for robustness this is not suitable. Instead, a bit of a
+% shuffle is used to ensure that |#2| can be multiple tokens.
+% \begin{macrocode}
+\cs_new:Npn \exp_last_two_unbraced:Noo #1#2#3
+ { \exp_after:wN \@@_last_two_unbraced:noN \exp_after:wN {#3} {#2} #1 }
+\cs_new:Npn \@@_last_two_unbraced:noN #1#2#3
+ { \exp_after:wN #3 #2 #1 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Preventing expansion}
+%
+% \begin{macro}{\__kernel_exp_not:w}
+% At the kernel level, we need the primitive behaviour to allow expansion
+% \emph{before} the brace group.
+% \begin{macrocode}
+\cs_new_eq:NN \__kernel_exp_not:w \tex_unexpanded:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\exp_not:c}
+% \begin{macro}[EXP]{\exp_not:o}
+% \begin{macro}[EXP]{\exp_not:e}
+% \begin{macro}[EXP]{\exp_not:f}
+% \begin{macro}[EXP]{\exp_not:V}
+% \begin{macro}[EXP]{\exp_not:v}
+% All these except \cs{exp_not:c} call the kernel-internal
+% \cs{__kernel_exp_not:w} namely \cs{tex_unexpanded:D}.
+% \begin{macrocode}
+\cs_new:Npn \exp_not:c #1 { \exp_after:wN \exp_not:N \cs:w #1 \cs_end: }
+\cs_new:Npn \exp_not:o #1 { \__kernel_exp_not:w \exp_after:wN {#1} }
+\cs_new:Npn \exp_not:e #1
+ { \__kernel_exp_not:w \tex_expanded:D { {#1} } }
+\cs_new:Npn \exp_not:f #1
+ { \__kernel_exp_not:w \exp_after:wN { \exp:w \exp_end_continue_f:w #1 } }
+\cs_new:Npn \exp_not:V #1
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ { \exp:w \@@_eval_register:N #1 }
+ }
+\cs_new:Npn \exp_not:v #1
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ { \exp:w \@@_eval_register:c {#1} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Controlled expansion}
+%
+% \begin{macro}{\exp:w}
+% \begin{macro}{\exp_end:}
+% \begin{macro}{\exp_end_continue_f:w}
+% \begin{macro}{\exp_end_continue_f:nw}
+% To trigger a sequence of \enquote{arbitrarily} many expansions we
+% need a method to invoke \TeX's expansion mechanism in such a way
+% that (a) we are able to stop it in a controlled manner and (b) the
+% result of what triggered the expansion in the first place is null,
+% i.e.\@, that we do not get any unwanted side effects. There aren't
+% that many possibilities in \TeX{}; in fact the one explained below
+% might well be the only one (as normally the result of expansion is
+% not null).
+%
+% The trick here is to make use of the fact that
+% \cs{tex_romannumeral:D} expands the tokens following it when looking
+% for a number and that its expansion is null if that number turns out
+% to be zero or negative. So we use that to start the expansion
+% sequence: \cs{exp:w} is set equal to \cs{tex_romannumeral:D} in
+% \pkg{l3basics}. To stop the expansion sequence in a controlled way
+% all we need to provide is a constant integer zero as part of
+% expanded tokens. As this is an integer constant it immediately stops
+% \cs{tex_romannumeral:D}'s search for a number. Again, the
+% definition of \cs{exp_end:} as the integer constant zero is in
+% \pkg{l3basics}. (Note that according to our specification all
+% tokens we expand initiated by \cs{exp:w} are supposed to be
+% expandable (as well as their replacement text in the expansion) so
+% we will not encounter a \enquote{number} that actually result in a
+% roman numeral being generated. Or if we do then the programmer made
+% a mistake.)
+%
+% If on the other hand we want to stop the initial expansion sequence
+% but continue with an \texttt{f}-type expansion we provide the
+% alphabetic constant |`^^@| that also represents |0| but this time
+% \TeX's syntax for a \meta{number} continues searching for an
+% optional space (and it continues expansion doing that) --- see
+% \TeX{}book page~269 for details.
+% \begin{macrocode}
+\group_begin:
+ \tex_catcode:D `\^^@ = 13
+ \cs_new_protected:Npn \exp_end_continue_f:w { `^^@ }
+% \end{macrocode}
+% If the above definition ever appears outside its proper context
+% the active character |^^@| will be executed so we turn this into an
+% error. The test for existence covers the (unlikely) case that some
+% other code has already defined |^^@|: this is true for example for
+% \texttt{xmltex.tex}.
+% \begin{macrocode}
+ \if_cs_exist:N ^^@
+ \else:
+ \cs_new:Npn ^^@
+ { \msg_expandable_error:nn { kernel } { bad-exp-end-f } }
+ \fi:
+% \end{macrocode}
+% The same but grabbing an argument to remove spaces and braces.
+% \begin{macrocode}
+ \cs_new:Npn \exp_end_continue_f:nw #1 { `^^@ #1 }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Defining function variants}
+%
+% \begin{macrocode}
+%<@@=cs>
+% \end{macrocode}
+%
+% \begin{variable}{\s_@@_mark,\s_@@_stop}
+% Internal scan marks. No \pkg{l3quark} yet, so do things by hand.
+% \begin{macrocode}
+\cs_new_eq:NN \s_@@_mark \scan_stop:
+\cs_new_eq:NN \s_@@_stop \scan_stop:
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\q_@@_recursion_stop}
+% Internal recursion quarks. No \pkg{l3quark} yet, so do things by hand.
+% \begin{macrocode}
+\cs_new:Npn \q_@@_recursion_stop { \q_@@_recursion_stop }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{
+% \@@_use_none_delimit_by_s_stop:w,
+% \@@_use_i_delimit_by_s_stop:nw,
+% \@@_use_none_delimit_by_q_recursion_stop:w
+% }
+% Internal scan marks.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_none_delimit_by_s_stop:w #1 \s_@@_stop { }
+\cs_new:Npn \@@_use_i_delimit_by_s_stop:nw #1 #2 \s_@@_stop {#1}
+\cs_new:Npn \@@_use_none_delimit_by_q_recursion_stop:w
+ #1 \q_@@_recursion_stop { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\cs_generate_variant:Nn, \cs_generate_variant:cn}
+% \begin{arguments}
+% \item Base form of a function; \emph{e.g.},~\cs{tl_set:Nn}
+% \item One or more variant argument specifiers; e.g., |{Nx,c,cx}|
+% \end{arguments}
+% After making sure that the base form exists, test whether it is
+% protected or not and define \cs{@@_tmp:w} as either
+% \cs{cs_new:Npe} or \cs{cs_new_protected:Npe}, which is
+% then used to define all the variants (except those involving
+% \texttt{x}-expansion, always protected). Split up the original base
+% function only once, to grab its name and signature. Then we wish to
+% iterate through the comma list of variant argument specifiers, which
+% we first convert to a string: the reason is explained later.
+% \begin{macrocode}
+\cs_new_protected:Npn \cs_generate_variant:Nn #1#2
+ {
+ \@@_generate_variant:N #1
+ \use:e
+ {
+ \@@_generate_variant:nnNN
+ \cs_split_function:N #1
+ \exp_not:N #1
+ \tl_to_str:n {#2} ,
+ \exp_not:N \scan_stop: ,
+ \exp_not:N \q_@@_recursion_stop
+ }
+ }
+\cs_new_protected:Npn \cs_generate_variant:cn
+ { \exp_args:Nc \cs_generate_variant:Nn }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_generate_variant:N}
+% \begin{macro}{\@@_generate_variant:ww, \@@_generate_variant:wwNw}
+% The goal here is to pick up protected parent functions. There are
+% four cases: the parent function can be a primitive or a macro, and
+% can be expandable or not. For non-expandable primitives, all
+% variants should be protected; skipping the \cs{else:} branch is safe
+% because non-expandable primitives cannot be \TeX{} conditionals.
+%
+% The other case where variants should be protected is when the parent
+% function is a protected macro: then |protected| appears in the
+% meaning before the first occurrence of |macro|. The |ww| auxiliary
+% removes everything in the meaning string after the first |ma|. We
+% use |ma| rather than the full |macro| because the meaning of the
+% \tn{firstmark} primitive (and four others) can contain an arbitrary
+% string after a leading |firstmark:|. Then, look for |pr| in the
+% part we extracted: no need to look for anything longer: the only
+% strings we can have are an empty string, \verb*|\long |,
+% \verb*|\protected |, \verb*|\protected\long |, |\first|, |\top|,
+% |\bot|, |\splittop|, or |\splitbot|, with |\| replaced by the
+% appropriate escape character. If |pr| appears in the part before
+% |ma|, the first \cs{s_@@_mark} is taken as an argument of the |wwNw|
+% auxiliary, and |#3| is \cs{cs_new_protected:Npe}, otherwise it
+% is \cs{cs_new:Npe}.
+% \begin{macrocode}
+\cs_new_protected:Npe \@@_generate_variant:N #1
+ {
+ \exp_not:N \exp_after:wN \exp_not:N \if_meaning:w
+ \exp_not:N \exp_not:N #1 #1
+ \cs_set_eq:NN \exp_not:N \@@_tmp:w \cs_new_protected:Npe
+ \exp_not:N \else:
+ \exp_not:N \exp_after:wN \exp_not:N \@@_generate_variant:ww
+ \exp_not:N \token_to_meaning:N #1 \tl_to_str:n { ma }
+ \s_@@_mark
+ \s_@@_mark \cs_new_protected:Npe
+ \tl_to_str:n { pr }
+ \s_@@_mark \cs_new:Npe
+ \s_@@_stop
+ \exp_not:N \fi:
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new_protected:Npn \@@_generate_variant:ww
+ #1 { \tl_to_str:n { ma } } #2 \s_@@_mark
+ { \@@_generate_variant:wwNw #1 }
+\exp_last_unbraced:NNNNo
+ \cs_new_protected:Npn \@@_generate_variant:wwNw
+ #1 { \tl_to_str:n { pr } } #2 \s_@@_mark #3 #4 \s_@@_stop
+ { \cs_set_eq:NN \@@_tmp:w #3 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_generate_variant:nnNN}
+% \begin{arguments}
+% \item Base name.
+% \item Base signature.
+% \item Boolean.
+% \item Base function.
+% \end{arguments}
+% If the boolean is \cs{c_false_bool}, the base function has no colon
+% and we abort with an error; otherwise, set off a loop through the
+% desired variant forms. The original function is retained as |#4| for
+% efficiency.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_generate_variant:nnNN #1#2#3#4
+ {
+ \if_meaning:w \c_false_bool #3
+ \msg_error:nne { kernel } { missing-colon }
+ { \token_to_str:c {#1} }
+ \exp_after:wN \@@_use_none_delimit_by_q_recursion_stop:w
+ \fi:
+ \@@_generate_variant:Nnnw #4 {#1}{#2}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_generate_variant:Nnnw}
+% \begin{arguments}
+% \item Base function.
+% \item Base name.
+% \item Base signature.
+% \item Beginning of variant signature.
+% \end{arguments}
+% First check whether to terminate the loop over variant forms. Then,
+% for each variant form, construct a new function name using the
+% original base name, the variant signature consisting of $l$ letters
+% and the last $k-l$ letters of the base signature (of length $k$).
+% For example, for a base function \cs{prop_put:Nnn} which needs a
+% |cV| variant form, we want the new signature to be |cVn|.
+%
+% There are further subtleties:
+% \begin{itemize}
+% \item In \cs{cs_generate_variant:Nn} |\foo:nnTF| |{xxTF}|, we must define
+% |\foo:xxTF| using |\exp_args:Nxx|,
+% rather than a hypothetical |\exp_args:NxxTF|. Thus, we wish to
+% trim a common trailing part from the base signature and the
+% variant signature.
+% \item In \cs{cs_generate_variant:Nn} |\foo:on| |{ox}|, the
+% function |\foo:ox| must be defined using |\exp_args:Nnx|, not
+% |\exp_args:Nox|, to avoid double |o| expansion.
+% \item Lastly, \cs{cs_generate_variant:Nn} |\foo:on| |{xn}| must
+% trigger an error, because we do not have a means to replace
+% |o|-expansion by |x|-expansion.
+% More generally, we can only convert |N| to |c|, or convert |n|
+% to |V|, |v|, |o|, |e|, |f|, or |x|.
+% \end{itemize}
+% All this boils down to a few rules. Only |n| and |N|-type
+% arguments can be replaced by \cs{cs_generate_variant:Nn}. Other
+% argument types are allowed to be passed unchanged from the base
+% form to the variant: in the process they are changed to |n|
+% except for |N| and |p|-type arguments. A common trailing
+% part is ignored.
+%
+% We compare the base and variant signatures one character at a time
+% within |e|-expansion. The result is given to
+% \cs{@@_generate_variant:wwNN} (defined later) in the form
+% \meta{processed variant signature} \cs{s_@@_mark} \meta{errors}
+% \cs{s_@@_stop} \meta{base function} \meta{new function}. If all went
+% well, \meta{errors} is empty; otherwise, it is a kernel error
+% message and some clean-up code.
+%
+% Note the space after |#3| and after the following brace group.
+% Those are ignored by \TeX{} when fetching the last argument for
+% \cs{@@_generate_variant_loop:nNwN}, but can be used as a delimiter
+% for \cs{@@_generate_variant_loop_end:nwwwNNnn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_generate_variant:Nnnw #1#2#3#4 ,
+ {
+ \if_meaning:w \scan_stop: #4
+ \exp_after:wN \@@_use_none_delimit_by_q_recursion_stop:w
+ \fi:
+ \use:e
+ {
+ \exp_not:N \@@_generate_variant:wwNN
+ \@@_generate_variant_loop:nNwN { }
+ #4
+ \@@_generate_variant_loop_end:nwwwNNnn
+ \s_@@_mark
+ #3 ~
+ { ~ { } \fi: \@@_generate_variant_loop_long:wNNnn } ~
+ { }
+ \s_@@_stop
+ \exp_not:N #1 {#2} {#4}
+ }
+ \@@_generate_variant:Nnnw #1 {#2} {#3}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_generate_variant_loop:nNwN,
+% \@@_generate_variant_loop_base:N,
+% \@@_generate_variant_loop_same:w,
+% \@@_generate_variant_loop_end:nwwwNNnn,
+% \@@_generate_variant_loop_long:wNNnn,
+% \@@_generate_variant_loop_invalid:NNwNNnn,
+% \@@_generate_variant_loop_special:NNwNNnn
+% }
+% \begin{arguments}
+% \item Last few consecutive letters common between the base and
+% variant (more precisely, \cs{@@_generate_variant_same:N}
+% \meta{letter} for each letter).
+% \item Next variant letter.
+% \item Remainder of variant form.
+% \item Next base letter.
+% \end{arguments}
+% The first argument is populated by
+% \cs{@@_generate_variant_loop_same:w} when a variant letter and a
+% base letter match. It is flushed into the input stream whenever the
+% two letters are different: if the loop ends before, the argument is
+% dropped, which means that trailing common letters are ignored.
+%
+% The case where the two letters are different is only allowed if the
+% base is |N| and the variant is |c|, or when the base is |n| and the
+% variant is |V|, |v|, |o|, |e|, |f|, or |x|. Otherwise, call
+% \cs{@@_generate_variant_loop_invalid:NNwNNnn} to remove the end of
+% the loop, get arguments at the end of the loop, and place an
+% appropriate error message as a second argument of
+% \cs{@@_generate_variant:wwNN}. If the letters are distinct and the
+% base letter is indeed |n| or |N|, leave in the input stream whatever
+% argument |#1| was collected, and the next variant letter |#2|, then
+% loop by calling \cs{@@_generate_variant_loop:nNwN}.
+%
+% The loop can stop in three ways.
+% \begin{itemize}
+% \item If the end of the variant form is encountered first, |#2| is
+% \cs{@@_generate_variant_loop_end:nwwwNNnn} (expanded by the
+% conditional \cs{if:w}), which inserts some tokens to end the
+% conditional; grabs the \meta{base name} as |#7|, the
+% \meta{variant signature} |#8|, the \meta{next base letter} |#1|
+% and the part |#3| of the base signature that wasn't read yet;
+% and combines those into the \meta{new function} to be defined.
+% \item If the end of the base form is encountered first, |#4| is
+% |~{}\fi:| which ends the conditional (with an empty expansion),
+% followed by \cs{@@_generate_variant_loop_long:wNNnn}, which
+% places an error as the second argument of
+% \cs{@@_generate_variant:wwNN}.
+% \item The loop can be interrupted early if the requested expansion
+% is unavailable, namely when the variant and base letters differ
+% and the base is not the right one (|n| or |N| to support the
+% variant). In that case too an error is placed as the second
+% argument of \cs{@@_generate_variant:wwNN}.
+% \end{itemize}
+% Note that if the variant form has the same length as the base form,
+% |#2| is as described in the first point, and |#4| as described in
+% the second point above. The \cs{@@_generate_variant_loop_end:nwwwNNnn}
+% breaking function takes the empty brace group in |#4| as its first
+% argument: this empty brace group produces the correct signature for
+% the full variant.
+% \begin{macrocode}
+\cs_new:Npn \@@_generate_variant_loop:nNwN #1#2#3 \s_@@_mark #4
+ {
+ \if:w #2 #4
+ \exp_after:wN \@@_generate_variant_loop_same:w
+ \else:
+ \if:w #4 \@@_generate_variant_loop_base:N #2 \else:
+ \if:w 0
+ \if:w N #4 \else: \if:w n #4 \else: 1 \fi: \fi:
+ \if:w \scan_stop: \@@_generate_variant_loop_base:N #2 1 \fi:
+ 0
+ \@@_generate_variant_loop_special:NNwNNnn #4#2
+ \else:
+ \@@_generate_variant_loop_invalid:NNwNNnn #4#2
+ \fi:
+ \fi:
+ \fi:
+ #1
+ \prg_do_nothing:
+ #2
+ \@@_generate_variant_loop:nNwN { } #3 \s_@@_mark
+ }
+\cs_new:Npn \@@_generate_variant_loop_base:N #1
+ {
+ \if:w c #1 N \else:
+ \if:w o #1 n \else:
+ \if:w V #1 n \else:
+ \if:w v #1 n \else:
+ \if:w f #1 n \else:
+ \if:w e #1 n \else:
+ \if:w x #1 n \else:
+ \if:w n #1 n \else:
+ \if:w N #1 N \else:
+ \scan_stop:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ }
+\cs_new:Npn \@@_generate_variant_loop_same:w
+ #1 \prg_do_nothing: #2#3#4
+ { #3 { #1 \@@_generate_variant_same:N #2 } }
+\cs_new:Npn \@@_generate_variant_loop_end:nwwwNNnn
+ #1#2 \s_@@_mark #3 ~ #4 \s_@@_stop #5#6#7#8
+ {
+ \scan_stop: \scan_stop: \fi:
+ \s_@@_mark \s_@@_stop
+ \exp_not:N #6
+ \exp_not:c { #7 : #8 #1 #3 }
+ }
+\cs_new:Npn \@@_generate_variant_loop_long:wNNnn #1 \s_@@_stop #2#3#4#5
+ {
+ \exp_not:n
+ {
+ \s_@@_mark
+ \msg_error:nnee { kernel } { variant-too-long }
+ {#5} { \token_to_str:N #3 }
+ \use_none:nnn
+ \s_@@_stop
+ #3
+ #3
+ }
+ }
+\cs_new:Npn \@@_generate_variant_loop_invalid:NNwNNnn
+ #1#2 \fi: \fi: \fi: #3 \s_@@_stop #4#5#6#7
+ {
+ \fi: \fi: \fi:
+ \exp_not:n
+ {
+ \s_@@_mark
+ \msg_error:nneeee { kernel } { invalid-variant }
+ {#7} { \token_to_str:N #5 } {#1} {#2}
+ \use_none:nnn
+ \s_@@_stop
+ #5
+ #5
+ }
+ }
+\cs_new:Npn \@@_generate_variant_loop_special:NNwNNnn
+ #1#2#3 \s_@@_stop #4#5#6#7
+ {
+ #3 \s_@@_stop #4 #5 {#6} {#7}
+ \exp_not:n
+ {
+ \msg_error:nneeee
+ { kernel } { deprecated-variant }
+ {#7} { \token_to_str:N #5 } {#1} {#2}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_generate_variant_same:N}
+% When the base and variant letters are identical, don't do any
+% expansion. For most argument types, we can use the |n|-type
+% no-expansion, but the |N| and |p| types require a slightly different
+% behaviour with respect to braces. For |V|-type this function could
+% output |N| to avoid adding useless braces but that is not a problem.
+% \begin{macrocode}
+\cs_new:Npn \@@_generate_variant_same:N #1
+ {
+ \if:w N #1 #1 \else:
+ \if:w p #1 #1 \else:
+ \token_to_str:N n
+ \if:w n #1 \else:
+ \@@_generate_variant_loop_special:NNwNNnn #1#1
+ \fi:
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_generate_variant:wwNN}
+% If the variant form has already been defined, log its existence
+% (provided \texttt{log-functions} is active).
+% Otherwise, make sure that the |\exp_args:N #3| form is defined, and
+% if it contains |x|, change \cs{@@_tmp:w} locally to
+% \cs{cs_new_protected:Npe}. Then define the variant by
+% combining the |\exp_args:N #3| variant and the base function.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_generate_variant:wwNN
+ #1 \s_@@_mark #2 \s_@@_stop #3#4
+ {
+ #2
+ \cs_if_free:NT #4
+ {
+ \group_begin:
+ \@@_generate_internal_variant:n {#1}
+ \@@_tmp:w #4 { \exp_not:c { exp_args:N #1 } \exp_not:N #3 }
+ \group_end:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_generate_internal_variant:n}
+% \begin{macro}[rEXP]{\@@_generate_internal_variant_loop:n}
+% First test for the presence of |x| (this is where working with
+% strings makes our lives easier), as the result should be protected,
+% and the next variant to be defined using that internal variant
+% should be protected (done by setting \cs{@@_tmp:w}). Then call
+% \cs{@@_generate_internal_variant:NNn} with arguments
+% \cs{cs_new_protected:cpn} \cs{use:x} (for protected) or
+% \cs{cs_new:cpn} \cs{tex_expanded:D} (expandable) and the signature. If |p|
+% appears in the signature, or if the function to be defined is
+% expandable and the primitive \tn{expanded} is not available, or if there
+% are more than $8$ arguments, call
+% some fall-back code that just puts the appropriate |\::| commands.
+% Otherwise, call \cs{@@_generate_internal_one_go:NNn} to construct
+% the \cs[no-index]{exp_args:N\dots} function as a macro taking up to
+% $9$~arguments and expanding them using \cs{use:x} or \cs{tex_expanded:D}.
+% \begin{macrocode}
+\cs_new_protected:Npe \@@_generate_internal_variant:n #1
+ {
+ \exp_not:N \@@_generate_internal_variant:wwnNwn
+ #1 \s_@@_mark
+ { \cs_set_eq:NN \exp_not:N \@@_tmp:w \cs_new_protected:Npe }
+ \cs_new_protected:cpn
+ \use:x
+ \token_to_str:N x \s_@@_mark
+ { }
+ \cs_new:cpn
+ \exp_not:N \tex_expanded:D
+ \s_@@_stop
+ {#1}
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new_protected:Npn \@@_generate_internal_variant:wwnNwn #1
+ { \token_to_str:N x } #2 \s_@@_mark #3#4#5#6 \s_@@_stop #7
+ {
+ #3
+ \cs_if_free:cT { exp_args:N #7 }
+ { \@@_generate_internal_variant:NNn #4 #5 {#7} }
+ }
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_new_protected:Npn \@@_generate_internal_variant:NNn ##1##2##3
+ {
+ \if_catcode:w X \use_none:nnnnnnnn ##3
+ \prg_do_nothing: \prg_do_nothing: \prg_do_nothing:
+ \prg_do_nothing: \prg_do_nothing: \prg_do_nothing:
+ \prg_do_nothing: \prg_do_nothing: X
+ \exp_after:wN \@@_generate_internal_test:Nw \exp_after:wN ##2
+ \else:
+ \exp_after:wN \@@_generate_internal_test_aux:w \exp_after:wN #1
+ \fi:
+ ##3
+ \s_@@_mark
+ {
+ \use:e
+ {
+ ##1 { exp_args:N ##3 }
+ { \@@_generate_internal_variant_loop:n ##3 { : \use_i:nn } }
+ }
+ }
+ #1
+ \s_@@_mark
+ { \exp_not:n { \@@_generate_internal_one_go:NNn ##1 ##2 {##3} } }
+ \s_@@_stop
+ }
+ \cs_new_protected:Npn \@@_generate_internal_test_aux:w
+ ##1 #1 ##2 \s_@@_mark ##3 ##4 \s_@@_stop {##3}
+ \cs_new_eq:NN \@@_generate_internal_test:Nw
+ \@@_generate_internal_test_aux:w
+ }
+\exp_args:No \@@_tmp:w { \token_to_str:N p }
+\cs_new_protected:Npn \@@_generate_internal_one_go:NNn #1#2#3
+ {
+ \@@_generate_internal_loop:nwnnw
+ { \exp_not:N ##1 } 1 . { } { }
+ #3 { ? \@@_generate_internal_end:w } X ;
+ 23456789 { ? \@@_generate_internal_long:w } ;
+ #1 #2 {#3}
+ }
+\cs_new_protected:Npn \@@_generate_internal_loop:nwnnw #1#2 . #3#4#5#6 ; #7
+ {
+ \use_none:n #5
+ \use_none:n #7
+ \cs_if_exist_use:cF { @@_generate_internal_#5:NN }
+ { \@@_generate_internal_other:NN }
+ #5 #7
+ #7 .
+ { #3 #1 } { #4 ## #2 }
+ #6 ;
+ }
+\cs_new_protected:Npn \@@_generate_internal_N:NN #1#2
+ { \@@_generate_internal_loop:nwnnw { \exp_not:N ###2 } }
+\cs_new_protected:Npn \@@_generate_internal_c:NN #1#2
+ { \exp_args:No \@@_generate_internal_loop:nwnnw { \exp_not:c {###2} } }
+\cs_new_protected:Npn \@@_generate_internal_n:NN #1#2
+ { \@@_generate_internal_loop:nwnnw { { \exp_not:n {###2} } } }
+\cs_new_protected:Npn \@@_generate_internal_x:NN #1#2
+ { \@@_generate_internal_loop:nwnnw { {###2} } }
+\cs_new_protected:Npn \@@_generate_internal_other:NN #1#2
+ {
+ \exp_args:No \@@_generate_internal_loop:nwnnw
+ {
+ \exp_after:wN
+ {
+ \exp:w \exp_args:NNc \exp_after:wN \exp_end:
+ { exp_not:#1 } {###2}
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_generate_internal_end:w #1 . #2#3#4 ; #5 ; #6#7#8
+ { #6 { exp_args:N #8 } #3 { #7 {#2} } }
+\cs_new_protected:Npn \@@_generate_internal_long:w #1 N #2#3 . #4#5#6#
+ {
+ \exp_args:Nx \@@_generate_internal_long:nnnNNn
+ { \@@_generate_internal_variant_loop:n #2 #6 { : \use_i:nn } }
+ {#4} {#5}
+ }
+\cs_new:Npn \@@_generate_internal_long:nnnNNn #1#2#3#4 ; ; #5#6#7
+ { #5 { exp_args:N #7 } #3 { #6 { \exp_not:n {#1} {#2} } } }
+% \end{macrocode}
+% This command grabs char by char outputting |\::#1| (not expanded
+% further). We avoid tests by putting a trailing |: \use_i:nn|, which
+% leaves \cs{cs_end:} and removes the looping macro. The colon is in
+% fact also turned into \cs{:::} so that the required structure for
+% |\exp_args:N...| commands is correctly terminated.
+% \begin{macrocode}
+\cs_new:Npn \@@_generate_internal_variant_loop:n #1
+ {
+ \exp_after:wN \exp_not:N \cs:w :: #1 \cs_end:
+ \@@_generate_internal_variant_loop:n
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \prg_generate_conditional_variant:Nnn,
+% \@@_generate_variant:nnNnn,
+% \@@_generate_variant:w,
+% \@@_generate_variant:n,
+% \@@_generate_variant_p_form:nnn,
+% \@@_generate_variant_T_form:nnn,
+% \@@_generate_variant_F_form:nnn,
+% \@@_generate_variant_TF_form:nnn,
+% }
+% \begin{macrocode}
+\cs_new_protected:Npn \prg_generate_conditional_variant:Nnn #1
+ {
+ \use:e
+ {
+ \@@_generate_variant:nnNnn
+ \cs_split_function:N #1
+ }
+ }
+\cs_new_protected:Npn \@@_generate_variant:nnNnn #1#2#3#4#5
+ {
+ \if_meaning:w \c_false_bool #3
+ \msg_error:nne { kernel } { missing-colon }
+ { \token_to_str:c {#1} }
+ \@@_use_i_delimit_by_s_stop:nw
+ \fi:
+ \exp_after:wN \@@_generate_variant:w
+ \tl_to_str:n {#5} , \scan_stop: , \q_@@_recursion_stop
+ \@@_use_none_delimit_by_s_stop:w \s_@@_mark {#1} {#2} {#4} \s_@@_stop
+ }
+\cs_new_protected:Npn \@@_generate_variant:w
+ #1 , #2 \s_@@_mark #3#4#5
+ {
+ \if_meaning:w \scan_stop: #1 \scan_stop:
+ \if_meaning:w \q_@@_nil #1 \q_@@_nil
+ \use_i:nnn
+ \fi:
+ \exp_after:wN \@@_use_none_delimit_by_q_recursion_stop:w
+ \else:
+ \cs_if_exist_use:cTF { @@_generate_variant_#1_form:nnn }
+ { {#3} {#4} {#5} }
+ {
+ \msg_error:nnee
+ { kernel } { conditional-form-unknown }
+ {#1} { \token_to_str:c { #3 : #4 } }
+ }
+ \fi:
+ \@@_generate_variant:w #2 \s_@@_mark {#3} {#4} {#5}
+ }
+\cs_new_protected:Npn \@@_generate_variant_p_form:nnn #1#2
+ { \cs_generate_variant:cn { #1 _p : #2 } }
+\cs_new_protected:Npn \@@_generate_variant_T_form:nnn #1#2
+ { \cs_generate_variant:cn { #1 : #2 T } }
+\cs_new_protected:Npn \@@_generate_variant_F_form:nnn #1#2
+ { \cs_generate_variant:cn { #1 : #2 F } }
+\cs_new_protected:Npn \@@_generate_variant_TF_form:nnn #1#2
+ { \cs_generate_variant:cn { #1 : #2 TF } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\exp_args_generate:n}
+% This function is not used in the kernel hence we can use functions
+% that are defined in later modules. It also does not need to be fast
+% so use inline mappings. For each requested variant we check that
+% there are no characters besides |NnpcofVvx|, in particular that
+% there are no spaces. Then we just call the internal function.
+% \begin{macrocode}
+\cs_new_protected:Npn \exp_args_generate:n #1
+ {
+ \exp_args:No \clist_map_inline:nn { \tl_to_str:n {#1} }
+ {
+ \str_map_inline:nn {##1}
+ {
+ \str_if_in:nnF { NnpcofeVvx } {####1}
+ {
+ \msg_error:nnnn { kernel } { invalid-exp-args }
+ {####1} {##1}
+ \str_map_break:n { \use_none:nn }
+ }
+ }
+ \@@_generate_internal_variant:n {##1}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Definitions with the automated technique}
+% \label{sec:l3expan:gendef}
+%
+% Some of these could be done more efficiently, but the complexity of
+% coding then becomes an issue. Notice that the auto-generated functions
+% actually take no arguments themselves.
+%
+% \begin{macro}[EXP]
+% {
+% \exp_args:Nnc, \exp_args:Nno, \exp_args:NnV, \exp_args:Nnv, \exp_args:Nne, \exp_args:Nnf,
+% \exp_args:Noc, \exp_args:Noo, \exp_args:Nof,
+% \exp_args:NVo, \exp_args:Nfo, \exp_args:Nff, \exp_args:Nee
+% }
+% \begin{macro}
+% {
+% \exp_args:NNx, \exp_args:Ncx, \exp_args:Nnx,
+% \exp_args:Nox, \exp_args:Nxo, \exp_args:Nxx,
+% }
+% Here are the actual function definitions, using the helper
+% functions above. The group is used because
+% \cs{@@_generate_internal_variant:n} redefines \cs{@@_tmp:w} locally.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \group_begin:
+ \exp_args:No \@@_generate_internal_variant:n
+ { \tl_to_str:n {#1} }
+ \group_end:
+ }
+\@@_tmp:w { nc }
+\@@_tmp:w { no }
+\@@_tmp:w { nV }
+\@@_tmp:w { nv }
+\@@_tmp:w { ne }
+\@@_tmp:w { nf }
+\@@_tmp:w { oc }
+\@@_tmp:w { oo }
+\@@_tmp:w { of }
+\@@_tmp:w { Vo }
+\@@_tmp:w { fo }
+\@@_tmp:w { ff }
+\@@_tmp:w { ee }
+\@@_tmp:w { Nx }
+\@@_tmp:w { cx }
+\@@_tmp:w { nx }
+\@@_tmp:w { ox }
+\@@_tmp:w { xo }
+\@@_tmp:w { xx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \exp_args:NNcf,
+% \exp_args:NNno, \exp_args:NNnV, \exp_args:NNoo, \exp_args:NNVV,
+% \exp_args:Ncno, \exp_args:NcnV, \exp_args:Ncoo, \exp_args:NcVV,
+% \exp_args:Nnnc, \exp_args:Nnno, \exp_args:Nnnf, \exp_args:Nnff,
+% \exp_args:Nooo, \exp_args:Noof, \exp_args:Nffo, \exp_args:Neee
+% }
+% \begin{macro}
+% {
+% \exp_args:NNNx, \exp_args:NNnx, \exp_args:NNox,
+% \exp_args:Nccx, \exp_args:Ncnx,
+% \exp_args:Nnnx, \exp_args:Nnox, \exp_args:Noox,
+% }
+% \begin{macrocode}
+\@@_tmp:w { Ncf }
+\@@_tmp:w { Nno }
+\@@_tmp:w { NnV }
+\@@_tmp:w { Noo }
+\@@_tmp:w { NVV }
+\@@_tmp:w { cno }
+\@@_tmp:w { cnV }
+\@@_tmp:w { coo }
+\@@_tmp:w { cVV }
+\@@_tmp:w { nnc }
+\@@_tmp:w { nno }
+\@@_tmp:w { nnf }
+\@@_tmp:w { nff }
+\@@_tmp:w { ooo }
+\@@_tmp:w { oof }
+\@@_tmp:w { ffo }
+\@@_tmp:w { eee }
+\@@_tmp:w { NNx }
+\@@_tmp:w { Nnx }
+\@@_tmp:w { Nox }
+\@@_tmp:w { nnx }
+\@@_tmp:w { nox }
+\@@_tmp:w { ccx }
+\@@_tmp:w { cnx }
+\@@_tmp:w { oox }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Held-over variant generation}
+%
+% \begin{macro}[documented-as = \cs_generate_from_arg_count:NNnn]
+% {\cs_generate_from_arg_count:NNno}
+% \begin{macro}[documented-as = \cs_replacement_spec:N]{\cs_replacement_spec:c}
+% A couple of variants that are from early functions.
+% \begin{macrocode}
+\cs_generate_variant:Nn \cs_generate_from_arg_count:NNnn { NNno }
+\cs_generate_variant:Nn \cs_replacement_spec:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3file.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3file.dtx
new file mode 100644
index 00000000000..6111d270cab
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3file.dtx
@@ -0,0 +1,4026 @@
+% \iffalse meta-comment
+%
+%% File: l3file.dtx
+%
+% Copyright (C) 1990-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+\DocInput{\jobname.dtx}
+\PrintIndex
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3file} module\\ File and I/O operations^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% This module provides functions for working with external files. Some of these
+% functions apply to an entire file, and have prefix \cs[no-index]{file_\ldots}, while
+% others are used to work with files on a line by line basis and have prefix
+% \cs[no-index]{ior_\ldots} (reading) or \cs[no-index]{iow_\ldots} (writing).
+%
+% It is important to remember that when reading external files \TeX{}
+% attempts to locate them using both the operating system path and entries in the
+% \TeX{} file database (most \TeX{} systems use such a database). Thus the
+% \enquote{current path} for \TeX{} is somewhat broader than that for other
+% programs.
+%
+% For functions which expect a \meta{file name} argument, this argument
+% may contain both literal items and expandable content, which should on
+% full expansion be the desired file name. Active characters (as
+% declared in \cs{l_char_active_seq}) are \emph{not} expanded,
+% allowing the direct use of these in file names. Quote tokens (|"|) are
+% not permitted in file names as they are reserved for internal use by some
+% \TeX{} primitives.
+%
+% Spaces are trimmed at the beginning and end of the file name:
+% this reflects the fact that some file systems do not allow or interact
+% unpredictably with spaces in these positions. When no extension is given,
+% this will trim spaces from the start of the name only.
+%
+% \section{Input--output stream management}
+%
+% As \TeX{} engines have a limited number of input and output streams, direct
+% use of the streams by the programmer is not supported in \LaTeX3. Instead, an
+% internal pool of streams is maintained, and these are allocated and
+% deallocated as needed by other modules. As a result, the programmer should
+% close streams when they are no longer needed, to release them for other
+% processes.
+%
+% Note that I/O operations are global: streams should all be declared
+% with global names and treated accordingly.
+%
+% \begin{function}[added = 2011-09-26, updated = 2011-12-27]
+% {\ior_new:N, \ior_new:c, \iow_new:N, \iow_new:c}
+% \begin{syntax}
+% \cs{ior_new:N} \meta{stream}
+% \cs{iow_new:N} \meta{stream}
+% \end{syntax}
+% Globally reserves the name of the \meta{stream}, either for reading
+% or for writing as appropriate. The \meta{stream} is not opened until
+% the appropriate \cs[no-index]{\ldots_open:Nn} function is used. Attempting to
+% use a \meta{stream} which has not been opened is an error, and the
+% \meta{stream} will behave as the corresponding \cs[no-index]{c_term_\ldots}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-02-10]{\ior_open:Nn, \ior_open:cn}
+% \begin{syntax}
+% \cs{ior_open:Nn} \meta{stream} \Arg{file name}
+% \end{syntax}
+% Opens \meta{file name} for reading using \meta{stream} as the
+% control sequence for file access. If the \meta{stream} was already
+% open it is closed before the new operation begins. The
+% \meta{stream} is available for access immediately and will remain
+% allocated to \meta{file name} until an \cs{ior_close:N} instruction
+% is given or the \TeX{} run ends.
+% If the file is not found, an error is raised.
+% \end{function}
+%
+% \begin{function}[added = 2013-01-12, TF]{\ior_open:Nn, \ior_open:cn}
+% \begin{syntax}
+% \cs{ior_open:NnTF} \meta{stream} \Arg{file name} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Opens \meta{file name} for reading using \meta{stream} as the
+% control sequence for file access. If the \meta{stream} was already
+% open it is closed before the new operation begins. The
+% \meta{stream} is available for access immediately and will remain
+% allocated to \meta{file name} until a \cs{ior_close:N} instruction
+% is given or the \TeX{} run ends. The \meta{true code} is then inserted
+% into the input stream. If the file is not found, no error is raised and
+% the \meta{false code} is inserted into the input stream.
+% \end{function}
+%
+% \begin{function}[updated = 2012-02-09]
+% {\iow_open:Nn, \iow_open:NV, \iow_open:cn, \iow_open:cV}
+% \begin{syntax}
+% \cs{iow_open:Nn} \meta{stream} \Arg{file name}
+% \end{syntax}
+% Opens \meta{file name} for writing using \meta{stream} as the
+% control sequence for file access. If the \meta{stream} was already
+% open it is closed before the new operation begins. The
+% \meta{stream} is available for access immediately and will remain
+% allocated to \meta{file name} until a \cs{iow_close:N} instruction
+% is given or the \TeX{} run ends. Opening a file for writing clears
+% any existing content in the file (\emph{i.e.}~writing is \emph{not}
+% additive).
+% \end{function}
+%
+% \begin{function}[added = 2019-05-08]{\ior_shell_open:Nn}
+% \begin{syntax}
+% \cs{ior_shell_open:Nn} \meta{stream} \Arg{shell~command}
+% \end{syntax}
+% Opens the \emph{pseudo}-file created by the output of the
+% \meta{shell command} for reading using \meta{stream} as the
+% control sequence for access. If the \meta{stream} was already
+% open it is closed before the new operation begins. The
+% \meta{stream} is available for access immediately and will remain
+% allocated to \meta{shell command} until a \cs{ior_close:N} instruction
+% is given or the \TeX{} run ends.
+% If piped system calls are disabled an error is raised.
+%
+% For details of handling of the \meta{shell command}, see
+% \cs{sys_get_shell:nnNTF}.
+% \end{function}
+%
+% \begin{function}[added = 2023-05-25]{\iow_shell_open:Nn}
+% \begin{syntax}
+% \cs{iow_shell_open:Nn} \meta{stream} \Arg{shell~command}
+% \end{syntax}
+% Opens the \emph{pseudo}-file created by the output of the
+% \meta{shell command} for writing using \meta{stream} as the
+% control sequence for access. If the \meta{stream} was already
+% open it is closed before the new operation begins. The
+% \meta{stream} is available for access immediately and will remain
+% allocated to \meta{shell command} until an \cs{iow_close:N} instruction
+% is given or the \TeX{} run ends.
+% If piped system calls are disabled an error is raised.
+%
+% For details of handling of the \meta{shell command}, see
+% \cs{sys_get_shell:nnNTF}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-07-31]
+% {\ior_close:N, \ior_close:c, \iow_close:N, \iow_close:c}
+% \begin{syntax}
+% \cs{ior_close:N} \meta{stream}
+% \cs{iow_close:N} \meta{stream}
+% \end{syntax}
+% Closes the \meta{stream}. Streams should always be closed when
+% they are finished with as this ensures that they remain available
+% to other programmers.
+% \end{function}
+%
+% \begin{function}[added = 2021-05-11]
+% {
+% \ior_show:N, \ior_show:c, \ior_log:N, \ior_log:c,
+% \iow_show:N, \iow_show:c, \iow_log:N, \iow_log:c
+% }
+% \begin{syntax}
+% \cs{ior_show:N} \meta{stream}
+% \cs{ior_log:N} \meta{stream}
+% \cs{iow_show:N} \meta{stream}
+% \cs{iow_log:N} \meta{stream}
+% \end{syntax}
+% Display (to the terminal or log file) the file name associated to
+% the (read or write) \meta{stream}.
+% \end{function}
+%
+% \begin{function}[added = 2017-06-27]
+% {
+% \ior_show_list:, \ior_log_list:,
+% \iow_show_list:, \iow_log_list:
+% }
+% \begin{syntax}
+% \cs{ior_show_list:}
+% \cs{ior_log_list:}
+% \cs{iow_show_list:}
+% \cs{iow_log_list:}
+% \end{syntax}
+% Display (to the terminal or log file) a list of the file names
+% associated with each open (read or write) stream. This is intended
+% for tracking down problems.
+% \end{function}
+%
+% \subsection{Reading from files}
+%
+% Reading from files and reading from the terminal are separate processes in
+% \pkg{expl3}. The functions \cs{ior_get:NN} and \cs{ior_str_get:NN}, and their
+% branching equivalents, are designed to work with \emph{files}.
+%
+% \begin{function}[noTF, added = 2012-06-24, updated = 2019-03-23]{\ior_get:NN}
+% \begin{syntax}
+% \cs{ior_get:NN} \meta{stream} \meta{token list variable}
+% \cs{ior_get:NNTF} \meta{stream} \meta{token list variable} \meta{true code} \meta{false code}
+% \end{syntax}
+% Function that reads one or more lines (until an equal number of left
+% and right braces are found) from the file input \meta{stream} and stores
+% the result locally in the \meta{token list} variable.
+% The material read from the \meta{stream} is tokenized by \TeX{}
+% according to the category codes and \tn{endlinechar} in force when
+% the function is used. Assuming normal settings, any lines which do
+% not end in a comment character~|%| have the line ending
+% converted to a space, so for example input
+% \begin{verbatim}
+% a b c
+% \end{verbatim}
+% results in a token list \verb*|a b c |. Any blank line is
+% converted to the token \cs{par}. Therefore, blank lines can be
+% skipped by using a test such as
+% \begin{verbatim}
+% \ior_get:NN \l_my_stream \l_tmpa_tl
+% \tl_set:Nn \l_tmpb_tl { \par }
+% \tl_if_eq:NNF \l_tmpa_tl \l_tmpb_tl
+% ...
+% \end{verbatim}
+% Also notice that if multiple lines are read to match braces
+% then the resulting token list can contain \cs{par} tokens.
+% In the non-branching version, where the \meta{stream} is not open
+% the \meta{tl var} is set to \cs{q_no_value}.
+% \begin{texnote}
+% This protected macro is a wrapper around the \TeX{} primitive
+% \tn{read}. Regardless of settings, \TeX{} replaces trailing space
+% and tab characters (character codes 32 and~9) in each line by an
+% end-of-line character (character code \tn{endlinechar}, omitted if
+% \tn{endlinechar} is negative or too large) before turning
+% characters into tokens according to current category codes. With
+% default settings, spaces appearing at the beginning of lines are
+% also ignored.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[noTF, added = 2016-12-04, updated = 2019-03-23]
+% {\ior_str_get:NN}
+% \begin{syntax}
+% \cs{ior_str_get:NN} \meta{stream} \meta{token list variable}
+% \cs{ior_str_get:NNTF} \meta{stream} \meta{token list variable} \meta{true code} \meta{false code}
+% \end{syntax}
+% Function that reads one line from the file input \meta{stream} and stores
+% the result locally in the \meta{token list} variable.
+% The material is read from the \meta{stream} as a series of tokens with
+% category code $12$ (other), with the exception of space
+% characters which are given category code $10$ (space).
+% Multiple whitespace characters are retained by this process. It
+% always only reads one line and any blank lines in the input
+% result in the \meta{token list variable} being empty. Unlike
+% \cs{ior_get:NN}, line ends do not receive any special treatment. Thus
+% input
+% \begin{verbatim}
+% a b c
+% \end{verbatim}
+% results in a token list |a b c| with the letters |a|, |b|, and |c|
+% having category code~12.
+% In the non-branching version, where the\meta{stream} is not open
+% the \meta{tl var} is set to \cs{q_no_value}.
+% \begin{texnote}
+% This protected macro is a wrapper around the \eTeX{} primitive
+% \tn{readline}. Regardless of settings, \TeX{} removes trailing
+% space and tab characters (character codes 32 and~9). However, the
+% end-line character normally added by this primitive is not
+% included in the result of \cs{ior_str_get:NN}.
+% \end{texnote}
+% \end{function}
+%
+% All mappings are done at the current group level, \emph{i.e.}~any
+% local assignments made by the \meta{function} or \meta{code} discussed
+% below remain in effect after the loop.
+%
+% \begin{function}[added = 2012-02-11]{\ior_map_inline:Nn}
+% \begin{syntax}
+% \cs{ior_map_inline:Nn} \meta{stream} \Arg{inline function}
+% \end{syntax}
+% Applies the \meta{inline function} to each set of \meta{lines}
+% obtained by calling \cs{ior_get:NN} until reaching the end of the
+% file. \TeX{} ignores any trailing new-line marker from the file it
+% reads. The \meta{inline function} should consist of code which
+% receives the \meta{line} as |#1|.
+% \end{function}
+%
+% \begin{function}[added = 2012-02-11]{\ior_str_map_inline:Nn}
+% \begin{syntax}
+% \cs{ior_str_map_inline:Nn} \meta{stream} \Arg{inline function}
+% \end{syntax}
+% Applies the \meta{inline function} to every \meta{line}
+% in the \meta{stream}. The material is read from the \meta{stream}
+% as a series of tokens with category code $12$ (other), with the
+% exception of space characters which are given category code $10$
+% (space). The \meta{inline function} should consist of code which
+% receives the \meta{line} as |#1|.
+% Note that \TeX{} removes trailing space and tab characters
+% (character codes 32 and 9) from every line upon input. \TeX{} also
+% ignores any trailing new-line marker from the file it reads.
+% \end{function}
+%
+% \begin{function}[added = 2019-01-13]{\ior_map_variable:NNn}
+% \begin{syntax}
+% \cs{ior_map_variable:NNn} \meta{stream} \meta{tl~var} \Arg{code}
+% \end{syntax}
+% For each set of \meta{lines} obtained by calling \cs{ior_get:NN}
+% until reaching the end of the file, stores the \meta{lines} in the
+% \meta{tl~var} then applies the \meta{code}. The \meta{code} will
+% usually make use of the \meta{variable}, but this is not enforced.
+% The assignments to the \meta{variable} are local.
+% Its value after the loop is the last set of \meta{lines}, or its
+% original value if the \meta{stream} is empty. \TeX{} ignores
+% any trailing new-line marker from the file it reads.
+% This function is typically faster than \cs{ior_map_inline:Nn}.
+% \end{function}
+%
+% \begin{function}[added = 2019-01-13]{\ior_str_map_variable:NNn}
+% \begin{syntax}
+% \cs{ior_str_map_variable:NNn} \meta{stream} \meta{variable} \Arg{code}
+% \end{syntax}
+% For each \meta{line} in the \meta{stream}, stores the \meta{line} in
+% the \meta{variable} then applies the \meta{code}. The material is
+% read from the \meta{stream} as a series of tokens with category code
+% $12$ (other), with the exception of space characters which are given
+% category code $10$ (space). The \meta{code} will usually make use
+% of the \meta{variable}, but this is not enforced. The assignments
+% to the \meta{variable} are local. Its value after the loop is the
+% last \meta{line}, or its original value if the \meta{stream} is
+% empty. Note that \TeX{} removes trailing
+% space and tab characters (character codes 32 and 9) from every line
+% upon input. \TeX{} also ignores any trailing new-line marker from
+% the file it reads.
+% This function is typically faster than \cs{ior_str_map_inline:Nn}.
+% \end{function}
+%
+% \begin{function}[added = 2012-06-29]{\ior_map_break:}
+% \begin{syntax}
+% \cs{ior_map_break:}
+% \end{syntax}
+% Used to terminate a \cs[no-index]{ior_map_\ldots} function before all
+% lines from the \meta{stream} have been processed. This
+% normally takes place within a conditional statement, for example
+% \begin{verbatim}
+% \ior_map_inline:Nn \l_my_ior
+% {
+% \str_if_eq:nnTF { #1 } { bingo }
+% { \ior_map_break: }
+% {
+% % Do something useful
+% }
+% }
+% \end{verbatim}
+% Use outside of a \cs[no-index]{ior_map_\ldots} scenario leads to low
+% level \TeX{} errors.
+% \begin{texnote}
+% When the mapping is broken, additional tokens may be inserted
+% before further items are taken
+% from the input stream. This depends on the design of the mapping
+% function.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2012-06-29]{\ior_map_break:n}
+% \begin{syntax}
+% \cs{ior_map_break:n} \Arg{code}
+% \end{syntax}
+% Used to terminate a \cs[no-index]{ior_map_\ldots} function before all
+% lines in the \meta{stream} have been processed, inserting
+% the \meta{code} after the mapping has ended. This
+% normally takes place within a conditional statement, for example
+% \begin{verbatim}
+% \ior_map_inline:Nn \l_my_ior
+% {
+% \str_if_eq:nnTF { #1 } { bingo }
+% { \ior_map_break:n { <code> } }
+% {
+% % Do something useful
+% }
+% }
+% \end{verbatim}
+% Use outside of a \cs[no-index]{ior_map_\ldots} scenario leads to low
+% level \TeX{} errors.
+% \begin{texnote}
+% When the mapping is broken, additional tokens may be inserted
+% before the \meta{code} is
+% inserted into the input stream.
+% This depends on the design of the mapping function.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[updated = 2012-02-10, EXP, pTF]{\ior_if_eof:N}
+% \begin{syntax}
+% \cs{ior_if_eof_p:N} \meta{stream} \\
+% \cs{ior_if_eof:NTF} \meta{stream} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the end of a file \meta{stream} has been reached during a reading
+% operation. The test also returns a \texttt{true} value if
+% the \meta{stream} is not open.
+% \end{function}
+%
+% \subsection{Reading from the terminal}
+%
+% \begin{function}[added = 2019-03-23]{\ior_get_term:nN, \ior_str_get_term:nN}
+% \begin{syntax}
+% \cs{ior_get_term:nN} \meta{prompt} \meta{token list variable}
+% \end{syntax}
+% Function that reads one or more lines (until an equal number of left
+% and right braces are found) from the terminal and stores
+% the result locally in the \meta{token list} variable. Tokenization
+% occurs as described for \cs{ior_get:NN} or \cs{ior_str_get:NN}, respectively.
+% When the \meta{prompt}
+% is empty, \TeX{} will wait for input without any other indication:
+% typically the programmer will have provided a suitable text using
+% e.g.~\cs{iow_term:n}. Where the \meta{prompt} is given, it will appear
+% in the terminal followed by an |=|, e.g.
+% \begin{verbatim}
+% prompt=
+% \end{verbatim}
+% \end{function}
+%
+% \subsection{Writing to files}
+%
+% \begin{function}[updated = 2012-06-05]
+% {
+% \iow_now:Nn, \iow_now:NV, \iow_now:Ne,
+% \iow_now:cn, \iow_now:cV, \iow_now:ce
+% }
+% \begin{syntax}
+% \cs{iow_now:Nn} \meta{stream} \Arg{tokens}
+% \end{syntax}
+% This function writes \meta{tokens} to the specified
+% \meta{stream} immediately (\emph{i.e.}~the write operation is called
+% on expansion of \cs{iow_now:Nn}).
+% \end{function}
+%
+% \begin{function}{\iow_log:n, \iow_log:e}
+% \begin{syntax}
+% \cs{iow_log:n} \Arg{tokens}
+% \end{syntax}
+% This function writes the given \meta{tokens} to the log (transcript)
+% file immediately: it is a dedicated version of \cs{iow_now:Nn}.
+% \end{function}
+%
+% \begin{function}{\iow_term:n, \iow_term:e}
+% \begin{syntax}
+% \cs{iow_term:n} \Arg{tokens}
+% \end{syntax}
+% This function writes the given \meta{tokens} to the terminal
+% file immediately: it is a dedicated version of \cs{iow_now:Nn}.
+% \end{function}
+%
+% \begin{function}
+% {
+% \iow_shipout:Nn, \iow_shipout:Ne,
+% \iow_shipout:cn, \iow_shipout:ce
+% }
+% \begin{syntax}
+% \cs{iow_shipout:Nn} \meta{stream} \Arg{tokens}
+% \end{syntax}
+% This function writes \meta{tokens} to the specified
+% \meta{stream} when the current page is finalised (\emph{i.e.}~at
+% shipout). The \texttt{e}-type variants expand the \meta{tokens}
+% at the point where the function is used but \emph{not} when the
+% resulting tokens are written to the \meta{stream}
+% (\emph{cf.}~\cs{iow_shipout_e:Nn}).
+% \begin{texnote}
+% When using \pkg{expl3} with a format other than \LaTeX{}, new line
+% characters inserted using \cs{iow_newline:} or using the
+% line-wrapping code \cs{iow_wrap:nnnN} are not recognized in
+% the argument of \cs{iow_shipout:Nn}. This may lead to the
+% insertion of additional unwanted line-breaks.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[updated = 2023-09-17]
+% {
+% \iow_shipout_e:Nn, \iow_shipout_e:Ne,
+% \iow_shipout_e:cn, \iow_shipout_e:ce
+% }
+% \begin{syntax}
+% \cs{iow_shipout_e:Nn} \meta{stream} \Arg{tokens}
+% \end{syntax}
+% This function writes \meta{tokens} to the specified
+% \meta{stream} when the current page is finalised (\emph{i.e.}~at
+% shipout). The \meta{tokens} are expanded at the time of writing
+% in addition to any expansion when the function is used. This makes
+% these functions suitable for including material finalised during
+% the page building process (such as the page number integer).
+% \begin{texnote}
+% This is a wrapper around the \TeX{} primitive \tn{write}.
+% When using \pkg{expl3} with a format other than \LaTeX{}, new line
+% characters inserted using \cs{iow_newline:} or using the
+% line-wrapping code \cs{iow_wrap:nnnN} are not recognized in
+% the argument of \cs{iow_shipout:Nn}. This may lead to the
+% insertion of additional unwanted line-breaks.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\iow_char:N}
+% \begin{syntax}
+% \cs{iow_char:N} |\|\meta{char}
+% \end{syntax}
+% Inserts \meta{char} into the output stream. Useful when trying to
+% write difficult characters such as |%|, |{|, |}|,
+% \emph{etc.}~in messages, for example:
+% \begin{verbatim}
+% \iow_now:Ne \g_my_iow { \iow_char:N \{ text \iow_char:N \} }
+% \end{verbatim}
+% The function has no effect if writing is taking place without
+% expansion (\emph{e.g.}~in the second argument of \cs{iow_now:Nn}).
+% \end{function}
+%
+% \begin{function}[EXP]{\iow_newline:}
+% \begin{syntax}
+% \cs{iow_newline:}
+% \end{syntax}
+% Function to add a new line within the \meta{tokens} written to a
+% file. The function has no effect if writing is taking place without
+% expansion (\emph{e.g.}~in the second argument of \cs{iow_now:Nn}).
+% \begin{texnote}
+% When using \pkg{expl3} with a format other than \LaTeX{}, the
+% character inserted by \cs{iow_newline:} is not recognized by
+% \TeX{}, which may lead to the insertion of additional unwanted
+% line-breaks. This issue only affects \cs{iow_shipout:Nn},
+% \cs{iow_shipout_e:Nn} and direct uses of primitive operations.
+% \end{texnote}
+% \end{function}
+%
+% \subsection{Wrapping lines in output}
+%
+% \begin{function}[added = 2012-06-28, updated = 2017-12-04]
+% {\iow_wrap:nnnN, \iow_wrap:nenN}
+% \begin{syntax}
+% \cs{iow_wrap:nnnN} \Arg{text} \Arg{run-on text} \Arg{set up} \meta{function}
+% \end{syntax}
+% This function wraps the \meta{text} to a fixed number of
+% characters per line. At the start of each line which is wrapped,
+% the \meta{run-on text} is inserted. The line character count
+% targeted is the value of \cs{l_iow_line_count_int} minus the
+% number of characters in the \meta{run-on text} for all lines except
+% the first, for which the target number of characters is simply
+% \cs{l_iow_line_count_int} since there is no run-on text. The
+% \meta{text} and \meta{run-on text} are exhaustively expanded by the
+% function, with the following substitutions:
+% \begin{itemize}
+% \item |\\| or \cs{iow_newline:} may be used to force a new line,
+% \item \verb*|\ | may be used to represent a forced space
+% (for example after a control sequence),
+% \item |\#|, |\%|, |\{|, |\}|, |\~| may be used to represent
+% the corresponding character,
+% \item \cs{iow_wrap_allow_break:} may be used to allow a line-break
+% without inserting a space,
+% \item \cs{iow_indent:n} may be used to indent a part of the
+% \meta{text} (not the \meta{run-on text}).
+% \end{itemize}
+% Additional functions may be added to the wrapping by using the
+% \meta{set up}, which is executed before the wrapping takes place: this
+% may include overriding the substitutions listed.
+%
+% Any expandable material in the \meta{text} which is not to be expanded
+% on wrapping should be converted to a string using \cs{token_to_str:N},
+% \cs{tl_to_str:n}, \cs{tl_to_str:N}, \emph{etc.}
+%
+% The result of the wrapping operation is passed as a braced argument to the
+% \meta{function}, which is typically a wrapper around a write
+% operation. The output of \cs{iow_wrap:nnnN} (\emph{i.e.}~the argument
+% passed to the \meta{function}) consists of characters of category
+% \enquote{other} (category code~12), with the exception of spaces which
+% have category \enquote{space} (category code~10). This means that the
+% output does \emph{not} expand further when written to a file.
+%
+% \begin{texnote}
+% Internally, \cs{iow_wrap:nnnN} carries out an \texttt{e}-type expansion
+% on the \meta{text} to expand it. This is done in such a way that
+% \cs{exp_not:N} or \cs{exp_not:n} \emph{could} be used to prevent
+% expansion of material. However, this is less conceptually clear than
+% conversion to a string, which is therefore the supported method for
+% handling expandable material in the \meta{text}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2023-04-25]{\iow_wrap_allow_break:}
+% \begin{syntax}
+% \cs{iow_wrap_allow_break:}
+% \end{syntax}
+% In the first argument of \cs{iow_wrap:nnnN} (for instance in
+% messages), inserts a break-point that allows a line break. If
+% no break occurs, this function adds nothing to the output.
+% \end{function}
+%
+% \begin{function}[added = 2011-09-21]{\iow_indent:n}
+% \begin{syntax}
+% \cs{iow_indent:n} \Arg{text}
+% \end{syntax}
+% In the first argument of \cs{iow_wrap:nnnN} (for instance in messages),
+% indents \meta{text} by four spaces. This function does not cause
+% a line break, and only affects lines which start within the scope
+% of the \meta{text}. In case the indented \meta{text} should appear
+% on separate lines from the surrounding text, use |\\| to force
+% line breaks.
+% \end{function}
+%
+% \begin{variable}[added = 2012-06-24]{\l_iow_line_count_int}
+% The maximum number of characters in a line to be written
+% by the \cs{iow_wrap:nnnN}
+% function. This value depends on the \TeX{} system in use: the standard
+% value is $78$, which is typically correct for unmodified \TeX{} Live
+% and \hologo{MiKTeX} systems.
+% \end{variable}
+%
+% \subsection{Constant input--output streams, and variables}
+%
+% \begin{variable}[added = 2017-12-11]{\g_tmpa_ior, \g_tmpb_ior}
+% Scratch input stream for global use. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \begin{variable}{\c_log_iow, \c_term_iow}
+% Constant output streams for writing to the log and to the terminal
+% (plus the log), respectively.
+% \end{variable}
+%
+% \begin{variable}[added = 2017-12-11]{\g_tmpa_iow, \g_tmpb_iow}
+% Scratch output stream for global use. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \subsection{Primitive conditionals}
+%
+% \begin{function}[EXP]{\if_eof:w}
+% \begin{syntax}
+% \cs{if_eof:w} \meta{stream}
+% ~~\meta{true code}
+% \cs{else:}
+% ~~\meta{false code}
+% \cs{fi:}
+% \end{syntax}
+% Tests if the \meta{stream} returns \enquote{end of file}, which is true
+% for non-existent files. The \cs{else:} branch is optional.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{ifeof}.
+% \end{texnote}
+% \end{function}
+%
+% \section{File opertions}
+%
+% \subsection{Basic file operations}
+%
+% \begin{variable}[added = 2017-06-21]
+% {
+% \g_file_curr_dir_str,
+% \g_file_curr_name_str,
+% \g_file_curr_ext_str
+% }
+% Contain the directory, name and extension of the current file. The
+% directory is empty if the file was loaded without an explicit
+% path (\emph{i.e.}~if it is in the \TeX{} search path), and does
+% \emph{not} end in |/| other than the case that it is exactly equal
+% to the root directory. The \meta{name} and \meta{ext} parts together
+% make up the file name, thus the \meta{name} part may be thought of
+% as the \enquote{job name} for the current file.
+%
+% Note that \TeX{} does not provide information on the \meta{dir} and
+% \meta{ext} part for the main (top level) file and that this file
+% always has empty \meta{dir} and \meta{ext} components.
+% Also, the \meta{name} here will be equal to \cs{c_sys_jobname_str},
+% which may be different from the real file name (if set using
+% |--jobname|, for example).
+% \end{variable}
+%
+% \begin{variable}[added = 2017-06-18, updated = 2023-06-15]
+% {\l_file_search_path_seq}
+% Each entry is the path to a directory which should be searched when
+% seeking a file. Each path can be relative or absolute, and need
+% not include the trailing slash. Spaces need not be quoted.
+%
+% \begin{texnote}
+% When working as a package in \LaTeXe{}, \pkg{expl3} will
+% automatically append the current \tn{input@path} to the
+% set of values from \cs{l_file_search_path_seq}.
+% \end{texnote}
+% \end{variable}
+%
+% \begin{function}[EXP, pTF, updated = 2023-09-18]
+% {\file_if_exist:n, \file_if_exist:V}
+% \begin{syntax}
+% \cs{file_if_exist_p:n} \Arg{file name}
+% \cs{file_if_exist:nTF} \Arg{file name} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{file name} is found in the path as detailed for
+% \cs{file_if_exist:nTF}.
+% \end{function}
+%
+% \subsection{Information about files and file contents}
+%
+% Functions in this section return information about files as \pkg{expl3}
+% \texttt{str} data, \emph{except} that the non-expandable functions set their
+% return \emph{token list} to \cs{q_no_value} if the file requested is not
+% found. As such, comparison of file names, hashes, sizes, etc., should use
+% \cs{str_if_eq:nnTF} rather than \cs{tl_if_eq:nnTF} and so on.
+%
+% \begin{function}[rEXP, added = 2019-11-19]
+% {
+% \file_hex_dump:n, \file_hex_dump:V,
+% \file_hex_dump:nnn, \file_hex_dump:Vnn
+% }
+% \begin{syntax}
+% \cs{file_hex_dump:n} \Arg{file name}
+% \cs{file_hex_dump:nnn} \Arg{file name} \Arg{start index} \Arg{end index}
+% \end{syntax}
+% Searches for \meta{file name} using the current \TeX{} search
+% path and the additional paths controlled by \cs{l_file_search_path_seq}.
+% It then expands to leave the hexadecimal dump of the file content in the
+% input stream. The file is read as bytes, which means that in
+% contrast to most \TeX{} behaviour there will be a difference in result
+% depending on the line endings used in text files. The same file will
+% produce the same result between different engines: the algorithm used
+% is the same in all cases. When the file is not found, the result of
+% expansion is empty. The \Arg{start index} and \Arg{end index} values
+% work as described for \cs{str_range:nnn}.
+% \end{function}
+%
+% \begin{function}[noTF, added = 2019-11-19]
+% {
+% \file_get_hex_dump:nN, \file_get_hex_dump:VN,
+% \file_get_hex_dump:nnnN, \file_get_hex_dump:VnnN
+% }
+% \begin{syntax}
+% \cs{file_get_hex_dump:nN} \Arg{file name} \meta{tl var}
+% \cs{file_get_hex_dump:nnnN} \Arg{file name} \Arg{start index} \Arg{end index} \meta{tl var}
+% \end{syntax}
+% Sets the \meta{tl var} to the result of applying
+% \cs{file_hex_dump:n}/\cs{file_hex_dump:nnn} to the \meta{file}.
+% If the file is not found, the \meta{tl var} will be set to \cs{q_no_value}.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2019-09-03]
+% {\file_mdfive_hash:n, \file_mdfive_hash:V}
+% \begin{syntax}
+% \cs{file_mdfive_hash:n} \Arg{file name}
+% \end{syntax}
+% Searches for \meta{file name} using the current \TeX{} search
+% path and the additional paths controlled by \cs{l_file_search_path_seq}.
+% It then expands to leave the MD5 sum generated from the contents of
+% the file in the input stream. The file is read as bytes, which means that in
+% contrast to most \TeX{} behaviour there will be a difference in result
+% depending on the line endings used in text files. The same file will
+% produce the same result between different engines: the algorithm used
+% is the same in all cases. When the file is not found, the result of
+% expansion is empty.
+% \end{function}
+%
+% \begin{function}[noTF, added = 2017-07-11, updated = 2019-02-16]
+% {\file_get_mdfive_hash:nN, \file_get_mdfive_hash:VN}
+% \begin{syntax}
+% \cs{file_get_mdfive_hash:nN} \Arg{file name} \meta{tl var}
+% \end{syntax}
+% Sets the \meta{tl var} to the result of applying
+% \cs{file_mdfive_hash:n} to the \meta{file}. If the file is not found,
+% the \meta{tl var} will be set to \cs{q_no_value}.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2019-09-03]{\file_size:n, \file_size:V}
+% \begin{syntax}
+% \cs{file_size:n} \Arg{file name}
+% \end{syntax}
+% Searches for \meta{file name} using the current \TeX{} search
+% path and the additional paths controlled by \cs{l_file_search_path_seq}.
+% It then expands to leave the size of the file in bytes in the input stream.
+% When the file is not found, the result of expansion is empty.
+% \end{function}
+%
+% \begin{function}[noTF, added = 2017-07-09, updated = 2019-02-16]
+% {\file_get_size:nN, \file_get_size:VN}
+% \begin{syntax}
+% \cs{file_get_size:nN} \Arg{file name} \meta{tl var}
+% \end{syntax}
+% Sets the \meta{tl var} to the result of applying
+% \cs{file_size:n} to the \meta{file}. If the file is not found,
+% the \meta{tl var} will be set to \cs{q_no_value}.
+% This is not available in older versions of \XeTeX{}.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2019-09-03]
+% {\file_timestamp:n, \file_timestamp:V}
+% \begin{syntax}
+% \cs{file_timestamp:n} \Arg{file name}
+% \end{syntax}
+% Searches for \meta{file name} using the current \TeX{} search
+% path and the additional paths controlled by \cs{l_file_search_path_seq}.
+% It then expands to leave the modification timestamp of
+% the file in the input stream. The timestamp is of the form
+% |D:|\meta{year}\meta{month}\meta{day}\meta{hour}^^A
+% \meta{minute}\meta{second}\meta{offset}, where the latter may be |Z|
+% (UTC) or \meta{plus-minus}\meta{hours}|'|\meta{minutes}|'|.
+% When the file is not found, the result of expansion is empty.
+% This is not available in older versions of \XeTeX{}.
+% \end{function}
+%
+% \begin{function}[noTF, added = 2017-07-09, updated = 2019-02-16]
+% {\file_get_timestamp:nN, \file_get_timestamp:VN}
+% \begin{syntax}
+% \cs{file_get_timestamp:nN} \Arg{file name} \meta{tl var}
+% \end{syntax}
+% Sets the \meta{tl var} to the result of applying
+% \cs{file_timestamp:n} to the \meta{file}. If the file is not found,
+% the \meta{tl var} will be set to \cs{q_no_value}.
+% This is not available in older versions of \XeTeX{}.
+% \end{function}
+%
+% \begin{function}[added = 2019-05-13, updated = 2019-09-20, pTF, EXP]
+% {
+% \file_compare_timestamp:nNn,
+% \file_compare_timestamp:nNV,
+% \file_compare_timestamp:VNn,
+% \file_compare_timestamp:VNV
+% }
+% \begin{syntax}
+% \cs{file_compare_timestamp_p:nNn} \Arg{file-1} \meta{comparator} \Arg{file-2}
+% \cs{file_compare_timestamp:nNnTF} \Arg{file-1} \meta{comparator} \Arg{file-2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Compares the file stamps on the two \meta{files} as indicated by
+% the \meta{comparator}, and inserts either the \meta{true code}
+% or \meta{false case} as required. A file which is not found
+% is treated as older than any file which is found. This allows for
+% example the construct
+% \begin{verbatim}
+% \file_compare_timestamp:nNnT { source-file } > { derived-file }
+% {
+% % Code to regenerate derived file
+% }
+% \end{verbatim}
+% to work when the derived file is entirely absent. The timestamp
+% of two absent files is regarded as different.
+% This is not available in older versions of \XeTeX{}.
+% \end{function}
+%
+% \begin{function}[noTF, updated = 2019-02-16]
+% {\file_get_full_name:nN, \file_get_full_name:VN}
+% \begin{syntax}
+% \cs{file_get_full_name:nN} \Arg{file name} \meta{tl}
+% \cs{file_get_full_name:nNTF} \Arg{file name} \meta{tl} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Searches for \meta{file name} in the path as detailed for
+% \cs{file_if_exist:nTF}, and if found sets the \meta{tl var} the
+% fully-qualified name of the file, \emph{i.e.}~the path and file name.
+% This includes an extension |.tex| when the given \meta{file name}
+% has no extension but the file found has that extension.
+% In the non-branching version, the \meta{tl var} will be set to
+% \cs{q_no_value} in the case that the file does not exist.
+% \end{function}
+%
+% \begin{function}[added = 2019-09-03, rEXP]{\file_full_name:n, \file_full_name:V}
+% \begin{syntax}
+% \cs{file_full_name:n} \Arg{file name}
+% \end{syntax}
+% Searches for \meta{file name} in the path as detailed for
+% \cs{file_if_exist:nTF}, and if found leaves the
+% fully-qualified name of the file, \emph{i.e.}~the path and file name,
+% in the input stream.
+% This includes an extension |.tex| when the given \meta{file name}
+% has no extension but the file found has that extension.
+% If the file is not found on the path, the expansion is empty.
+% \end{function}
+%
+% \begin{function}[added = 2017-06-23, updated = 2020-06-24]
+% {\file_parse_full_name:nNNN, \file_parse_full_name:VNNN}
+% \begin{syntax}
+% \cs{file_parse_full_name:nNNN} \Arg{full name} \meta{dir} \meta{name} \meta{ext}
+% \end{syntax}
+% Parses the \meta{full name} and splits it into three parts, each of
+% which is returned by setting the appropriate local string variable:
+% \begin{itemize}
+% \item The \meta{dir}: everything up to the last |/| (path separator)
+% in the \meta{file path}. As with system \texttt{PATH} variables
+% and related functions, the \meta{dir} does \emph{not} include the
+% trailing |/| unless it points to the root directory. If there is no path (only
+% a file name), \meta{dir} is empty.
+% \item The \meta{name}: everything after the last |/| up to the last |.|,
+% where both of those characters are optional. The \meta{name} may
+% contain multiple |.| characters. It is empty if \meta{full name}
+% consists only of a directory name.
+% \item The \meta{ext}: everything after the last |.| (including the dot).
+% The \meta{ext} is empty if there is no |.| after the last |/|.
+% \end{itemize}
+%
+% Before parsing, the \meta{full name} is expanded until only non-expandable
+% tokens remain, except that active characters are also not expanded.
+% Quotes (|"|) are invalid in file names and are discarded from the input.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2020-06-24]
+% {\file_parse_full_name:n, \file_parse_full_name:V}
+% \begin{syntax}
+% \cs{file_parse_full_name:n} \Arg{full name}
+% \end{syntax}
+% Parses the \meta{full name} as described for \cs{file_parse_full_name:nNNN},
+% and leaves \meta{dir}, \meta{name}, and \meta{ext} in the input stream,
+% each inside a pair of braces.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2020-06-24]
+% {\file_parse_full_name_apply:nN, \file_parse_full_name_apply:VN}
+% \begin{syntax}
+% \cs{file_parse_full_name_apply:nN} \Arg{full name} \meta{function}
+% \end{syntax}
+% Parses the \meta{full name} as described for \cs{file_parse_full_name:nNNN},
+% and passes \meta{dir}, \meta{name}, and \meta{ext} as arguments to \meta{function},
+% as an \texttt{n}-type argument each, in this order.
+% \end{function}
+%
+% \subsection{Accessing file contents}
+%
+% \begin{function}[noTF, added = 2019-01-16, updated = 2019-02-16]
+% {\file_get:nnN, \file_get:VnN}
+% \begin{syntax}
+% \cs{file_get:nnN} \Arg{file name} \Arg{setup} \meta{tl}
+% \cs{file_get:nnNTF} \Arg{file name} \Arg{setup} \meta{tl} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Defines \meta{tl} to the contents of \meta{file name}.
+% Category codes may need to be set appropriately via the \meta{setup}
+% argument.
+% The non-branching version sets the \meta{tl} to \cs{q_no_value} if the file is
+% not found. The branching version runs the \meta{true code} after the
+% assignment to \meta{tl} if the file is found, and \meta{false code}
+% otherwise. The file content will be tokenized using the current
+% category code régime,
+% \end{function}
+%
+% \begin{function}[updated = 2017-06-26]{\file_input:n, \file_input:V}
+% \begin{syntax}
+% \cs{file_input:n} \Arg{file name}
+% \end{syntax}
+% Searches for \meta{file name} in the path as detailed for
+% \cs{file_if_exist:nTF}, and if found reads in the file as
+% additional \LaTeX{} source. All files read are recorded
+% for information and the file name stack is updated by this
+% function. An error is raised if the file is not found.
+% \end{function}
+%
+% \begin{function}[added = 2023-05-18, EXP]
+% {\file_input_raw:n, \file_input_raw:V}
+% \begin{syntax}
+% \cs{file_input_raw:n} \Arg{file name}
+% \end{syntax}
+% Searches for \meta{file name} in the path as detailed for
+% \cs{file_if_exist:nTF}, and if found reads in the file as
+% additional \TeX{} source. No data concerning the file is
+% tracked. If the file is not found, no action is taken.
+% \begin{texnote}
+% This function is intended only for contexts where files must
+% be read purely by expansion, for example at the start of a
+% table cell in an \tn{halign}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2014-07-02]
+% {
+% \file_if_exist_input:n,
+% \file_if_exist_input:V,
+% \file_if_exist_input:nF,
+% \file_if_exist_input:VF
+% }
+% \begin{syntax}
+% \cs{file_if_exist_input:n} \Arg{file name}
+% \cs{file_if_exist_input:nF} \Arg{file name} \Arg{false code}
+% \end{syntax}
+% Searches for \meta{file name} using the current \TeX{} search
+% path and the additional paths included in \cs{l_file_search_path_seq}.
+% If found then
+% reads in the file as additional \LaTeX{} source as described for
+% \cs{file_input:n}, otherwise inserts the \meta{false code}.
+% Note that these functions do not raise
+% an error if the file is not found, in contrast to \cs{file_input:n}.
+% \end{function}
+%
+% \begin{function}[added = 2017-07-07]{\file_input_stop:}
+% \begin{syntax}
+% \cs{file_input_stop:}
+% \end{syntax}
+% Ends the reading of a file started by \cs{file_input:n} or similar before
+% the end of the file is reached. Where the file reading is being terminated
+% due to an error, \cs[index = msg_critical:nn]{msg_critical:nn(nn)}
+% should be preferred.
+% \begin{texnote}
+% This function must be used on a line on its own: \TeX{} reads files
+% line-by-line and so any additional tokens in the \enquote{current} line
+% will still be read.
+%
+% This is also true if the function is hidden inside another function
+% (which will be the normal case), i.e., all tokens on the same line
+% in the source file are still processed. Putting it on a line by itself
+% in the definition doesn't help as it is the line where it is used that
+% counts!
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}{\file_show_list:, \file_log_list:}
+% \begin{syntax}
+% \cs{file_show_list:}
+% \cs{file_log_list:}
+% \end{syntax}
+% These functions list all files loaded by \LaTeXe{} commands that
+% populate \tn{@filelist} or by \cs{file_input:n}. While
+% \cs{file_show_list:} displays the list in the terminal,
+% \cs{file_log_list:} outputs it to the log file only.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3file} implementation}
+%
+% \TestFiles{m3file001}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \subsection{Input operations}
+%
+% \begin{macrocode}
+%<@@=ior>
+% \end{macrocode}
+%
+% \subsubsection{Variables and constants}
+%
+% \begin{variable}{\l_@@_internal_tl}
+% Used as a short-term scratch variable.
+% \begin{macrocode}
+\tl_new:N \l_@@_internal_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_term_ior}
+% Reading from the terminal (with a prompt) is done using a positive
+% but non-existent stream number. Unlike writing, there is no concept
+% of reading from the log.
+% \begin{macrocode}
+\int_const:Nn \c_@@_term_ior { 16 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_streams_seq}
+% A list of the currently-available input streams to be used as a
+% stack.
+% \begin{macrocode}
+\seq_new:N \g_@@_streams_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_stream_tl}
+% Used to recover the raw stream number from the stack.
+% \begin{macrocode}
+\tl_new:N \l_@@_stream_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_streams_prop}
+% The name of the file attached to each stream is tracked in a property
+% list. To get the correct number of reserved streams in package mode the
+% underlying mechanism needs to be queried. For \LaTeXe{} and plain \TeX{}
+% this data is stored in |\count16|: with the \pkg{etex} package loaded
+% we need to subtract $1$ as the register holds the number of the next
+% stream to use. In \ConTeXt{}, we need to look at |\count38| but there
+% is no subtraction: like the original plain \TeX{}/\LaTeXe{} mechanism
+% it holds the value of the \emph{last} stream allocated.
+% \begin{macrocode}
+\prop_new:N \g_@@_streams_prop
+\int_step_inline:nnn
+ { 0 }
+ {
+ \cs_if_exist:NTF \contextversion
+ { \tex_count:D 38 ~ }
+ {
+ \tex_count:D 16 ~ %
+ \cs_if_exist:NT \loccount { - 1 }
+ }
+ }
+ {
+ \prop_gput:Nnn \g_@@_streams_prop {#1} { Reserved~by~format }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Stream management}
+%
+% \begin{macro}{\ior_new:N, \ior_new:c}
+% Reserving a new stream is done by defining the name as equal to using the
+% terminal.
+% \begin{macrocode}
+\cs_new_protected:Npn \ior_new:N #1 { \cs_new_eq:NN #1 \c_@@_term_ior }
+\cs_generate_variant:Nn \ior_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\g_tmpa_ior, \g_tmpb_ior}
+% The usual scratch space.
+% \begin{macrocode}
+\ior_new:N \g_tmpa_ior
+\ior_new:N \g_tmpb_ior
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\ior_open:Nn, \ior_open:cn}
+% Use the conditional version, with an error if the file is not found.
+% \begin{macrocode}
+\cs_new_protected:Npn \ior_open:Nn #1#2
+ { \ior_open:NnF #1 {#2} { \__kernel_file_missing:n {#2} } }
+\cs_generate_variant:Nn \ior_open:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_file_name_tl}
+% Data storage.
+% \begin{macrocode}
+\tl_new:N \l_@@_file_name_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[TF]{\ior_open:Nn, \ior_open:cn}
+% An auxiliary searches for the file in the \TeX{}, \LaTeXe{} and
+% \LaTeX3 paths. Then pass the file found to the lower-level function
+% which deals with streams. The |full_name| is empty when the file is
+% not found.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \ior_open:Nn #1#2 { T , F , TF }
+ {
+ \file_get_full_name:nNTF {#2} \l_@@_file_name_tl
+ {
+ \__kernel_ior_open:No #1 \l_@@_file_name_tl
+ \prg_return_true:
+ }
+ { \prg_return_false: }
+ }
+\prg_generate_conditional_variant:Nnn \ior_open:Nn { c } { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_new:N}
+% Streams are reserved using \tn{newread} before they
+% can be managed by \pkg{ior}. To prevent \pkg{ior} from being
+% affected by redefinitions of \tn{newread} (such as done by the
+% third-party package \pkg{morewrites}), this macro is saved here
+% under a private name. The complicated code ensures that
+% \cs{@@_new:N} is not \tn{outer} despite plain \TeX{}'s \tn{newread}
+% being \tn{outer}. For \ConTeXt{}, we have to deal with the fact
+% that \tn{newread} works like our own: it actually checks before
+% altering definition.
+% \begin{macrocode}
+\exp_args:NNf \cs_new_protected:Npn \@@_new:N
+ { \exp_args:NNc \exp_after:wN \exp_stop_f: { newread } }
+\cs_if_exist:NT \contextversion
+ {
+ \cs_new_eq:NN \@@_new_aux:N \@@_new:N
+ \cs_gset_protected:Npn \@@_new:N #1
+ {
+ \cs_undefine:N #1
+ \@@_new_aux:N #1
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__kernel_ior_open:Nn, \__kernel_ior_open:No}
+% \begin{macro}{\@@_open_stream:Nn}
+% The stream allocation itself uses the fact that there is a list of all of
+% those available. Life gets more complex as it's
+% important to keep things in sync. That is done using a two-part approach:
+% any streams that have already been taken up by \pkg{ior} but are now free
+% are tracked, so we first try those. If that fails, ask plain \TeX{} or \LaTeXe{}
+% for a new stream and use that number (after a bit of conversion).
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_ior_open:Nn #1#2
+ {
+ \ior_close:N #1
+ \seq_gpop:NNTF \g_@@_streams_seq \l_@@_stream_tl
+ { \@@_open_stream:Nn #1 {#2} }
+ {
+ \@@_new:N #1
+ \__kernel_tl_set:Nx \l_@@_stream_tl { \int_eval:n {#1} }
+ \@@_open_stream:Nn #1 {#2}
+ }
+ }
+\cs_generate_variant:Nn \__kernel_ior_open:Nn { No }
+% \end{macrocode}
+% Here, we act defensively in case \LuaTeX{} is in use with an
+% extensionless file name.
+% \begin{macrocode}
+\cs_new_protected:Npe \@@_open_stream:Nn #1#2
+ {
+ \tex_global:D \tex_chardef:D #1 = \exp_not:N \l_@@_stream_tl \scan_stop:
+ \prop_gput:NVn \exp_not:N \g_@@_streams_prop #1 {#2}
+ \tex_openin:D #1
+ \sys_if_engine_luatex:TF
+ { {#2} }
+ { \exp_not:N \__kernel_file_name_quote:n {#2} \scan_stop: }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\ior_shell_open:Nn}
+% \begin{macro}{\@@_shell_open:nN, \@@_shell_open:oN}
+% Actually much easier than either the standard open or input versions!
+% When calling \cs{__kernel_ior_open:Nn} the file the pipe is added to
+% signal a shell command, but the quotes are not added yet---they are
+% added later by \cs{__kernel_file_name_quote:n}.
+% \begin{macrocode}
+\cs_new_protected:Npn \ior_shell_open:Nn #1#2
+ {
+ \sys_if_shell:TF
+ { \@@_shell_open:oN { \tl_to_str:n {#2} } #1 }
+ { \msg_error:nn { kernel } { pipe-failed } }
+ }
+\cs_new_protected:Npn \@@_shell_open:nN #1#2
+ {
+ \tl_if_in:nnTF {#1} { " }
+ {
+ \msg_error:nne
+ { kernel } { quote-in-shell } {#1}
+ }
+ { \__kernel_ior_open:Nn #2 { |#1 } }
+ }
+\cs_generate_variant:Nn \@@_shell_open:nN { o }
+\msg_new:nnnn { kernel } { pipe-failed }
+ { Cannot~run~piped~system~commands. }
+ {
+ LaTeX~tried~to~call~a~system~process~but~this~was~not~possible.\\
+ Try~the~"--shell-escape"~(or~"--enable-pipes")~option.
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\ior_close:N, \ior_close:c}
+% Closing a stream means getting rid of it at the \TeX{} level and
+% removing from the various data structures. Unless the name passed
+% is an invalid stream number (outside the range $[0,15]$), it can be
+% closed. On the other hand, it only gets added to the stack if it
+% was not already there, to avoid duplicates building up.
+% \begin{macrocode}
+\cs_new_protected:Npn \ior_close:N #1
+ {
+ \int_compare:nT { -1 < #1 < \c_@@_term_ior }
+ {
+ \tex_closein:D #1
+ \prop_gremove:NV \g_@@_streams_prop #1
+ \seq_if_in:NVF \g_@@_streams_seq #1
+ { \seq_gpush:NV \g_@@_streams_seq #1 }
+ \cs_gset_eq:NN #1 \c_@@_term_ior
+ }
+ }
+\cs_generate_variant:Nn \ior_close:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ior_show:N, \ior_log:N, \@@_show:NN}
+% Seek the stream in the \cs{g_@@_streams_prop} list, then show the
+% stream as open or closed accordingly.
+% \begin{macrocode}
+\cs_new_protected:Npn \ior_show:N { \@@_show:NN \tl_show:n }
+\cs_generate_variant:Nn \ior_show:N { c }
+\cs_new_protected:Npn \ior_log:N { \@@_show:NN \tl_log:n }
+\cs_generate_variant:Nn \ior_log:N { c }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ \prop_get:NVNTF \g_@@_streams_prop #2 \l_@@_internal_tl
+ {
+ \exp_args:Ne #1
+ { \token_to_str:N #2 ~ open: ~ \l_@@_internal_tl }
+ }
+ { \exp_args:Ne #1 { \token_to_str:N #2 ~ closed } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ior_show_list:, \ior_log_list:}
+% \begin{macro}{\@@_list:N}
+% Show the property lists, but with some \enquote{pretty printing}.
+% See the \pkg{l3msg} module. The first argument of the message is
+% |ior| (as opposed to |iow|) and the second is empty if no read
+% stream is open and non-empty (the list of streams formatted using
+% \cs{msg_show_item_unbraced:nn}) otherwise. The code of the message
+% \texttt{show-streams} takes care of translating |ior|/|iow| to
+% English.
+% \begin{macrocode}
+\cs_new_protected:Npn \ior_show_list: { \@@_list:N \msg_show:nneeee }
+\cs_new_protected:Npn \ior_log_list: { \@@_list:N \msg_log:nneeee }
+\cs_new_protected:Npn \@@_list:N #1
+ {
+ #1 { kernel } { show-streams }
+ { ior }
+ {
+ \prop_map_function:NN \g_@@_streams_prop
+ \msg_show_item_unbraced:nn
+ }
+ { } { }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Reading input}
+%
+% \begin{macro}{\if_eof:w}
+% The primitive conditional
+% \begin{macrocode}
+\cs_new_eq:NN \if_eof:w \tex_ifeof:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\ior_if_eof:N}
+% To test if some particular input stream is exhausted the following
+% conditional is provided. The primitive test can only deal with
+% numbers in the range $[0,15]$ so we catch outliers (they are
+% exhausted).
+% \begin{macrocode}
+\prg_new_conditional:Npnn \ior_if_eof:N #1 { p , T , F , TF }
+ {
+ \if_int_compare:w -1 < #1
+ \if_int_compare:w #1 < \c_@@_term_ior
+ \if_eof:w #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \else:
+ \prg_return_true:
+ \fi:
+ \else:
+ \prg_return_true:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ior_get:NN, \@@_get:NN}
+% \begin{macro}[TF]{\ior_get:NN}
+% And here we read from files.
+% \begin{macrocode}
+\cs_new_protected:Npn \ior_get:NN #1#2
+ { \ior_get:NNF #1 #2 { \tl_set:Nn #2 { \q_no_value } } }
+\cs_new_protected:Npn \@@_get:NN #1#2
+ { \tex_read:D #1 to #2 }
+\prg_new_protected_conditional:Npnn \ior_get:NN #1#2 { T , F , TF }
+ {
+ \ior_if_eof:NTF #1
+ { \prg_return_false: }
+ {
+ \@@_get:NN #1 #2
+ \prg_return_true:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\ior_str_get:NN, \@@_str_get:NN}
+% \begin{macro}[TF]{\ior_str_get:NN}
+% Reading as strings is a more complicated wrapper, as we wish to
+% remove the endline character and restore it afterwards.
+% \begin{macrocode}
+\cs_new_protected:Npn \ior_str_get:NN #1#2
+ { \ior_str_get:NNF #1 #2 { \tl_set:Nn #2 { \q_no_value } } }
+\cs_new_protected:Npn \@@_str_get:NN #1#2
+ {
+ \exp_args:Nno \use:n
+ {
+ \int_set:Nn \tex_endlinechar:D { -1 }
+ \tex_readline:D #1 to #2
+ \int_set:Nn \tex_endlinechar:D
+ } { \int_use:N \tex_endlinechar:D }
+ }
+\prg_new_protected_conditional:Npnn \ior_str_get:NN #1#2 { T , F , TF }
+ {
+ \ior_if_eof:NTF #1
+ { \prg_return_false: }
+ {
+ \@@_str_get:NN #1 #2
+ \prg_return_true:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\c_@@_term_noprompt_ior}
+% For reading without a prompt.
+% \begin{macrocode}
+\int_const:Nn \c_@@_term_noprompt_ior { -1 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\ior_get_term:nN, \ior_str_get_term:nN}
+% \begin{macro}{\@@_get_term:NnN}
+% Getting from the terminal is better with pretty-printing.
+% \begin{macrocode}
+\cs_new_protected:Npn \ior_get_term:nN #1#2
+ { \@@_get_term:NnN \@@_get:NN {#1} #2 }
+\cs_new_protected:Npn \ior_str_get_term:nN #1#2
+ { \@@_get_term:NnN \@@_str_get:NN {#1} #2 }
+\cs_new_protected:Npn \@@_get_term:NnN #1#2#3
+ {
+ \group_begin:
+ \tex_escapechar:D = -1 \scan_stop:
+ \tl_if_blank:nTF {#2}
+ { \exp_args:NNc #1 \c_@@_term_noprompt_ior }
+ { \exp_args:NNc #1 \c_@@_term_ior }
+ {#2}
+ \exp_args:NNNv \group_end:
+ \tl_set:Nn #3 {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\ior_map_break:, \ior_map_break:n}
+% Usual map breaking functions.
+% \begin{macrocode}
+\cs_new:Npn \ior_map_break:
+ { \prg_map_break:Nn \ior_map_break: { } }
+\cs_new:Npn \ior_map_break:n
+ { \prg_map_break:Nn \ior_map_break: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ior_map_inline:Nn, \ior_str_map_inline:Nn}
+% \begin{macro}{\@@_map_inline:NNn}
+% \begin{macro}{\@@_map_inline:NNNn}
+% \begin{macro}{\@@_map_inline_loop:NNN}
+% Mapping over an input stream can be done on either a token or a string
+% basis, hence the set up. Within that, there is a check to avoid reading
+% past the end of a file, hence the two applications of \cs{ior_if_eof:N}
+% and its lower-level analogue \cs{if_eof:w}.
+% This mapping cannot be nested with twice the same stream, as the
+% stream has only one \enquote{current line}.
+% \begin{macrocode}
+\cs_new_protected:Npn \ior_map_inline:Nn
+ { \@@_map_inline:NNn \@@_get:NN }
+\cs_new_protected:Npn \ior_str_map_inline:Nn
+ { \@@_map_inline:NNn \@@_str_get:NN }
+\cs_new_protected:Npn \@@_map_inline:NNn
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:Nc \@@_map_inline:NNNn
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :n }
+ }
+\cs_new_protected:Npn \@@_map_inline:NNNn #1#2#3#4
+ {
+ \cs_gset_protected:Npn #1 ##1 {#4}
+ \ior_if_eof:NF #3 { \@@_map_inline_loop:NNN #1#2#3 }
+ \prg_break_point:Nn \ior_map_break:
+ { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+\cs_new_protected:Npn \@@_map_inline_loop:NNN #1#2#3
+ {
+ #2 #3 \l_@@_internal_tl
+ \if_eof:w #3
+ \exp_after:wN \ior_map_break:
+ \fi:
+ \exp_args:No #1 \l_@@_internal_tl
+ \@@_map_inline_loop:NNN #1#2#3
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\ior_map_variable:NNn, \ior_str_map_variable:NNn}
+% \begin{macro}{\@@_map_variable:NNNn}
+% \begin{macro}{\@@_map_variable_loop:NNNn}
+% Since the \TeX{} primitive (\tn{read} or \tn{readline}) assigns the
+% tokens read in the same way as a token list assignment, we simply
+% call the appropriate primitive. The end-of-loop is checked using
+% the primitive conditional for speed.
+% \begin{macrocode}
+\cs_new_protected:Npn \ior_map_variable:NNn
+ { \@@_map_variable:NNNn \ior_get:NN }
+\cs_new_protected:Npn \ior_str_map_variable:NNn
+ { \@@_map_variable:NNNn \ior_str_get:NN }
+\cs_new_protected:Npn \@@_map_variable:NNNn #1#2#3#4
+ {
+ \ior_if_eof:NF #2 { \@@_map_variable_loop:NNNn #1#2#3 {#4} }
+ \prg_break_point:Nn \ior_map_break: { }
+ }
+\cs_new_protected:Npn \@@_map_variable_loop:NNNn #1#2#3#4
+ {
+ #1 #2 #3
+ \if_eof:w #2
+ \exp_after:wN \ior_map_break:
+ \fi:
+ #4
+ \@@_map_variable_loop:NNNn #1#2#3 {#4}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Output operations}
+%
+% \begin{macrocode}
+%<@@=iow>
+% \end{macrocode}
+%
+% There is a lot of similarity here to the input operations, at least for
+% many of the basics. Thus quite a bit is copied from the earlier material
+% with minor alterations.
+%
+% \subsubsection{Variables and constants}
+%
+% \begin{variable}{\l_@@_internal_tl}
+% Used as a short-term scratch variable.
+% \begin{macrocode}
+\tl_new:N \l_@@_internal_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_log_iow, \c_term_iow}
+% Here we allocate two output streams for writing to the transcript
+% file only (\cs{c_log_iow}) and to both the terminal and transcript
+% file (\cs{c_term_iow}). Recent \LuaTeX{} provide $128$ write
+% streams; we also use \cs{c_term_iow} as the first non-allowed write
+% stream so its value depends on the engine.
+% \begin{macrocode}
+\int_const:Nn \c_log_iow { -1 }
+\int_const:Nn \c_term_iow
+ {
+ \bool_lazy_and:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \int_compare_p:nNn \tex_luatexversion:D > { 80 } }
+ { 128 }
+ { 16 }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_streams_seq}
+% A list of the currently-available output streams to be used as a
+% stack.
+% \begin{macrocode}
+\seq_new:N \g_@@_streams_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_stream_tl}
+% Used to recover the raw stream number from the stack.
+% \begin{macrocode}
+\tl_new:N \l_@@_stream_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_streams_prop}
+% As for reads with the appropriate adjustment of the register numbers to
+% check on.
+% \begin{macrocode}
+\prop_new:N \g_@@_streams_prop
+\int_step_inline:nnn
+ { 0 }
+ {
+ \cs_if_exist:NTF \contextversion
+ { \tex_count:D 39 ~ }
+ {
+ \tex_count:D 17 ~
+ \cs_if_exist:NT \loccount { - 1 }
+ }
+ }
+ {
+ \prop_gput:Nnn \g_@@_streams_prop {#1} { Reserved~by~format }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Internal auxiliaries}
+%
+% \begin{variable}{\s_@@_mark,\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_use_i_delimit_by_s_stop:nw}
+% Functions to gobble up to a scan mark.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_i_delimit_by_s_stop:nw #1 #2 \s_@@_stop {#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\q_@@_nil}
+% Internal quarks.
+% \begin{macrocode}
+\quark_new:N \q_@@_nil
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Stream management}
+%
+% \begin{macro}{\iow_new:N, \iow_new:c}
+% Reserving a new stream is done by defining the name as equal to writing
+% to the terminal: odd but at least consistent.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_new:N #1 { \cs_new_eq:NN #1 \c_term_iow }
+\cs_generate_variant:Nn \iow_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\g_tmpa_iow, \g_tmpb_iow}
+% The usual scratch space.
+% \begin{macrocode}
+\iow_new:N \g_tmpa_iow
+\iow_new:N \g_tmpb_iow
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_new:N}
+% As for read streams, copy \tn{newwrite}, making sure
+% that it is not \tn{outer}. For \ConTeXt{}, we have to
+% deal with the fact that \tn{newwrite} works like our
+% own: it actually checks before altering definition.
+% \begin{macrocode}
+\exp_args:NNf \cs_new_protected:Npn \@@_new:N
+ { \exp_args:NNc \exp_after:wN \exp_stop_f: { newwrite } }
+\cs_if_exist:NT \contextversion
+ {
+ \cs_new_eq:NN \@@_new_aux:N \@@_new:N
+ \cs_gset_protected:Npn \@@_new:N #1
+ {
+ \cs_undefine:N #1
+ \@@_new_aux:N #1
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_file_name_tl}
+% Data storage.
+% \begin{macrocode}
+\tl_new:N \l_@@_file_name_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\iow_open:Nn, \iow_open:NV, \iow_open:cn, \iow_open:cV}
+% \begin{macro}{\__kernel_iow_open:Nn, \__kernel_iow_open:No}
+% \begin{macro}{\@@_open_stream:Nn, \@@_open_stream:NV}
+% The same idea as for reading, but without the path and without the need
+% to allow for a conditional version.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_open:Nn #1#2
+ {
+ \__kernel_tl_set:Nx \l_@@_file_name_tl
+ { \__kernel_file_name_sanitize:n {#2} }
+ \__kernel_iow_open:No #1 \l_@@_file_name_tl
+ }
+\cs_generate_variant:Nn \iow_open:Nn { NV , c , cV }
+\cs_new_protected:Npn \__kernel_iow_open:Nn #1#2
+ {
+ \iow_close:N #1
+ \seq_gpop:NNTF \g_@@_streams_seq \l_@@_stream_tl
+ { \@@_open_stream:Nn #1 {#2} }
+ {
+ \@@_new:N #1
+ \__kernel_tl_set:Nx \l_@@_stream_tl { \int_eval:n {#1} }
+ \@@_open_stream:Nn #1 {#2}
+ }
+ }
+\cs_generate_variant:Nn \__kernel_iow_open:Nn { No }
+\cs_new_protected:Npn \@@_open_stream:Nn #1#2
+ {
+ \tex_global:D \tex_chardef:D #1 = \l_@@_stream_tl \scan_stop:
+ \prop_gput:NVn \g_@@_streams_prop #1 {#2}
+ \tex_immediate:D \tex_openout:D
+ #1 \__kernel_file_name_quote:n {#2} \scan_stop:
+ }
+\cs_generate_variant:Nn \@@_open_stream:Nn { NV }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\iow_shell_open:Nn}
+% \begin{macro}{\@@_shell_open:nN, \@@_shell_open:oN}
+% Very similar to the \texttt{ior} version
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_shell_open:Nn #1#2
+ {
+ \sys_if_shell:TF
+ { \@@_shell_open:oN { \tl_to_str:n {#2} } #1 }
+ { \msg_error:nn { kernel } { pipe-failed } }
+ }
+\cs_new_protected:Npn \@@_shell_open:nN #1#2
+ {
+ \tl_if_in:nnTF {#1} { " }
+ {
+ \msg_error:nne
+ { kernel } { quote-in-shell } {#1}
+ }
+ { \__kernel_iow_open:Nn #2 { |#1 } }
+ }
+\cs_generate_variant:Nn \@@_shell_open:nN { o }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\iow_close:N, \iow_close:c}
+% Closing a stream is not quite the reverse of opening one. First,
+% the close operation is easier than the open one, and second as the
+% stream is actually a number we can use it directly to show that the
+% slot has been freed up.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_close:N #1
+ {
+ \int_compare:nT { \c_log_iow < #1 < \c_term_iow }
+ {
+ \tex_immediate:D \tex_closeout:D #1
+ \prop_gremove:NV \g_@@_streams_prop #1
+ \seq_if_in:NVF \g_@@_streams_seq #1
+ { \seq_gpush:NV \g_@@_streams_seq #1 }
+ \cs_gset_eq:NN #1 \c_term_iow
+ }
+ }
+\cs_generate_variant:Nn \iow_close:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\iow_show:N, \iow_log:N, \@@_show:NN}
+% Seek the stream in the \cs{g_@@_streams_prop} list, then show the
+% stream as open or closed accordingly.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_show:N { \@@_show:NN \tl_show:n }
+\cs_generate_variant:Nn \iow_show:N { c }
+\cs_new_protected:Npn \iow_log:N { \@@_show:NN \tl_log:n }
+\cs_generate_variant:Nn \iow_log:N { c }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ \prop_get:NVNTF \g_@@_streams_prop #2 \l_@@_internal_tl
+ {
+ \exp_args:Ne #1
+ { \token_to_str:N #2 ~ open: ~ \l_@@_internal_tl }
+ }
+ { \exp_args:Ne #1 { \token_to_str:N #2 ~ closed } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\iow_show_list:, \iow_log_list:}
+% \begin{macro}{\@@_list:N}
+% Done as for input, but with a copy of the auxiliary so the name is correct.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_show_list: { \@@_list:N \msg_show:nneeee }
+\cs_new_protected:Npn \iow_log_list: { \@@_list:N \msg_log:nneeee }
+\cs_new_protected:Npn \@@_list:N #1
+ {
+ #1 { kernel } { show-streams }
+ { iow }
+ {
+ \prop_map_function:NN \g_@@_streams_prop
+ \msg_show_item_unbraced:nn
+ }
+ { } { }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Deferred writing}
+%
+% \begin{macro}
+% {
+% \iow_shipout_e:Nn, \iow_shipout_e:Ne,
+% \iow_shipout_e:cn, \iow_shipout_e:ce
+% }
+% First the easy part, this is the primitive, which expects its
+% argument to be braced.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_shipout_e:Nn #1#2
+ { \tex_write:D #1 {#2} }
+\cs_generate_variant:Nn \iow_shipout_e:Nn { Ne , c, ce }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \iow_shipout:Nn, \iow_shipout:Ne,
+% \iow_shipout:Nx,
+% \iow_shipout:cn, \iow_shipout:ce,
+% \iow_shipout:cx
+% }
+% With \eTeX{} available deferred writing without expansion is easy.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_shipout:Nn #1#2
+ { \tex_write:D #1 { \exp_not:n {#2} } }
+\cs_generate_variant:Nn \iow_shipout:Nn { Ne , c, ce }
+\cs_generate_variant:Nn \iow_shipout:Nn { Nx , cx }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Immediate writing}
+%
+% \begin{macro}{\__kernel_iow_with:Nnn}
+% \begin{macro}{\@@_with:nNnn, \@@_with:oNnn}
+% If the integer~|#1| is equal to~|#2|, just leave~|#3| in the input
+% stream. Otherwise, pass the old value to an auxiliary, which sets
+% the integer to the new value, runs the code, and restores the
+% integer.
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_iow_with:Nnn #1#2
+ {
+ \int_compare:nNnTF {#1} = {#2}
+ { \use:n }
+ { \@@_with:oNnn { \int_use:N #1 } #1 {#2} }
+ }
+\cs_new_protected:Npn \@@_with:nNnn #1#2#3#4
+ {
+ \int_set:Nn #2 {#3}
+ #4
+ \int_set:Nn #2 {#1}
+ }
+\cs_generate_variant:Nn \@@_with:nNnn { o }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \iow_now:Nn, \iow_now:NV, \iow_now:Ne,
+% \iow_now:Nx,
+% \iow_now:cn, \iow_now:cV, \iow_now:ce,
+% \iow_now:cx
+% }
+% This routine writes the second argument onto the output stream without
+% expansion. If this stream isn't open, the output goes to the terminal
+% instead. If the first argument is no output stream at all, we get an
+% internal error. We don't use the expansion done by \tn{write} to
+% get the |Nx| variant, because it differs in subtle ways from
+% \texttt{x}-expansion, namely, macro parameter characters would not
+% need to be doubled. We set the \tn{newlinechar} to~$10$ using
+% \cs{__kernel_iow_with:Nnn} to support formats such as plain \TeX{}: otherwise,
+% \cs{iow_newline:} would not work. We do not do this for
+% \cs{iow_shipout:Nn} or \cs{iow_shipout_x:Nn}, as \TeX{} looks at the
+% value of the \tn{newlinechar} at shipout time in those cases.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_now:Nn #1#2
+ {
+ \__kernel_iow_with:Nnn \tex_newlinechar:D { `\^^J }
+ { \tex_immediate:D \tex_write:D #1 { \exp_not:n {#2} } }
+ }
+\cs_generate_variant:Nn \iow_now:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \iow_now:Nn { Nx , cx }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\iow_log:n, \iow_log:e, \iow_log:x}
+% \begin{macro}{\iow_term:n, \iow_term:e, \iow_term:x}
+% Writing to the log and the terminal directly are relatively easy;
+% as we need the two \texttt{e}-type variants for bootstrapping,
+% they are redefinitions here.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_log:n { \iow_now:Nn \c_log_iow }
+\cs_set_protected:Npn \iow_log:e { \iow_now:Ne \c_log_iow }
+\cs_generate_variant:Nn \iow_log:n { x }
+\cs_new_protected:Npn \iow_term:n { \iow_now:Nn \c_term_iow }
+\cs_set_protected:Npn \iow_term:e { \iow_now:Ne \c_term_iow }
+\cs_generate_variant:Nn \iow_term:n { x }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Special characters for writing}
+%
+% \begin{macro}{\iow_newline:}
+% Global variable holding the character that forces a new line when
+% something is written to an output stream.
+% \begin{macrocode}
+\cs_new:Npn \iow_newline: { ^^J }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\iow_char:N}
+% Function to write any escaped char to an output stream.
+% \begin{macrocode}
+\cs_new_eq:NN \iow_char:N \cs_to_str:N
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Hard-wrapping lines to a character count}
+%
+% The code here implements a generic hard-wrapping function. This is
+% used by the messaging system, but is designed such that it is
+% available for other uses.
+%
+% \begin{variable}{\l_iow_line_count_int}
+% This is the \enquote{raw} number of characters in a line which
+% can be written to the terminal.
+% The standard value is the line length typically used by
+% \TeX{} Live and \hologo{MiKTeX}.
+% \begin{macrocode}
+\int_new:N \l_iow_line_count_int
+\int_set:Nn \l_iow_line_count_int { 78 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_newline_tl}
+% The token list inserted to produce a new line, with the
+% \meta{run-on text}.
+% \begin{macrocode}
+\tl_new:N \l_@@_newline_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_line_target_int}
+% This stores the target line count: the full number of characters
+% in a line, minus any part for a leader at the start of each line.
+% \begin{macrocode}
+\int_new:N \l_@@_line_target_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_set_indent:n}
+% \begin{macro}{\@@_unindent:w}
+% \begin{variable}{\l_@@_one_indent_tl, \l_@@_one_indent_int}
+% The \texttt{one_indent} variables hold one indentation marker and
+% its length. The \cs{@@_unindent:w} auxiliary removes one
+% indentation. The function \cs{@@_set_indent:n} (that could possibly
+% be public) sets the indentation in a consistent way. We set it to
+% four spaces by default.
+% \begin{macrocode}
+\tl_new:N \l_@@_one_indent_tl
+\int_new:N \l_@@_one_indent_int
+\cs_new:Npn \@@_unindent:w { }
+\cs_new_protected:Npn \@@_set_indent:n #1
+ {
+ \__kernel_tl_set:Nx \l_@@_one_indent_tl
+ { \exp_args:No \__kernel_str_to_other_fast:n { \tl_to_str:n {#1} } }
+ \int_set:Nn \l_@@_one_indent_int
+ { \str_count:N \l_@@_one_indent_tl }
+ \exp_last_unbraced:NNo
+ \cs_set:Npn \@@_unindent:w \l_@@_one_indent_tl { }
+ }
+\exp_args:Ne \@@_set_indent:n { \prg_replicate:nn { 4 } { ~ } }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_indent_tl, \l_@@_indent_int}
+% The current indentation (some copies of \cs{l_@@_one_indent_tl}) and
+% its number of characters.
+% \begin{macrocode}
+\tl_new:N \l_@@_indent_tl
+\int_new:N \l_@@_indent_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_line_tl, \l_@@_line_part_tl}
+% These hold the current line of text and a partial line to be added
+% to it, respectively.
+% \begin{macrocode}
+\tl_new:N \l_@@_line_tl
+\tl_new:N \l_@@_line_part_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_line_break_bool}
+% Indicates whether the line was broken precisely at a chunk boundary.
+% \begin{macrocode}
+\bool_new:N \l_@@_line_break_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_wrap_tl}
+% Used for the expansion step before detokenizing, and for the output
+% from wrapping text: fully expanded and with lines which are not
+% overly long.
+% \begin{macrocode}
+\tl_new:N \l_@@_wrap_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_wrap_marker_tl}
+% \begin{variable}
+% {
+% \c_@@_wrap_end_marker_tl,
+% \c_@@_wrap_newline_marker_tl,
+% \c_@@_wrap_allow_break_marker_tl,
+% \c_@@_wrap_indent_marker_tl,
+% \c_@@_wrap_unindent_marker_tl
+% }
+% Every special action of the wrapping code is starts with
+% the same recognizable string, \cs{c_@@_wrap_marker_tl}.
+% Upon seeing that \enquote{word}, the wrapping code reads
+% one space-delimited argument to know what operation to
+% perform. The setting of \tn{escapechar} here is not
+% very important, but makes \cs{c_@@_wrap_marker_tl} look
+% marginally nicer.
+% \begin{macrocode}
+\group_begin:
+ \int_set:Nn \tex_escapechar:D { -1 }
+ \tl_const:Ne \c_@@_wrap_marker_tl
+ { \tl_to_str:n { \^^I \^^O \^^W \^^_ \^^W \^^R \^^A \^^P } }
+\group_end:
+\tl_map_inline:nn
+ { { end } { newline } { allow_break } { indent } { unindent } }
+ {
+ \tl_const:ce { c_@@_wrap_ #1 _marker_tl }
+ {
+ \c_@@_wrap_marker_tl
+ #1
+ \c_catcode_other_space_tl
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{macro}{\iow_wrap_allow_break:}
+% \begin{macro}[EXP]{\@@_wrap_allow_break:}
+% \begin{macro}[EXP]{\@@_wrap_allow_break_error:}
+% We set \cs{iow_wrap_allow_break:n} to produce an error when outside
+% messages. Within wrapped message, it is set to \cs{@@_wrap_allow_break:}
+% when valid and otherwise to \cs{@@_wrap_allow_break_error:}. The second
+% produces an error expandably.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_wrap_allow_break:
+ {
+ \msg_error:nnnn { kernel } { iow-indent }
+ { \iow_wrap:nnnN } { \iow_wrap_allow_break: }
+ }
+\cs_new:Npe \@@_wrap_allow_break: { \c_@@_wrap_allow_break_marker_tl }
+\cs_new:Npn \@@_wrap_allow_break_error:
+ {
+ \msg_expandable_error:nnnn { kernel } { iow-indent }
+ { \iow_wrap:nnnN } { \iow_wrap_allow_break: }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\iow_indent:n}
+% \begin{macro}[EXP]{\@@_indent:n}
+% \begin{macro}[EXP]{\@@_indent_error:n}
+% We set \cs{iow_indent:n} to produce an error when outside
+% messages. Within wrapped message, it is set to \cs{@@_indent:n} when
+% valid and otherwise to \cs{@@_indent_error:n}. The first places the
+% instruction for increasing the indentation before its argument, and
+% the instruction for unindenting afterwards. The second produces an
+% error expandably. Note that there are no forced line-break, so
+% the indentation only changes when the next line is started.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_indent:n #1
+ {
+ \msg_error:nnnnn { kernel } { iow-indent }
+ { \iow_wrap:nnnN } { \iow_indent:n } {#1}
+ #1
+ }
+\cs_new:Npe \@@_indent:n #1
+ {
+ \c_@@_wrap_indent_marker_tl
+ #1
+ \c_@@_wrap_unindent_marker_tl
+ }
+\cs_new:Npn \@@_indent_error:n #1
+ {
+ \msg_expandable_error:nnnnn { kernel } { iow-indent }
+ { \iow_wrap:nnnN } { \iow_indent:n } {#1}
+ #1
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\iow_wrap:nnnN, \iow_wrap:nenN}
+% The main wrapping function works as follows. First give |\\|,
+% \verb*|\ | and other formatting commands the correct definition for
+% messages and perform the given setup~|#3|. The definition of
+% \verb*|\ | uses an \enquote{other} space rather than a normal space,
+% because the latter might be absorbed by \TeX{} to end a number or
+% other \texttt{f}-type expansions.
+% Use \cs{conditionally@traceoff} if defined; it is introduced by the
+% \pkg{trace} package and suppresses uninteresting tracing of the
+% wrapping code.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_wrap:nnnN #1#2#3#4
+ {
+ \group_begin:
+ \cs_if_exist_use:N \conditionally@traceoff
+ \int_set:Nn \tex_escapechar:D { -1 }
+ \cs_set:Npe \{ { \token_to_str:N \{ }
+ \cs_set:Npe \# { \token_to_str:N \# }
+ \cs_set:Npe \} { \token_to_str:N \} }
+ \cs_set:Npe \% { \token_to_str:N \% }
+ \cs_set:Npe \~ { \token_to_str:N \~ }
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \cs_set_eq:NN \\ \iow_newline:
+ \cs_set_eq:NN \ \c_catcode_other_space_tl
+ \cs_set_eq:NN \iow_wrap_allow_break: \@@_wrap_allow_break:
+ \cs_set_eq:NN \iow_indent:n \@@_indent:n
+ #3
+% \end{macrocode}
+% Then fully-expand the input: in package mode, the expansion uses
+% \LaTeXe{}'s \tn{protect} mechanism in the same way as \tn{typeout}.
+% In generic mode this setting is useless but harmless. As soon as
+% the expansion is done, reset \cs{iow_indent:n} to its error
+% definition: it only works in the first argument of
+% \cs{iow_wrap:nnnN}.
+% \begin{macrocode}
+ \cs_set_eq:NN \protect \token_to_str:N
+ \__kernel_tl_set:Nx \l_@@_wrap_tl {#1}
+ \cs_set_eq:NN \iow_wrap_allow_break: \@@_wrap_allow_break_error:
+ \cs_set_eq:NN \iow_indent:n \@@_indent_error:n
+% \end{macrocode}
+% Afterwards, set the newline marker (two assignments to fully expand,
+% then convert to a string) and initialize the target count for lines
+% (the first line has target count \cs{l_iow_line_count_int} instead).
+% \begin{macrocode}
+ \__kernel_tl_set:Nx \l_@@_newline_tl { \iow_newline: #2 }
+ \__kernel_tl_set:Nx \l_@@_newline_tl { \tl_to_str:N \l_@@_newline_tl }
+ \int_set:Nn \l_@@_line_target_int
+ { \l_iow_line_count_int - \str_count:N \l_@@_newline_tl + 1 }
+% \end{macrocode}
+% Sanity check.
+% \begin{macrocode}
+ \int_compare:nNnT { \l_@@_line_target_int } < 0
+ {
+ \tl_set:Nn \l_@@_newline_tl { \iow_newline: }
+ \int_set:Nn \l_@@_line_target_int
+ { \l_iow_line_count_int + 1 }
+ }
+% \end{macrocode}
+% There is then a loop over the input, which stores the wrapped
+% result in \cs{l_@@_wrap_tl}. After the loop, the resulting text is
+% passed on to the function which has been given as a post-processor.
+% The \cs{tl_to_str:N} step converts the \enquote{other} spaces back
+% to normal spaces. The \texttt{f}-expansion removes a leading space
+% from \cs{l_@@_wrap_tl}.
+% \begin{macrocode}
+ \@@_wrap_do:
+ \exp_args:NNf \group_end:
+ #4 { \tl_to_str:N \l_@@_wrap_tl }
+ }
+\cs_generate_variant:Nn \iow_wrap:nnnN { ne }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_wrap_do:, \@@_wrap_fix_newline:w, \@@_wrap_start:w}
+% Escape spaces and change newlines to \cs{c_@@_wrap_newline_marker_tl}.
+% Set up a few variables, in particular the initial
+% value of \cs{l_@@_wrap_tl}: the space stops the
+% \texttt{f}-expansion of the main wrapping function and
+% \cs{use_none:n} removes a newline marker inserted by later code.
+% The main loop consists of repeatedly calling the \texttt{chunk}
+% auxiliary to wrap chunks delimited by (newline or indentation)
+% markers.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_wrap_do:
+ {
+ \__kernel_tl_set:Nx \l_@@_wrap_tl
+ {
+ \exp_args:No \__kernel_str_to_other_fast:n \l_@@_wrap_tl
+ \c_@@_wrap_end_marker_tl
+ }
+ \__kernel_tl_set:Nx \l_@@_wrap_tl
+ {
+ \exp_after:wN \@@_wrap_fix_newline:w \l_@@_wrap_tl
+ ^^J \q_@@_nil ^^J \s_@@_stop
+ }
+ \exp_after:wN \@@_wrap_start:w \l_@@_wrap_tl
+ }
+\cs_new:Npn \@@_wrap_fix_newline:w #1 ^^J #2 ^^J
+ {
+ #1
+ \if_meaning:w \q_@@_nil #2
+ \@@_use_i_delimit_by_s_stop:nw
+ \fi:
+ \c_@@_wrap_newline_marker_tl
+ \@@_wrap_fix_newline:w #2 ^^J
+ }
+\cs_new_protected:Npn \@@_wrap_start:w
+ {
+ \bool_set_false:N \l_@@_line_break_bool
+ \tl_clear:N \l_@@_line_tl
+ \tl_clear:N \l_@@_line_part_tl
+ \tl_set:Nn \l_@@_wrap_tl { ~ \use_none:n }
+ \int_zero:N \l_@@_indent_int
+ \tl_clear:N \l_@@_indent_tl
+ \@@_wrap_chunk:nw { \l_iow_line_count_int }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_wrap_chunk:nw, \@@_wrap_next:nw}
+% The \texttt{chunk} and \texttt{next} auxiliaries are defined
+% indirectly to obtain the expansions of \cs{c_catcode_other_space_tl}
+% and \cs{c_@@_wrap_marker_tl} in their definition. The \texttt{next}
+% auxiliary calls a function corresponding to the type of marker (its
+% |##2|), which can be \texttt{newline} or \texttt{indent} or
+% \texttt{unindent} or \texttt{end}. The first argument of the
+% \texttt{chunk} auxiliary is a target number of characters and the
+% second is some string to wrap. If the chunk is empty simply call
+% \texttt{next}. Otherwise, set up a call to \cs{@@_wrap_line:nw},
+% including the indentation if the current line is empty, and
+% including a trailing space (|#1|) before the
+% \cs{@@_wrap_end_chunk:w} auxiliary.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2
+ {
+ \cs_new_protected:Npn \@@_wrap_chunk:nw ##1##2 #2
+ {
+ \tl_if_empty:nTF {##2}
+ {
+ \tl_clear:N \l_@@_line_part_tl
+ \@@_wrap_next:nw {##1}
+ }
+ {
+ \tl_if_empty:NTF \l_@@_line_tl
+ {
+ \@@_wrap_line:nw
+ { \l_@@_indent_tl }
+ ##1 - \l_@@_indent_int ;
+ }
+ { \@@_wrap_line:nw { } ##1 ; }
+ ##2 #1
+ \@@_wrap_end_chunk:w 7 6 5 4 3 2 1 0 \s_@@_stop
+ }
+ }
+ \cs_new_protected:Npn \@@_wrap_next:nw ##1##2 #1
+ { \use:c { @@_wrap_##2:n } {##1} }
+ }
+\exp_args:NVV \@@_tmp:w \c_catcode_other_space_tl \c_@@_wrap_marker_tl
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_wrap_line:nw}
+% \begin{macro}[EXP]
+% {
+% \@@_wrap_line_loop:w,
+% \@@_wrap_line_aux:Nw,
+% \@@_wrap_line_seven:nnnnnnn,
+% \@@_wrap_line_end:NnnnnnnnN,
+% \@@_wrap_line_end:nw,
+% \@@_wrap_end_chunk:w
+% }
+% This is followed by \Arg{string} \meta{int expr} |;|. It stores the
+% \meta{string} and up to \meta{int expr} characters from the current
+% chunk into \cs{l_@@_line_part_tl}. Characters are grabbed 8~at a
+% time and left in \cs{l_@@_line_part_tl} by the \texttt{line_loop}
+% auxiliary. When $k<8$ remain to be found, the \texttt{line_aux}
+% auxiliary calls the \texttt{line_end} auxiliary followed by (the
+% single digit) $k$, then $7-k$ empty brace groups, then the chunk's
+% remaining characters. The \texttt{line_end} auxiliary leaves
+% $k$~characters from the chunk in the line part, then ends the
+% assignment. Ignore the \cs{use_none:nnnnn} line for now. If the
+% next character is a space the line can be broken there:
+% store what we found into the result and get the next line.
+% Otherwise some work is needed to find a break-point. So far we have
+% ignored what happens if the chunk is shorter than the requested
+% number of characters: this is dealt with by the \texttt{end_chunk}
+% auxiliary, which gets treated like a character by the rest of the
+% code. It ends up being called either as one of the arguments
+% |#2|--|#9| of the \texttt{line_loop} auxiliary or as one of the
+% arguments |#2|--|#8| of the \texttt{line_end} auxiliary. In both
+% cases stop the assignment and work out how many characters are still
+% needed. Notice that when we have exactly seven arguments to clean up,
+% a \cs{exp_stop_f:} has to be inserted to stop the \cs{exp:w}.
+% The weird \cs{use_none:nnnnn} ensures that the required
+% data is in the right place.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_wrap_line:nw #1
+ {
+ \tex_edef:D \l_@@_line_part_tl { \if_false: } \fi:
+ #1
+ \exp_after:wN \@@_wrap_line_loop:w
+ \int_value:w \int_eval:w
+ }
+\cs_new:Npn \@@_wrap_line_loop:w #1 ; #2#3#4#5#6#7#8#9
+ {
+ \if_int_compare:w #1 < 8 \exp_stop_f:
+ \@@_wrap_line_aux:Nw #1
+ \fi:
+ #2 #3 #4 #5 #6 #7 #8 #9
+ \exp_after:wN \@@_wrap_line_loop:w
+ \int_value:w \int_eval:w #1 - 8 ;
+ }
+\cs_new:Npn \@@_wrap_line_aux:Nw #1#2#3 \exp_after:wN #4 ;
+ {
+ #2
+ \exp_after:wN \@@_wrap_line_end:NnnnnnnnN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN \exp_after:wN
+ \if_case:w #1 \exp_stop_f:
+ \prg_do_nothing:
+ \or: \use_none:n
+ \or: \use_none:nn
+ \or: \use_none:nnn
+ \or: \use_none:nnnn
+ \or: \use_none:nnnnn
+ \or: \use_none:nnnnnn
+ \or: \@@_wrap_line_seven:nnnnnnn
+ \fi:
+ { } { } { } { } { } { } { } #3
+ }
+\cs_new:Npn \@@_wrap_line_seven:nnnnnnn #1#2#3#4#5#6#7 { \exp_stop_f: }
+\cs_new:Npn \@@_wrap_line_end:NnnnnnnnN #1#2#3#4#5#6#7#8#9
+ {
+ #2 #3 #4 #5 #6 #7 #8
+ \use_none:nnnnn \int_eval:w 8 - ; #9
+ \token_if_eq_charcode:NNTF \c_space_token #9
+ { \@@_wrap_line_end:nw { } }
+ { \if_false: { \fi: } \@@_wrap_break:w #9 }
+ }
+\cs_new:Npn \@@_wrap_line_end:nw #1
+ {
+ \if_false: { \fi: }
+ \@@_wrap_store_do:n {#1}
+ \@@_wrap_next_line:w
+ }
+\cs_new:Npn \@@_wrap_end_chunk:w
+ #1 \int_eval:w #2 - #3 ; #4#5 \s_@@_stop
+ {
+ \if_false: { \fi: }
+ \exp_args:Nf \@@_wrap_next:nw { \int_eval:n { #2 - #4 } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_wrap_break:w}
+% \begin{macro}[EXP]
+% {
+% \@@_wrap_break_first:w,
+% \@@_wrap_break_none:w,
+% \@@_wrap_break_loop:w,
+% \@@_wrap_break_end:w,
+% }
+% Functions here are defined indirectly: \cs{@@_tmp:w} is eventually
+% called with an \enquote{other} space as its argument. The goal is
+% to remove from \cs{l_@@_line_part_tl} the part after the last space.
+% In most cases this is done by repeatedly calling the
+% \texttt{break_loop} auxiliary, which leaves \enquote{words}
+% (delimited by spaces) until it hits the trailing space: then its
+% argument |##3| is |?| \cs{@@_wrap_break_end:w} instead of a single
+% token, and that \texttt{break_end} auxiliary leaves in the
+% assignment the line until the last space, then calls
+% \cs{@@_wrap_line_end:nw} to finish up the line and move on to the
+% next. If there is no space in \cs{l_@@_line_part_tl} then the
+% \texttt{break_first} auxiliary calls the \texttt{break_none}
+% auxiliary. In that case, if the current line is empty, the complete
+% word (including |##4|, characters beyond what we had grabbed) is
+% added to the line, making it over-long. Otherwise, the word is
+% used for the following line (and the last space of the line so far
+% is removed because it was inserted due to the presence of a marker).
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_new:Npn \@@_wrap_break:w
+ {
+ \tex_edef:D \l_@@_line_part_tl
+ { \if_false: } \fi:
+ \exp_after:wN \@@_wrap_break_first:w
+ \l_@@_line_part_tl
+ #1
+ { ? \@@_wrap_break_end:w }
+ \s_@@_mark
+ }
+ \cs_new:Npn \@@_wrap_break_first:w ##1 #1 ##2
+ {
+ \use_none:nn ##2 \@@_wrap_break_none:w
+ \@@_wrap_break_loop:w ##1 #1 ##2
+ }
+ \cs_new:Npn \@@_wrap_break_none:w ##1##2 #1 ##3 \s_@@_mark ##4 #1
+ {
+ \tl_if_empty:NTF \l_@@_line_tl
+ { ##2 ##4 \@@_wrap_line_end:nw { } }
+ { \@@_wrap_line_end:nw { \@@_wrap_trim:N } ##2 ##4 #1 }
+ }
+ \cs_new:Npn \@@_wrap_break_loop:w ##1 #1 ##2 #1 ##3
+ {
+ \use_none:n ##3
+ ##1 #1
+ \@@_wrap_break_loop:w ##2 #1 ##3
+ }
+ \cs_new:Npn \@@_wrap_break_end:w ##1 #1 ##2 ##3 #1 ##4 \s_@@_mark
+ { ##1 \@@_wrap_line_end:nw { } ##3 }
+ }
+\exp_args:NV \@@_tmp:w \c_catcode_other_space_tl
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_wrap_next_line:w}
+% The special case where the end of a line coincides with the end of a
+% chunk is detected here, to avoid a spurious empty line. Otherwise,
+% call \cs{@@_wrap_line:nw} to find characters for the next line
+% (remembering to account for the indentation).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_wrap_next_line:w #1#2 \s_@@_stop
+ {
+ \tl_clear:N \l_@@_line_tl
+ \token_if_eq_meaning:NNTF #1 \@@_wrap_end_chunk:w
+ {
+ \tl_clear:N \l_@@_line_part_tl
+ \bool_set_true:N \l_@@_line_break_bool
+ \@@_wrap_next:nw { \l_@@_line_target_int }
+ }
+ {
+ \@@_wrap_line:nw
+ { \l_@@_indent_tl }
+ \l_@@_line_target_int - \l_@@_indent_int ;
+ #1 #2 \s_@@_stop
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_wrap_allow_break:n}
+% This is called after a chunk has been wrapped. The
+% \cs{l_@@_line_part_tl} typically ends with a space (except at the
+% beginning of a line?), which we remove since the
+% \texttt{allow\_break} marker should not insert a space. Then move
+% on with the next chunk, making sure to adjust the target number of
+% characters for the line in case we did remove a space.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_wrap_allow_break:n #1
+ {
+ \__kernel_tl_set:Nx \l_@@_line_tl
+ { \l_@@_line_tl \@@_wrap_trim:N \l_@@_line_part_tl }
+ \bool_set_false:N \l_@@_line_break_bool
+ \tl_if_empty:NTF \l_@@_line_part_tl
+ { \@@_wrap_chunk:nw {#1} }
+ { \exp_args:Nf \@@_wrap_chunk:nw { \int_eval:n { #1 + 1 } } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_wrap_indent:n, \@@_wrap_unindent:n}
+% These functions are called after a chunk has been wrapped, when
+% encountering \texttt{indent}/\texttt{unindent} markers. Add the
+% line part (last line part of the previous chunk) to the line so far
+% and reset a boolean denoting the presence of a line-break. Most
+% importantly, add or remove one indent from the current indent (both
+% the integer and the token list). Finally, continue wrapping.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_wrap_indent:n #1
+ {
+ \tl_put_right:Ne \l_@@_line_tl { \l_@@_line_part_tl }
+ \bool_set_false:N \l_@@_line_break_bool
+ \int_add:Nn \l_@@_indent_int { \l_@@_one_indent_int }
+ \tl_put_right:No \l_@@_indent_tl { \l_@@_one_indent_tl }
+ \@@_wrap_chunk:nw {#1}
+ }
+\cs_new_protected:Npn \@@_wrap_unindent:n #1
+ {
+ \tl_put_right:Ne \l_@@_line_tl { \l_@@_line_part_tl }
+ \bool_set_false:N \l_@@_line_break_bool
+ \int_sub:Nn \l_@@_indent_int { \l_@@_one_indent_int }
+ \__kernel_tl_set:Nx \l_@@_indent_tl
+ { \exp_after:wN \@@_unindent:w \l_@@_indent_tl }
+ \@@_wrap_chunk:nw {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_wrap_newline:n, \@@_wrap_end:n}
+% These functions are called after a chunk has been line-wrapped, when
+% encountering a \texttt{newline}/\texttt{end} marker. Unless we just
+% took a line-break, store the line part and the line so far into the
+% whole \cs{l_@@_wrap_tl}, trimming a trailing space. In the
+% \texttt{newline} case look for a new line (of length
+% \cs{l_@@_line_target_int}) in a new chunk.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_wrap_newline:n #1
+ {
+ \bool_if:NF \l_@@_line_break_bool
+ { \@@_wrap_store_do:n { \@@_wrap_trim:N } }
+ \bool_set_false:N \l_@@_line_break_bool
+ \@@_wrap_chunk:nw { \l_@@_line_target_int }
+ }
+\cs_new_protected:Npn \@@_wrap_end:n #1
+ {
+ \bool_if:NF \l_@@_line_break_bool
+ { \@@_wrap_store_do:n { \@@_wrap_trim:N } }
+ \bool_set_false:N \l_@@_line_break_bool
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_wrap_store_do:n}
+% First add the last line part to the line, then append it to
+% \cs{l_@@_wrap_tl} with the appropriate new line (with
+% \enquote{run-on} text), possibly with its last space removed (|#1|
+% is empty or \cs{@@_wrap_trim:N}).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_wrap_store_do:n #1
+ {
+ \__kernel_tl_set:Nx \l_@@_line_tl
+ { \l_@@_line_tl \l_@@_line_part_tl }
+ \__kernel_tl_set:Nx \l_@@_wrap_tl
+ {
+ \l_@@_wrap_tl
+ \l_@@_newline_tl
+ #1 \l_@@_line_tl
+ }
+ \tl_clear:N \l_@@_line_tl
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_wrap_trim:N, \@@_wrap_trim:w, \@@_wrap_trim_aux:w}
+% Remove one trailing \enquote{other} space from the argument if present.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_new:Npn \@@_wrap_trim:N ##1
+ { \exp_after:wN \@@_wrap_trim:w ##1 \s_@@_mark #1 \s_@@_mark \s_@@_stop }
+ \cs_new:Npn \@@_wrap_trim:w ##1 #1 \s_@@_mark
+ { \@@_wrap_trim_aux:w ##1 \s_@@_mark }
+ \cs_new:Npn \@@_wrap_trim_aux:w ##1 \s_@@_mark ##2 \s_@@_stop {##1}
+ }
+\exp_args:NV \@@_tmp:w \c_catcode_other_space_tl
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%<@@=file>
+% \end{macrocode}
+%
+% \subsection{File operations}
+%
+% \begin{variable}{\l_@@_internal_tl}
+% Used as a short-term scratch variable.
+% \begin{macrocode}
+\tl_new:N \l_@@_internal_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \g_file_curr_dir_str ,
+% \g_file_curr_ext_str ,
+% \g_file_curr_name_str
+% }
+% The name of the current file should be available at all times:
+% the name itself is set dynamically.
+% \begin{macrocode}
+\str_new:N \g_file_curr_dir_str
+\str_new:N \g_file_curr_ext_str
+\str_new:N \g_file_curr_name_str
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_stack_seq}
+% The input list of files is stored as a sequence stack. In package
+% mode we can recover the information from the details held by
+% \LaTeXe{} (we must be in the preamble and loaded using \tn{usepackage}
+% or \tn{RequirePackage}). As \LaTeXe{} doesn't store directory and
+% name separately, we stick to the same convention here. In pre-loading,
+% \tn{@currnamestack} is empty so is skipped.
+% \begin{macrocode}
+\seq_new:N \g_@@_stack_seq
+\group_begin:
+ \cs_set_protected:Npn \@@_tmp:w #1#2#3
+ {
+ \tl_if_blank:nTF {#1}
+ {
+ \cs_set:Npn \@@_tmp:w ##1 " ##2 " ##3 \s_@@_stop
+ { { } {##2} { } }
+ \seq_gput_right:Ne \g_@@_stack_seq
+ {
+ \exp_after:wN \@@_tmp:w \tex_jobname:D
+ " \tex_jobname:D " \s_@@_stop
+ }
+ }
+ {
+ \seq_gput_right:Nn \g_@@_stack_seq { { } {#1} {#2} }
+ \@@_tmp:w
+ }
+ }
+ \cs_if_exist:NT \@currnamestack
+ {
+ \tl_if_empty:NF \@currnamestack
+ { \exp_after:wN \@@_tmp:w \@currnamestack }
+ }
+\group_end:
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_record_seq}
+% The total list of files used is recorded separately from the current
+% file stack, as nothing is ever popped from this list. The current
+% file name should be included in the file list! We
+% will eventually copy the contents of \cs{@filelist}.
+% \begin{macrocode}
+\seq_new:N \g_@@_record_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_base_name_tl, \l_@@_full_name_tl}
+% For storing the basename and full path whilst passing data internally.
+% \begin{macrocode}
+\tl_new:N \l_@@_base_name_tl
+\tl_new:N \l_@@_full_name_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_dir_str, \l_@@_ext_str, \l_@@_name_str}
+% Used in parsing a path into parts: in contrast to the above, these are
+% never used outside of the current module.
+% \begin{macrocode}
+\str_new:N \l_@@_dir_str
+\str_new:N \l_@@_ext_str
+\str_new:N \l_@@_name_str
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_file_search_path_seq}
+% The current search path.
+% \begin{macrocode}
+\seq_new:N \l_file_search_path_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_tmp_seq}
+% Scratch space for comma list conversion.
+% \begin{macrocode}
+\seq_new:N \l_@@_tmp_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Internal auxiliaries}
+%
+% \begin{variable}{\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\q_@@_nil}
+% Internal quarks.
+% \begin{macrocode}
+\quark_new:N \q_@@_nil
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[pTF]{\@@_quark_if_nil:n}
+% Branching quark conditional.
+% \begin{macrocode}
+\__kernel_quark_new_conditional:Nn \@@_quark_if_nil:n { TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\q_@@_recursion_tail,\q_@@_recursion_stop}
+% Internal recursion quarks.
+% \begin{macrocode}
+\quark_new:N \q_@@_recursion_tail
+\quark_new:N \q_@@_recursion_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{
+% \@@_if_recursion_tail_break:NN,
+% \@@_if_recursion_tail_stop_do:Nn
+% }
+% Functions to query recursion quarks.
+% \begin{macrocode}
+\__kernel_quark_new_test:N \@@_if_recursion_tail_stop:N
+\__kernel_quark_new_test:N \@@_if_recursion_tail_stop_do:nn
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\__kernel_file_name_sanitize:n}
+% \begin{macro}[EXP]{
+% \@@_name_expand:n,
+% \@@_name_expand_cleanup:Nw,
+% \@@_name_expand_cleanup:w,
+% \@@_name_expand_end:,
+% \@@_name_expand_error:Nw,
+% \@@_name_expand_error_aux:Nw,
+% }
+% \begin{macro}[EXP]{
+% \@@_name_strip_quotes:n,
+% \@@_name_strip_quotes:nnnw,
+% \@@_name_strip_quotes:nnn,
+% }
+% \begin{macro}[EXP]{
+% \@@_name_trim_spaces:n,
+% \@@_name_trim_spaces:nw,
+% \@@_name_trim_spaces_aux:n,
+% \@@_name_trim_spaces_aux:w,
+% }
+% Expanding the file name uses a \tn{csname}-based approach, and
+% relies on active characters (for example from UTF-8 characters)
+% being properly set up to expand to a expansion-safe version using
+% \cs{ifcsname}. This is less conservative than the token-by-token
+% approach used before, but it is much faster.
+% \begin{macrocode}
+\cs_new:Npn \__kernel_file_name_sanitize:n #1
+ {
+ \exp_args:Ne \@@_name_trim_spaces:n
+ {
+ \exp_args:Ne \@@_name_strip_quotes:n
+ { \@@_name_expand:n {#1} }
+ }
+ }
+% \end{macrocode}
+%
+% We'll use \cs{cs:w} to start expanding the file name, and to avoid
+% creating csnames equal to \tn{relax} with \enquote{common} names,
+% there's a prefix |__file_name=| to the csname. There's also a guard
+% token at the end so we can check if there was an error during the
+% process and (try to) clean up gracefully.
+% \begin{macrocode}
+\cs_new:Npn \@@_name_expand:n #1
+ {
+ \exp_after:wN \@@_name_expand_cleanup:Nw
+ \cs:w @@_name = #1 \cs_end:
+ \@@_name_expand_end:
+ }
+% \end{macrocode}
+% With the csname built, we grab it, and grab the remaining tokens
+% delimited by \cs{@@_name_expand_end:}. If there are any remaining
+% tokens, something bad happened, so we'll call the error procedure
+% \cs{@@_name_expand_error:Nw}.
+% If everything went according to plan, then use \cs{token_to_str:N}
+% on the csname built, and call \cs{@@_name_expand_cleanup:w} to
+% remove the prefix we added a while back.
+% \cs{@@_name_expand_cleanup:w} takes a leading argument so we don't
+% have to bother about the value of \cs{tex_escapechar:D}.
+% \begin{macrocode}
+\cs_new:Npn \@@_name_expand_cleanup:Nw #1 #2 \@@_name_expand_end:
+ {
+ \tl_if_empty:nF {#2}
+ { \@@_name_expand_error:Nw #2 \@@_name_expand_end: }
+ \exp_after:wN \@@_name_expand_cleanup:w \token_to_str:N #1
+ }
+\exp_last_unbraced:NNNNo
+\cs_new:Npn \@@_name_expand_cleanup:w #1 \tl_to_str:n { @@_name = } { }
+% \end{macrocode}
+% In non-error cases \cs{@@_name_expand_end:} should not expand. It
+% will only do so in case there is a \cs{csname} too much in the file
+% name, so it will throw an error (while expanding), then insert the
+% missing \cs{cs_end:} and yet another \cs{@@_name_expand_end:} that
+% will be used as a delimiter by \cs{@@_name_expand_cleanup:Nw} (or
+% that will expand again if yet another \cs{endcsname} is missing).
+% \begin{macrocode}
+\cs_new:Npn \@@_name_expand_end:
+ {
+ \msg_expandable_error:nn
+ { kernel } { filename-missing-endcsname }
+ \cs_end: \@@_name_expand_end:
+ }
+% \end{macrocode}
+% Now to the error case. \cs{@@_name_expand_error:Nw} adds an extra
+% \cs{cs_end:} so that in case there was an extra \tn{csname} in the
+% file name, then \cs{@@_name_expand_error_aux:Nw} throws the error.
+% \begin{macrocode}
+\cs_new:Npn \@@_name_expand_error:Nw #1 #2 \@@_name_expand_end:
+ { \@@_name_expand_error_aux:Nw #1 #2 \cs_end: \@@_name_expand_end: }
+\cs_new:Npn \@@_name_expand_error_aux:Nw #1 #2 \cs_end: #3
+ \@@_name_expand_end:
+ {
+ \msg_expandable_error:nnff
+ { kernel } { filename-chars-lost }
+ { \token_to_str:N #1 } { \exp_stop_f: #2 }
+ }
+% \end{macrocode}
+% Quoting file name uses basically the same approach as for
+% \texttt{luaquotejobname}: count the |"| tokens and remove them.
+% \begin{macrocode}
+\cs_new:Npn \@@_name_strip_quotes:n #1
+ {
+ \@@_name_strip_quotes:nw { 0 }
+ #1 " \q_@@_recursion_tail " \q_@@_recursion_stop {#1}
+ }
+\cs_new:Npn \@@_name_strip_quotes:nw #1#2 "
+ {
+ \if_meaning:w \q_@@_recursion_tail #2
+ \@@_name_strip_quotes_end:wnwn
+ \fi:
+ #2
+ \@@_name_strip_quotes:nw { #1 + 1 }
+ }
+\cs_new:Npn \@@_name_strip_quotes_end:wnwn \fi: #1
+ \@@_name_strip_quotes:nw #2 \q_@@_recursion_stop #3
+ {
+ \fi:
+ \int_if_odd:nT {#2}
+ {
+ \msg_expandable_error:nnn
+ { kernel } { unbalanced-quote-in-filename } {#3}
+ }
+ }
+% \end{macrocode}
+% Spaces need to be trimmed from the start of the name and from the end of
+% any extension. However, the name we are passed might not have an extension:
+% that means we have to look for one. If there is no extension, we still use
+% the standard trimming function but deliberately prevent any spaces being
+% removed at the end.
+% \begin{macrocode}
+\cs_new:Npn \@@_name_trim_spaces:n #1
+ { \@@_name_trim_spaces:nw {#1} #1 . \q_@@_nil . \s_@@_stop }
+\cs_new:Npn \@@_name_trim_spaces:nw #1#2 . #3 . #4 \s_@@_stop
+ {
+ \@@_quark_if_nil:nTF {#3}
+ {
+ \tl_trim_spaces_apply:nN { #1 \s_@@_stop }
+ \@@_name_trim_spaces_aux:n
+ }
+ { \tl_trim_spaces:n {#1} }
+ }
+\cs_new:Npn \@@_name_trim_spaces_aux:n #1
+ { \@@_name_trim_spaces_aux:w #1 }
+\cs_new:Npn \@@_name_trim_spaces_aux:w #1 \s_@@_stop {#1}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\__kernel_file_name_quote:n}
+% \begin{macro}[EXP]{\@@_name_quote:nw}
+% \begin{macrocode}
+\cs_new:Npn \__kernel_file_name_quote:n #1
+ { \@@_name_quote:nw {#1} #1 ~ \q_@@_nil \s_@@_stop }
+\cs_new:Npn \@@_name_quote:nw #1 #2 ~ #3 \s_@@_stop
+ {
+ \@@_quark_if_nil:nTF {#3}
+ { #1 }
+ { "#1" }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\c_@@_marker_tl}
+% The same idea as the marker for rescanning token lists: this pair of
+% tokens cannot appear in a file that is being input.
+% \begin{macrocode}
+\tl_const:Ne \c_@@_marker_tl { : \token_to_str:N : }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[TF]{\file_get:nnN, \file_get:VnN}
+% \begin{macro}{\file_get:nnN,\@@_get_aux:nnN,\@@_get_do:Nw}
+% The approach here is similar to that for \cs{tl_set_rescan:Nnn}.
+% The file contents are grabbed as an argument delimited by
+% \cs{c_@@_marker_tl}. A few subtleties: braces in \cs{if_false:}
+% \ldots{} \cs{fi:} to deal with possible alignment tabs,
+% \tn{tracingnesting} to avoid a warning about a group being closed
+% inside the \tn{scantokens}, and \cs{prg_return_true:} is placed
+% after the end-of-file marker.
+% \begin{macrocode}
+\cs_new_protected:Npn \file_get:nnN #1#2#3
+ {
+ \file_get:nnNF {#1} {#2} #3
+ { \tl_set:Nn #3 { \q_no_value } }
+ }
+\cs_generate_variant:Nn \file_get:nnN { V }
+\prg_new_protected_conditional:Npnn \file_get:nnN #1#2#3 { T , F , TF }
+ {
+ \file_get_full_name:nNTF {#1} \l_@@_full_name_tl
+ {
+ \exp_args:NV \@@_get_aux:nnN
+ \l_@@_full_name_tl
+ {#2} #3
+ \prg_return_true:
+ }
+ { \prg_return_false: }
+ }
+\prg_generate_conditional_variant:Nnn \file_get:nnN { V } { T , F , TF }
+\cs_new_protected:Npe \@@_get_aux:nnN #1#2#3
+ {
+ \exp_not:N \if_false: { \exp_not:N \fi:
+ \group_begin:
+ \int_set_eq:NN \tex_tracingnesting:D \c_zero_int
+ \exp_not:N \exp_args:No \tex_everyeof:D
+ { \exp_not:N \c_@@_marker_tl }
+ #2 \scan_stop:
+ \exp_not:N \exp_after:wN \exp_not:N \@@_get_do:Nw
+ \exp_not:N \exp_after:wN #3
+ \exp_not:N \exp_after:wN \exp_not:N \prg_do_nothing:
+ \exp_not:N \tex_input:D
+ \sys_if_engine_luatex:TF
+ { {#1} }
+ { \exp_not:N \__kernel_file_name_quote:n {#1} \scan_stop: }
+ \exp_not:N \if_false: } \exp_not:N \fi:
+ }
+\exp_args:Nno \use:nn
+ { \cs_new_protected:Npn \@@_get_do:Nw #1#2 }
+ { \c_@@_marker_tl }
+ {
+ \group_end:
+ \tl_set:No #1 {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_size:n}
+% A copy of the primitive where it's available.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_size:n \tex_filesize:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\file_full_name:n, \@@_full_name:n, \@@_full_name_aux:n}
+% \begin{macro}[EXP]{\@@_full_name_auxi:nn, \@@_full_name_auxii:nn}
+% \begin{macro}[EXP]{\@@_full_name_aux:Nnn}
+% \begin{macro}[EXP]{\@@_full_name_slash:n}
+% \begin{macro}[EXP]{\@@_full_name_slash:w}
+% \begin{macro}[EXP]{\@@_full_name_aux:nN}
+% \begin{macro}[EXP]{\@@_full_name_aux:nnN}
+% \begin{macro}[EXP]{\@@_name_cleanup:w}
+% \begin{macro}[EXP]{\@@_name_end:}
+% \begin{macro}[EXP]{\@@_name_ext_check:nn}
+% \begin{macro}[EXP]{\@@_name_ext_check:nnw}
+% \begin{macro}[EXP]{\@@_name_ext_check:nnnw}
+% \begin{macro}[EXP]{\@@_name_ext_check:nnn}
+% \begin{macro}[EXP]{\@@_name_ext_check:nnnn}
+% File searching can be carried out if the \tn{pdffilesize} primitive
+% or an equivalent is available. That of course means we need to
+% arrange for everything else to here to be done by expansion too.
+% We start off by sanitizing the name and quoting if required: we
+% may need to remove those quotes, so the raw name is passed too.
+% \begin{macrocode}
+\cs_new:Npn \file_full_name:n #1
+ {
+ \exp_args:Ne \@@_full_name:n
+ { \__kernel_file_name_sanitize:n {#1} }
+ }
+\cs_generate_variant:Nn \file_full_name:n { V }
+% \end{macrocode}
+% First, we check of the file is just here: no mapping so we do not
+% need the break part of the broader auxiliary. We are using the fact
+% that the primitive here returns nothing if the file is entirely absent.
+% To avoid unnecessary filesystem lookups, the result of \tn{pdffilesize}
+% is kept available as an argument.
+% For package mode, \tn{input@path} is a token list not a sequence.
+% \begin{macrocode}
+\cs_new:Npn \@@_full_name:n #1
+ {
+ \tl_if_blank:nF {#1}
+ { \exp_args:Nne \@@_full_name_auxii:nn {#1} { \@@_full_name_aux:n {#1} } }
+ }
+% \end{macrocode}
+% To avoid repeated reading of files we need to cache the loading:
+% this is important as the code here is used by \emph{all} file checks.
+% The same marker is used in the \LaTeXe{} kernel, meaning that we get a
+% double-saving with for example \cs{IfFileExists}. As this is all about
+% performance, we use the low-level approach for the conditionals. For
+% a file already seen, the size is reported as $-1$ so it's distinct from
+% any non-cached ones.
+% \begin{macrocode}
+\cs_new:Npn \@@_full_name_aux:n #1
+ {
+ \if_cs_exist:w @@_seen_ \tl_to_str:n {#1} : \cs_end:
+ -1
+ \else:
+ \exp_args:Ne \@@_full_name_auxi:nn { \@@_size:n {#1} } {#1}
+ \fi:
+ }
+% \end{macrocode}
+% We will need the size of files later, and we have to avoid the
+% \cs{scan_stop:} causing issues if we are raising the flag. Thus there is
+% a slightly odd gobble here.
+% \begin{macrocode}
+\cs_new:Npn \@@_full_name_auxi:nn #1#2
+ {
+ \if:w \scan_stop: #1 \scan_stop:
+ \else:
+ \exp_after:wN \use_none:n
+ \cs:w @@_seen_ \tl_to_str:n {#2} : \cs_end:
+ #1
+ \fi:
+ }
+\cs_new:Npn \@@_full_name_auxii:nn #1 #2
+ {
+ \tl_if_blank:nTF {#2}
+ {
+ \seq_map_tokens:Nn \l_file_search_path_seq
+ { \@@_full_name_aux:Nnn \seq_map_break:n {#1} }
+ \cs_if_exist:NT \input@path
+ {
+ \tl_map_tokens:Nn \input@path
+ { \@@_full_name_aux:Nnn \tl_map_break:n {#1} }
+ }
+ \@@_name_end:
+ }
+ { \@@_ext_check:nn {#1} {#2} }
+ }
+% \end{macrocode}
+% Two pars to the auxiliary here so we can avoid doing quoting
+% twice in the event we find the right file.
+% \begin{macrocode}
+\cs_new:Npn \@@_full_name_aux:Nnn #1#2#3
+ {
+ \exp_args:Ne \@@_full_name_aux:nN
+ { \@@_full_name_slash:n {#3} #2 }
+ #1
+ }
+\cs_new:Npn \@@_full_name_slash:n #1
+ {
+ \@@_full_name_slash:nw {#1} #1 \q_nil / \q_nil / \q_nil \q_stop
+ }
+\cs_new:Npn \@@_full_name_slash:nw #1#2 / \q_nil / #3 \q_stop
+ {
+ \quark_if_nil:nTF {#3}
+ { #1 / }
+ { #2 / }
+ }
+\cs_new:Npn \@@_full_name_aux:nN #1
+ { \exp_args:Nne \@@_full_name_aux:nnN {#1} { \@@_full_name_aux:n {#1} } }
+\cs_new:Npn \@@_full_name_aux:nnN #1 #2 #3
+ {
+ \tl_if_blank:nF {#2}
+ {
+ #3
+ {
+ \@@_ext_check:nn {#1} {#2}
+ \@@_name_cleanup:w
+ }
+ }
+ }
+\cs_new:Npn \@@_name_cleanup:w #1 \@@_name_end: { }
+\cs_new:Npn \@@_name_end: { }
+% \end{macrocode}
+% As \TeX{} automatically adds |.tex| if there is no extension,
+% there is a little clean up to do here. First, make sure we are not in the
+% directory part, saving that. Then check for an extension.
+% \begin{macrocode}
+\cs_new:Npn \@@_ext_check:nn #1 #2
+{ \@@_ext_check:nnw {#2} { / } #1 / \q_@@_nil / \s_@@_stop }
+\cs_new:Npn \@@_ext_check:nnw #1 #2 #3 / #4 / #5 \s_@@_stop
+ {
+ \@@_quark_if_nil:nTF {#4}
+ {
+ \exp_args:No \@@_ext_check:nnnw
+ { \use_none:n #2 } {#1} {#3} #3 . \q_@@_nil . \s_@@_stop
+ }
+ { \@@_ext_check:nnw {#1} { #2 #3 / } #4 / #5 \s_@@_stop }
+ }
+\cs_new:Npe \@@_ext_check:nnnw #1#2#3#4 . #5 . #6 \s_@@_stop
+ {
+ \exp_not:N \@@_quark_if_nil:nTF {#5}
+ {
+ \exp_not:N \@@_ext_check:nnn
+ { #1 #3 \tl_to_str:n { .tex } } { #1 #3 } {#2}
+ }
+ { #1 #3 }
+ }
+\cs_new:Npn \@@_ext_check:nnn #1
+ { \exp_args:Nne \@@_ext_check:nnnn {#1} { \@@_full_name_aux:n {#1} } }
+\cs_new:Npn \@@_ext_check:nnnn #1#2#3#4
+ {
+ \tl_if_blank:nTF {#2}
+ {#3}
+ {
+ \bool_lazy_or:nnTF
+ { \int_compare_p:nNn {#4} = {#2} }
+ { \int_compare_p:nNn {#2} = { -1 } }
+ {#1}
+ {#3}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\file_get_full_name:nN, \file_get_full_name:VN}
+% \begin{macro}[TF]{\file_get_full_name:nN, \file_get_full_name:VN}
+% \begin{macro}{\@@_get_full_name_search:nN}
+% These functions pre-date using \cs{tex_filesize:D} for file searching,
+% so are |get| functions with protection. To avoid having different
+% search set ups, they are simply wrappers around the code above.
+% \begin{macrocode}
+\cs_new_protected:Npn \file_get_full_name:nN #1#2
+ {
+ \file_get_full_name:nNF {#1} #2
+ { \tl_set:Nn #2 { \q_no_value } }
+ }
+\cs_generate_variant:Nn \file_get_full_name:nN { V }
+\prg_new_protected_conditional:Npnn \file_get_full_name:nN #1#2 { T , F , TF }
+ {
+ \__kernel_tl_set:Nx #2
+ { \file_full_name:n {#1} }
+ \tl_if_empty:NTF #2
+ { \prg_return_false: }
+ { \prg_return_true: }
+ }
+\prg_generate_conditional_variant:Nnn \file_get_full_name:nN
+ { V } { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\g_@@_internal_ior}
+% A reserved stream to test for opening a shell.
+% \begin{macrocode}
+\ior_new:N \g_@@_internal_ior
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[rEXP]
+% {
+% \file_mdfive_hash:n, \file_mdfive_hash:V,
+% \file_size:n, \file_size:V,
+% \file_timestamp:n, \file_timestamp:V
+% }
+% \begin{macro}[rEXP]{\@@_details:nn, \@@_details_aux:nn}
+% \begin{macro}[rEXP]{\@@_mdfive_hash:n}
+% Getting file details by expansion is relatively easy if a bit repetitive.
+% As the MD5 function has a slightly different syntax from the other commands,
+% there is a little cleaning up to do.
+% \begin{macrocode}
+\cs_new:Npn \file_size:n #1
+ { \@@_details:nn {#1} { size } }
+\cs_generate_variant:Nn \file_size:n { V }
+\cs_new:Npn \file_timestamp:n #1
+ { \@@_details:nn {#1} { moddate } }
+\cs_generate_variant:Nn \file_timestamp:n { V }
+\cs_new:Npn \@@_details:nn #1#2
+ {
+ \exp_args:Ne \@@_details_aux:nn
+ { \file_full_name:n {#1} } {#2}
+ }
+\cs_new:Npn \@@_details_aux:nn #1#2
+ {
+ \tl_if_blank:nF {#1}
+ { \use:c { tex_file #2 :D } {#1} }
+ }
+\cs_new:Npn \file_mdfive_hash:n #1
+ { \exp_args:Ne \@@_mdfive_hash:n { \file_full_name:n {#1} } }
+\cs_generate_variant:Nn \file_mdfive_hash:n { V }
+\cs_new:Npn \@@_mdfive_hash:n #1
+ { \tex_mdfivesum:D file {#1} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\file_hex_dump:nnn, \file_hex_dump:Vnn, \@@_hex_dump_auxi:nnn}
+% \begin{macro}[rEXP]{\@@_hex_dump_auxii:nnnn, \@@_hex_dump_auxiii:nnnn}
+% \begin{macro}[rEXP]{\@@_hex_dump_auxiiv:nnn}
+% \begin{macro}[rEXP]{\file_hex_dump:n, \file_hex_dump:V, \@@_hex_dump:n}
+% These are separate as they need multiple arguments \emph{or} the
+% file size. For \LuaTeX{}, the emulation does not need the file
+% size so we save a little on expansion.
+% \begin{macrocode}
+\cs_new:Npn \file_hex_dump:nnn #1#2#3
+ {
+ \exp_args:Neee \@@_hex_dump_auxi:nnn
+ { \file_full_name:n {#1} }
+ { \int_eval:n {#2} }
+ { \int_eval:n {#3} }
+ }
+\cs_generate_variant:Nn \file_hex_dump:nnn { V }
+\cs_new:Npn \@@_hex_dump_auxi:nnn #1#2#3
+ {
+ \bool_lazy_any:nF
+ {
+ { \tl_if_blank_p:n {#1} }
+ { \int_compare_p:nNn {#2} = 0 }
+ { \int_compare_p:nNn {#3} = 0 }
+ }
+ {
+ \exp_args:Ne \@@_hex_dump_auxii:nnnn
+ { \@@_details_aux:nn {#1} { size } }
+ {#1} {#2} {#3}
+ }
+ }
+\cs_new:Npn \@@_hex_dump_auxii:nnnn #1#2#3#4
+ {
+ \int_compare:nNnTF {#3} > 0
+ { \@@_hex_dump_auxiii:nnnn {#3} }
+ {
+ \exp_args:Ne \@@_hex_dump_auxiii:nnnn
+ { \int_eval:n { #1 + #3 } }
+ }
+ {#1} {#2} {#4}
+ }
+\cs_new:Npn \@@_hex_dump_auxiii:nnnn #1#2#3#4
+ {
+ \int_compare:nNnTF {#4} > 0
+ { \@@_hex_dump_auxiv:nnn {#4} }
+ {
+ \exp_args:Ne \@@_hex_dump_auxiv:nnn
+ { \int_eval:n { #2 + #4 } }
+ }
+ {#1} {#3}
+ }
+\cs_new:Npn \@@_hex_dump_auxiv:nnn #1#2#3
+ {
+ \tex_filedump:D
+ offset ~ \int_eval:n { #2 - 1 } ~
+ length ~ \int_eval:n { #1 - #2 + 1 }
+ {#3}
+ }
+\cs_new:Npn \file_hex_dump:n #1
+ { \exp_args:Ne \@@_hex_dump:n { \file_full_name:n {#1} } }
+\cs_generate_variant:Nn \file_hex_dump:n { V }
+\sys_if_engine_luatex:TF
+ {
+ \cs_new:Npn \@@_hex_dump:n #1
+ {
+ \tl_if_blank:nF {#1}
+ { \tex_filedump:D whole {#1} {#1} }
+ }
+ }
+ {
+ \cs_new:Npn \@@_hex_dump:n #1
+ {
+ \tl_if_blank:nF {#1}
+ { \tex_filedump:D length \tex_filesize:D {#1} {#1} }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[noTF]
+% {
+% \file_get_hex_dump:nN, \file_get_hex_dump:VN,
+% \file_get_mdfive_hash:nN, \file_get_mdfive_hash:VN,
+% \file_get_size:nN, \file_get_size:VN,
+% \file_get_timestamp:nN, \file_get_timestamp:VN
+% }
+% \begin{macro}{\@@_get_details:nnN}
+% Non-expandable wrappers around the above in the case where appropriate
+% primitive support exists.
+% \begin{macrocode}
+\cs_new_protected:Npn \file_get_hex_dump:nN #1#2
+ { \file_get_hex_dump:nNF {#1} #2 { \tl_set:Nn #2 { \q_no_value } } }
+\cs_generate_variant:Nn \file_get_hex_dump:nN { V }
+\cs_new_protected:Npn \file_get_mdfive_hash:nN #1#2
+ { \file_get_mdfive_hash:nNF {#1} #2 { \tl_set:Nn #2 { \q_no_value } } }
+\cs_generate_variant:Nn \file_get_mdfive_hash:nN { V }
+\cs_new_protected:Npn \file_get_size:nN #1#2
+ { \file_get_size:nNF {#1} #2 { \tl_set:Nn #2 { \q_no_value } } }
+\cs_generate_variant:Nn \file_get_size:nN { V }
+\cs_new_protected:Npn \file_get_timestamp:nN #1#2
+ { \file_get_timestamp:nNF {#1} #2 { \tl_set:Nn #2 { \q_no_value } } }
+\cs_generate_variant:Nn \file_get_timestamp:nN { V }
+\prg_new_protected_conditional:Npnn \file_get_hex_dump:nN #1#2 { T , F , TF }
+ { \@@_get_details:nnN {#1} { hex_dump } #2 }
+\prg_generate_conditional_variant:Nnn \file_get_hex_dump:nN
+ { V } { T , F , TF }
+\prg_new_protected_conditional:Npnn \file_get_mdfive_hash:nN #1#2 { T , F , TF }
+ { \@@_get_details:nnN {#1} { mdfive_hash } #2 }
+\prg_generate_conditional_variant:Nnn \file_get_mdfive_hash:nN
+ { V } { T , F , TF }
+\prg_new_protected_conditional:Npnn \file_get_size:nN #1#2 { T , F , TF }
+ { \@@_get_details:nnN {#1} { size } #2 }
+\prg_generate_conditional_variant:Nnn \file_get_size:nN
+ { V } { T , F , TF }
+\prg_new_protected_conditional:Npnn \file_get_timestamp:nN #1#2 { T , F , TF }
+ { \@@_get_details:nnN {#1} { timestamp } #2 }
+\prg_generate_conditional_variant:Nnn \file_get_timestamp:nN
+ { V } { T , F , TF }
+\cs_new_protected:Npn \@@_get_details:nnN #1#2#3
+ {
+ \__kernel_tl_set:Nx #3
+ { \use:c { file_ #2 :n } {#1} }
+ \tl_if_empty:NTF #3
+ { \prg_return_false: }
+ { \prg_return_true: }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[noTF]{\file_get_hex_dump:nnnN, \file_get_hex_dump:VnnN}
+% Custom code due to the additional arguments.
+% \begin{macrocode}
+\cs_new_protected:Npn \file_get_hex_dump:nnnN #1#2#3#4
+ {
+ \file_get_hex_dump:nnnNF {#1} {#2} {#3} #4
+ { \tl_set:Nn #4 { \q_no_value } }
+ }
+\cs_generate_variant:Nn \file_get_hex_dump:nnnN { V }
+\prg_new_protected_conditional:Npnn \file_get_hex_dump:nnnN #1#2#3#4
+ { T , F , TF }
+ {
+ \__kernel_tl_set:Nx #4
+ { \file_hex_dump:nnn {#1} {#2} {#3} }
+ \tl_if_empty:NTF #4
+ { \prg_return_false: }
+ { \prg_return_true: }
+ }
+\prg_generate_conditional_variant:Nnn \file_get_hex_dump:nnnN
+ { V } { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_str_cmp:nn}
+% As we are doing a fixed-length \enquote{big} integer comparison, it
+% is easiest to use the low-level behavior of string comparisons.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_str_cmp:nn \tex_strcmp:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, pTF]
+% {
+% \file_compare_timestamp:nNn,
+% \file_compare_timestamp:nNV,
+% \file_compare_timestamp:VNn,
+% \file_compare_timestamp:VNV
+% }
+% \begin{macro}[EXP]{\@@_compare_timestamp:nnN}
+% \begin{macro}[EXP]{\@@_timestamp:n}
+% Comparison of file date can be done by using the low-level nature of the
+% string comparison functions.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \file_compare_timestamp:nNn #1#2#3
+ { p , T , F , TF }
+ {
+ \exp_args:Nee \@@_compare_timestamp:nnN
+ { \file_full_name:n {#1} }
+ { \file_full_name:n {#3} }
+ #2
+ }
+\prg_generate_conditional_variant:Nnn \file_compare_timestamp:nNn
+ { nNV , V , VNV } { p , T , F , TF }
+\cs_new:Npn \@@_compare_timestamp:nnN #1#2#3
+ {
+ \tl_if_blank:nTF {#1}
+ {
+ \if_charcode:w #3 <
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ {
+ \tl_if_blank:nTF {#2}
+ {
+ \if_charcode:w #3 >
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ {
+ \if_int_compare:w
+ \@@_str_cmp:nn
+ { \@@_timestamp:n {#1} }
+ { \@@_timestamp:n {#2} }
+ #3 \c_zero_int
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ }
+ }
+\cs_new_eq:NN \@@_timestamp:n \tex_filemoddate:D
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\file_if_exist:n, \file_if_exist:V}
+% The test for the existence of a file is a wrapper around the function to
+% add a path to a file. If the file was found, the path contains
+% something, whereas if the file was not located then the return value
+% is empty.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \file_if_exist:n #1 { p , T , F , TF }
+ {
+ \tl_if_blank:eTF { \file_full_name:n {#1} }
+ { \prg_return_false: }
+ { \prg_return_true: }
+ }
+\prg_generate_conditional_variant:Nnn \file_if_exist:n { V } { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \file_if_exist_input:n,
+% \file_if_exist_input:V,
+% \file_if_exist_input:nF,
+% \file_if_exist_input:VF
+% }
+% Input of a file with a test for existence. We do not define the |T|
+% or |TF| variants because the most useful place to place the
+% \meta{true code} would be inconsistent with other conditionals.
+% \begin{macrocode}
+\cs_new_protected:Npn \file_if_exist_input:n #1
+ {
+ \file_get_full_name:nNT {#1} \l_@@_full_name_tl
+ { \@@_input:V \l_@@_full_name_tl }
+ }
+\cs_generate_variant:Nn \file_if_exist_input:n { V }
+\cs_new_protected:Npn \file_if_exist_input:nF #1#2
+ {
+ \file_get_full_name:nNTF {#1} \l_@@_full_name_tl
+ { \@@_input:V \l_@@_full_name_tl }
+ {#2}
+ }
+\cs_generate_variant:Nn \file_if_exist_input:nF { V }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\file_input_stop:}
+% A simple rename.
+% \begin{macrocode}
+\cs_new_protected:Npn \file_input_stop: { \tex_endinput:D }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__kernel_file_missing:n}
+% An error message for a missing file, also used in \cs{ior_open:Nn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_file_missing:n #1
+ {
+ \msg_error:nne { kernel } { file-not-found }
+ { \__kernel_file_name_sanitize:n {#1} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\file_input:n, \file_input:V}
+% \begin{macro}{\@@_input:n, \@@_input:V}
+% \begin{macro}{\@@_input_push:n, \__kernel_file_input_push:n}
+% \begin{macro}{\@@_input_pop:, \__kernel_file_input_pop:}
+% \begin{macro}{\@@_input_pop:nnn}
+% Loading a file is done in a safe way, checking first that the file
+% exists and loading only if it does. Push the file name on the
+% \cs{g_@@_stack_seq}, and add it to the file list, either
+% \cs{g_@@_record_seq}, or \cs{@filelist} in package mode.
+% \begin{macrocode}
+\cs_new_protected:Npn \file_input:n #1
+ {
+ \file_get_full_name:nNTF {#1} \l_@@_full_name_tl
+ { \@@_input:V \l_@@_full_name_tl }
+ { \__kernel_file_missing:n {#1} }
+ }
+\cs_generate_variant:Nn \file_input:n { V }
+\cs_new_protected:Npe \@@_input:n #1
+ {
+ \exp_not:N \clist_if_exist:NTF \exp_not:N \@filelist
+ { \exp_not:N \@addtofilelist {#1} }
+ { \seq_gput_right:Nn \exp_not:N \g_@@_record_seq {#1} }
+ \exp_not:N \@@_input_push:n {#1}
+ \exp_not:N \tex_input:D
+ \sys_if_engine_luatex:TF
+ { {#1} }
+ { \exp_not:N \__kernel_file_name_quote:n {#1} \scan_stop: }
+ \exp_not:N \@@_input_pop:
+ }
+\cs_generate_variant:Nn \@@_input:n { V }
+% \end{macrocode}
+% Keeping a track of the file data is easy enough: we store the separated
+% parts so we do not need to parse them twice.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_input_push:n #1
+ {
+ \seq_gpush:Ne \g_@@_stack_seq
+ {
+ { \g_file_curr_dir_str }
+ { \g_file_curr_name_str }
+ { \g_file_curr_ext_str }
+ }
+ \file_parse_full_name:nNNN {#1}
+ \l_@@_dir_str \l_@@_name_str \l_@@_ext_str
+ \str_gset_eq:NN \g_file_curr_dir_str \l_@@_dir_str
+ \str_gset_eq:NN \g_file_curr_name_str \l_@@_name_str
+ \str_gset_eq:NN \g_file_curr_ext_str \l_@@_ext_str
+ }
+\cs_new_eq:NN \__kernel_file_input_push:n \@@_input_push:n
+\cs_new_protected:Npn \@@_input_pop:
+ {
+ \seq_gpop:NN \g_@@_stack_seq \l_@@_internal_tl
+ \exp_after:wN \@@_input_pop:nnn \l_@@_internal_tl
+ }
+\cs_new_eq:NN \__kernel_file_input_pop: \@@_input_pop:
+\cs_new_protected:Npn \@@_input_pop:nnn #1#2#3
+ {
+ \str_gset:Nn \g_file_curr_dir_str {#1}
+ \str_gset:Nn \g_file_curr_name_str {#2}
+ \str_gset:Nn \g_file_curr_ext_str {#3}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\file_input_raw:n, \file_input_raw:V, \@@_input_raw:nn}
+% No error checking, no tracking.
+% \begin{macrocode}
+\cs_new:Npn \file_input_raw:n #1
+ { \exp_args:Ne \@@_input_raw:nn { \file_full_name:n {#1} } {#1} }
+\cs_generate_variant:Nn \file_input_raw:n { V }
+\cs_new:Npe \@@_input_raw:nn #1#2
+ {
+ \exp_not:N \tl_if_blank:nTF {#1}
+ {
+ \exp_not:N \exp_args:Nnne \exp_not:N \msg_expandable_error:nnn
+ { kernel } { file-not-found }
+ { \exp_not:N \__kernel_file_name_sanitize:n {#2} }
+ }
+ {
+ \exp_not:N \tex_input:D
+ \sys_if_engine_luatex:TF
+ { {#1} }
+ { \exp_not:N \__kernel_file_name_quote:n {#1} \scan_stop: }
+ }
+ }
+\exp_args_generate:n { nne }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\file_parse_full_name:n, \file_parse_full_name:V}
+% \begin{macro}{\file_parse_full_name_apply:nN, \file_parse_full_name_apply:VN}
+% The main parsing macro \cs{file_parse_full_name_apply:nN} passes the
+% file name |#1| through \cs{__kernel_file_name_sanitize:n} so that we
+% have a single normalised way to treat files internally.
+% \cs{file_parse_full_name:n} uses the former, with
+% \cs{prg_do_nothing:} to
+% leave each part of the name within a pair of braces.
+% \begin{macrocode}
+\cs_new:Npn \file_parse_full_name:n #1
+ {
+ \file_parse_full_name_apply:nN {#1}
+ \prg_do_nothing:
+ }
+\cs_generate_variant:Nn \file_parse_full_name:n { V }
+\cs_new:Npn \file_parse_full_name_apply:nN #1
+ {
+ \exp_args:Ne \@@_parse_full_name_auxi:nN
+ { \__kernel_file_name_sanitize:n {#1} }
+ }
+\cs_generate_variant:Nn \file_parse_full_name_apply:nN { V }
+% \end{macrocode}
+%
+% \begin{macro}{\@@_parse_full_name_auxi:nN}
+% \begin{macro}{\@@_parse_full_name_area:nw}
+% \cs{@@_parse_full_name_area:nw} splits the file name into chunks
+% separated by |/|, until the last one is reached. The last chunk is
+% the file name plus the extension, and everything before that is the
+% path. When \cs{@@_parse_full_name_area:nw} is done, it leaves
+% the path within braces after the scan mark \cs{s_@@_stop} and
+% proceeds parsing the actual file name.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_full_name_auxi:nN #1
+ {
+ \@@_parse_full_name_area:nw { } #1
+ / \s_@@_stop
+ }
+\cs_new:Npn \@@_parse_full_name_area:nw #1 #2 / #3 \s_@@_stop
+ {
+ \tl_if_empty:nTF {#3}
+ { \@@_parse_full_name_base:nw { } #2 . \s_@@_stop {#1} }
+ { \@@_parse_full_name_area:nw { #1 / #2 } #3 \s_@@_stop }
+ }
+% \end{macrocode}
+%
+% \begin{macro}{\@@_parse_full_name_base:nw}
+% \cs{@@_parse_full_name_base:nw} does roughly the same as above, but
+% it separates the chunks at each period. However here there's some
+% extra complications: In case |#1| is empty, it is assumed that the
+% extension is actually empty, and the file name is |#2|. Besides, an
+% extra |.| has to be added to |#2| because it is later removed in
+% \cs{@@_parse_full_name_tidy:nnnN}. In any case, if there's an
+% extension, it is returned with a leading |.|.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_full_name_base:nw #1 #2 . #3 \s_@@_stop
+ {
+ \tl_if_empty:nTF {#3}
+ {
+ \tl_if_empty:nTF {#1}
+ {
+ \tl_if_empty:nTF {#2}
+ { \@@_parse_full_name_tidy:nnnN { } { } }
+ { \@@_parse_full_name_tidy:nnnN { .#2 } { } }
+ }
+ { \@@_parse_full_name_tidy:nnnN {#1} { .#2 } }
+ }
+ { \@@_parse_full_name_base:nw { #1 . #2 } #3 \s_@@_stop }
+ }
+% \end{macrocode}
+%
+% \begin{macro}{\@@_parse_full_name_tidy:nnnN}
+% Now we just need to tidy some bits left loose before. The loop
+% used in the two macros above start with a leading |/| and |.| in the
+% file path an name, so here we need to remove them, except in the
+% path, if it is a single |/|, in which case it's left as is. After
+% all's done, pass to |#4|.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_full_name_tidy:nnnN #1 #2 #3 #4
+ {
+ \exp_args:Nee #4
+ {
+ \str_if_eq:nnF {#3} { / } { \use_none:n }
+ #3 \prg_do_nothing:
+ }
+ { \use_none:n #1 \prg_do_nothing: }
+ {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\file_parse_full_name:nNNN, \file_parse_full_name:VNNN}
+% \begin{macrocode}
+\cs_new_protected:Npn \file_parse_full_name:nNNN #1 #2 #3 #4
+ {
+ \file_parse_full_name_apply:nN {#1}
+ \@@_full_name_assign:nnnNNN #2 #3 #4
+ }
+\cs_new_protected:Npn \@@_full_name_assign:nnnNNN #1 #2 #3 #4 #5 #6
+ {
+ \str_set:Nn #4 {#1}
+ \str_set:Nn #5 {#2}
+ \str_set:Nn #6 {#3}
+ }
+\cs_generate_variant:Nn \file_parse_full_name:nNNN { V }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\file_show_list:, \file_log_list:, \@@_list:N}
+% \begin{macro}[EXP]{\@@_list_aux:n}
+% A function to list all files used to the log, without duplicates.
+% In package mode, if \cs{@filelist} is still defined, we need to take
+% this list of file names into account (we capture it
+% \cs{AtBeginDocument} into \cs{g_@@_record_seq}), turning it to a
+% string (this does not affect the commas of this comma list).
+% \begin{macrocode}
+\cs_new_protected:Npn \file_show_list: { \@@_list:N \msg_show:nneeee }
+\cs_new_protected:Npn \file_log_list: { \@@_list:N \msg_log:nneeee }
+\cs_new_protected:Npn \@@_list:N #1
+ {
+ \seq_clear:N \l_@@_tmp_seq
+ \clist_if_exist:NT \@filelist
+ {
+ \exp_args:NNe \seq_set_from_clist:Nn \l_@@_tmp_seq
+ { \tl_to_str:N \@filelist }
+ }
+ \seq_concat:NNN \l_@@_tmp_seq \l_@@_tmp_seq \g_@@_record_seq
+ \seq_remove_duplicates:N \l_@@_tmp_seq
+ #1 { kernel } { file-list }
+ { \seq_map_function:NN \l_@@_tmp_seq \@@_list_aux:n }
+ { } { } { }
+ }
+\cs_new:Npn \@@_list_aux:n #1 { \iow_newline: #1 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% When used as a package, there is a need to hold onto the standard file
+% list as well as the new one here. File names recorded in
+% \cs{@filelist} must be turned to strings before being added to
+% \cs{g_@@_record_seq}.
+% \begin{macrocode}
+\cs_if_exist:NT \@filelist
+ {
+ \AtBeginDocument
+ {
+ \exp_args:NNe \seq_set_from_clist:Nn \l_@@_tmp_seq
+ { \tl_to_str:N \@filelist }
+ \seq_gconcat:NNN
+ \g_@@_record_seq
+ \g_@@_record_seq
+ \l_@@_tmp_seq
+ }
+ }
+% \end{macrocode}
+%
+% \subsection{GetIdInfo}
+%
+% \begin{macro}{\GetIdInfo}
+% \begin{macro}{\@@_id_info_auxi:w, \@@_id_info_auxii:w, \@@_id_info_auxiii:w}
+% As documented in \pkg{expl3.dtx} this function extracts file name
+% etc from an \textsc{svn} \texttt{Id} line. This used to be how we
+% got version number and so on in all modules, so it had to be defined
+% in \pkg{l3bootstrap}. Now it's more convenient to define it after
+% we have set up quite a lot of tools, and \pkg{l3file} seems the
+% least unreasonable place for it.
+%
+% The idea here is to extract out the information needed from a standard
+% \textsc{svn} \texttt{Id} line, but to avoid a line that would get
+% changed when the file is checked in. Hence the fact that none of the
+% lines here include both a dollar sign and the \texttt{Id} keyword!
+% \begin{macrocode}
+\cs_new_protected:Npn \GetIdInfo
+ {
+ \tl_clear_new:N \ExplFileDescription
+ \tl_clear_new:N \ExplFileDate
+ \tl_clear_new:N \ExplFileName
+ \tl_clear_new:N \ExplFileExtension
+ \tl_clear_new:N \ExplFileVersion
+ \group_begin:
+ \char_set_catcode_space:n { 32 }
+ \exp_after:wN
+ \group_end:
+ \@@_id_info_auxi:w
+ }
+% \end{macrocode}
+% A first check for a completely empty \textsc{svn} field. If that is
+% not the case, there is a second case when a file created using
+% \texttt{svn cp} but has not been checked in. That leaves a special
+% marker \texttt{-1} version, which has no further data. Dealing
+% correctly with that is the reason for the space in the line to use
+% \cs{@@_id_info_auxii:w}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_id_info_auxi:w $ #1 $ #2
+ {
+ \tl_set:Nn \ExplFileDescription {#2}
+ \str_if_eq:nnTF {#1} { Id }
+ {
+ \tl_set:Nn \ExplFileDate { 0000/00/00 }
+ \tl_set:Nn \ExplFileName { [unknown] }
+ \tl_set:Nn \ExplFileExtension { [unknown~extension] }
+ \tl_set:Nn \ExplFileVersion {-1}
+ }
+ { \@@_id_info_auxii:w #1 ~ \s_@@_stop }
+ }
+% \end{macrocode}
+% Here, |#1| is |Id|, |#2| is the file name, |#3| is the extension,
+% |#4| is the version, |#5| is the check in date and |#6| is the check
+% in time and user, plus some trailing spaces. If |#4| is the marker
+% |-1| value then |#5| and |#6| are empty.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_id_info_auxii:w
+ #1 ~ #2.#3 ~ #4 ~ #5 ~ #6 \s_@@_stop
+ {
+ \tl_set:Nn \ExplFileName {#2}
+ \tl_set:Nn \ExplFileExtension {#3}
+ \tl_set:Nn \ExplFileVersion {#4}
+ \str_if_eq:nnTF {#4} {-1}
+ { \tl_set:Nn \ExplFileDate { 0000/00/00 } }
+ { \@@_id_info_auxiii:w #5 - 0 - 0 - \s_@@_stop }
+ }
+% \end{macrocode}
+% Convert an \textsc{svn}-style date into a \LaTeX{}-style one.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_id_info_auxiii:w #1 - #2 - #3 - #4 \s_@@_stop
+ { \tl_set:Nn \ExplFileDate { #1/#2/#3 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Checking the version of kernel dependencies}
+%
+% \begin{macro}{\__kernel_dependency_version_check:Nn}
+% \begin{macro}{\__kernel_dependency_version_check:nn}
+% \begin{macro}{\@@_kernel_dependency_compare:nnn,\@@_parse_version:w}
+% This function is responsible for checking if dependencies of the
+% \LaTeX3 kernel match the version preloaded in the \LaTeXe{} kernel.
+% If versions don't match, the function attempts to tell why by
+% searching for a possible stray format file.
+%
+% The function starts by checking that the kernel date is defined, and
+% if not zero is used to force the error route. The kernel date is
+% then compared with the argument requested date (ususally the
+% packaging date of the dependency). If the kernel date is less than
+% the required date, it's an error and the loading should abort.
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_dependency_version_check:Nn #1
+ { \exp_args:NV \__kernel_dependency_version_check:nn #1 }
+\cs_new_protected:Npn \__kernel_dependency_version_check:nn #1
+ {
+ \cs_if_exist:NTF \c__kernel_expl_date_tl
+ {
+ \exp_args:NV \@@_kernel_dependency_compare:nnn
+ \c__kernel_expl_date_tl {#1}
+ }
+ { \@@_kernel_dependency_compare:nnn { 0000-00-00 } {#1} }
+ }
+\cs_new_protected:Npn \@@_kernel_dependency_compare:nnn #1 #2 #3
+ {
+ \int_compare:nNnT
+ { \@@_parse_version:w #1 \s_@@_stop } <
+ { \@@_parse_version:w #2 \s_@@_stop }
+ { \@@_mismatched_dependency_error:nn {#2} {#3} }
+ }
+\cs_new:Npn \@@_parse_version:w #1 - #2 - #3 \s_@@_stop {#1#2#3}
+% \end{macrocode}
+%
+% \begin{macro}{\@@_mismatched_dependency_error:nn}
+% If the versions differ, then we try to give the user some guidance.
+% This function starts by taking the engine name \cs{c_sys_engine_str}
+% and replacing |tex| by |latex|, then building a command of the form:
+% \begin{texttt}
+% kpsewhich --all --engine=\meta{engine} \meta{format}[-dev].fmt
+% \end{texttt}
+% to query the format files available. A shell is opened and each
+% line is read into a sequence.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_mismatched_dependency_error:nn #1 #2
+ {
+ \exp_args:NNe \ior_shell_open:Nn \g_@@_internal_ior
+ {
+ kpsewhich ~ --all ~
+ --engine = \c_sys_engine_exec_str
+ \c_space_tl \c_sys_engine_format_str
+ \bool_lazy_and:nnT
+ { \tl_if_exist_p:N \development@branch@name }
+ { ! \tl_if_empty_p:N \development@branch@name }
+ { -dev } .fmt
+ }
+ \seq_clear:N \l_@@_tmp_seq
+ \ior_map_inline:Nn \g_@@_internal_ior
+ { \seq_put_right:Nn \l_@@_tmp_seq {##1} }
+ \ior_close:N \g_@@_internal_ior
+ \msg_error:nnnn { kernel } { mismatched-support-file }
+ {#1} {#2}
+% \end{macrocode}
+% And finish by ending the current file.
+% \begin{macrocode}
+ \tex_endinput:D
+ }
+% \end{macrocode}
+%
+% Now define the actual error message:
+% \begin{macrocode}
+\msg_new:nnnn { kernel } { mismatched-support-file }
+ {
+ Mismatched~LaTeX~support~files~detected. \\
+ Loading~'#2'~aborted!
+% \end{macrocode}
+% \cs{c__kernel_expl_date_tl} may not exist, due to an older format,
+% so only print the dates when the sentinel token list exists:
+% \begin{macrocode}
+ \tl_if_exist:NT \c__kernel_expl_date_tl
+ {
+ \\ \\
+ The~L3~programming~layer~in~the~LaTeX~format \\
+ is~dated~\c__kernel_expl_date_tl,~but~in~your~TeX~
+ tree~the~files~require \\ at~least~#1.
+ }
+ }
+ {
+% \end{macrocode}
+% The sequence containing the format files should have exactly one
+% item: the format file currently being run. If that's the case, the
+% cause of the error is not that, so print a generic help with some
+% possible causes. If more than one format file was found, then print
+% the list to the user, with appropriate indications of what's in the
+% system and what's in the user tree.
+% \begin{macrocode}
+ \int_compare:nNnTF { \seq_count:N \l_@@_tmp_seq } > 1
+ {
+ The~cause~seems~to~be~an~old~format~file~in~the~user~tree. \\
+ LaTeX~found~these~files:
+ \seq_map_tokens:Nn \l_@@_tmp_seq { \\~-~\use:n } \\
+ Try~deleting~the~file~in~the~user~tree~then~run~LaTeX~again.
+ }
+ {
+ The~most~likely~causes~are:
+ \\~-~A~recent~format~generation~failed;
+ \\~-~A~stray~format~file~in~the~user~tree~which~needs~
+ to~be~removed~or~rebuilt;
+ \\~-~You~are~running~a~manually~installed~version~of~#2 \\
+ \ \ \ which~is~incompatible~with~the~version~in~LaTeX. \\
+ }
+ \\
+ LaTeX~will~abort~loading~the~incompatible~support~files~
+ but~this~may~lead~to \\ later~errors.~Please~ensure~that~
+ your~LaTeX~format~is~correctly~regenerated.
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+% \begin{macrocode}
+\msg_new:nnnn { kernel } { file-not-found }
+ { File~'#1'~not~found. }
+ {
+ The~requested~file~could~not~be~found~in~the~current~directory,~
+ in~the~TeX~search~path~or~in~the~LaTeX~search~path.
+ }
+\msg_new:nnn { kernel } { file-list }
+ {
+ >~File~List~<
+ #1 \\
+ .............
+ }
+\msg_new:nnnn { kernel } { filename-chars-lost }
+ { #1~invalid~in~file~name.~Lost:~#2. }
+ {
+ There~was~an~invalid~token~in~the~file~name~that~caused~
+ the~characters~following~it~to~be~lost.
+ }
+\msg_new:nnnn { kernel } { filename-missing-endcsname }
+ { Missing~\iow_char:N\\endcsname~inserted~in~filename. }
+ {
+ The~file~name~had~more~\iow_char:N\\csname~commands~than~
+ \iow_char:N\\endcsname~ones.~LaTeX~will~add~the~missing~
+ \iow_char:N\\endcsname~and~try~to~continue~as~best~as~it~can.
+ }
+\msg_new:nnnn { kernel } { unbalanced-quote-in-filename }
+ { Unbalanced~quotes~in~file~name~'#1'. }
+ {
+ File~names~must~contain~balanced~numbers~of~quotes~(").
+ }
+\msg_new:nnnn { kernel } { iow-indent }
+ { Only~#1 allows~#2 }
+ {
+ The~command~#2 can~only~be~used~in~messages~
+ which~will~be~wrapped~using~#1.
+ \tl_if_empty:nF {#3} { ~ It~was~called~with~argument~'#3'. }
+ }
+% \end{macrocode}
+%
+% \subsection{Functions delayed from earlier modules}
+%
+%<@@=sys>
+%
+% \begin{variable}{\c_sys_platform_str}
+% Detecting the platform on \LuaTeX{} is easy: for other engines, we use
+% the fact that the two common cases have special null files. It is possible
+% to probe further (see package \pkg{platform}), but that requires shell
+% escape and seems unlikely to be useful. This is set up here as it requires
+% file searching.
+% \begin{macrocode}
+\sys_if_engine_luatex:TF
+ {
+ \str_const:Ne \c_sys_platform_str
+ { \tex_directlua:D { tex.print(os.type) } }
+ }
+ {
+ \file_if_exist:nTF { nul: }
+ {
+ \file_if_exist:nF { /dev/null }
+ { \str_const:Nn \c_sys_platform_str { windows } }
+ }
+ {
+ \file_if_exist:nT { /dev/null }
+ { \str_const:Nn \c_sys_platform_str { unix } }
+ }
+ }
+\cs_if_exist:NF \c_sys_platform_str
+ { \str_const:Nn \c_sys_platform_str { unknown } }
+% \end{macrocode}
+% \end{variable}
+% \begin{macro}[pTF]{\sys_if_platform_unix:}
+% \begin{macro}[pTF]{\sys_if_platform_windows:}
+% We can now set up the tests.
+% \begin{macrocode}
+\clist_map_inline:nn { unix , windows }
+ {
+ \@@_const:nn { sys_if_platform_ #1 }
+ { \str_if_eq_p:Vn \c_sys_platform_str { #1 } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3flag.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3flag.dtx
new file mode 100644
index 00000000000..c3bd4a5085a
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3flag.dtx
@@ -0,0 +1,396 @@
+% \iffalse meta-comment
+%
+%% File: l3flag.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3flag} module\\ Expandable flags^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% Flags are the only data-type that can be modified in expansion-only
+% contexts. This module is meant mostly for kernel use: in almost all
+% cases, booleans or integers should be preferred to flags because they
+% are very significantly faster.
+%
+% A flag can hold any (small) non-negative value, which we call its
+% \meta{height}. In expansion-only contexts, a flag can only be
+% \enquote{raised}: this increases the \meta{height} by $1$. The \meta{height}
+% can also be queried expandably. However, decreasing it, or setting it
+% to zero requires non-expandable assignments.
+%
+% Flag variables are always local.
+%
+% A typical use case of flags would be to keep track of whether an
+% exceptional condition has occurred during expandable processing, and
+% produce a meaningful (non-expandable) message after the end of the
+% expandable processing. This is exemplified by \pkg{l3str-convert},
+% which for performance reasons performs conversions of individual
+% characters expandably and for readability reasons produces a single
+% error message describing incorrect inputs that were encountered.
+%
+% Flags should not be used without carefully considering the fact that
+% raising a flag takes a time and memory proportional to its height and
+% that the memory cannot be reclaimed even if the flag is cleared.
+% Flags should not be used unless it is unavoidable.
+%
+% In earlier versions, flags were referenced by an \texttt{n}-type
+% \meta{flag name} such as \texttt{fp_overflow}, used as part of
+% \cs{use:c} constructions. All of the commands described below have
+% \texttt{n}-type analogues that can still appear in old code, but the
+% \texttt{N}-type commands are to be preferred moving forward. The
+% \texttt{n}-type \meta{flag name} is simply mapped to
+% \cs[no-index]{l_\meta{flag name}_flag}, which makes it easier for
+% packages using public flags (such as \pkg{l3fp}) to retain backwards
+% compatibility.
+%
+% \section{Setting up flags}
+%
+% \begin{function}[added = 2024-01-12]{\flag_new:N, \flag_new:c}
+% \begin{syntax}
+% \cs{flag_new:N} \meta{flag~var}
+% \end{syntax}
+% Creates a new \meta{flag~var}, or raises an error if the name is
+% already taken. The declaration is global, but flags are always local
+% variables. The \meta{flag~var} initially has zero height.
+% \end{function}
+%
+% \begin{function}[added = 2024-01-12]{\flag_clear:N, \flag_clear:c}
+% \begin{syntax}
+% \cs{flag_clear:N} \meta{flag~var}
+% \end{syntax}
+% Sets the height of the \meta{flag~var} to zero. The assignment is local.
+% \end{function}
+%
+% \begin{function}[added = 2024-01-12]{\flag_clear_new:N, \flag_clear_new:c}
+% \begin{syntax}
+% \cs{flag_clear_new:N} \meta{flag~var}
+% \end{syntax}
+% Ensures that the \meta{flag~var} exists globally by applying
+% \cs{flag_new:N} if necessary, then applies \cs{flag_clear:N}, setting
+% the height to zero locally.
+% \end{function}
+%
+% \begin{function}[added = 2024-01-12]{\flag_show:N, \flag_show:c}
+% \begin{syntax}
+% \cs{flag_show:N} \meta{flag~var}
+% \end{syntax}
+% Displays the height of the \meta{flag~var} in the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2024-01-12]{\flag_log:N, \flag_log:c}
+% \begin{syntax}
+% \cs{flag_log:N} \meta{flag~var}
+% \end{syntax}
+% Writes the height of the \meta{flag~var} in the log file.
+% \end{function}
+%
+% \section{Expandable flag commands}
+%
+% \begin{function}[EXP, pTF, added = 2024-01-12]{\flag_if_exist:N, \flag_if_exist:c}
+% \begin{syntax}
+% \cs{flag_if_exist_p:N} \meta{flag~var}
+% \cs{flag_if_exist:NTF} \meta{flag~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% This function returns \texttt{true} if the \meta{flag~var} is
+% currently defined, and \texttt{false} otherwise. This does not check
+% that the \meta{flag~var} really is a flag variable.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added = 2024-01-12]{\flag_if_raised:N, \flag_if_raised:c}
+% \begin{syntax}
+% \cs{flag_if_raised_p:N} \meta{flag~var}
+% \cs{flag_if_raised:NTF} \meta{flag~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% This function returns \texttt{true} if the \meta{flag~var} has non-zero
+% height, and \texttt{false} if the \meta{flag~var} has zero height.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2024-01-12]{\flag_height:N, \flag_height:c}
+% \begin{syntax}
+% \cs{flag_height:N} \meta{flag~var}
+% \end{syntax}
+% Expands to the height of the \meta{flag~var} as an integer denotation.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2024-01-12]{\flag_raise:N, \flag_raise:c}
+% \begin{syntax}
+% \cs{flag_raise:N} \meta{flag~var}
+% \end{syntax}
+% The height of \meta{flag~var} is increased by $1$ locally.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2024-01-12]{\flag_ensure_raised:N, \flag_ensure_raised:c}
+% \begin{syntax}
+% \cs{flag_ensure_raised:N} \meta{flag~var}
+% \end{syntax}
+% Ensures the \meta{flag~var} is raised by making its height at least~$1$,
+% locally.
+% \end{function}
+%
+% \begin{variable}[added = 2024-01-12]{\l_tmpa_flag, \l_tmpb_flag}
+% Scratch flag for local assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3flag} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=flag>
+% \end{macrocode}
+%
+% \TestFiles{m3flag001}
+%
+% \subsection{Protected flag commands}
+%
+% The height $h$ of a flag (which is initially zero) is stored by
+% setting control sequences of the form \cs[no-index]{\meta{flag
+% name}\meta{integer}} to \tn{relax} for $0\leq\meta{integer}<h$. These
+% control sequences are produced by \cs{cs:w} \meta{flag~var}
+% \meta{integer} \cs{cs_end:}, namely the \meta{flag~var} is actually a
+% (protected) macro expanding to its own csname.
+%
+% \begin{macro}{\flag_new:N, \flag_new:c}
+% Evaluate the csname of~|#1| for use in constructing the various
+% indexed macros.
+% \begin{macrocode}
+\cs_new_protected:Npn \flag_new:N #1
+ { \cs_new_protected:Npe #1 { \cs_to_str:N #1 } }
+\cs_generate_variant:Nn \flag_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\l_tmpa_flag, \l_tmpb_flag}
+% Two flag variables for scratch use.
+% \begin{macrocode}
+\flag_new:N \l_tmpa_flag
+\flag_new:N \l_tmpb_flag
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\flag_clear:N, \flag_clear:c}
+% \begin{macro}{\@@_clear:wN}
+% Undefine control sequences, starting from the |0| flag, upwards,
+% until reaching an undefined control sequence. We don't use
+% \cs{cs_undefine:c} because that would act globally.
+% \begin{macrocode}
+\cs_new_protected:Npn \flag_clear:N #1
+ {
+ \@@_clear:wN 0 ; #1
+ \prg_break_point:
+ }
+\cs_generate_variant:Nn \flag_clear:N { c }
+\cs_new_protected:Npn \@@_clear:wN #1 ; #2
+ {
+ \if_cs_exist:w #2 #1 \cs_end: \else:
+ \prg_break:n
+ \fi:
+ \cs_set_eq:cN { #2 #1 } \tex_undefined:D
+ \exp_after:wN \@@_clear:wN
+ \int_value:w \int_eval:w \c_one_int + #1 ; #2
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\flag_clear_new:N, \flag_clear_new:c}
+% As for other datatypes, clear the \meta{flag~var} or create a new one,
+% as appropriate.
+% \begin{macrocode}
+\cs_new_protected:Npn \flag_clear_new:N #1
+ { \flag_if_exist:NTF #1 { \flag_clear:N } { \flag_new:N } #1 }
+\cs_generate_variant:Nn \flag_clear_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\flag_show:N, \flag_show:c, \flag_log:N, \flag_log:c, \@@_show:NN}
+% Show the height (terminal or log file) using appropriate \pkg{l3msg}
+% auxiliaries.
+% \begin{macrocode}
+\cs_new_protected:Npn \flag_show:N { \@@_show:NN \tl_show:n }
+\cs_generate_variant:Nn \flag_show:N { c }
+\cs_new_protected:Npn \flag_log:N { \@@_show:NN \tl_log:n }
+\cs_generate_variant:Nn \flag_log:N { c }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ { \exp_args:Ne #1 { \tl_to_str:n { #2 height } = \flag_height:N #2 } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Expandable flag commands}
+%
+% \begin{macro}[EXP, pTF]{\flag_if_exist:N, \flag_if_exist:c}
+% Copies of the \texttt{cs} functions defined in \pkg{l3basics}.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \flag_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \flag_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, pTF]{\flag_if_raised:N, \flag_if_raised:c}
+% Test if the flag has a non-zero height, by checking the |0| control sequence.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \flag_if_raised:N #1 { p , T , F , TF }
+ {
+ \if_cs_exist:w #1 0 \cs_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \flag_if_raised:N
+ { c } { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\flag_height:N, \flag_height:c}
+% \begin{macro}[EXP]{\@@_height_loop:wN, \@@_height_end:wN}
+% Extract the value of the flag by going through all of the
+% control sequences starting from |0|.
+% \begin{macrocode}
+\cs_new:Npn \flag_height:N #1 { \@@_height_loop:wN 0; #1 }
+\cs_new:Npn \@@_height_loop:wN #1 ; #2
+ {
+ \if_cs_exist:w #2 #1 \cs_end: \else:
+ \exp_after:wN \@@_height_end:wN
+ \fi:
+ \exp_after:wN \@@_height_loop:wN
+ \int_value:w \int_eval:w \c_one_int + #1 ; #2
+ }
+\cs_new:Npn \@@_height_end:wN #1 + #2 ; #3 {#2}
+\cs_generate_variant:Nn \flag_height:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\flag_raise:N, \flag_raise:c}
+% Change the appropriate control sequence to \tn{relax} by expanding a
+% \cs{cs:w} \ldots{} \cs{cs_end:} construction, then pass it to
+% \cs{use_none:n} to avoid leaving anything in the input stream.
+% \begin{macrocode}
+\cs_new:Npn \flag_raise:N #1
+ { \exp_after:wN \use_none:n \cs:w #1 \flag_height:N #1 \cs_end: }
+\cs_generate_variant:Nn \flag_raise:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\flag_ensure_raised:N, \flag_ensure_raised:c}
+% Pass the control sequence with name \meta{flag name}\texttt{0} to
+% \cs{use_none:n}. Constructing the control sequence ensures that it
+% changes from being undefined (if it was so) to being \tn{relax}.
+% \begin{macrocode}
+\cs_new:Npn \flag_ensure_raised:N #1
+ { \exp_after:wN \use_none:n \cs:w #1 0 \cs_end: }
+\cs_generate_variant:Nn \flag_ensure_raised:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Old \texttt{n}-type flag commands}
+%
+% Here we keep the old flag commands since our policy is to no longer
+% delete deprecated functions. The idea is to simply map \meta{flag
+% name} to \cs[no-index]{l_\meta{flag name}_flag}. When the debugging
+% code is activated, it checks existence of the \texttt{N}-type flag
+% variables that result.
+%
+% \begin{macro}[no-user-doc]{\flag_new:n, \flag_clear:n, \flag_clear_new:n}
+% \begin{macro}[EXP, pTF, no-user-doc]{\flag_if_exist:n, \flag_if_raised:n}
+% \begin{macro}[EXP, no-user-doc]{\flag_height:n, \flag_raise:n, \flag_ensure_raised:n}
+% \begin{macrocode}
+\cs_new_protected:Npn \flag_new:n #1 { \flag_new:c { l_#1_flag } }
+\cs_new_protected:Npn \flag_clear:n #1 { \flag_clear:c { l_#1_flag } }
+\cs_new_protected:Npn \flag_clear_new:n #1 { \flag_clear_new:c { l_#1_flag } }
+\cs_new:Npn \flag_if_exist_p:n #1 { \flag_if_exist_p:c { l_#1_flag } }
+\cs_new:Npn \flag_if_exist:nT #1 { \flag_if_exist:cT { l_#1_flag } }
+\cs_new:Npn \flag_if_exist:nF #1 { \flag_if_exist:cF { l_#1_flag } }
+\cs_new:Npn \flag_if_exist:nTF #1 { \flag_if_exist:cTF { l_#1_flag } }
+\cs_new:Npn \flag_if_raised_p:n #1 { \flag_if_raised_p:c { l_#1_flag } }
+\cs_new:Npn \flag_if_raised:nT #1 { \flag_if_raised:cT { l_#1_flag } }
+\cs_new:Npn \flag_if_raised:nF #1 { \flag_if_raised:cF { l_#1_flag } }
+\cs_new:Npn \flag_if_raised:nTF #1 { \flag_if_raised:cTF { l_#1_flag } }
+\cs_new:Npn \flag_height:n #1 { \flag_height:c { l_#1_flag } }
+\cs_new:Npn \flag_raise:n #1 { \flag_raise:c { l_#1_flag } }
+\cs_new:Npn \flag_ensure_raised:n #1 { \flag_ensure_raised:c { l_#1_flag } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[no-user-doc]{\flag_show:n, \flag_log:n, \@@_show:Nn}
+% To avoid changing the output here we mostly keep the old code.
+% \begin{macrocode}
+\cs_new_protected:Npn \flag_show:n { \@@_show:Nn \tl_show:n }
+\cs_new_protected:Npn \flag_log:n { \@@_show:Nn \tl_log:n }
+\cs_new_protected:Npn \@@_show:Nn #1#2
+ {
+ \exp_args:Nc \__kernel_chk_defined:NT { l_#2_flag }
+ {
+ \exp_args:Ne #1
+ { \tl_to_str:n { flag~#2~height } = \flag_height:n {#2} }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-assign.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-assign.dtx
new file mode 100644
index 00000000000..d54f0920858
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-assign.dtx
@@ -0,0 +1,316 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-assign.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3fp-assign} module\\
+% Floating point expressions^^A
+% }
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2024-04-11}
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-assign} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% \subsection{Assigning values}
+%
+% \begin{macro}{\fp_new:N}
+% Floating point variables are initialized to be $+0$.
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_new:N #1
+ { \cs_new_eq:NN #1 \c_zero_fp }
+\cs_generate_variant:Nn \fp_new:N {c}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \fp_set:Nn, \fp_set:cn,
+% \fp_gset:Nn, \fp_gset:cn,
+% \fp_const:Nn, \fp_const:cn
+% }
+% Simply use \cs{@@_parse:n} within various \texttt{f}-expanding
+% assignments.
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_set:Nn #1#2
+ { \__kernel_tl_set:Nx #1 { \exp_not:f { \@@_parse:n {#2} } } }
+\cs_new_protected:Npn \fp_gset:Nn #1#2
+ { \__kernel_tl_gset:Nx #1 { \exp_not:f { \@@_parse:n {#2} } } }
+\cs_new_protected:Npn \fp_const:Nn #1#2
+ { \tl_const:Ne #1 { \exp_not:f { \@@_parse:n {#2} } } }
+\cs_generate_variant:Nn \fp_set:Nn {c}
+\cs_generate_variant:Nn \fp_gset:Nn {c}
+\cs_generate_variant:Nn \fp_const:Nn {c}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \fp_set_eq:NN , \fp_set_eq:cN , \fp_set_eq:Nc , \fp_set_eq:cc,
+% \fp_gset_eq:NN, \fp_gset_eq:cN, \fp_gset_eq:Nc, \fp_gset_eq:cc
+% }
+% Copying a floating point is the same as copying the underlying token
+% list.
+% \begin{macrocode}
+\cs_new_eq:NN \fp_set_eq:NN \tl_set_eq:NN
+\cs_new_eq:NN \fp_gset_eq:NN \tl_gset_eq:NN
+\cs_generate_variant:Nn \fp_set_eq:NN { c , Nc , cc }
+\cs_generate_variant:Nn \fp_gset_eq:NN { c , Nc , cc }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\fp_zero:N, \fp_zero:c, \fp_gzero:N, \fp_gzero:c}
+% Setting a floating point to zero: copy \cs{c_zero_fp}.
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_zero:N #1 { \fp_set_eq:NN #1 \c_zero_fp }
+\cs_new_protected:Npn \fp_gzero:N #1 { \fp_gset_eq:NN #1 \c_zero_fp }
+\cs_generate_variant:Nn \fp_zero:N { c }
+\cs_generate_variant:Nn \fp_gzero:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {\fp_zero_new:N, \fp_zero_new:c, \fp_gzero_new:N, \fp_gzero_new:c}
+% Set the floating point to zero, or define it if needed.
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_zero_new:N #1
+ { \fp_if_exist:NTF #1 { \fp_zero:N #1 } { \fp_new:N #1 } }
+\cs_new_protected:Npn \fp_gzero_new:N #1
+ { \fp_if_exist:NTF #1 { \fp_gzero:N #1 } { \fp_new:N #1 } }
+\cs_generate_variant:Nn \fp_zero_new:N { c }
+\cs_generate_variant:Nn \fp_gzero_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Updating values}
+%
+% These match the equivalent functions in \pkg{l3int} and \pkg{l3skip}.
+%
+% \begin{macro}
+% {
+% \fp_add:Nn, \fp_add:cn, \fp_gadd:Nn, \fp_gadd:cn,
+% \fp_sub:Nn, \fp_sub:cn, \fp_gsub:Nn, \fp_gsub:cn,
+% }
+% \begin{macro}{\@@_add:NNNn}
+% For the sake of error recovery we should not simply set |#1| to
+% $|#1| \pm (|#2|)$: for instance, if |#2| is % ^^A(
+% |0)+2|, the parsing error would be raised at the last closing
+% parenthesis rather than at the closing parenthesis in the user
+% argument. Thus we evaluate |#2| instead of just putting
+% parentheses. As an optimization we use \cs{@@_parse:n} rather than
+% \cs{fp_eval:n}, which would convert the result away from the
+% internal representation and back.
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_add:Nn { \@@_add:NNNn \fp_set:Nn + }
+\cs_new_protected:Npn \fp_gadd:Nn { \@@_add:NNNn \fp_gset:Nn + }
+\cs_new_protected:Npn \fp_sub:Nn { \@@_add:NNNn \fp_set:Nn - }
+\cs_new_protected:Npn \fp_gsub:Nn { \@@_add:NNNn \fp_gset:Nn - }
+\cs_new_protected:Npn \@@_add:NNNn #1#2#3#4
+ { #1 #3 { #3 #2 \@@_parse:n {#4} } }
+\cs_generate_variant:Nn \fp_add:Nn { c }
+\cs_generate_variant:Nn \fp_gadd:Nn { c }
+\cs_generate_variant:Nn \fp_sub:Nn { c }
+\cs_generate_variant:Nn \fp_gsub:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Showing values}
+%
+% \begin{macro}{\fp_show:N, \fp_show:c, \fp_log:N, \fp_log:c, \@@_show:NN}
+% This shows the result of computing its argument by
+% passing the right data to \cs{tl_show:n} or \cs{tl_log:n}.
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_show:N { \@@_show:NN \tl_show:n }
+\cs_generate_variant:Nn \fp_show:N { c }
+\cs_new_protected:Npn \fp_log:N { \@@_show:NN \tl_log:n }
+\cs_generate_variant:Nn \fp_log:N { c }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \__kernel_chk_tl_type:NnnT #2 { fp }
+ { \exp_args:No \@@_show_validate:n #2 }
+ { \exp_args:Ne #1 { \token_to_str:N #2 = \fp_to_tl:N #2 } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_show_validate:n, \@@_show_validate_aux:n, \@@_show_validate:nn,
+% }
+% \begin{macro}[EXP]
+% {
+% \@@_show_validate:w,
+% \@@_tuple_show_validate:w,
+% \@@_symbolic_show_validate:w
+% }
+% To support symbolic expression, validation has to be done recursively.
+% Two |\@@_show_validate:nn| wrappers are used to distinguish between
+% initial and recursive calls, in which the former provides a demo of
+% possible forms a |fp| variable would have.
+% \begin{macrocode}
+\cs_new:Npn \@@_show_validate:n #1
+ {
+ \@@_show_validate:nn { #1 }
+ {
+ \s_@@ \@@_chk:w ??? ;~ or \iow_newline:
+ \s_@@_tuple \_@@_tuple_chk:w ? ;~ or \iow_newline:
+ \s_@@_symbolic \@@_symbolic_chk:w ? , ? ;
+ }
+ }
+\cs_new:Npn \@@_show_validate_aux:n #1
+ {
+ \@@_show_validate:nn { #1 } { }
+ }
+\cs_new:Npn \@@_show_validate:nn #1#2
+ {
+ \tl_if_empty:nF { #1 }
+ {
+ \str_case:enF { \tl_head:n { #1 } }
+ {
+ { \s_@@ }
+ {
+ \@@_show_validate:w #1 \s_@@
+ \@@_chk:w ??? ; \s_@@_stop
+ }
+ { \s_@@_tuple }
+ {
+ \@@_tuple_show_validate:w #1
+ \s_@@_tuple \_@@_tuple_chk:w ?? ; \s_@@_stop
+ }
+ { \s_@@_symbolic }
+ {
+ \@@_symbolic_show_validate:w #1
+ \s_@@_symbolic \@@_symbolic_chk:w ? , ?? ; \s_@@_stop
+ }
+ }
+ { #2 }
+ }
+ }
+\cs_new:Npn \@@_show_validate:w
+ #1 \s_@@ \@@_chk:w #2#3#4#5 ; #6 \s_@@_stop
+ {
+ \str_if_eq:nnF { #2 } {?}
+ {
+ \token_if_eq_meaning:NNTF #2 1
+ { \s_@@ \@@_chk:w #2 #3 { #4 } #5 ; }
+ { \s_@@ \@@_chk:w #2 #3 #4 #5 ; }
+ \@@_show_validate_aux:n { #6 }
+ }
+ }
+\cs_new:Npn \@@_tuple_show_validate:w
+ #1 \s_@@_tuple \_@@_tuple_chk:w #2#3 ; #4 \s_@@_stop
+ {
+ \str_if_eq:nnF { #2 } {?}
+ { \s_@@_tuple \@@_tuple_chk:w { \@@_show_validate_aux:n { #2 } } ; }
+ }
+\cs_new:Npn \@@_symbolic_show_validate:w
+ #1 \s_@@_symbolic \@@_symbolic_chk:w #2 , #3#4 ; #5 \s_@@_stop
+ {
+ \str_if_eq:nnF { #2 } {?}
+ {
+ \s_@@_symbolic \@@_symbolic_chk:w \exp_not:n { #2 } ,
+ { \@@_show_validate_aux:n { #3 } };
+ \@@_show_validate_aux:n { #5 }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\fp_show:n, \fp_log:n}
+% Use general tools.
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_show:n
+ { \__kernel_msg_show_eval:Nn \fp_to_tl:n }
+\cs_new_protected:Npn \fp_log:n
+ { \__kernel_msg_log_eval:Nn \fp_to_tl:n }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Some useful constants and scratch variables}
+%
+% \begin{variable}{\c_one_fp, \c_e_fp}
+% Some constants.
+% \begin{macrocode}
+\fp_const:Nn \c_e_fp { 2.718 2818 2845 9045 }
+\fp_const:Nn \c_one_fp { 1 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_pi_fp, \c_one_degree_fp}
+% We simply round $\pi$ to and $\pi/180$ to $16$ significant digits.
+% \begin{macrocode}
+\fp_const:Nn \c_pi_fp { 3.141 5926 5358 9793 }
+\fp_const:Nn \c_one_degree_fp { 0.0 1745 3292 5199 4330 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_tmpa_fp, \l_tmpb_fp, \g_tmpa_fp, \g_tmpb_fp}
+% Scratch variables are simply initialized there.
+% \begin{macrocode}
+\fp_new:N \l_tmpa_fp
+\fp_new:N \l_tmpb_fp
+\fp_new:N \g_tmpa_fp
+\fp_new:N \g_tmpb_fp
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-aux.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-aux.dtx
new file mode 100644
index 00000000000..c9d0c228f56
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-aux.dtx
@@ -0,0 +1,1291 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-aux.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3fp-aux} module\\ Support for floating points^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-aux} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% ^^A todo: make sanitize and pack more homogeneous between modules.
+%
+% \subsection{Access to primitives}
+%
+% \begin{macro}{\@@_int_eval:w, \@@_int_eval_end:, \@@_int_to_roman:w}
+% Largely for performance reasons, we need to directly access primitives
+% rather than use \cs{int_eval:n}. This happens \emph{a lot}, so we
+% use private names. The same is true for \tn{romannumeral}, although it
+% is used much less widely.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_int_eval:w \tex_numexpr:D
+\cs_new_eq:NN \@@_int_eval_end: \scan_stop:
+\cs_new_eq:NN \@@_int_to_roman:w \tex_romannumeral:D
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Internal representation}
+%
+% Internally, a floating point number \meta{X} is a
+% token list containing
+% \begin{quote}
+% \cs{s_@@} \cs{@@_chk:w} \meta{case} \meta{sign} \meta{body} |;|
+% \end{quote}
+% Let us explain each piece separately.
+%
+% Internal floating point numbers are used in expressions,
+% and in this context are subject to \texttt{f}-expansion. They must
+% leave a recognizable mark after \texttt{f}-expansion, to prevent the
+% floating point number from being re-parsed. Thus, \cs{s_@@}
+% is simply another name for \tn{relax}.
+%
+% When used directly without an accessor function, floating points
+% should produce an error: this is the role of \cs{@@_chk:w}. We could
+% make floating point variables be protected to prevent them from
+% expanding under \texttt{e}/\texttt{x}-expansion, but it seems more
+% convenient to treat them as a subcase of token list variables.
+%
+% The (decimal part of the) IEEE-754-2008 standard requires the format
+% to be able to represent special floating point numbers besides the
+% usual positive and negative cases. We distinguish the various
+% possibilities by their \meta{case}, which is a single digit:
+% \begin{itemize}
+% \item[0] zeros: |+0| and |-0|,
+% \item[1] \enquote{normal} numbers (positive and negative),
+% \item[2] infinities: |+inf| and |-inf|,
+% \item[3] quiet and signalling \texttt{nan}.
+% \end{itemize}
+% The \meta{sign} is |0| (positive) or |2| (negative),
+% except in the case of \texttt{nan}, which have $\meta{sign} = 1$.
+% This ensures that changing the \meta{sign} digit to $2-\meta{sign}$
+% is exactly equivalent to changing the sign of the number.
+%
+% Special floating point numbers have the form
+% \begin{quote}
+% \cs{s_@@} \cs{@@_chk:w} \meta{case} \meta{sign} \cs[no-index]{s_@@_\ldots} |;|
+% \end{quote}
+% where \cs[no-index]{s_@@_\ldots} is a scan mark carrying information about how the
+% number was formed (useful for debugging).
+%
+% Normal floating point numbers ($\meta{case} = 1$) have the form
+% \begin{quote}
+% \cs{s_@@} \cs{@@_chk:w} 1 \meta{sign} \Arg{exponent}
+% \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} |;|
+% \end{quote}
+% Here, the \meta{exponent} is an integer, between
+% $-\ExplSyntaxOn\int_use:N\c__fp_minus_min_exponent_int$ and
+% $\ExplSyntaxOn\int_use:N\c__fp_max_exponent_int$. The body consists
+% in four blocks of exactly $4$ digits,
+% $0000 \leq \meta{X_i} \leq 9999$, and the floating point is
+% \[
+% (-1)^{\meta{sign}/2} \meta{X_1}\meta{X_2}\meta{X_3}\meta{X_4}\cdot 10^{\meta{exponent}-16}
+% \]
+% where we have concatenated the $16$ digits. Currently, floating point numbers are normalized such that
+% the \meta{exponent} is minimal, in other words, $1000 \leq \meta{X_1} \leq 9999$.
+%
+% \begin{table}\centering
+% \caption{Internal representation of floating point numbers.}
+% \label{tab:fp-convert-special}
+% \begin{tabular}{ll}
+% \toprule
+% \multicolumn{1}{c}{Representation} & Meaning \\
+% \midrule
+% 0 0 \cs[no-index]{s_@@_\ldots} \texttt{;} & Positive zero. \\
+% 0 2 \cs[no-index]{s_@@_\ldots} \texttt{;} & Negative zero. \\
+% 1 0 \Arg{exponent} \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} \texttt{;}
+% & Positive floating point. \\
+% 1 2 \Arg{exponent} \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} \texttt{;}
+% & Negative floating point. \\
+% 2 0 \cs[no-index]{s_@@_\ldots} \texttt{;} & Positive infinity. \\
+% 2 2 \cs[no-index]{s_@@_\ldots} \texttt{;} & Negative infinity. \\
+% 3 1 \cs[no-index]{s_@@_\ldots} \texttt{;} & Quiet \texttt{nan}. \\
+% 3 1 \cs[no-index]{s_@@_\ldots} \texttt{;} & Signalling \texttt{nan}. \\
+% \bottomrule
+% \end{tabular}
+% \end{table}
+%
+% Calculations are done in base $10000$, \emph{i.e.} one myriad.
+%
+% \subsection{Using arguments and semicolons}
+%
+% \begin{macro}[EXP]{\@@_use_none_stop_f:n}
+% This function removes an argument (typically a digit) and replaces
+% it by \cs{exp_stop_f:}, a marker which stops \texttt{f}-type
+% expansion.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_none_stop_f:n #1 { \exp_stop_f: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_use_s:n, \@@_use_s:nn}
+% Those functions place a semicolon after one or two arguments
+% (typically digits).
+% \begin{macrocode}
+\cs_new:Npn \@@_use_s:n #1 { #1; }
+\cs_new:Npn \@@_use_s:nn #1#2 { #1#2; }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_use_none_until_s:w, \@@_use_i_until_s:nw, \@@_use_ii_until_s:nnw}
+% Those functions select specific arguments among a set of arguments
+% delimited by a semicolon.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_none_until_s:w #1; { }
+\cs_new:Npn \@@_use_i_until_s:nw #1#2; {#1}
+\cs_new:Npn \@@_use_ii_until_s:nnw #1#2#3; {#2}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_reverse_args:Nww}
+% Many internal functions take arguments delimited by semicolons, and
+% it is occasionally useful to swap two such arguments.
+% \begin{macrocode}
+\cs_new:Npn \@@_reverse_args:Nww #1 #2; #3; { #1 #3; #2; }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_rrot:www}
+% Rotate three arguments delimited by semicolons. This is the inverse
+% (or the square) of the Forth primitive |ROT|, hence the name.
+% \begin{macrocode}
+\cs_new:Npn \@@_rrot:www #1; #2; #3; { #2; #3; #1; }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_use_i:ww, \@@_use_i:www}
+% Many internal functions take arguments delimited by semicolons, and
+% it is occasionally useful to remove one or two such arguments.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_i:ww #1; #2; { #1; }
+\cs_new:Npn \@@_use_i:www #1; #2; #3; { #1; }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Constants, and structure of floating points}
+%
+% \begin{macro}{\@@_misused:n}
+% This receives a floating point object (floating point number or
+% tuple) and generates an error stating that it was misused. This is
+% called when for instance an |fp| variable is left in the input
+% stream and its contents reach \TeX{}'s stomach.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_misused:n #1
+ { \msg_error:nne { fp } { misused } { \fp_to_tl:n {#1} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\s_@@, \@@_chk:w}
+% Floating points numbers all start with \cs{s_@@} \cs{@@_chk:w},
+% where \cs{s_@@} is equal to the \TeX{} primitive \tn{relax}, and
+% \cs{@@_chk:w} is protected. The rest of the floating point number
+% is made of characters (or \tn{relax}). This ensures that nothing
+% expands under \texttt{f}-expansion, nor under
+% \texttt{e}/\texttt{x}-expansion.
+% However, when typeset, \cs{s_@@} does nothing, and \cs{@@_chk:w} is
+% expanded. We define \cs{@@_chk:w} to produce an error.
+% \begin{macrocode}
+\scan_new:N \s_@@
+\cs_new_protected:Npn \@@_chk:w #1 ;
+ { \@@_misused:n { \s_@@ \@@_chk:w #1 ; } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\s_@@_expr_mark, \s_@@_expr_stop}
+% Aliases of \cs{tex_relax:D}, used to terminate expressions.
+% \begin{macrocode}
+\scan_new:N \s_@@_expr_mark
+\scan_new:N \s_@@_expr_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\s_@@_mark, \s_@@_stop}
+% Generic scan marks used throughout the module.
+% \begin{macrocode}
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_use_i_delimit_by_s_stop:nw}
+% Functions to gobble up to a scan mark.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_i_delimit_by_s_stop:nw #1 #2 \s_@@_stop {#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \s_@@_invalid, \s_@@_underflow, \s_@@_overflow,
+% \s_@@_division, \s_@@_exact
+% }
+% A couple of scan marks used to indicate where special floating point
+% numbers come from.
+% \begin{macrocode}
+\scan_new:N \s_@@_invalid
+\scan_new:N \s_@@_underflow
+\scan_new:N \s_@@_overflow
+\scan_new:N \s_@@_division
+\scan_new:N \s_@@_exact
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}
+% {\c_zero_fp, \c_minus_zero_fp, \c_inf_fp, \c_minus_inf_fp, \c_nan_fp}
+% The special floating points. We define the floating points here as \enquote{exact}.
+% \begin{macrocode}
+\tl_const:Nn \c_zero_fp { \s_@@ \@@_chk:w 0 0 \s_@@_exact ; }
+\tl_const:Nn \c_minus_zero_fp { \s_@@ \@@_chk:w 0 2 \s_@@_exact ; }
+\tl_const:Nn \c_inf_fp { \s_@@ \@@_chk:w 2 0 \s_@@_exact ; }
+\tl_const:Nn \c_minus_inf_fp { \s_@@ \@@_chk:w 2 2 \s_@@_exact ; }
+\tl_const:Nn \c_nan_fp { \s_@@ \@@_chk:w 3 1 \s_@@_exact ; }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_prec_int, \c_@@_half_prec_int, \c_@@_block_int}
+% The number of digits of floating points.
+% \begin{macrocode}
+\int_const:Nn \c_@@_prec_int { 16 }
+\int_const:Nn \c_@@_half_prec_int { 8 }
+\int_const:Nn \c_@@_block_int { 4 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_myriad_int}
+% Blocks have $4$~digits so this integer is useful.
+% \begin{macrocode}
+\int_const:Nn \c_@@_myriad_int { 10000 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_minus_min_exponent_int, \c_@@_max_exponent_int}
+% Normal floating point numbers have an exponent between $-$
+% \texttt{minus_min_exponent} and \texttt{max_exponent} inclusive.
+% Larger numbers are rounded to $\pm\infty$. Smaller numbers are
+% rounded to $\pm 0$. It would be more natural to define a
+% \texttt{min_exponent} with the opposite sign but that would waste
+% one \TeX{} count.
+% \begin{macrocode}
+\int_const:Nn \c_@@_minus_min_exponent_int { 10000 }
+\int_const:Nn \c_@@_max_exponent_int { 10000 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_max_exp_exponent_int}
+% If a number's exponent is larger than that, its exponential
+% overflows/underflows.
+% \begin{macrocode}
+\int_const:Nn \c_@@_max_exp_exponent_int { 5 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_overflowing_fp}
+% A floating point number that is bigger than all normal floating
+% point numbers. This replaces infinities when converting to formats
+% that do not support infinities.
+% \begin{macrocode}
+\tl_const:Ne \c_@@_overflowing_fp
+ {
+ \s_@@ \@@_chk:w 1 0
+ { \int_eval:n { \c_@@_max_exponent_int + 1 } }
+ {1000} {0000} {0000} {0000} ;
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_zero_fp:N, \@@_inf_fp:N}
+% In case of overflow or underflow, we have to output
+% a zero or infinity with a given sign.
+% \begin{macrocode}
+\cs_new:Npn \@@_zero_fp:N #1
+ { \s_@@ \@@_chk:w 0 #1 \s_@@_underflow ; }
+\cs_new:Npn \@@_inf_fp:N #1
+ { \s_@@ \@@_chk:w 2 #1 \s_@@_overflow ; }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_exponent:w}
+% For normal numbers, the function expands to the exponent, otherwise
+% to $0$. This is used in \pkg{l3str-format}.
+% \begin{macrocode}
+\cs_new:Npn \@@_exponent:w \s_@@ \@@_chk:w #1
+ {
+ \if_meaning:w 1 #1
+ \exp_after:wN \@@_use_ii_until_s:nnw
+ \else:
+ \exp_after:wN \@@_use_i_until_s:nw
+ \exp_after:wN 0
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_neg_sign:N}
+% When appearing in an integer expression or after \cs{int_value:w},
+% this expands to the sign opposite to |#1|, namely $0$ (positive) is
+% turned to $2$ (negative), $1$ (\texttt{nan}) to $1$, and $2$ to $0$.
+% \begin{macrocode}
+\cs_new:Npn \@@_neg_sign:N #1
+ { \@@_int_eval:w 2 - #1 \@@_int_eval_end: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_kind:w}
+% Expands to $0$ for zeros, $1$ for normal floating point numbers, $2$
+% for infinities, $3$ for \nan{}, $4$ for tuples.
+% \begin{macrocode}
+\cs_new:Npn \@@_kind:w #1
+ {
+ \@@_if_type_fp:NTwFw
+ #1 \@@_use_ii_until_s:nnw
+ \s_@@ { \@@_use_i_until_s:nw 4 }
+ \s_@@_stop
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Overflow, underflow, and exact zero}
+%
+%^^A todo: the sign of exact zeros should depend on the rounding mode.
+%
+% \begin{macro}[EXP]{\@@_sanitize:Nw, \@@_sanitize:wN}
+% \begin{macro}[EXP]{\@@_sanitize_zero:w}
+% Expects the sign and the exponent in some order, then the
+% significand (which we don't touch). Outputs the corresponding
+% floating point number, possibly underflowed to $\pm 0$ or overflowed
+% to $\pm\infty$. The functions \cs{@@_underflow:w} and
+% \cs{@@_overflow:w} are defined in \pkg{l3fp-traps}.
+% \begin{macrocode}
+\cs_new:Npn \@@_sanitize:Nw #1 #2;
+ {
+ \if_case:w
+ \if_int_compare:w #2 > \c_@@_max_exponent_int 1 ~ \else:
+ \if_int_compare:w #2 < - \c_@@_minus_min_exponent_int 2 ~ \else:
+ \if_meaning:w 1 #1 3 ~ \fi: \fi: \fi: 0 ~
+ \or: \exp_after:wN \@@_overflow:w
+ \or: \exp_after:wN \@@_underflow:w
+ \or: \exp_after:wN \@@_sanitize_zero:w
+ \fi:
+ \s_@@ \@@_chk:w 1 #1 {#2}
+ }
+\cs_new:Npn \@@_sanitize:wN #1; #2 { \@@_sanitize:Nw #2 #1; }
+\cs_new:Npn \@@_sanitize_zero:w \s_@@ \@@_chk:w #1 #2 #3;
+ { \c_zero_fp }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Expanding after a floating point number}
+%
+% \begin{macro}[EXP]{\@@_exp_after_o:w}
+% \begin{macro}[EXP]{\@@_exp_after_f:nw}
+% \begin{syntax}
+% \cs{@@_exp_after_o:w} \meta{floating point}
+% \cs{@@_exp_after_f:nw} \Arg{tokens} \meta{floating point}
+% \end{syntax}
+% Places \meta{tokens} (empty in the case of \cs{@@_exp_after_o:w})
+% between the \meta{floating point} and the following tokens, then
+% hits those tokens with \texttt{o} or \texttt{f}-expansion, and
+% leaves the floating point number unchanged.
+%
+% We first distinguish normal floating points, which have a significand,
+% from the much simpler special floating points.
+% \begin{macrocode}
+\cs_new:Npn \@@_exp_after_o:w \s_@@ \@@_chk:w #1
+ {
+ \if_meaning:w 1 #1
+ \exp_after:wN \@@_exp_after_normal:nNNw
+ \else:
+ \exp_after:wN \@@_exp_after_special:nNNw
+ \fi:
+ { }
+ #1
+ }
+\cs_new:Npn \@@_exp_after_f:nw #1 \s_@@ \@@_chk:w #2
+ {
+ \if_meaning:w 1 #2
+ \exp_after:wN \@@_exp_after_normal:nNNw
+ \else:
+ \exp_after:wN \@@_exp_after_special:nNNw
+ \fi:
+ { \exp:w \exp_end_continue_f:w #1 }
+ #2
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_exp_after_special:nNNw}
+% \begin{syntax}
+% \cs{@@_exp_after_special:nNNw} \Arg{after} \meta{case} \meta{sign} \meta{scan mark} |;|
+% \end{syntax}
+% Special floating point numbers are easy to jump over since they
+% contain few tokens.
+% \begin{macrocode}
+\cs_new:Npn \@@_exp_after_special:nNNw #1#2#3#4;
+ {
+ \exp_after:wN \s_@@
+ \exp_after:wN \@@_chk:w
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN #4
+ \exp_after:wN ;
+ #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_exp_after_normal:nNNw}
+% For normal floating point numbers, life is slightly harder, since we
+% have many tokens to jump over. Here it would be slightly better if
+% the digits were not braced but instead were delimited arguments (for
+% instance delimited by |,|). That may be changed some day.
+% \begin{macrocode}
+\cs_new:Npn \@@_exp_after_normal:nNNw #1 1 #2 #3 #4#5#6#7;
+ {
+ \exp_after:wN \@@_exp_after_normal:Nwwwww
+ \exp_after:wN #2
+ \int_value:w #3 \exp_after:wN ;
+ \int_value:w 1 #4 \exp_after:wN ;
+ \int_value:w 1 #5 \exp_after:wN ;
+ \int_value:w 1 #6 \exp_after:wN ;
+ \int_value:w 1 #7 \exp_after:wN ; #1
+ }
+\cs_new:Npn \@@_exp_after_normal:Nwwwww
+ #1 #2; 1 #3 ; 1 #4 ; 1 #5 ; 1 #6 ;
+ { \s_@@ \@@_chk:w 1 #1 {#2} {#3} {#4} {#5} {#6} ; }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Other floating point types}
+%
+% \begin{macro}{\s_@@_tuple, \@@_tuple_chk:w}
+% \begin{variable}{\c_@@_empty_tuple_fp}
+% Floating point tuples take the form \cs{s_@@_tuple}
+% \cs{@@_tuple_chk:w} |{| \meta{fp 1} \meta{fp 2} \dots |}| |;| where
+% each \meta{fp} is a floating point number or tuple, hence ends with
+% |;| itself. When a tuple is typeset, \cs{@@_tuple_chk:w} produces
+% an error, just like usual floating point numbers.
+% Tuples may have zero or one element.
+% \begin{macrocode}
+\scan_new:N \s_@@_tuple
+\cs_new_protected:Npn \@@_tuple_chk:w #1 ;
+ { \@@_misused:n { \s_@@_tuple \@@_tuple_chk:w #1 ; } }
+\tl_const:Nn \c_@@_empty_tuple_fp
+ { \s_@@_tuple \@@_tuple_chk:w { } ; }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_tuple_count:w, \@@_array_count:n}
+% \begin{macro}[EXP]{\@@_tuple_count_loop:Nw}
+% Count the number of items in a tuple of floating points by counting
+% semicolons. The technique is very similar to \cs{tl_count:n}, but
+% with the loop built-in. Checking for the end of the loop is done
+% with the |\use_none:n #1| construction.
+% \begin{macrocode}
+\cs_new:Npn \@@_array_count:n #1
+ { \@@_tuple_count:w \s_@@_tuple \@@_tuple_chk:w {#1} ; }
+\cs_new:Npn \@@_tuple_count:w \s_@@_tuple \@@_tuple_chk:w #1 ;
+ {
+ \int_value:w \@@_int_eval:w 0
+ \@@_tuple_count_loop:Nw #1 { ? \prg_break: } ;
+ \prg_break_point:
+ \@@_int_eval_end:
+ }
+\cs_new:Npn \@@_tuple_count_loop:Nw #1#2;
+ { \use_none:n #1 + 1 \@@_tuple_count_loop:Nw }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_if_type_fp:NTwFw}
+% Used as \cs{@@_if_type_fp:NTwFw} \meta{marker} \Arg{true code}
+% \cs{s_@@} \Arg{false code} \cs{s_@@_stop}, this test whether the
+% \meta{marker} is \cs{s_@@} or not and runs the appropriate
+% \meta{code}. The very unusual syntax is for optimization purposes
+% as that function is used for all floating point operations.
+% \begin{macrocode}
+\cs_new:Npn \@@_if_type_fp:NTwFw #1 \s_@@ #2 #3 \s_@@_stop {#2}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_array_if_all_fp:nTF, \@@_array_if_all_fp_loop:w}
+% True if all items are floating point numbers. Used for |min|.
+% \begin{macrocode}
+\cs_new:Npn \@@_array_if_all_fp:nTF #1
+ {
+ \@@_array_if_all_fp_loop:w #1 { \s_@@ \prg_break: } ;
+ \prg_break_point: \use_i:nn
+ }
+\cs_new:Npn \@@_array_if_all_fp_loop:w #1#2 ;
+ {
+ \@@_if_type_fp:NTwFw
+ #1 \@@_array_if_all_fp_loop:w
+ \s_@@ { \prg_break:n \use_iii:nnn }
+ \s_@@_stop
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_type_from_scan:N, \@@_type_from_scan_other:N, \@@_type_from_scan:w}
+% Used as \cs{@@_type_from_scan:N} \meta{token}.
+% Grabs the pieces of the stringified \meta{token} which lies after
+% the first |s__fp|. If the \meta{token} does not contain that
+% string, the result is |_?|.
+% \begin{macrocode}
+\cs_new:Npn \@@_type_from_scan:N #1
+ {
+ \@@_if_type_fp:NTwFw
+ #1 { }
+ \s_@@ { \@@_type_from_scan_other:N #1 }
+ \s_@@_stop
+ }
+\cs_new:Npe \@@_type_from_scan_other:N #1
+ {
+ \exp_not:N \exp_after:wN \exp_not:N \@@_type_from_scan:w
+ \exp_not:N \token_to_str:N #1 \s_@@_mark
+ \tl_to_str:n { s_@@ _? } \s_@@_mark \s_@@_stop
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new:Npn \@@_type_from_scan:w #1
+ { \tl_to_str:n { s_@@ } } #2 \s_@@_mark #3 \s_@@_stop {#2}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_change_func_type:NNN}
+% \begin{macro}[EXP]{\@@_change_func_type_aux:w, \@@_change_func_type_chk:NNN}
+% Arguments are \meta{type marker} \meta{function} \meta{recovery}.
+% This gives the function obtained by placing the type after |@@|. If
+% the function is not defined then \meta{recovery} \meta{function} is
+% used instead; however that test is not run when the \meta{type
+% marker} is \cs{s_@@}.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_func_type:NNN #1#2#3
+ {
+ \@@_if_type_fp:NTwFw
+ #1 #2
+ \s_@@
+ {
+ \exp_after:wN \@@_change_func_type_chk:NNN
+ \cs:w
+ @@ \@@_type_from_scan_other:N #1
+ \exp_after:wN \@@_change_func_type_aux:w \token_to_str:N #2
+ \cs_end:
+ #2 #3
+ }
+ \s_@@_stop
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new:Npn \@@_change_func_type_aux:w #1 { \tl_to_str:n { @@ } } { }
+\cs_new:Npn \@@_change_func_type_chk:NNN #1#2#3
+ {
+ \if_meaning:w \scan_stop: #1
+ \exp_after:wN #3 \exp_after:wN #2
+ \else:
+ \exp_after:wN #1
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_exp_after_any_f:Nnw, \@@_exp_after_any_f:nw}
+% \begin{macro}[EXP]{\@@_exp_after_expr_stop_f:nw}
+% The |Nnw| function simply dispatches to the appropriate
+% \cs[no-index]{@@_exp_after\ldots{}_f:nw} with \enquote{\ldots{}}
+% (either empty or |_|\meta{type}) extracted from |#1|, which should
+% start with |\s__fp|. If it doesn't start with |\s__fp| the function
+% \cs{@@_exp_after_?_f:nw} defined in \pkg{l3fp-parse} gives an error;
+% another special \meta{type} is |stop|, useful for loops, see below.
+% The |nw| function has an important optimization for floating points
+% numbers; it also fetches its type marker |#2| from the floating
+% point.
+% \begin{macrocode}
+\cs_new:Npn \@@_exp_after_any_f:Nnw #1
+ { \cs:w @@_exp_after \@@_type_from_scan_other:N #1 _f:nw \cs_end: }
+\cs_new:Npn \@@_exp_after_any_f:nw #1#2
+ {
+ \@@_if_type_fp:NTwFw
+ #2 \@@_exp_after_f:nw
+ \s_@@ { \@@_exp_after_any_f:Nnw #2 }
+ \s_@@_stop
+ {#1} #2
+ }
+\cs_new_eq:NN \@@_exp_after_expr_stop_f:nw \use_none:nn
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_exp_after_tuple_o:w}
+% \begin{macro}[EXP]{\@@_exp_after_tuple_f:nw, \@@_exp_after_array_f:w}
+% The loop works by using the |n| argument of
+% \cs{@@_exp_after_any_f:nw} to place the loop macro after the next
+% item in the tuple and expand it.
+% \begin{quote}
+% \cs{@@_exp_after_array_f:w}\\
+% \meta{fp_1} |;|\\
+% \ldots{}\\
+% \meta{fp_n} |;|\\
+% \cs{s_@@_expr_stop}
+% \end{quote}
+% \begin{macrocode}
+\cs_new:Npn \@@_exp_after_tuple_o:w
+ { \@@_exp_after_tuple_f:nw { \exp_after:wN \exp_stop_f: } }
+\cs_new:Npn \@@_exp_after_tuple_f:nw
+ #1 \s_@@_tuple \@@_tuple_chk:w #2 ;
+ {
+ \exp_after:wN \s_@@_tuple
+ \exp_after:wN \@@_tuple_chk:w
+ \exp_after:wN {
+ \exp:w \exp_end_continue_f:w
+ \@@_exp_after_array_f:w #2 \s_@@_expr_stop
+ \exp_after:wN }
+ \exp_after:wN ;
+ \exp:w \exp_end_continue_f:w #1
+ }
+\cs_new:Npn \@@_exp_after_array_f:w
+ { \@@_exp_after_any_f:nw { \@@_exp_after_array_f:w } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Packing digits}
+%
+% When a positive integer |#1| is known to be less than $10^8$, the
+% following trick splits it into two blocks of $4$ digits, padding
+% with zeros on the left.
+% \begin{verbatim}
+% \cs_new:Npn \pack:NNNNNw #1 #2#3#4#5 #6; { {#2#3#4#5} {#6} }
+% \exp_after:wN \pack:NNNNNw
+% \__fp_int_value:w \__fp_int_eval:w 1 0000 0000 + #1 ;
+% \end{verbatim}
+% The idea is that adding $10^8$ to the number ensures that it has
+% exactly $9$ digits, and can then easily find which digits correspond
+% to what position in the number. Of course, this can be modified
+% for any number of digits less or equal to~$9$ (we are limited by
+% \TeX{}'s integers). This method is very heavily relied upon in
+% \texttt{l3fp-basics}.
+%
+% More specifically, the auxiliary inserts |+ #1#2#3#4#5 ; {#6}|, which
+% allows us to compute several blocks of $4$ digits in a nested manner,
+% performing carries on the fly. Say we want to compute $1\,2345 \times
+% 6677\,8899$. With simplified names, we would do
+% \begin{verbatim}
+% \exp_after:wN \post_processing:w
+% \__fp_int_value:w \__fp_int_eval:w - 5 0000
+% \exp_after:wN \pack:NNNNNw
+% \__fp_int_value:w \__fp_int_eval:w 4 9995 0000
+% + 12345 * 6677
+% \exp_after:wN \pack:NNNNNw
+% \__fp_int_value:w \__fp_int_eval:w 5 0000 0000
+% + 12345 * 8899 ;
+% \end{verbatim}
+% The \cs{exp_after:wN} triggers \cs{int_value:w} \cs{@@_int_eval:w}, which
+% starts a first computation, whose initial value is $- 5\,0000$ (the
+% \enquote{leading shift}). In that computation appears an
+% \cs{exp_after:wN}, which triggers the nested computation
+% \cs{int_value:w} \cs{@@_int_eval:w} with starting value $4\,9995\,0000$ (the
+% \enquote{middle shift}). That, in turn, expands \cs{exp_after:wN}
+% which triggers the third computation. The third computation's value
+% is $5\,0000\,0000 + 12345 \times 8899$, which has $9$ digits. Adding
+% $5\cdot 10^{8}$ to the product allowed us to know how many digits to
+% expect as long as the numbers to multiply are not too big; it
+% also works to some extent with negative results. The \texttt{pack}
+% function puts the last $4$ of those $9$ digits into a brace group,
+% moves the semi-colon delimiter, and inserts a |+|, which combines the
+% carry with the previous computation. The shifts nicely combine into
+% $5\,0000\,0000 / 10^{4} + 4\,9995\,0000 = 5\,0000\,0000$. As long as
+% the operands are in some range, the result of this second computation
+% has $9$ digits. The corresponding \texttt{pack} function,
+% expanded after the result is computed, braces the last $4$ digits, and
+% leaves |+| \meta{5 digits} for the initial computation. The
+% \enquote{leading shift} cancels the combination of the other shifts,
+% and the |\post_processing:w| takes care of packing the last few
+% digits.
+%
+% Admittedly, this is quite intricate. It is probably the key in making
+% \pkg{l3fp} as fast as other pure \TeX{} floating point units despite
+% its increased precision. In fact, this is used so much that we
+% provide different sets of packing functions and shifts, depending on
+% ranges of input.
+%
+% \begin{macro}[EXP]{\@@_pack:NNNNNw}
+% \begin{variable}
+% {
+% \c_@@_trailing_shift_int ,
+% \c_@@_middle_shift_int ,
+% \c_@@_leading_shift_int ,
+% }
+% This set of shifts allows for computations involving results in the
+% range $[-4\cdot 10^{8}, 5\cdot 10^{8}-1]$. Shifted values all have
+% exactly $9$ digits.
+% \begin{macrocode}
+\int_const:Nn \c_@@_leading_shift_int { - 5 0000 }
+\int_const:Nn \c_@@_middle_shift_int { 5 0000 * 9999 }
+\int_const:Nn \c_@@_trailing_shift_int { 5 0000 * 10000 }
+\cs_new:Npn \@@_pack:NNNNNw #1 #2#3#4#5 #6; { + #1#2#3#4#5 ; {#6} }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_pack_big:NNNNNNw}
+% \begin{variable}
+% {
+% \c_@@_big_trailing_shift_int ,
+% \c_@@_big_middle_shift_int ,
+% \c_@@_big_leading_shift_int ,
+% }
+% This set of shifts allows for computations involving results in the
+% range $[-5\cdot 10^{8}, 6\cdot 10^{8}-1]$ (actually a bit more).
+% Shifted values all have exactly $10$ digits. Note that the upper
+% bound is due to \TeX{}'s limit of $2^{31}-1$ on integers. The
+% shifts are chosen to be roughly the mid-point of $10^{9}$ and
+% $2^{31}$, the two bounds on $10$-digit integers in \TeX{}.
+% \begin{macrocode}
+\int_const:Nn \c_@@_big_leading_shift_int { - 15 2374 }
+\int_const:Nn \c_@@_big_middle_shift_int { 15 2374 * 9999 }
+\int_const:Nn \c_@@_big_trailing_shift_int { 15 2374 * 10000 }
+\cs_new:Npn \@@_pack_big:NNNNNNw #1#2 #3#4#5#6 #7;
+ { + #1#2#3#4#5#6 ; {#7} }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% ^^A \@@_pack_Bigg:NNNNNNw = \@@_pack_big:NNNNNNw ?
+% \begin{macro}[EXP]{\@@_pack_Bigg:NNNNNNw}
+% \begin{variable}
+% {
+% \c_@@_Bigg_trailing_shift_int ,
+% \c_@@_Bigg_middle_shift_int ,
+% \c_@@_Bigg_leading_shift_int ,
+% }
+% This set of shifts allows for computations with results in the
+% range $[-1\cdot 10^{9}, 147483647]$; the end-point is $2^{31} - 1 -
+% 2\cdot 10^{9} \simeq 1.47\cdot 10^{8}$. Shifted values all have
+% exactly $10$ digits.
+% \begin{macrocode}
+\int_const:Nn \c_@@_Bigg_leading_shift_int { - 20 0000 }
+\int_const:Nn \c_@@_Bigg_middle_shift_int { 20 0000 * 9999 }
+\int_const:Nn \c_@@_Bigg_trailing_shift_int { 20 0000 * 10000 }
+\cs_new:Npn \@@_pack_Bigg:NNNNNNw #1#2 #3#4#5#6 #7;
+ { + #1#2#3#4#5#6 ; {#7} }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_pack_twice_four:wNNNNNNNN}
+% \begin{syntax}
+% \cs{@@_pack_twice_four:wNNNNNNNN} \meta{tokens} |;| \meta{$\geq 8$ digits}
+% \end{syntax}
+% Grabs two sets of $4$ digits and places them before the semi-colon
+% delimiter. Putting several copies of this function before a
+% semicolon packs more digits since each takes the digits
+% packed by the others in its first argument.
+% \begin{macrocode}
+\cs_new:Npn \@@_pack_twice_four:wNNNNNNNN #1; #2#3#4#5 #6#7#8#9
+ { #1 {#2#3#4#5} {#6#7#8#9} ; }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_pack_eight:wNNNNNNNN}
+% \begin{syntax}
+% \cs{@@_pack_eight:wNNNNNNNN} \meta{tokens} |;| \meta{$\geq 8$ digits}
+% \end{syntax}
+% Grabs one set of $8$ digits and places them before the semi-colon
+% delimiter as a single group. Putting several copies of this
+% function before a semicolon packs more digits since each
+% takes the digits packed by the others in its first argument.
+% \begin{macrocode}
+\cs_new:Npn \@@_pack_eight:wNNNNNNNN #1; #2#3#4#5 #6#7#8#9
+ { #1 {#2#3#4#5#6#7#8#9} ; }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_basics_pack_low:NNNNNw,
+% \@@_basics_pack_high:NNNNNw,
+% \@@_basics_pack_high_carry:w
+% }
+% Addition and multiplication of significands are done in two steps:
+% first compute a (more or less) exact result, then round and pack
+% digits in the final (braced) form. These functions take care of the
+% packing, with special attention given to the case where rounding has
+% caused a carry. Since rounding can only shift the final digit by
+% $1$, a carry always produces an exact power of $10$. Thus,
+% \cs{@@_basics_pack_high_carry:w} is always followed by four times
+% |{0000}|.
+%
+% This is used in \pkg{l3fp-basics} and \pkg{l3fp-extended}.
+% \begin{macrocode}
+\cs_new:Npn \@@_basics_pack_low:NNNNNw #1 #2#3#4#5 #6;
+ { + #1 - 1 ; {#2#3#4#5} {#6} ; }
+\cs_new:Npn \@@_basics_pack_high:NNNNNw #1 #2#3#4#5 #6;
+ {
+ \if_meaning:w 2 #1
+ \@@_basics_pack_high_carry:w
+ \fi:
+ ; {#2#3#4#5} {#6}
+ }
+\cs_new:Npn \@@_basics_pack_high_carry:w \fi: ; #1
+ { \fi: + 1 ; {1000} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_basics_pack_weird_low:NNNNw,
+% \@@_basics_pack_weird_high:NNNNNNNNw
+% }
+% This is used in \pkg{l3fp-basics} for additions and
+% divisions. Their syntax is confusing, hence the name.
+% \begin{macrocode}
+\cs_new:Npn \@@_basics_pack_weird_low:NNNNw #1 #2#3#4 #5;
+ {
+ \if_meaning:w 2 #1
+ + 1
+ \fi:
+ \@@_int_eval_end:
+ #2#3#4; {#5} ;
+ }
+\cs_new:Npn \@@_basics_pack_weird_high:NNNNNNNNw
+ 1 #1#2#3#4 #5#6#7#8 #9; { ; {#1#2#3#4} {#5#6#7#8} {#9} }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Decimate (dividing by a power of 10)}
+%
+% ^^A begin[todo]
+% \begin{macro}[EXP]{\@@_decimate:nNnnnn}
+% \begin{syntax}
+% \cs{@@_decimate:nNnnnn} \Arg{shift} \meta{f_1}
+% ~~\Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4}
+% \end{syntax}
+% Each \meta{X_i} consists in $4$ digits exactly,
+% and $1000\leq\meta{X_1}<9999$. The first argument determines
+% by how much we shift the digits. \meta{f_1} is called as follows:
+% \begin{syntax}
+% \meta{f_1} \meta{rounding} \Arg{X'_1} \Arg{X'_2} \meta{extra-digits} |;|
+% \end{syntax}
+% where $0\leq\meta{X'_i}<10^{8}-1$ are $8$ digit integers,
+% forming the truncation of our number. In other words,
+% \[
+% \left(
+% \sum_{i=1}^{4} \meta{X_i} \cdot 10^{-4i} \cdot 10^{-\meta{shift}}
+% \right)
+% - \bigl( \meta{X'_1} \cdot 10^{-8} + \meta{X'_2} \cdot 10^{-16} \bigr)
+% = 0.\meta{extra-digits} \cdot 10^{-16}
+% \in [0,10^{-16}).
+% \]
+% To round properly later, we need to remember some information
+% about the difference. The \meta{rounding} digit is $0$ if and
+% only if the difference is exactly $0$, and $5$ if and only if
+% the difference is exactly $0.5\cdot 10^{-16}$. Otherwise, it
+% is the (non-$0$, non-$5$) digit closest to $10^{17}$ times the
+% difference. In particular, if the shift is $17$ or more, all
+% the digits are dropped, \meta{rounding} is $1$ (not $0$), and
+% \meta{X'_1} and \meta{X'_2} are both zero.
+%
+% If the shift is $1$, the \meta{rounding} digit is simply the
+% only digit that was pushed out of the brace groups (this is
+% important for subtraction). It would be more natural for the
+% \meta{rounding} digit to be placed after the \meta{X'_i},
+% but the choice we make involves less reshuffling.
+%
+% Note that this function treats negative \meta{shift} as $0$.
+% \begin{macrocode}
+\cs_new:Npn \@@_decimate:nNnnnn #1
+ {
+ \cs:w
+ @@_decimate_
+ \if_int_compare:w \@@_int_eval:w #1 > \c_@@_prec_int
+ tiny
+ \else:
+ \@@_int_to_roman:w \@@_int_eval:w #1
+ \fi:
+ :Nnnnn
+ \cs_end:
+ }
+% \end{macrocode}
+% Each of the auxiliaries see the function \meta{f_1},
+% followed by $4$ blocks of $4$ digits.
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_decimate_:Nnnnn, \@@_decimate_tiny:Nnnnn}
+% If the \meta{shift} is zero, or too big, life is very easy.
+% \begin{macrocode}
+\cs_new:Npn \@@_decimate_:Nnnnn #1 #2#3#4#5
+ { #1 0 {#2#3} {#4#5} ; }
+\cs_new:Npn \@@_decimate_tiny:Nnnnn #1 #2#3#4#5
+ { #1 1 { 0000 0000 } { 0000 0000 } 0 #2#3#4#5 ; }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_decimate_auxi:Nnnnn, \@@_decimate_auxii:Nnnnn,
+% \@@_decimate_auxiii:Nnnnn, \@@_decimate_auxiv:Nnnnn,
+% \@@_decimate_auxv:Nnnnn, \@@_decimate_auxvi:Nnnnn,
+% \@@_decimate_auxvii:Nnnnn, \@@_decimate_auxviii:Nnnnn,
+% \@@_decimate_auxix:Nnnnn, \@@_decimate_auxx:Nnnnn,
+% \@@_decimate_auxxi:Nnnnn, \@@_decimate_auxxii:Nnnnn,
+% \@@_decimate_auxxiii:Nnnnn, \@@_decimate_auxxiv:Nnnnn,
+% \@@_decimate_auxxv:Nnnnn, \@@_decimate_auxxvi:Nnnnn
+% }
+% \begin{syntax}
+% \cs{@@_decimate_auxi:Nnnnn} \meta{f_1} \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4}
+% \end{syntax}
+% Shifting happens in two steps: compute the \meta{rounding} digit,
+% and repack digits into two blocks of $8$. The sixteen functions
+% are very similar, and defined through \cs{@@_tmp:w}.
+% The arguments are as follows: |#1| indicates which function is
+% being defined; after one step of expansion, |#2| yields the
+% \enquote{extra digits} which are then converted by
+% \cs{@@_round_digit:Nw} to the \meta{rounding} digit (note the |+|
+% separating blocks of digits to avoid overflowing \TeX{}'s integers).
+% This triggers the \texttt{f}-expansion of
+% \cs{@@_decimate_pack:nnnnnnnnnnw},\footnote{No, the argument
+% spec is not a mistake: the function calls an auxiliary to
+% do half of the job.} responsible for building two blocks of
+% $8$ digits, and removing the rest. For this to work, |#3|
+% alternates between braced and unbraced blocks of $4$ digits,
+% in such a way that the $5$ first and $5$ next token groups
+% yield the correct blocks of $8$ digits.
+% \begin{macrocode}
+\cs_new:Npn \@@_tmp:w #1 #2 #3
+ {
+ \cs_new:cpn { @@_decimate_ #1 :Nnnnn } ##1 ##2##3##4##5
+ {
+ \exp_after:wN ##1
+ \int_value:w
+ \exp_after:wN \@@_round_digit:Nw #2 ;
+ \@@_decimate_pack:nnnnnnnnnnw #3 ;
+ }
+ }
+\@@_tmp:w {i} {\use_none:nnn #50}{ 0{#2}#3{#4}#5 }
+\@@_tmp:w {ii} {\use_none:nn #5 }{ 00{#2}#3{#4}#5 }
+\@@_tmp:w {iii} {\use_none:n #5 }{ 000{#2}#3{#4}#5 }
+\@@_tmp:w {iv} { #5 }{ {0000}#2{#3}#4 #5 }
+\@@_tmp:w {v} {\use_none:nnn #4#5 }{ 0{0000}#2{#3}#4 #5 }
+\@@_tmp:w {vi} {\use_none:nn #4#5 }{ 00{0000}#2{#3}#4 #5 }
+\@@_tmp:w {vii} {\use_none:n #4#5 }{ 000{0000}#2{#3}#4 #5 }
+\@@_tmp:w {viii}{ #4#5 }{ {0000}0000{#2}#3 #4 #5 }
+\@@_tmp:w {ix} {\use_none:nnn #3#4+#5}{ 0{0000}0000{#2}#3 #4 #5 }
+\@@_tmp:w {x} {\use_none:nn #3#4+#5}{ 00{0000}0000{#2}#3 #4 #5 }
+\@@_tmp:w {xi} {\use_none:n #3#4+#5}{ 000{0000}0000{#2}#3 #4 #5 }
+\@@_tmp:w {xii} { #3#4+#5}{ {0000}0000{0000}#2 #3 #4 #5 }
+\@@_tmp:w {xiii}{\use_none:nnn#2#3+#4#5}{ 0{0000}0000{0000}#2 #3 #4 #5 }
+\@@_tmp:w {xiv} {\use_none:nn #2#3+#4#5}{ 00{0000}0000{0000}#2 #3 #4 #5 }
+\@@_tmp:w {xv} {\use_none:n #2#3+#4#5}{ 000{0000}0000{0000}#2 #3 #4 #5 }
+\@@_tmp:w {xvi} { #2#3+#4#5}{{0000}0000{0000}0000 #2 #3 #4 #5}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_decimate_pack:nnnnnnnnnnw}
+% The computation of the \meta{rounding} digit leaves an unfinished
+% \cs{int_value:w}, which expands the following functions. This
+% allows us to repack nicely the digits we keep. Those digits come
+% as an alternation of unbraced and braced blocks of $4$ digits,
+% such that the first $5$ groups of token consist in $4$ single digits,
+% and one brace group (in some order), and the next $5$ have the same
+% structure. This is followed by some digits and a semicolon.
+% \begin{macrocode}
+\cs_new:Npn \@@_decimate_pack:nnnnnnnnnnw #1#2#3#4#5
+ { \@@_decimate_pack:nnnnnnw { #1#2#3#4#5 } }
+\cs_new:Npn \@@_decimate_pack:nnnnnnw #1 #2#3#4#5#6
+ { {#1} {#2#3#4#5#6} }
+% \end{macrocode}
+% \end{macro}
+% ^^A end[todo]
+%
+% \subsection{Functions for use within primitive conditional branches}
+%
+% The functions described in this section are not pretty and can easily
+% be misused. When correctly used, each of them removes one \cs{fi:} as
+% part of its parameter text, and puts one back as part of its
+% replacement text.
+%
+% Many computation functions in \pkg{l3fp} must perform tests on the
+% type of floating points that they receive. This is often done in an
+% \cs{if_case:w} statement or another conditional statement, and only a
+% few cases lead to actual computations: most of the special cases are
+% treated using a few standard functions which we define now. A typical
+% use context for those functions would be
+% \begin{syntax}
+% \cs{if_case:w} \meta{integer} \cs{exp_stop_f:}
+% | |\cs{@@_case_return_o:Nw} \meta{fp var}
+% \cs{or:} \cs{@@_case_use:nw} \Arg{some computation}
+% \cs{or:} \cs{@@_case_return_same_o:w}
+% \cs{or:} \cs{@@_case_return:nw} \Arg{something}
+% \cs{fi:}
+% \meta{junk}
+% \meta{floating point}
+% \end{syntax}
+% In this example, the case $0$ returns the floating point
+% \meta{fp~var}, expanding once after that floating point. Case $1$
+% does \meta{some computation} using the \meta{floating point}
+% (presumably compute the operation requested by the user in that
+% non-trivial case). Case $2$ returns the \meta{floating point}
+% without modifying it, removing the \meta{junk} and expanding once
+% after. Case $3$ closes the conditional, removes the \meta{junk}
+% and the \meta{floating point}, and expands \meta{something} next. In
+% other cases, the \enquote{\meta{junk}} is expanded, performing some
+% other operation on the \meta{floating point}. We provide similar
+% functions with two trailing \meta{floating points}.
+%
+% \begin{macro}[EXP]{\@@_case_use:nw}
+% This function ends a \TeX{} conditional, removes junk until the next
+% floating point, and places its first argument before that floating
+% point, to perform some operation on the floating point.
+% \begin{macrocode}
+\cs_new:Npn \@@_case_use:nw #1#2 \fi: #3 \s_@@ { \fi: #1 \s_@@ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_case_return:nw}
+% This function ends a \TeX{} conditional, removes junk and a floating
+% point, and places its first argument in the input stream. A quirk
+% is that we don't define this function requiring a floating point to
+% follow, simply anything ending in a semicolon. This, in turn, means
+% that the \meta{junk} may not contain semicolons.
+% \begin{macrocode}
+\cs_new:Npn \@@_case_return:nw #1#2 \fi: #3 ; { \fi: #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_case_return_o:Nw}
+% This function ends a \TeX{} conditional, removes junk and a floating
+% point, and returns its first argument (an \meta{fp~var}) then expands
+% once after it.
+% \begin{macrocode}
+\cs_new:Npn \@@_case_return_o:Nw #1#2 \fi: #3 \s_@@ #4 ;
+ { \fi: \exp_after:wN #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_case_return_same_o:w}
+% This function ends a \TeX{} conditional, removes junk, and returns
+% the following floating point, expanding once after it.
+% \begin{macrocode}
+\cs_new:Npn \@@_case_return_same_o:w #1 \fi: #2 \s_@@
+ { \fi: \@@_exp_after_o:w \s_@@ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_case_return_o:Nww}
+% Same as \cs{@@_case_return_o:Nw} but with two trailing floating
+% points.
+% \begin{macrocode}
+\cs_new:Npn \@@_case_return_o:Nww #1#2 \fi: #3 \s_@@ #4 ; #5 ;
+ { \fi: \exp_after:wN #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_case_return_i_o:ww, \@@_case_return_ii_o:ww}
+% Similar to \cs{@@_case_return_same_o:w}, but this returns the first
+% or second of two trailing floating point numbers, expanding once
+% after the result.
+% \begin{macrocode}
+\cs_new:Npn \@@_case_return_i_o:ww #1 \fi: #2 \s_@@ #3 ; \s_@@ #4 ;
+ { \fi: \@@_exp_after_o:w \s_@@ #3 ; }
+\cs_new:Npn \@@_case_return_ii_o:ww #1 \fi: #2 \s_@@ #3 ;
+ { \fi: \@@_exp_after_o:w }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Integer floating points}
+%
+% \begin{macro}[EXP, pTF]{\@@_int:w}
+% Tests if the floating point argument is an integer. For normal
+% floating point numbers, this holds if the rounding digit resulting
+% from \cs{@@_decimate:nNnnnn} is~$0$.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_int:w \s_@@ \@@_chk:w #1 #2 #3 #4;
+ { TF , T , F , p }
+ {
+ \if_case:w #1 \exp_stop_f:
+ \prg_return_true:
+ \or:
+ \if_charcode:w 0
+ \@@_decimate:nNnnnn { \c_@@_prec_int - #3 }
+ \@@_use_i_until_s:nw #4
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \else: \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Small integer floating points}
+%
+% \begin{macro}[EXP]{\@@_small_int:wTF}
+% \begin{macro}[EXP]
+% {
+% \@@_small_int_true:wTF,
+% \@@_small_int_normal:NnwTF,
+% \@@_small_int_test:NnnwNTF
+% }
+% Tests if the floating point argument is an integer or $\pm\infty$.
+% If so, it is clipped to an integer in the range $[-10^{8},10^{8}]$
+% and fed as a braced argument to the \meta{true code}.
+% Otherwise, the \meta{false code} is performed.
+%
+% First filter special cases: zeros and infinities are integers,
+% \texttt{nan} is not. For normal numbers, decimate. If the rounding
+% digit is not $0$ run the \meta{false code}. If it is, then the
+% integer is |#2| |#3|; use |#3| if |#2| vanishes and otherwise
+% $10^{8}$.
+% \begin{macrocode}
+\cs_new:Npn \@@_small_int:wTF \s_@@ \@@_chk:w #1#2
+ {
+ \if_case:w #1 \exp_stop_f:
+ \@@_case_return:nw { \@@_small_int_true:wTF 0 ; }
+ \or: \exp_after:wN \@@_small_int_normal:NnwTF
+ \or:
+ \@@_case_return:nw
+ {
+ \exp_after:wN \@@_small_int_true:wTF \int_value:w
+ \if_meaning:w 2 #2 - \fi: 1 0000 0000 ;
+ }
+ \else: \@@_case_return:nw \use_ii:nn
+ \fi:
+ #2
+ }
+\cs_new:Npn \@@_small_int_true:wTF #1; #2#3 { #2 {#1} }
+\cs_new:Npn \@@_small_int_normal:NnwTF #1#2#3;
+ {
+ \@@_decimate:nNnnnn { \c_@@_prec_int - #2 }
+ \@@_small_int_test:NnnwNw
+ #3 #1
+ }
+\cs_new:Npn \@@_small_int_test:NnnwNw #1#2#3#4; #5
+ {
+ \if_meaning:w 0 #1
+ \exp_after:wN \@@_small_int_true:wTF
+ \int_value:w \if_meaning:w 2 #5 - \fi:
+ \if_int_compare:w #2 > \c_zero_int
+ 1 0000 0000
+ \else:
+ #3
+ \fi:
+ \exp_after:wN ;
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Fast string comparison}
+%
+% \begin{macro}{\@@_str_if_eq:nn}
+% A private version of the low-level string comparison function.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_str_if_eq:nn \tex_strcmp:D
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Name of a function from its \pkg{l3fp-parse} name}
+%
+% \begin{macro}[EXP]{\@@_func_to_name:N, \@@_func_to_name_aux:w}
+% The goal is to convert for instance \cs{@@_sin_o:w} to |sin|.
+% This is used in error messages hence does not need to be fast.
+% \begin{macrocode}
+\cs_new:Npn \@@_func_to_name:N #1
+ {
+ \exp_last_unbraced:Nf
+ \@@_func_to_name_aux:w { \cs_to_str:N #1 } X
+ }
+\cs_set_protected:Npn \@@_tmp:w #1 #2
+ { \cs_new:Npn \@@_func_to_name_aux:w ##1 #1 ##2 #2 ##3 X {##2} }
+\exp_args:Nff \@@_tmp:w { \tl_to_str:n { @@_ } }
+ { \tl_to_str:n { _o: } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+% Using a floating point directly is an error.
+% \begin{macrocode}
+\msg_new:nnnn { fp } { misused }
+ { A~floating~point~with~value~'#1'~was~misused. }
+ {
+ To~obtain~the~value~of~a~floating~point~variable,~use~
+ '\token_to_str:N \fp_to_decimal:N',~
+ '\token_to_str:N \fp_to_tl:N',~or~other~
+ conversion~functions.
+ }
+\prop_gput:Nnn \g_msg_module_name_prop { fp } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { fp } { }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-basics.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-basics.dtx
new file mode 100644
index 00000000000..d3dcb68dde3
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-basics.dtx
@@ -0,0 +1,2181 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-basics.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3fp-basics} module\\
+% Floating point arithmetic^^A
+% }
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-basics} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% The \pkg{l3fp-basics} module implements addition, subtraction,
+% multiplication, and division of two floating points, and the absolute
+% value and sign-changing operations on one floating point.
+% All operations implemented in this module yield the outcome of
+% rounding the infinitely precise result of the operation to the
+% nearest floating point.
+%
+% Some algorithms used below end up being quite similar to some
+% described in \enquote{What Every Computer Scientist Should Know About
+% Floating Point Arithmetic}, by David Goldberg, which can be found at
+% \texttt{http://cr.yp.to/2005-590/goldberg.pdf}.
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_word_abs:N ,
+% \@@_parse_word_logb:N ,
+% \@@_parse_word_sign:N ,
+% \@@_parse_word_sqrt:N ,
+% }
+% Unary functions.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_word_abs:N
+ { \@@_parse_unary_function:NNN \@@_set_sign_o:w 0 }
+\cs_new:Npn \@@_parse_word_logb:N
+ { \@@_parse_unary_function:NNN \@@_logb_o:w ? }
+\cs_new:Npn \@@_parse_word_sign:N
+ { \@@_parse_unary_function:NNN \@@_sign_o:w ? }
+\cs_new:Npn \@@_parse_word_sqrt:N
+ { \@@_parse_unary_function:NNN \@@_sqrt_o:w ? }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Addition and subtraction}
+%
+% We define here two functions, \cs{@@_-_o:ww} and \cs{@@_+_o:ww}, which
+% perform the subtraction and addition of their two floating point
+% operands, and expand the tokens following the result once.
+%
+% A more obscure function, \cs{@@_add_big_i_o:wNww}, is used in
+% \pkg{l3fp-expo}.
+%
+% The logic goes as follows:
+% \begin{itemize}
+% \item \cs{@@_-_o:ww} calls \cs{@@_+_o:ww} to do the work, with the
+% sign of the second operand flipped;
+% \item \cs{@@_+_o:ww} dispatches depending on the type of floating
+% point, calling specialized auxiliaries;
+% \item in all cases except summing two normal floating point numbers,
+% we return one or the other operands depending on the signs, or
+% detect an invalid operation in the case of $\infty - \infty$;
+% \item for normal floating point numbers, compare the signs;
+% \item to add two floating point numbers of the same sign or of
+% opposite signs, shift the significand of the smaller one to match the
+% bigger one, perform the addition or subtraction of significands,
+% check for a carry, round, and pack using the
+% \cs[no-index]{@@_basics_pack_\ldots{}} functions.
+% \end{itemize}
+% The trickiest part is to round correctly when adding or subtracting
+% normal floating point numbers.
+%
+% \subsubsection{Sign, exponent, and special numbers}
+%
+% \begin{macro}[EXP]{\@@_-_o:ww}
+% The \cs{@@_+_o:ww} auxiliary has a hook: it takes one argument
+% between the first \cs{s_@@} and \cs{@@_chk:w}, which is applied to
+% the sign of the second operand. Positioning the hook there means
+% that \cs{@@_+_o:ww} can still perform the sanity check that it was
+% followed by \cs{s_@@}.
+% \begin{macrocode}
+\cs_new:cpe { @@_-_o:ww } \s_@@
+ {
+ \exp_not:c { @@_+_o:ww }
+ \exp_not:n { \s_@@ \@@_neg_sign:N }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_+_o:ww}
+% This function is either called directly with an empty |#1| to
+% compute an addition, or it is called by \cs{@@_-_o:ww} with
+% \cs{@@_neg_sign:N} as |#1| to compute a subtraction, in which case
+% the second operand's sign should be changed. If the
+% \meta{types} |#2| and |#4| are the same, dispatch to case |#2| ($0$,
+% $1$, $2$, or $3$), where we call specialized functions: thanks to
+% \cs{int_value:w}, those receive the tweaked \meta{sign_2}
+% (expansion of |#1#5|) as an argument. If the \meta{types} are
+% distinct, the result is simply the floating point number with the
+% highest \meta{type}. Since case $3$ (used for two \texttt{nan})
+% also picks the first operand, we can also use it when \meta{type_1}
+% is greater than \meta{type_2}. Also note that we don't need to
+% worry about \meta{sign_2} in that case since the second operand is
+% discarded.
+% \begin{macrocode}
+\cs_new:cpn { @@_+_o:ww }
+ \s_@@ #1 \@@_chk:w #2 #3 ; \s_@@ \@@_chk:w #4 #5
+ {
+ \if_case:w
+ \if_meaning:w #2 #4
+ #2
+ \else:
+ \if_int_compare:w #2 > #4 \exp_stop_f:
+ 3
+ \else:
+ 4
+ \fi:
+ \fi:
+ \exp_stop_f:
+ \exp_after:wN \@@_add_zeros_o:Nww \int_value:w
+ \or: \exp_after:wN \@@_add_normal_o:Nww \int_value:w
+ \or: \exp_after:wN \@@_add_inf_o:Nww \int_value:w
+ \or: \@@_case_return_i_o:ww
+ \else: \exp_after:wN \@@_add_return_ii_o:Nww \int_value:w
+ \fi:
+ #1 #5
+ \s_@@ \@@_chk:w #2 #3 ;
+ \s_@@ \@@_chk:w #4 #5
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_add_return_ii_o:Nww}
+% Ignore the first operand, and return the second, but using the sign
+% |#1| rather than |#4|. As usual, expand after the floating point.
+% \begin{macrocode}
+\cs_new:Npn \@@_add_return_ii_o:Nww #1 #2 ; \s_@@ \@@_chk:w #3 #4
+ { \@@_exp_after_o:w \s_@@ \@@_chk:w #3 #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_add_zeros_o:Nww}
+% Adding two zeros yields \cs{c_zero_fp}, except if both zeros were
+% $-0$.
+% \begin{macrocode}
+\cs_new:Npn \@@_add_zeros_o:Nww #1 \s_@@ \@@_chk:w 0 #2
+ {
+ \if_int_compare:w #2 #1 = 20 \exp_stop_f:
+ \exp_after:wN \@@_add_return_ii_o:Nww
+ \else:
+ \@@_case_return_i_o:ww
+ \fi:
+ #1
+ \s_@@ \@@_chk:w 0 #2
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_add_inf_o:Nww}
+% If both infinities have the same sign, just return that infinity,
+% otherwise, it is an invalid operation. We find out if that invalid
+% operation is an addition or a subtraction by testing whether the
+% tweaked \meta{sign_2} (|#1|) and the \meta{sign_2} (|#4|) are
+% identical.
+% \begin{macrocode}
+\cs_new:Npn \@@_add_inf_o:Nww
+ #1 \s_@@ \@@_chk:w 2 #2 #3; \s_@@ \@@_chk:w 2 #4
+ {
+ \if_meaning:w #1 #2
+ \@@_case_return_i_o:ww
+ \else:
+ \@@_case_use:nw
+ {
+ \exp_last_unbraced:Nf \@@_invalid_operation_o:Nww
+ { \token_if_eq_meaning:NNTF #1 #4 + - }
+ }
+ \fi:
+ \s_@@ \@@_chk:w 2 #2 #3;
+ \s_@@ \@@_chk:w 2 #4
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_add_normal_o:Nww}
+% \begin{quote}
+% \cs{@@_add_normal_o:Nww} \meta{sign_2}
+% \cs{s_@@} \cs{@@_chk:w} |1| \meta{sign_1}
+% \meta{exp_1} \meta{body_1} |;|
+% \cs{s_@@} \cs{@@_chk:w} |1| \meta{initial sign_2}
+% \meta{exp_2} \meta{body_2} |;|
+% \end{quote}
+% We now have two normal numbers to add, and we have to check signs
+% and exponents more carefully before performing the addition.
+% \begin{macrocode}
+\cs_new:Npn \@@_add_normal_o:Nww #1 \s_@@ \@@_chk:w 1 #2
+ {
+ \if_meaning:w #1#2
+ \exp_after:wN \@@_add_npos_o:NnwNnw
+ \else:
+ \exp_after:wN \@@_sub_npos_o:NnwNnw
+ \fi:
+ #2
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Absolute addition}
+%
+% In this subsection, we perform the addition of two positive normal
+% numbers.
+%
+% \begin{macro}[EXP]{\@@_add_npos_o:NnwNnw}
+% \begin{quote}
+% \cs{@@_add_npos_o:NnwNnw} \meta{sign_1} \meta{exp_1} \meta{body_1}
+% |;| \cs{s_@@} \cs{@@_chk:w} |1| \meta{initial sign_2} \meta{exp_2}
+% \meta{body_2} |;|
+% \end{quote}
+% Since we are doing an addition, the final sign is \meta{sign_1}.
+% Start an \cs{@@_int_eval:w}, responsible for computing the exponent:
+% the result, and the \meta{final sign} are then given to
+% \cs{@@_sanitize:Nw} which checks for overflow. The exponent is
+% computed as the largest exponent |#2| or |#5|, incremented if there
+% is a carry. To add the significands, we decimate the smaller number by
+% the difference between the exponents. This is done by
+% \cs{@@_add_big_i:wNww} or \cs{@@_add_big_ii:wNww}. We need to bring
+% the final sign with us in the midst of the calculation to round
+% properly at the end.
+% \begin{macrocode}
+\cs_new:Npn \@@_add_npos_o:NnwNnw #1#2#3 ; \s_@@ \@@_chk:w 1 #4 #5
+ {
+ \exp_after:wN \@@_sanitize:Nw
+ \exp_after:wN #1
+ \int_value:w \@@_int_eval:w
+ \if_int_compare:w #2 > #5 \exp_stop_f:
+ #2
+ \exp_after:wN \@@_add_big_i_o:wNww \int_value:w -
+ \else:
+ #5
+ \exp_after:wN \@@_add_big_ii_o:wNww \int_value:w
+ \fi:
+ \@@_int_eval:w #5 - #2 ; #1 #3;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_add_big_i_o:wNww}
+% \begin{macro}[rEXP]{\@@_add_big_ii_o:wNww}
+% \begin{quote}
+% \cs{@@_add_big_i_o:wNww} \meta{shift} |;| \meta{final sign}
+% \meta{body_1} |;| \meta{body_2} |;|
+% \end{quote}
+% Used in \pkg{l3fp-expo}.
+% Shift the significand of the small number, then add with
+% \cs{@@_add_significand_o:NnnwnnnnN}.
+% \begin{macrocode}
+\cs_new:Npn \@@_add_big_i_o:wNww #1; #2 #3; #4;
+ {
+ \@@_decimate:nNnnnn {#1}
+ \@@_add_significand_o:NnnwnnnnN
+ #4
+ #3
+ #2
+ }
+\cs_new:Npn \@@_add_big_ii_o:wNww #1; #2 #3; #4;
+ {
+ \@@_decimate:nNnnnn {#1}
+ \@@_add_significand_o:NnnwnnnnN
+ #3
+ #4
+ #2
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_add_significand_o:NnnwnnnnN}
+% \begin{macro}[rEXP]
+% {\@@_add_significand_pack:NNNNNNN, \@@_add_significand_test_o:N}
+% \begin{quote}\raggedright
+% \cs{@@_add_significand_o:NnnwnnnnN}
+% \meta{rounding digit}
+% \Arg{Y'_1} \Arg{Y'_2} \meta{extra-digits} |;|
+% \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4}
+% \meta{final sign}
+% \end{quote}
+% To round properly, we must know at which digit the rounding
+% should occur. This requires to know whether the addition
+% produces an overall carry or not. Thus, we do the computation
+% now and check for a carry, then go back and do the rounding.
+% The rounding may cause a carry in very rare cases such as
+% $0.99\cdots 95 \to 1.00\cdots 0$, but this situation always
+% give an exact power of $10$, for which it is easy to correct
+% the result at the end.
+% \begin{macrocode}
+\cs_new:Npn \@@_add_significand_o:NnnwnnnnN #1 #2#3 #4; #5#6#7#8
+ {
+ \exp_after:wN \@@_add_significand_test_o:N
+ \int_value:w \@@_int_eval:w 1#5#6 + #2
+ \exp_after:wN \@@_add_significand_pack:NNNNNNN
+ \int_value:w \@@_int_eval:w 1#7#8 + #3 ; #1
+ }
+\cs_new:Npn \@@_add_significand_pack:NNNNNNN #1 #2#3#4#5#6#7
+ {
+ \if_meaning:w 2 #1
+ + 1
+ \fi:
+ ; #2 #3 #4 #5 #6 #7 ;
+ }
+\cs_new:Npn \@@_add_significand_test_o:N #1
+ {
+ \if_meaning:w 2 #1
+ \exp_after:wN \@@_add_significand_carry_o:wwwNN
+ \else:
+ \exp_after:wN \@@_add_significand_no_carry_o:wwwNN
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_add_significand_no_carry_o:wwwNN}
+% \begin{quote}
+% \cs{@@_add_significand_no_carry_o:wwwNN}
+% \meta{8d} |;| \meta{6d} |;| \meta{2d} |;|
+% \meta{rounding digit} \meta{sign}
+% \end{quote}
+% If there's no carry, grab all the digits again and round. The
+% packing function \cs{@@_basics_pack_high:NNNNNw} takes care of the
+% case where rounding brings a carry.
+% \begin{macrocode}
+\cs_new:Npn \@@_add_significand_no_carry_o:wwwNN
+ #1; #2; #3#4 ; #5#6
+ {
+ \exp_after:wN \@@_basics_pack_high:NNNNNw
+ \int_value:w \@@_int_eval:w 1 #1
+ \exp_after:wN \@@_basics_pack_low:NNNNNw
+ \int_value:w \@@_int_eval:w 1 #2 #3#4
+ + \@@_round:NNN #6 #4 #5
+ \exp_after:wN ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_add_significand_carry_o:wwwNN}
+% \begin{quote}
+% \cs{@@_add_significand_carry_o:wwwNN}
+% \meta{8d} |;| \meta{6d} |;| \meta{2d} |;|
+% \meta{rounding digit} \meta{sign}
+% \end{quote}
+% The case where there is a carry is very similar. Rounding can even
+% raise the first digit from $1$ to $2$, but we don't care.
+% \begin{macrocode}
+\cs_new:Npn \@@_add_significand_carry_o:wwwNN
+ #1; #2; #3#4; #5#6
+ {
+ + 1
+ \exp_after:wN \@@_basics_pack_weird_high:NNNNNNNNw
+ \int_value:w \@@_int_eval:w 1 1 #1
+ \exp_after:wN \@@_basics_pack_weird_low:NNNNw
+ \int_value:w \@@_int_eval:w 1 #2#3 +
+ \exp_after:wN \@@_round:NNN
+ \exp_after:wN #6
+ \exp_after:wN #3
+ \int_value:w \@@_round_digit:Nw #4 #5 ;
+ \exp_after:wN ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Absolute subtraction}
+%
+% \begin{macro}[EXP]{\@@_sub_npos_o:NnwNnw}
+% \begin{macro}[EXP]{\@@_sub_eq_o:Nnwnw, \@@_sub_npos_ii_o:Nnwnw}
+% \begin{quote}
+% \cs{@@_sub_npos_o:NnwNnw}
+% \meta{sign_1} \meta{exp_1} \meta{body_1} |;|
+% \cs{s_@@} \cs{@@_chk:w} |1|
+% \meta{initial sign_2} \meta{exp_2} \meta{body_2} |;|
+% \end{quote}
+% Rounding properly in some modes requires to know what the sign of
+% the result will be. Thus, we start by comparing the exponents and
+% significands. If the numbers coincide, return zero. If the second
+% number is larger, swap the numbers and call
+% \cs{@@_sub_npos_i_o:Nnwnw} with the opposite of \meta{sign_1}.
+% \begin{macrocode}
+\cs_new:Npn \@@_sub_npos_o:NnwNnw #1#2#3; \s_@@ \@@_chk:w 1 #4#5#6;
+ {
+ \if_case:w \@@_compare_npos:nwnw {#2} #3; {#5} #6; \exp_stop_f:
+ \exp_after:wN \@@_sub_eq_o:Nnwnw
+ \or:
+ \exp_after:wN \@@_sub_npos_i_o:Nnwnw
+ \else:
+ \exp_after:wN \@@_sub_npos_ii_o:Nnwnw
+ \fi:
+ #1 {#2} #3; {#5} #6;
+ }
+\cs_new:Npn \@@_sub_eq_o:Nnwnw #1#2; #3; { \exp_after:wN \c_zero_fp }
+\cs_new:Npn \@@_sub_npos_ii_o:Nnwnw #1 #2; #3;
+ {
+ \exp_after:wN \@@_sub_npos_i_o:Nnwnw
+ \int_value:w \@@_neg_sign:N #1
+ #3; #2;
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_sub_npos_i_o:Nnwnw}
+% After the computation is done, \cs{@@_sanitize:Nw} checks for
+% overflow/underflow. It expects the \meta{final sign} and the
+% \meta{exponent} (delimited by |;|). Start an integer expression for
+% the exponent, which starts with the exponent of the largest number,
+% and may be decreased if the two numbers are very close. If the two
+% numbers have the same exponent, call the \texttt{near} auxiliary.
+% Otherwise, decimate $y$, then call the \texttt{far} auxiliary to
+% evaluate the difference between the two significands. Note that we
+% decimate by $1$ less than one could expect.
+% \begin{macrocode}
+\cs_new:Npn \@@_sub_npos_i_o:Nnwnw #1 #2#3; #4#5;
+ {
+ \exp_after:wN \@@_sanitize:Nw
+ \exp_after:wN #1
+ \int_value:w \@@_int_eval:w
+ #2
+ \if_int_compare:w #2 = #4 \exp_stop_f:
+ \exp_after:wN \@@_sub_back_near_o:nnnnnnnnN
+ \else:
+ \exp_after:wN \@@_decimate:nNnnnn \exp_after:wN
+ { \int_value:w \@@_int_eval:w #2 - #4 - 1 \exp_after:wN }
+ \exp_after:wN \@@_sub_back_far_o:NnnwnnnnN
+ \fi:
+ #5
+ #3
+ #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_sub_back_near_o:nnnnnnnnN}
+% \begin{macro}[rEXP]
+% {\@@_sub_back_near_pack:NNNNNNw, \@@_sub_back_near_after:wNNNNw}
+% \begin{quote}
+% \cs{@@_sub_back_near_o:nnnnnnnnN}
+% \Arg{Y_1} \Arg{Y_2} \Arg{Y_3} \Arg{Y_4}
+% \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4}
+% \meta{final sign}
+% \end{quote}
+% In this case, the subtraction is exact, so we discard the
+% \meta{final sign} |#9|. The very large shifts of $10^{9}$ and
+% $1.1\cdot10^{9}$ are unnecessary here, but allow the auxiliaries to
+% be reused later. Each integer expression produces a $10$ digit
+% result. If the resulting $16$ digits start with a $0$, then we need
+% to shift the group, padding with trailing zeros.
+% \begin{macrocode}
+\cs_new:Npn \@@_sub_back_near_o:nnnnnnnnN #1#2#3#4 #5#6#7#8 #9
+ {
+ \exp_after:wN \@@_sub_back_near_after:wNNNNw
+ \int_value:w \@@_int_eval:w 10#5#6 - #1#2 - 11
+ \exp_after:wN \@@_sub_back_near_pack:NNNNNNw
+ \int_value:w \@@_int_eval:w 11#7#8 - #3#4 \exp_after:wN ;
+ }
+\cs_new:Npn \@@_sub_back_near_pack:NNNNNNw #1#2#3#4#5#6#7 ;
+ { + #1#2 ; {#3#4#5#6} {#7} ; }
+\cs_new:Npn \@@_sub_back_near_after:wNNNNw 10 #1#2#3#4 #5 ;
+ {
+ \if_meaning:w 0 #1
+ \exp_after:wN \@@_sub_back_shift:wnnnn
+ \fi:
+ ; {#1#2#3#4} {#5}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_sub_back_shift:wnnnn}
+% \begin{macro}[rEXP]
+% {
+% \@@_sub_back_shift_ii:ww,
+% \@@_sub_back_shift_iii:NNNNNNNNw,
+% \@@_sub_back_shift_iv:nnnnw
+% }
+% \begin{quote}
+% \cs{@@_sub_back_shift:wnnnn} |;|
+% \Arg{Z_1} \Arg{Z_2} \Arg{Z_3} \Arg{Z_4} |;|
+% \end{quote}
+% This function is called with $\meta{Z_1}\leq 999$. Act with
+% \tn{number} to trim leading zeros from \meta{Z_1} \meta{Z_2} (we
+% don't do all four blocks at once, since non-zero blocks would then
+% overflow \TeX{}'s integers). If the first two blocks are zero, the
+% auxiliary receives an empty |#1| and trims |#2#30| from leading
+% zeros, yielding a total shift between $7$ and~$16$ to the exponent.
+% Otherwise we get the shift from |#1| alone, yielding a result
+% between $1$ and~$6$. Once the exponent is taken care of, trim
+% leading zeros from |#1#2#3| (when |#1| is empty, the space before
+% |#2#3| is ignored), get four blocks of $4$~digits and finally clean
+% up. Trailing zeros are added so that digits can be grabbed safely.
+% \begin{macrocode}
+\cs_new:Npn \@@_sub_back_shift:wnnnn ; #1#2
+ {
+ \exp_after:wN \@@_sub_back_shift_ii:ww
+ \int_value:w #1 #2 0 ;
+ }
+\cs_new:Npn \@@_sub_back_shift_ii:ww #1 0 ; #2#3 ;
+ {
+ \if_meaning:w @ #1 @
+ - 7
+ - \exp_after:wN \use_i:nnn
+ \exp_after:wN \@@_sub_back_shift_iii:NNNNNNNNw
+ \int_value:w #2#3 0 ~ 123456789;
+ \else:
+ - \@@_sub_back_shift_iii:NNNNNNNNw #1 123456789;
+ \fi:
+ \exp_after:wN \@@_pack_twice_four:wNNNNNNNN
+ \exp_after:wN \@@_pack_twice_four:wNNNNNNNN
+ \exp_after:wN \@@_sub_back_shift_iv:nnnnw
+ \exp_after:wN ;
+ \int_value:w
+ #1 ~ #2#3 0 ~ 0000 0000 0000 000 ;
+ }
+\cs_new:Npn \@@_sub_back_shift_iii:NNNNNNNNw #1#2#3#4#5#6#7#8#9; {#8}
+\cs_new:Npn \@@_sub_back_shift_iv:nnnnw #1 ; #2 ; { ; #1 ; }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_sub_back_far_o:NnnwnnnnN}
+% \begin{quote}\raggedright
+% \cs{@@_sub_back_far_o:NnnwnnnnN}
+% \meta{rounding} \Arg{Y'_1} \Arg{Y'_2} \meta{extra-digits} |;|
+% \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4}
+% \meta{final sign}
+% \end{quote}
+% If the difference is greater than $10^{\meta{expo_x}}$, call the
+% \texttt{very_far} auxiliary. If the result is less than
+% $10^{\meta{expo_x}}$, call the \texttt{not_far} auxiliary. If it is
+% too close a call to know yet, namely if $1 \meta{Y'_1} \meta{Y'_2} =
+% \meta{X_1} \meta{X_2} \meta{X_3} \meta{X_4} 0$, then call the
+% \texttt{quite_far} auxiliary. We use the odd combination of space
+% and semi-colon delimiters to allow the \texttt{not_far} auxiliary to
+% grab each piece individually, the \texttt{very_far} auxiliary to use
+% \cs{@@_pack_eight:wNNNNNNNN}, and the \texttt{quite_far} to ignore
+% the significands easily (using the |;| delimiter).
+% \begin{macrocode}
+\cs_new:Npn \@@_sub_back_far_o:NnnwnnnnN #1 #2#3 #4; #5#6#7#8
+ {
+ \if_case:w
+ \if_int_compare:w 1 #2 = #5#6 \use_i:nnnn #7 \exp_stop_f:
+ \if_int_compare:w #3 = \use_none:n #7#8 0 \exp_stop_f:
+ 0
+ \else:
+ \if_int_compare:w #3 > \use_none:n #7#8 0 - \fi: 1
+ \fi:
+ \else:
+ \if_int_compare:w 1 #2 > #5#6 \use_i:nnnn #7 - \fi: 1
+ \fi:
+ \exp_stop_f:
+ \exp_after:wN \@@_sub_back_quite_far_o:wwNN
+ \or: \exp_after:wN \@@_sub_back_very_far_o:wwwwNN
+ \else: \exp_after:wN \@@_sub_back_not_far_o:wwwwNN
+ \fi:
+ #2 ~ #3 ; #5 #6 ~ #7 #8 ; #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_sub_back_quite_far_o:wwNN}
+% \begin{macro}[EXP]{\@@_sub_back_quite_far_ii:NN}
+% The easiest case is when $x-y$ is extremely close to a power of
+% $10$, namely the first digit of $x$ is $1$, and all others vanish
+% when subtracting $y$. Then the \meta{rounding} |#3| and the
+% \meta{final sign} |#4| control whether we get $1$ or $0.9999 9999
+% 9999 9999$. In the usual round-to-nearest mode, we get $1$
+% whenever the \meta{rounding} digit is less than or equal to $5$
+% (remember that the \meta{rounding} digit is only equal to $5$ if
+% there was no further non-zero digit).
+% \begin{macrocode}
+\cs_new:Npn \@@_sub_back_quite_far_o:wwNN #1; #2; #3#4
+ {
+ \exp_after:wN \@@_sub_back_quite_far_ii:NN
+ \exp_after:wN #3
+ \exp_after:wN #4
+ }
+\cs_new:Npn \@@_sub_back_quite_far_ii:NN #1#2
+ {
+ \if_case:w \@@_round_neg:NNN #2 0 #1
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ { ; {1000} {0000} {0000} {0000} ; }
+ { - 1 ; {9999} {9999} {9999} {9999} ; }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_sub_back_not_far_o:wwwwNN}
+% In the present case, $x$ and $y$ have different exponents, but
+% $y$~is large enough that $x-y$ has a smaller exponent than~$x$.
+% Decrement the exponent (with |-1|). Then proceed in a way
+% similar to the \texttt{near} auxiliaries seen earlier, but
+% multiplying $x$ by~$10$ (|#30| and |#40| below), and with the added
+% quirk that the \meta{rounding} digit has to be taken into account.
+% Namely, we may have to decrease the result by one unit if
+% \cs{@@_round_neg:NNN} returns~$1$. This function expects the
+% \meta{final sign}~|#6|, the last digit of |1100000000+#40-#2|, and
+% the \meta{rounding} digit. Instead of redoing the computation for
+% the second argument, we note that \cs{@@_round_neg:NNN} only cares
+% about its parity, which is identical to that of the last digit
+% of~|#2|.
+% \begin{macrocode}
+\cs_new:Npn \@@_sub_back_not_far_o:wwwwNN #1 ~ #2; #3 ~ #4; #5#6
+ {
+ - 1
+ \exp_after:wN \@@_sub_back_near_after:wNNNNw
+ \int_value:w \@@_int_eval:w 1#30 - #1 - 11
+ \exp_after:wN \@@_sub_back_near_pack:NNNNNNw
+ \int_value:w \@@_int_eval:w 11 0000 0000 + #40 - #2
+ - \exp_after:wN \@@_round_neg:NNN
+ \exp_after:wN #6
+ \use_none:nnnnnnn #2 #5
+ \exp_after:wN ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_sub_back_very_far_o:wwwwNN}
+% \begin{macro}[EXP]{\@@_sub_back_very_far_ii_o:nnNwwNN}
+% The case where $x-y$ and $x$ have the same exponent is a bit more
+% tricky, mostly because it cannot reuse the same auxiliaries. Shift
+% the $y$~significand by adding a leading~$0$. Then the logic is similar
+% to the \texttt{not_far} functions above. Rounding is a bit more
+% complicated: we have two \meta{rounding} digits |#3| and |#6| (from
+% the decimation, and from the new shift) to take into account, and
+% getting the parity of the main result requires a computation. The
+% first \cs{int_value:w} triggers the second one because the number
+% is unfinished; we can thus not use $0$ in place of $2$ there.
+% \begin{macrocode}
+\cs_new:Npn \@@_sub_back_very_far_o:wwwwNN #1#2#3#4#5#6#7
+ {
+ \@@_pack_eight:wNNNNNNNN
+ \@@_sub_back_very_far_ii_o:nnNwwNN
+ { 0 #1#2#3 #4#5#6#7 }
+ ;
+ }
+\cs_new:Npn \@@_sub_back_very_far_ii_o:nnNwwNN #1#2 ; #3 ; #4 ~ #5; #6#7
+ {
+ \exp_after:wN \@@_basics_pack_high:NNNNNw
+ \int_value:w \@@_int_eval:w 1#4 - #1 - 1
+ \exp_after:wN \@@_basics_pack_low:NNNNNw
+ \int_value:w \@@_int_eval:w 2#5 - #2
+ - \exp_after:wN \@@_round_neg:NNN
+ \exp_after:wN #7
+ \int_value:w
+ \if_int_odd:w \@@_int_eval:w #5 - #2 \@@_int_eval_end:
+ 1 \else: 2 \fi:
+ \int_value:w \@@_round_digit:Nw #3 #6 ;
+ \exp_after:wN ;
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Multiplication}
+%
+% \subsubsection{Signs, and special numbers}
+%
+% \begin{macro}[EXP]{\@@_*_o:ww}
+% We go through an auxiliary, which is common with \cs{@@_/_o:ww}.
+% The first argument is the operation, used for the invalid operation
+% exception. The second is inserted in a formula to dispatch cases
+% slightly differently between multiplication and division. The third
+% is the operation for normal floating points. The fourth is there
+% for extra cases needed in \cs{@@_/_o:ww}.
+% \begin{macrocode}
+\cs_new:cpn { @@_*_o:ww }
+ {
+ \@@_mul_cases_o:NnNnww
+ *
+ { - 2 + }
+ \@@_mul_npos_o:Nww
+ { }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_mul_cases_o:nNnnww}
+% Split into $10$ cases ($12$ for division).
+% If both numbers are normal, go to case $0$
+% (same sign) or case $1$ (opposite signs): in both cases, call
+% \cs{@@_mul_npos_o:Nww} to do the work. If the first operand is
+% \texttt{nan}, go to case $2$, in which the second operand is
+% discarded; if the second operand is \texttt{nan}, go to case $3$, in
+% which the first operand is discarded (note the weird interaction
+% with the final test on signs). Then we separate the case where the
+% first number is normal and the second is zero: this goes to cases
+% $4$ and $5$ for multiplication, $10$ and $11$ for division.
+% Otherwise, we do a computation which
+% dispatches the products $0\times 0 = 0\times 1 = 1\times 0 = 0$ to
+% case $4$ or $5$ depending on the combined sign, the products
+% $0\times\infty$ and $\infty\times0$ to case $6$ or $7$ (invalid
+% operation), and the products $1\times\infty = \infty\times1 =
+% \infty\times\infty = \infty$ to cases $8$ and $9$. Note that the
+% code for these two cases (which return $\pm\infty$) is inserted as
+% argument |#4|, because it differs in the case of divisions.
+% \begin{macrocode}
+\cs_new:Npn \@@_mul_cases_o:NnNnww
+ #1#2#3#4 \s_@@ \@@_chk:w #5#6#7; \s_@@ \@@_chk:w #8#9
+ {
+ \if_case:w \@@_int_eval:w
+ \if_int_compare:w #5 #8 = 11 ~
+ 1
+ \else:
+ \if_meaning:w 3 #8
+ 3
+ \else:
+ \if_meaning:w 3 #5
+ 2
+ \else:
+ \if_int_compare:w #5 #8 = 10 ~
+ 9 #2 - 2
+ \else:
+ (#5 #2 #8) / 2 * 2 + 7
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \if_meaning:w #6 #9 - 1 \fi:
+ \@@_int_eval_end:
+ \@@_case_use:nw { #3 0 }
+ \or: \@@_case_use:nw { #3 2 }
+ \or: \@@_case_return_i_o:ww
+ \or: \@@_case_return_ii_o:ww
+ \or: \@@_case_return_o:Nww \c_zero_fp
+ \or: \@@_case_return_o:Nww \c_minus_zero_fp
+ \or: \@@_case_use:nw { \@@_invalid_operation_o:Nww #1 }
+ \or: \@@_case_use:nw { \@@_invalid_operation_o:Nww #1 }
+ \or: \@@_case_return_o:Nww \c_inf_fp
+ \or: \@@_case_return_o:Nww \c_minus_inf_fp
+ #4
+ \fi:
+ \s_@@ \@@_chk:w #5 #6 #7;
+ \s_@@ \@@_chk:w #8 #9
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Absolute multiplication}
+%
+% In this subsection, we perform the multiplication
+% of two positive normal numbers.
+%
+% \begin{macro}[EXP]{\@@_mul_npos_o:Nww}
+% \begin{quote}
+% \cs{@@_mul_npos_o:Nww} \meta{final sign}
+% \cs{s_@@} \cs{@@_chk:w} |1| \meta{sign_1} \Arg{exp_1} \meta{body_1} |;|
+% \cs{s_@@} \cs{@@_chk:w} |1| \meta{sign_2} \Arg{exp_2} \meta{body_2} |;|
+% \end{quote}
+% After the computation, \cs{@@_sanitize:Nw} checks for overflow or
+% underflow. As we did for addition, \cs{@@_int_eval:w} computes the
+% exponent, catching any shift coming from the computation in the
+% significand. The \meta{final sign} is needed to do the rounding
+% properly in the significand computation. We setup the post-expansion
+% here, triggered by \cs{@@_mul_significand_o:nnnnNnnnn}.
+%
+% This is also used in \pkg{l3fp-convert}.
+% \begin{macrocode}
+\cs_new:Npn \@@_mul_npos_o:Nww
+ #1 \s_@@ \@@_chk:w #2 #3 #4 #5 ; \s_@@ \@@_chk:w #6 #7 #8 #9 ;
+ {
+ \exp_after:wN \@@_sanitize:Nw
+ \exp_after:wN #1
+ \int_value:w \@@_int_eval:w
+ #4 + #8
+ \@@_mul_significand_o:nnnnNnnnn #5 #1 #9
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_mul_significand_o:nnnnNnnnn}
+% \begin{macro}[EXP]
+% {\@@_mul_significand_drop:NNNNNw, \@@_mul_significand_keep:NNNNNw}
+% \begin{quote}
+% \cs{@@_mul_significand_o:nnnnNnnnn}
+% \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} \meta{sign}
+% \Arg{Y_1} \Arg{Y_2} \Arg{Y_3} \Arg{Y_4}
+% \end{quote}
+% Note the three semicolons at the end of the definition. One is for
+% the last \cs{@@_mul_significand_drop:NNNNNw}; one is for
+% \cs{@@_round_digit:Nw} later on; and one, preceded by
+% \cs{exp_after:wN}, which is correctly expanded (within an
+% \cs{@@_int_eval:w}), is used by \cs{@@_basics_pack_low:NNNNNw}.
+%
+% The product of two $16$ digit integers has $31$ or $32$ digits,
+% but it is impossible to know which one before computing. The place
+% where we round depends on that number of digits, and may depend
+% on all digits until the last in some rare cases. The approach is
+% thus to compute the $5$ first blocks of $4$ digits (the first one
+% is between $100$ and $9999$ inclusive), and a compact version of
+% the remaining $3$ blocks. Afterwards, the number of digits is
+% known, and we can do the rounding within yet another set of
+% \cs{@@_int_eval:w}.
+% \begin{macrocode}
+\cs_new:Npn \@@_mul_significand_o:nnnnNnnnn #1#2#3#4 #5 #6#7#8#9
+ {
+ \exp_after:wN \@@_mul_significand_test_f:NNN
+ \exp_after:wN #5
+ \int_value:w \@@_int_eval:w 99990000 + #1*#6 +
+ \exp_after:wN \@@_mul_significand_keep:NNNNNw
+ \int_value:w \@@_int_eval:w 99990000 + #1*#7 + #2*#6 +
+ \exp_after:wN \@@_mul_significand_keep:NNNNNw
+ \int_value:w \@@_int_eval:w 99990000 + #1*#8 + #2*#7 + #3*#6 +
+ \exp_after:wN \@@_mul_significand_drop:NNNNNw
+ \int_value:w \@@_int_eval:w 99990000 + #1*#9 + #2*#8 +
+ #3*#7 + #4*#6 +
+ \exp_after:wN \@@_mul_significand_drop:NNNNNw
+ \int_value:w \@@_int_eval:w 99990000 + #2*#9 + #3*#8 +
+ #4*#7 +
+ \exp_after:wN \@@_mul_significand_drop:NNNNNw
+ \int_value:w \@@_int_eval:w 99990000 + #3*#9 + #4*#8 +
+ \exp_after:wN \@@_mul_significand_drop:NNNNNw
+ \int_value:w \@@_int_eval:w 100000000 + #4*#9 ;
+ ; \exp_after:wN ;
+ }
+\cs_new:Npn \@@_mul_significand_drop:NNNNNw #1#2#3#4#5 #6;
+ { #1#2#3#4#5 ; + #6 }
+\cs_new:Npn \@@_mul_significand_keep:NNNNNw #1#2#3#4#5 #6;
+ { #1#2#3#4#5 ; #6 ; }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_mul_significand_test_f:NNN}
+% \begin{quote}
+% \cs{@@_mul_significand_test_f:NNN} \meta{sign} |1|
+% \meta{digits 1--8} |;| \meta{digits 9--12} |;| \meta{digits 13--16} |;|
+% |+| \meta{digits 17--20} |+| \meta{digits 21--24}
+% |+| \meta{digits 25--28} |+| \meta{digits 29--32} |;|
+% \cs{exp_after:wN} |;|
+% \end{quote}
+% If the \meta{digit 1} is non-zero, then for rounding we only care
+% about the digits $16$ and $17$, and whether further digits are zero
+% or not (check for exact ties). On the other hand, if \meta{digit 1}
+% is zero, we care about digits $17$ and $18$, and whether further
+% digits are zero.
+% \begin{macrocode}
+\cs_new:Npn \@@_mul_significand_test_f:NNN #1 #2 #3
+ {
+ \if_meaning:w 0 #3
+ \exp_after:wN \@@_mul_significand_small_f:NNwwwN
+ \else:
+ \exp_after:wN \@@_mul_significand_large_f:NwwNNNN
+ \fi:
+ #1 #3
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_mul_significand_large_f:NwwNNNN}
+% In this branch, \meta{digit 1} is non-zero. The result is thus
+% \meta{digits 1--16}, plus some rounding which depends on the digits
+% $16$, $17$, and whether all subsequent digits are zero or not.
+% Here, \cs{@@_round_digit:Nw} takes digits $17$ and further (as an
+% integer expression), and replaces it by a \meta{rounding digit},
+% suitable for \cs{@@_round:NNN}.
+% \begin{macrocode}
+\cs_new:Npn \@@_mul_significand_large_f:NwwNNNN #1 #2; #3; #4#5#6#7; +
+ {
+ \exp_after:wN \@@_basics_pack_high:NNNNNw
+ \int_value:w \@@_int_eval:w 1#2
+ \exp_after:wN \@@_basics_pack_low:NNNNNw
+ \int_value:w \@@_int_eval:w 1#3#4#5#6#7
+ + \exp_after:wN \@@_round:NNN
+ \exp_after:wN #1
+ \exp_after:wN #7
+ \int_value:w \@@_round_digit:Nw
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_mul_significand_small_f:NNwwwN}
+% In this branch, \meta{digit 1} is zero. Our result is thus
+% \meta{digits 2--17}, plus some rounding which depends on the digits
+% $17$, $18$, and whether all subsequent digits are zero or not.
+% The $8$ digits |1#3| are followed, after expansion of the
+% \texttt{small_pack} auxiliary, by the next digit, to form a $9$
+% digit number.
+% \begin{macrocode}
+\cs_new:Npn \@@_mul_significand_small_f:NNwwwN #1 #2#3; #4#5; #6; + #7
+ {
+ - 1
+ \exp_after:wN \@@_basics_pack_high:NNNNNw
+ \int_value:w \@@_int_eval:w 1#3#4
+ \exp_after:wN \@@_basics_pack_low:NNNNNw
+ \int_value:w \@@_int_eval:w 1#5#6#7
+ + \exp_after:wN \@@_round:NNN
+ \exp_after:wN #1
+ \exp_after:wN #7
+ \int_value:w \@@_round_digit:Nw
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Division}
+%
+% \subsubsection{Signs, and special numbers}
+%
+% Time is now ripe to tackle the hardest of the four elementary
+% operations: division.
+%
+% \begin{macro}[EXP]{\@@_/_o:ww}
+% Filtering special floating point is very similar to what we did for
+% multiplications, with a few variations. Invalid operation
+% exceptions display |/| rather than |*|. In the formula for
+% dispatch, we replace |- 2 +| by |-|. The case of normal
+% numbers is treated using \cs{@@_div_npos_o:Nww} rather than
+% \cs{@@_mul_npos_o:Nww}. There are two additional cases: if the
+% first operand is normal and the second is a zero, then the division
+% by zero exception is raised: cases $10$ and $11$ of the
+% \cs{if_case:w} construction in \cs{@@_mul_cases_o:NnNnww} are
+% provided as the fourth argument here.
+% \begin{macrocode}
+\cs_new:cpn { @@_/_o:ww }
+ {
+ \@@_mul_cases_o:NnNnww
+ /
+ { - }
+ \@@_div_npos_o:Nww
+ {
+ \or:
+ \@@_case_use:nw
+ { \@@_division_by_zero_o:NNww \c_inf_fp / }
+ \or:
+ \@@_case_use:nw
+ { \@@_division_by_zero_o:NNww \c_minus_inf_fp / }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_div_npos_o:Nww}
+% \begin{quote}
+% \cs{@@_div_npos_o:Nww} \meta{final sign}
+% \cs{s_@@} \cs{@@_chk:w} |1| \meta{sign_A} \Arg{exp A}
+% \Arg{A_1} \Arg{A_2} \Arg{A_3} \Arg{A_4} |;|
+% \cs{s_@@} \cs{@@_chk:w} |1| \meta{sign_Z} \Arg{exp Z}
+% \Arg{Z_1} \Arg{Z_2} \Arg{Z_3} \Arg{Z_4} |;|
+% \end{quote}
+% We want to compute $A/Z$. As for multiplication,
+% \cs{@@_sanitize:Nw} checks for overflow or underflow; we provide it
+% with the \meta{final sign}, and an integer expression in which we
+% compute the exponent. We set up the arguments of
+% \cs{@@_div_significand_i_o:wnnw}, namely an integer \meta{y} obtained
+% by adding $1$ to the first $5$ digits of $Z$ (explanation given soon
+% below), then the four \Arg{A_{i}}, then the four \Arg{Z_{i}}, a
+% semi-colon, and the \meta{final sign}, used for rounding at the end.
+% \begin{macrocode}
+\cs_new:Npn \@@_div_npos_o:Nww
+ #1 \s_@@ \@@_chk:w 1 #2 #3 #4 ; \s_@@ \@@_chk:w 1 #5 #6 #7#8#9;
+ {
+ \exp_after:wN \@@_sanitize:Nw
+ \exp_after:wN #1
+ \int_value:w \@@_int_eval:w
+ #3 - #6
+ \exp_after:wN \@@_div_significand_i_o:wnnw
+ \int_value:w \@@_int_eval:w #7 \use_i:nnnn #8 + 1 ;
+ #4
+ {#7}{#8}#9 ;
+ #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Work plan}
+%
+% In this subsection, we explain how to avoid overflowing \TeX{}'s
+% integers when performing the division of two positive normal numbers.
+%
+% We are given two numbers, $A=0.A_{1}A_{2}A_{3}A_{4}$ and
+% $Z=0.Z_{1}Z_{2}Z_{3}Z_{4}$, in blocks of $4$ digits, and we know that
+% the first digits of $A_{1}$ and of $Z_{1}$ are non-zero. To compute
+% $A/Z$, we proceed as follows.
+% \begin{itemize}
+% \item Find an integer $Q_{A} \simeq 10^{4} A / Z$.
+% \item Replace $A$ by $B = 10^{4} A - Q_{A} Z$.
+% \item Find an integer $Q_{B} \simeq 10^{4} B / Z$.
+% \item Replace $B$ by $C = 10^{4} B - Q_{B} Z$.
+% \item Find an integer $Q_{C} \simeq 10^{4} C / Z$.
+% \item Replace $C$ by $D = 10^{4} C - Q_{C} Z$.
+% \item Find an integer $Q_{D} \simeq 10^{4} D / Z$.
+% \item Consider $E = 10^{4} D - Q_{D} Z$, and ensure
+% correct rounding.
+% \end{itemize}
+% The result is then $Q = 10^{-4} Q_{A} + 10^{-8} Q_{B} + 10^{-12} Q_{C}
+% + 10^{-16} Q_{D} + \text{rounding}$. Since the $Q_{i}$ are integers,
+% $B$, $C$, $D$, and~$E$ are all exact multiples of $10^{-16}$, in other
+% words, computing with $16$ digits after the decimal separator yields
+% exact results. The problem is the risk of overflow: in general $B$, $C$,
+% $D$, and $E$ may be greater than $1$.
+%
+% Unfortunately, things are not as easy as they seem. In particular, we
+% want all intermediate steps to be positive, since negative results
+% would require extra calculations at the end. This requires that
+% $Q_{A} \leq 10^{4} A / Z$ \emph{etc.} A reasonable attempt would be
+% to define $Q_{A}$ as
+% \begin{equation*}
+% \cs{int_eval:n} \left\{
+% \frac{ A_{1} A_{2} }{ Z_{1} + 1 } - 1 \right\}
+% \leq 10^{4} \frac{A}{Z}
+% \end{equation*}
+% Subtracting $1$ at the end takes care of the fact that \eTeX{}'s
+% \cs{@@_int_eval:w} rounds divisions instead of truncating (really,
+% $1/2$ would be sufficient, but we work with integers). We add $1$ to
+% $Z_{1}$ because $Z_{1} \leq 10^{4}Z < Z_{1}+1$ and we need $Q_{A}$ to
+% be an underestimate. However, we are now underestimating $Q_{A}$ too
+% much: it can be wrong by up to $100$, for instance when $Z = 0.1$ and
+% $A \simeq 1$. Then $B$ could take values up to $10$ (maybe more), and
+% a few steps down the line, we would run into arithmetic overflow,
+% since \TeX{} can only handle integers less than roughly $2\cdot
+% 10^{9}$.
+%
+% A better formula is to take
+% \begin{equation*}
+% Q_{A} = \cs{int_eval:n} \left\{
+% \frac{ 10 \cdot A_{1} A_{2} }
+% { \left\lfloor 10^{-3} \cdot Z_{1} Z_{2} \right\rfloor + 1 }
+% - 1 \right\}.
+% \end{equation*}
+% This is always less than $10^{9} A / (10^{5} Z)$, as we wanted. In
+% words, we take the $5$ first digits of $Z$ into account, and the $8$
+% first digits of $A$, using $0$ as a $9$-th digit rather than the true
+% digit for efficiency reasons. We shall prove that using this formula
+% to define all the $Q_{i}$ avoids any overflow. For convenience, let
+% us denote
+% \begin{equation*}
+% y = \left\lfloor 10^{-3} \cdot Z_{1} Z_{2} \right\rfloor + 1,
+% \end{equation*}
+% so that, taking into account the fact that \eTeX{} rounds ties away
+% from zero,
+% \begin{align*}
+% Q_{A}
+% &= \left\lfloor \frac{A_{1}A_{2}0}{y} - \frac{1}{2} \right\rfloor
+% \\
+% &>\frac{A_{1}A_{2}0}{y} - \frac{3}{2}.
+% \end{align*}
+% Note that $10^{4}<y\leq 10^{5}$, and $999 \leq Q_{A} \leq 99989$.
+% Also note that this formula does not cause an overflow as long as $A <
+% (2^{31}-1) / 10^{9} \simeq 2.147\cdots$, since the numerator involves an
+% integer slightly smaller than $10^{9} A$.
+%
+% Let us bound $B$:
+% \begin{align*}
+% 10^{5} B
+% &=
+% A_{1}A_{2}0 + 10 \cdot 0.A_{3}A_{4}
+% - 10 \cdot Z_{1}.Z_{2}Z_{3}Z_{4} \cdot Q_{A}
+% \\
+% &<
+% A_{1}A_{2}0
+% \cdot \left( 1 - 10 \cdot \frac{Z_{1}.Z_{2}Z_{3}Z_{4}}{y} \right)
+% + \frac{3}{2} \cdot 10 \cdot Z_{1}.Z_{2}Z_{3}Z_{4} + 10
+% \\
+% &\leq
+% \frac{A_{1}A_{2}0 \cdot (y - 10 \cdot Z_{1}.Z_{2}Z_{3}Z_{4})}{y}
+% + \frac{3}{2} y + 10
+% \\
+% &\leq
+% \frac{A_{1}A_{2}0\cdot 1}{y} + \frac{3}{2} y + 10
+% \leq
+% \frac{10^{9} A}{y} + 1.6\cdot y.
+% \end{align*}
+% At the last step, we hide $10$ into the second term for later
+% convenience. The same reasoning yields
+% \begin{align*}
+% 10^{5} B &< 10^{9} A/y + 1.6 y, \\
+% 10^{5} C &< 10^{9} B/y + 1.6 y, \\
+% 10^{5} D &< 10^{9} C/y + 1.6 y, \\
+% 10^{5} E &< 10^{9} D/y + 1.6 y. \\
+% \end{align*}
+% The goal is now to prove that none of $B$, $C$, $D$, and $E$ can go
+% beyond $(2^{31}-1) / 10^{9} = 2.147\cdots$.
+%
+% Combining the various inequalities together with $A<1$, we get
+% \begin{align*}
+% 10^{5} B &< 10^{9}/y + 1.6 y, \\
+% 10^{5} C &< 10^{13}/y^{2} + 1.6 (y + 10^{4}), \\
+% 10^{5} D &< 10^{17}/y^{3} + 1.6 (y + 10^{4} + 10^{8}/y), \\
+% 10^{5} E &< 10^{21}/y^{4} + 1.6 (y + 10^{4} + 10^{8}/y + 10^{12}/y^{2}). \\
+% \end{align*}
+% All of those bounds are convex functions of $y$ (since every power of
+% $y$ involved is convex, and the coefficients are positive), and thus
+% maximal at one of the end-points of the allowed range $10^{4} < y \leq
+% 10^{5}$. Thus,
+% \begin{align*}
+% 10^{5} B &< \mathrm{max} ( 1.16\cdot 10^{5}, 1.7 \cdot 10^{5}), \\
+% 10^{5} C &< \mathrm{max} ( 1.32\cdot 10^{5}, 1.77 \cdot 10^{5}), \\
+% 10^{5} D &< \mathrm{max} ( 1.48\cdot 10^{5}, 1.777 \cdot 10^{5}), \\
+% 10^{5} E &< \mathrm{max} ( 1.64\cdot 10^{5}, 1.7777 \cdot 10^{5}). \\
+% \end{align*}
+% All of those bounds are less than $2.147\cdot 10^{5}$, and we are thus
+% within \TeX{}'s bounds in all cases!
+%
+% We later need to have a bound on the $Q_{i}$. Their definitions
+% imply that $Q_{A} < 10^{9} A/y - 1/2 < 10^{5} A$ and similarly for the
+% other $Q_{i}$. Thus, all of them are less than $177770$.
+%
+% The last step is to ensure correct rounding. We have
+% \begin{equation*}
+% A/Z = \sum_{i=1}^{4} \left(10^{-4i} Q_{i}\right) + 10^{-16} E/Z
+% \end{equation*}
+% exactly. Furthermore, we know that the result is in $[0.1,10)$,
+% hence will be rounded to a multiple of $10^{-16}$ or of $10^{-15}$, so
+% we only need to know the integer part of $E/Z$, and a
+% \enquote{rounding} digit encoding the rest. Equivalently, we need to
+% find the integer part of $2E/Z$, and determine whether it was an
+% exact integer or not (this serves to detect ties). Since
+% \begin{equation*}
+% \frac{2E}{Z} = 2\frac{10^{5} E}{10^{5} Z}
+% \leq 2\frac{10^{5} E}{10^{4}} < 36,
+% \end{equation*}
+% this integer part is between $0$ and $35$ inclusive. We let \eTeX{}
+% round
+% \begin{equation*}
+% P = \cs{int_eval:n} \left\{
+% \frac{2\cdot E_{1}E_{2}}{Z_{1}Z_{2}} \right\},
+% \end{equation*}
+% which differs from $2E/Z$ by at most
+% \begin{equation*}
+% \frac{1}{2}
+% + 2 \left\lvert \frac{E}{Z} - \frac{E}{10^{-8} Z_{1}Z_{2}}\right\rvert
+% + 2 \left\lvert \frac{10^{8} E - E_{1}E_{2}}{Z_{1}Z_{2}}\right\rvert
+% < 1,
+% \end{equation*}
+% ($1/2$ comes from \eTeX{}'s rounding) because each absolute value is
+% less than $10^{-7}$. Thus $P$ is either the correct integer part, or
+% is off by $1$; furthermore, if $2 E / Z$ is an integer, $P = 2 E / Z$.
+% We will check the sign of $2 E - P Z$. If it is negative, then $E / Z
+% \in \big((P - 1) / 2, P / 2\big)$. If it is zero, then $E / Z = P /
+% 2$. If it is positive, then $E / Z \in \big(P / 2, (P - 1) / 2\big)$.
+% In each case, we know how to round to an integer, depending on the
+% parity of $P$, and the rounding mode.
+%
+% \subsubsection{Implementing the significand division}
+%
+% \begin{macro}[rEXP]{\@@_div_significand_i_o:wnnw}
+% \begin{quote}
+% \cs{@@_div_significand_i_o:wnnw} \meta{y} |;|
+% \Arg{A_1} \Arg{A_2} \Arg{A_3} \Arg{A_4}
+% \Arg{Z_1} \Arg{Z_2} \Arg{Z_3} \Arg{Z_4} |;| \meta{sign}
+% \end{quote}
+% Compute $10^{6} + Q_{A}$ (a $7$~digit number thanks to the shift),
+% unbrace \meta{A_1} and \meta{A_2}, and prepare the
+% \meta{continuation} arguments for $4$ consecutive calls to
+% \cs{@@_div_significand_calc:wwnnnnnnn}. Each of these calls needs
+% \meta{y} (|#1|), and it turns out that we need post-expansion there,
+% hence the \cs{int_value:w}. Here, |#4| is six brace groups, which
+% give the six first |n|-type arguments of the \texttt{calc} function.
+% \begin{macrocode}
+\cs_new:Npn \@@_div_significand_i_o:wnnw #1 ; #2#3 #4 ;
+ {
+ \exp_after:wN \@@_div_significand_test_o:w
+ \int_value:w \@@_int_eval:w
+ \exp_after:wN \@@_div_significand_calc:wwnnnnnnn
+ \int_value:w \@@_int_eval:w 999999 + #2 #3 0 / #1 ;
+ #2 #3 ;
+ #4
+ { \exp_after:wN \@@_div_significand_ii:wwn \int_value:w #1 }
+ { \exp_after:wN \@@_div_significand_ii:wwn \int_value:w #1 }
+ { \exp_after:wN \@@_div_significand_ii:wwn \int_value:w #1 }
+ { \exp_after:wN \@@_div_significand_iii:wwnnnnn \int_value:w #1 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_div_significand_calc:wwnnnnnnn}
+% \begin{macro}[rEXP]
+% {
+% \@@_div_significand_calc_i:wwnnnnnnn,
+% \@@_div_significand_calc_ii:wwnnnnnnn,
+% }
+% \begin{quote}
+% \cs{@@_div_significand_calc:wwnnnnnnn} \meta{$10^{6}+{}$Q_{A}} |;|
+% \meta{A_1} \meta{A_2} |;| \Arg{A_3} \Arg{A_4}
+% \Arg{Z_1} \Arg{Z_2} \Arg{Z_3} \Arg{Z_4}
+% \Arg{continuation}
+% \end{quote}
+% expands to
+% \begin{quote}
+% \meta{$10^{6}+{}$Q_{A}} \meta{continuation} |;|
+% \meta{B_1} \meta{B_2} |;| \Arg{B_3} \Arg{B_4}
+% \Arg{Z_1} \Arg{Z_2} \Arg{Z_3} \Arg{Z_4}
+% \end{quote}
+% where $B = 10^{4} A - Q_{A} \cdot Z$. This function is also used to
+% compute $C$, $D$, $E$ (with the input shifted accordingly), and is
+% used in \pkg{l3fp-expo}.
+%
+% We know that $0<Q_{A}<1.8\cdot 10^{5}$, so the product of $Q_{A}$
+% with each $Z_{i}$ is within \TeX{}'s bounds. However, it is a
+% little bit too large for our purposes: we would not be able to use
+% the usual trick of adding a large power of $10$ to ensure that the
+% number of digits is fixed.
+%
+% The bound on $Q_{A}$, implies that $10^{6}+Q_{A}$ starts with the
+% digit $1$, followed by $0$ or $1$. We test, and call different
+% auxiliaries for the two cases. An earlier implementation did the
+% tests within the computation, but since we added a
+% \meta{continuation}, this is not possible because the macro has $9$
+% parameters.
+%
+% The result we want is then (the overall power of $10$ is arbitrary):
+% \begin{align*}
+% &10^{-4} ( \#2 - \#1 \cdot \#5 - 10 \cdot \meta{i} \cdot \#5\#6 )
+% + 10^{-8} ( \#3 - \#1 \cdot \#6 - 10 \cdot \meta{i} \cdot \#7 ) \\
+% &+ 10^{-12}( \#4 - \#1 \cdot \#7 - 10 \cdot \meta{i} \cdot \#8 )
+% + 10^{-16}( - \#1 \cdot \#8 ),
+% \end{align*}
+% where \meta{i} stands for the $10^{5}$ digit of $Q_{A}$, which is
+% $0$ or~$1$, and $\#1$, $\#2$, \emph{etc.\@} are the parameters of
+% either auxiliary. The factors of $10$ come from the fact that
+% $Q_{A} = 10\cdot 10^{4} \cdot \meta{i} + \#1$. As usual, to combine
+% all the terms, we need to choose some shifts which must ensure that
+% the number of digits of the second, third, and fourth terms are each
+% fixed. Here, the positive contributions are at most $10^{8}$ and
+% the negative contributions can go up to $10^{9}$. Indeed, for the
+% auxiliary with $\meta{i}=1$, |#1| is at most $80000$, leading to
+% contributions of at worse $-8\cdot 10^{8}4$, while the other
+% negative term is very small $<10^{6}$ (except in the first
+% expression, where we don't care about the number of digits); for the
+% auxiliary with $\meta{i}=0$, |#1| can go up to $99999$, but there is
+% no other negative term. Hence, a good choice is $2\cdot 10^{9}$,
+% which produces totals in the range $[10^{9}, 2.1\cdot 10^{9}]$. We
+% are flirting with \TeX{}'s limits once more.
+% \begin{macrocode}
+\cs_new:Npn \@@_div_significand_calc:wwnnnnnnn 1#1
+ {
+ \if_meaning:w 1 #1
+ \exp_after:wN \@@_div_significand_calc_i:wwnnnnnnn
+ \else:
+ \exp_after:wN \@@_div_significand_calc_ii:wwnnnnnnn
+ \fi:
+ }
+\cs_new:Npn \@@_div_significand_calc_i:wwnnnnnnn
+ #1; #2;#3#4 #5#6#7#8 #9
+ {
+ 1 1 #1
+ #9 \exp_after:wN ;
+ \int_value:w \@@_int_eval:w \c_@@_Bigg_leading_shift_int
+ + #2 - #1 * #5 - #5#60
+ \exp_after:wN \@@_pack_Bigg:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_Bigg_middle_shift_int
+ + #3 - #1 * #6 - #70
+ \exp_after:wN \@@_pack_Bigg:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_Bigg_middle_shift_int
+ + #4 - #1 * #7 - #80
+ \exp_after:wN \@@_pack_Bigg:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_Bigg_trailing_shift_int
+ - #1 * #8 ;
+ {#5}{#6}{#7}{#8}
+ }
+\cs_new:Npn \@@_div_significand_calc_ii:wwnnnnnnn
+ #1; #2;#3#4 #5#6#7#8 #9
+ {
+ 1 0 #1
+ #9 \exp_after:wN ;
+ \int_value:w \@@_int_eval:w \c_@@_Bigg_leading_shift_int
+ + #2 - #1 * #5
+ \exp_after:wN \@@_pack_Bigg:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_Bigg_middle_shift_int
+ + #3 - #1 * #6
+ \exp_after:wN \@@_pack_Bigg:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_Bigg_middle_shift_int
+ + #4 - #1 * #7
+ \exp_after:wN \@@_pack_Bigg:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_Bigg_trailing_shift_int
+ - #1 * #8 ;
+ {#5}{#6}{#7}{#8}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_div_significand_ii:wwn}
+% \begin{quote}
+% \cs{@@_div_significand_ii:wwn} \meta{y} |;|
+% \meta{B_1} |;| \Arg{B_2} \Arg{B_3} \Arg{B_4}
+% \Arg{Z_1} \Arg{Z_2} \Arg{Z_3} \Arg{Z_4}
+% \meta{continuations} \meta{sign}
+% \end{quote}
+% Compute $Q_{B}$ by evaluating $\meta{B_1}\meta{B_2}0 / y - 1$. The
+% result is output to the left, in an \cs{@@_int_eval:w} which we
+% start now. Once that is evaluated (and the other $Q_{i}$ also,
+% since later expansions are triggered by this one), a packing
+% auxiliary takes care of placing the digits of $Q_{B}$ in an
+% appropriate way for the final addition to obtain $Q$. This
+% auxiliary is also used to compute $Q_{C}$ and $Q_{D}$ with the
+% inputs $C$ and $D$ instead of $B$.
+% \begin{macrocode}
+\cs_new:Npn \@@_div_significand_ii:wwn #1; #2;#3
+ {
+ \exp_after:wN \@@_div_significand_pack:NNN
+ \int_value:w \@@_int_eval:w
+ \exp_after:wN \@@_div_significand_calc:wwnnnnnnn
+ \int_value:w \@@_int_eval:w 999999 + #2 #3 0 / #1 ; #2 #3 ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_div_significand_iii:wwnnnnn}
+% \begin{quote}
+% \cs{@@_div_significand_iii:wwnnnnn} \meta{y} |;|
+% \meta{E_1} |;| \Arg{E_2} \Arg{E_3} \Arg{E_4}
+% \Arg{Z_1} \Arg{Z_2} \Arg{Z_3} \Arg{Z_4} \meta{sign}
+% \end{quote}
+% We compute $P \simeq 2E/Z$ by rounding $2 E_{1} E_{2}/Z_{1}Z_{2}$.
+% Note the first $0$, which multiplies $Q_{D}$ by $10$: we later
+% add (roughly) $5\cdot P$, which amounts to adding $P/2 \simeq E/Z$
+% to $Q_{D}$, the appropriate correction from a hypothetical $Q_{E}$.
+% \begin{macrocode}
+\cs_new:Npn \@@_div_significand_iii:wwnnnnn #1; #2;#3#4#5 #6#7
+ {
+ 0
+ \exp_after:wN \@@_div_significand_iv:wwnnnnnnn
+ \int_value:w \@@_int_eval:w ( 2 * #2 #3) / #6 #7 ; % <- P
+ #2 ; {#3} {#4} {#5}
+ {#6} {#7}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_div_significand_iv:wwnnnnnnn,
+% \@@_div_significand_v:NNw,
+% \@@_div_significand_vi:Nw
+% }
+% \begin{quote}
+% \cs{@@_div_significand_iv:wwnnnnnnn} \meta{P} |;|
+% \meta{E_1} |;| \Arg{E_2} \Arg{E_3} \Arg{E_4}
+% \Arg{Z_1} \Arg{Z_2} \Arg{Z_3} \Arg{Z_4} \meta{sign}
+% \end{quote}
+% This adds to the current expression ($10^{7} + 10\cdot Q_{D}$) a
+% contribution of $5 \cdot P + \operatorname{sign}(T)$ with $T = 2 E -
+% P Z$. This amounts to adding $P / 2$ to $Q_{D}$, with an extra
+% \meta{rounding} digit. This \meta{rounding} digit is $0$ or $5$ if
+% $T$ does not contribute, \emph{i.e.,} if $0 = T = 2 E - P Z$, in
+% other words if $10^{16} A / Z$ is an integer or half-integer.
+% Otherwise it is in the appropriate range, $[1,4]$ or $[6,9]$. This
+% is precise enough for rounding purposes (in any mode).
+%
+% It seems an overkill to compute $T$ exactly as I do here, but I see
+% no faster way right now.
+%
+% Once more, we need to be careful and show that the calculation
+% $\#1\cdot\#6\#7$ below does not cause an overflow: naively, $P$ can
+% be up to $35$, and $\#6\#7$ up to $10^{8}$, but both cannot happen
+% simultaneously. To show that things are fine, we split in two
+% (non-disjoint) cases.
+% \begin{itemize}
+% \item For $P < 10$, the product obeys $P\cdot\#6\#7 < 10^{8} \cdot P
+% < 10^{9} $.
+% \item For large $P\geq 3$, the rounding error on $P$, which is at
+% most $1$, is less than a factor of $2$, hence $P\leq 4E/Z$. Also,
+% $\#6\#7 \leq 10^{8} \cdot Z$, hence $P\cdot \#6\#7 \leq 4E\cdot
+% 10^{8} < 10^{9}$.
+% \end{itemize}
+% Both inequalities could be made tighter if needed.
+%
+% Note however that $P\cdot \#8\#9$ may overflow, since the two
+% factors are now independent, and the result may reach $3.5\cdot
+% 10^{9}$. Thus we compute the two lower levels separately. The rest
+% is standard, except that we use |+| as a separator (ending integer
+% expressions explicitly). $T$ is negative if the first character is
+% |-|, it is positive if the first character is neither |0| nor |-|.
+% It is also positive if the first character is |0| and second
+% argument of \cs{@@_div_significand_vi:Nw}, a sum of several terms, is
+% also zero. Otherwise, there was an exact agreement: $T = 0$.
+% \begin{macrocode}
+\cs_new:Npn \@@_div_significand_iv:wwnnnnnnn #1; #2;#3#4#5 #6#7#8#9
+ {
+ + 5 * #1
+ \exp_after:wN \@@_div_significand_vi:Nw
+ \int_value:w \@@_int_eval:w -50 + 2*#2#3 - #1*#6#7 +
+ \exp_after:wN \@@_div_significand_v:NN
+ \int_value:w \@@_int_eval:w 499950 + 2*#4 - #1*#8 +
+ \exp_after:wN \@@_div_significand_v:NN
+ \int_value:w \@@_int_eval:w 500000 + 2*#5 - #1*#9 ;
+ }
+\cs_new:Npn \@@_div_significand_v:NN #1#2 { #1#2 \@@_int_eval_end: + }
+\cs_new:Npn \@@_div_significand_vi:Nw #1#2;
+ {
+ \if_meaning:w 0 #1
+ \if_int_compare:w \@@_int_eval:w #2 > 0 + 1 \fi:
+ \else:
+ \if_meaning:w - #1 - \else: + \fi: 1
+ \fi:
+ ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_div_significand_pack:NNN}
+% At this stage, we are in the following situation: \TeX{} is in the
+% process of expanding several integer expressions, thus functions at
+% the bottom expand before those above.
+% \begin{quote}
+% \cs{@@_div_significand_test_o:w} $10^{6} + Q_{A}$
+% \cs{@@_div_significand_pack:NNN} $10^{6} + Q_{B}$
+% \cs{@@_div_significand_pack:NNN} $10^{6} + Q_{C}$
+% \cs{@@_div_significand_pack:NNN}
+% $10^{7} + 10\cdot Q_{D} + 5 \cdot P + \varepsilon$ |;| \meta{sign}
+% \end{quote}
+% Here, $\varepsilon = \operatorname{sign}(T)$ is $0$ in case $2E=PZ$,
+% $1$ in case $2E>PZ$, which means that $P$ was the correct value, but
+% not with an exact quotient, and $-1$ if $2E<PZ$, \emph{i.e.}, $P$
+% was an overestimate. The packing function we define now does
+% nothing special: it removes the $10^{6}$ and carries two digits (for
+% the $10^{5}$'s and the $10^{4}$'s).
+% \begin{macrocode}
+\cs_new:Npn \@@_div_significand_pack:NNN 1 #1 #2 { + #1 #2 ; }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_div_significand_test_o:w}
+% \begin{quote}
+% \cs{@@_div_significand_test_o:w} |1| |0| \meta{5d} |;|
+% ~~\meta{4d} |;| \meta{4d} |;| \meta{5d} |;| \meta{sign}
+% \end{quote}
+% The reason we know that the first two digits are |1| and |0| is that
+% the final result is known to be between $0.1$ (inclusive) and $10$,
+% hence $\widetilde{Q_{A}}$ (the tilde denoting the contribution from
+% the other $Q_{i}$) is at most $99999$, and $10^{6}+\widetilde{Q_{A}}
+% = 10\cdots$.
+%
+% It is now time to round. This depends on how many digits the final
+% result will have.
+% \begin{macrocode}
+\cs_new:Npn \@@_div_significand_test_o:w 10 #1
+ {
+ \if_meaning:w 0 #1
+ \exp_after:wN \@@_div_significand_small_o:wwwNNNNwN
+ \else:
+ \exp_after:wN \@@_div_significand_large_o:wwwNNNNwN
+ \fi:
+ #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_div_significand_small_o:wwwNNNNwN}
+% \begin{quote}
+% \cs{@@_div_significand_small_o:wwwNNNNwN} |0| \meta{4d} |;|
+% ~~\meta{4d} |;| \meta{4d} |;| \meta{5d} |;| \meta{final sign}
+% \end{quote}
+% Standard use of the functions \cs{@@_basics_pack_low:NNNNNw} and
+% \cs{@@_basics_pack_high:NNNNNw}. We finally get to use the
+% \meta{final sign} which has been sitting there for a while.
+% \begin{macrocode}
+\cs_new:Npn \@@_div_significand_small_o:wwwNNNNwN
+ 0 #1; #2; #3; #4#5#6#7#8; #9
+ {
+ \exp_after:wN \@@_basics_pack_high:NNNNNw
+ \int_value:w \@@_int_eval:w 1 #1#2
+ \exp_after:wN \@@_basics_pack_low:NNNNNw
+ \int_value:w \@@_int_eval:w 1 #3#4#5#6#7
+ + \@@_round:NNN #9 #7 #8
+ \exp_after:wN ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_div_significand_large_o:wwwNNNNwN}
+% \begin{quote}
+% \cs{@@_div_significand_large_o:wwwNNNNwN} \meta{5d} |;|
+% ~~\meta{4d} |;| \meta{4d} |;| \meta{5d} |;| \meta{sign}
+% \end{quote}
+% We know that the final result cannot reach $10$, hence |1#1#2|,
+% together with contributions from the level below, cannot reach
+% $2\cdot 10^{9}$. For rounding, we build the \meta{rounding digit}
+% from the last two of our $18$ digits.
+% \begin{macrocode}
+\cs_new:Npn \@@_div_significand_large_o:wwwNNNNwN
+ #1; #2; #3; #4#5#6#7#8; #9
+ {
+ + 1
+ \exp_after:wN \@@_basics_pack_weird_high:NNNNNNNNw
+ \int_value:w \@@_int_eval:w 1 #1 #2
+ \exp_after:wN \@@_basics_pack_weird_low:NNNNw
+ \int_value:w \@@_int_eval:w 1 #3 #4 #5 #6 +
+ \exp_after:wN \@@_round:NNN
+ \exp_after:wN #9
+ \exp_after:wN #6
+ \int_value:w \@@_round_digit:Nw #7 #8 ;
+ \exp_after:wN ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Square root}
+%
+% \begin{macro}[EXP]{\@@_sqrt_o:w}
+% Zeros are unchanged: $\sqrt{-0} = -0$ and $\sqrt{+0} = +0$.
+% Negative numbers (other than $-0$) have no real square root.
+% Positive infinity, and \texttt{nan}, are unchanged. Finally, for
+% normal positive numbers, there is some work to do.
+% \begin{macrocode}
+\cs_new:Npn \@@_sqrt_o:w #1 \s_@@ \@@_chk:w #2#3#4; @
+ {
+ \if_meaning:w 0 #2 \@@_case_return_same_o:w \fi:
+ \if_meaning:w 2 #3
+ \@@_case_use:nw { \@@_invalid_operation_o:nw { sqrt } }
+ \fi:
+ \if_meaning:w 1 #2 \else: \@@_case_return_same_o:w \fi:
+ \@@_sqrt_npos_o:w
+ \s_@@ \@@_chk:w #2 #3 #4;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_sqrt_npos_o:w}
+% \begin{macro}[rEXP]
+% {\@@_sqrt_npos_auxi_o:wwnnN, \@@_sqrt_npos_auxii_o:wNNNNNNNN}
+% Prepare \cs{@@_sanitize:Nw} to receive the final sign~|0| (the
+% result is always positive) and the exponent, equal to half of the
+% exponent~|#1| of the argument. If the exponent~|#1| is even, find a
+% first approximation of the square root of the significand $10^{8}
+% a_1 + a_2 = 10^{8} |#2#3| + |#4#5|$ through Newton's method,
+% starting at $x = 57234133 \simeq 10^{7.75}$. Otherwise, first shift
+% the significand of the argument by one digit, getting
+% $a_1'\in[10^{6}, 10^{7})$ instead of $[10^{7}, 10^{8})$, then use
+% Newton's method starting at $17782794 \simeq 10^{7.25}$.
+% \begin{macrocode}
+\cs_new:Npn \@@_sqrt_npos_o:w \s_@@ \@@_chk:w 1 0 #1#2#3#4#5;
+ {
+ \exp_after:wN \@@_sanitize:Nw
+ \exp_after:wN 0
+ \int_value:w \@@_int_eval:w
+ \if_int_odd:w #1 \exp_stop_f:
+ \exp_after:wN \@@_sqrt_npos_auxi_o:wwnnN
+ \fi:
+ #1 / 2
+ \@@_sqrt_Newton_o:wwn 56234133; 0; {#2#3} {#4#5} 0
+ }
+\cs_new:Npn \@@_sqrt_npos_auxi_o:wwnnN #1 / 2 #2; 0; #3#4#5
+ {
+ ( #1 + 1 ) / 2
+ \@@_pack_eight:wNNNNNNNN
+ \@@_sqrt_npos_auxii_o:wNNNNNNNN
+ ;
+ 0 #3 #4
+ }
+\cs_new:Npn \@@_sqrt_npos_auxii_o:wNNNNNNNN #1; #2#3#4#5#6#7#8#9
+ { \@@_sqrt_Newton_o:wwn 17782794; 0; {#1} {#2#3#4#5#6#7#8#9} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_sqrt_Newton_o:wwn}
+% Newton's method maps $x\mapsto\bigl[(x + [10^{8} a_1 / x])/2\bigr]$
+% in each iteration, where $[b/c]$ denotes \eTeX{}'s division. This
+% division rounds the real number $b/c$ to the closest integer,
+% rounding ties away from zero, hence when $c$~is even,
+% $b/c - 1/2 + 1/c \leq [b/c] \leq b/c + 1/2$
+% and when $c$~is odd,
+% $b/c - 1/2 + 1/(2c) \leq [b/c] \leq b/c + 1/2 - 1/(2c)$.
+% For all~$c$, $b/c - 1/2 + 1/(2c) \leq [b/c] \leq b/c + 1/2$.
+%
+% Let us prove that the method converges when implemented with \eTeX{}
+% integer division, for any $10^{6} \leq a_1 < 10^{8}$ and starting
+% value $10^{6} \leq x < 10^{8}$. Using the inequalities above and
+% the arithmetic--geometric inequality $(x+t)/2 \geq \sqrt{xt}$ for $t
+% = 10^{8} a_1 / x$, we find
+% \[
+% x'
+% = \left[\frac{x + [10^{8} a_1 / x]}{2}\right]
+% \geq \frac{x + 10^{8} a_1 / x - 1/2 + 1/(2x)}{2}
+% \geq \sqrt{10^{8} a_1} - \frac{1}{4} + \frac{1}{4x} \,.
+% \]
+% After any step of iteration, we thus have $\delta = x - \sqrt{10^{8}
+% a_1} \geq -0.25 + 0.25 \cdot 10^{-8}$. The new difference
+% $\delta' = x' - \sqrt{10^{8} a_1}$ after one step is bounded above
+% as
+% \[
+% x' - \sqrt{10^{8} a_1}
+% \leq \frac{x + 10^{8} a_1 / x + 1/2}{2} + \frac{1}{2}
+% - \sqrt{10^{8} a_1}
+% \leq \frac{\delta}{2} \frac{\delta}{\sqrt{10^{8} a_1} + \delta}
+% + \frac{3}{4} \,.
+% \]
+% For $\delta > 3/2$, this last expression is
+% $\leq\delta/2+3/4<\delta$, hence $\delta$~decreases at each step:
+% since all~$x$ are integers, $\delta$~must reach a value
+% $-1/4<\delta\leq 3/2$. In this range of values, we get $\delta'
+% \leq \frac{3}{4} \frac{3}{2\sqrt{10^{8} a_1}} + \frac{3}{4} \leq
+% 0.75 + 1.125 \cdot 10^{-7}$. We deduce that the difference $\delta
+% = x - \sqrt{10^{8} a_1}$ eventually reaches a value in the interval
+% $[-0.25 + 0.25\cdot 10^{-8}, 0.75 + 11.25 \cdot 10^{-8}]$, whose
+% width is $1 + 11 \cdot 10^{-8}$. The corresponding interval for~$x$
+% may contain two integers, hence $x$~might oscillate between those
+% two values.
+%
+% However, the fact that $x\mapsto x-1$ and $x-1 \mapsto x$ puts
+% stronger constraints, which are not compatible: the first implies
+% \[
+% x + [10^{8} a_1 / x] \leq 2x - 2
+% \]
+% hence $10^{8} a_1 / x \leq x - 3/2$, while the second implies
+% \[
+% x - 1 + [10^{8} a_1 / (x - 1)] \geq 2x - 1
+% \]
+% hence $10^{8} a_1 / (x - 1) \geq x - 1/2$. Combining the two
+% inequalities yields $x^2 - 3x/2 \geq 10^{8} a_1 \geq x - 3x/2 +
+% 1/2$, which cannot hold. Therefore, the iteration always converges
+% to a single integer~$x$. To stop the iteration when two consecutive
+% results are equal, the function \cs{@@_sqrt_Newton_o:wwn} receives
+% the newly computed result as~|#1|, the previous result as~|#2|, and
+% $a_1$ as~|#3|. Note that \eTeX{} combines the computation of a
+% multiplication and a following division, thus avoiding overflow in
+% |#3 * 100000000 / #1|. In any case, the result is within $[10^{7},
+% 10^{8}]$.
+% \begin{macrocode}
+\cs_new:Npn \@@_sqrt_Newton_o:wwn #1; #2; #3
+ {
+ \if_int_compare:w #1 = #2 \exp_stop_f:
+ \exp_after:wN \@@_sqrt_auxi_o:NNNNwnnN
+ \int_value:w \@@_int_eval:w 9999 9999 +
+ \exp_after:wN \@@_use_none_until_s:w
+ \fi:
+ \exp_after:wN \@@_sqrt_Newton_o:wwn
+ \int_value:w \@@_int_eval:w (#1 + #3 * 1 0000 0000 / #1) / 2 ;
+ #1; {#3}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_sqrt_auxi_o:NNNNwnnN}
+% This function is followed by $10^{8}+x-1$, which has~$9$ digits
+% starting with~$1$, then |;| \Arg{a_1} \Arg{a_2} \meta{a'}. Here, $x
+% \simeq \sqrt{10^{8} a_1}$ and we want to estimate the square root of
+% $a = 10^{-8} a_1 + 10^{-16} a_2 + 10^{-17} a'$. We set up an
+% initial underestimate
+% \[
+% y = (x - 1) 10^{-8} + 0.2499998875 \cdot 10^{-8} \lesssim \sqrt{a}\,.
+% \]
+% From the inequalities shown earlier, we know that $y \leq
+% \sqrt{10^{-8} a_1} \leq \sqrt{a}$ and that $\sqrt{10^{-8} a_1} \leq
+% y + 10^{-8} + 11\cdot 10^{-16}$ hence (using $0.1\leq y\leq
+% \sqrt{a}\leq 1$)
+% \[
+% a - y^2 \leq 10^{-8} a_1 + 10^{-8} - y^2
+% \leq (y + 10^{-8} + 11\cdot 10^{-16})^2 - y^2 + 10^{-8}
+% < 3.2 \cdot 10^{-8} \,,
+% \]
+% and $\sqrt{a} - y = (a - y^2)/(\sqrt{a} + y) \leq 16 \cdot 10^{-8}$.
+% Next, \cs{@@_sqrt_auxii_o:NnnnnnnnN} is called several times to
+% get closer and closer underestimates of~$\sqrt{a}$. By
+% construction, the underestimates~$y$ are always increasing, $a - y^2
+% < 3.2 \cdot 10^{-8}$ for all. Also, $y<1$.
+% \begin{macrocode}
+\cs_new:Npn \@@_sqrt_auxi_o:NNNNwnnN 1 #1#2#3#4#5;
+ {
+ \@@_sqrt_auxii_o:NnnnnnnnN
+ \@@_sqrt_auxiii_o:wnnnnnnnn
+ {#1#2#3#4} {#5} {2499} {9988} {7500}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_sqrt_auxii_o:NnnnnnnnN}
+% This receives a continuation function~|#1|, then five blocks of~$4$
+% digits for~$y$, then two $8$-digit blocks and a single digit
+% for~$a$. A common estimate of $\sqrt{a} - y = (a - y^2) / (\sqrt{a}
+% + y)$ is $(a - y^2)/(2y)$, which leads to alternating overestimates
+% and underestimates. We tweak this, to only work with underestimates
+% (no need then to worry about signs in the computation). Each step
+% finds the largest integer $j\leq 6$ such that $10^{4j}(a-y^2) <
+% 2\cdot 10^{8}$, then computes the integer (with \eTeX{}'s rounding
+% division)
+% \[
+% 10^{4j} z =
+% \Bigl[\bigl(\lfloor 10^{4j}(a-y^2)\rfloor - 257\bigr)
+% \cdot (0.5\cdot 10^{8})
+% \Bigm/ \lfloor 10^{8} y + 1\rfloor\Bigr] \,.
+% \]
+% The choice of~$j$ ensures that $10^{4j} z < 2\cdot 10^{8} \cdot
+% 0.5\cdot 10^{8} / 10^{7} = 10^{9}$, thus $10^{9} + 10^{4j} z$ has
+% exactly $10$~digits, does not overflow \TeX{}'s integer range, and
+% starts with~$1$. Incidentally, since all $a - y^2 \leq 3.2\cdot
+% 10^{-8}$, we know that $j\geq 3$.
+%
+% Let us show that $z$ is an underestimate of $\sqrt{a} - y$. On the
+% one hand, $\sqrt{a} - y \leq 16\cdot 10^{-8}$ because this holds for
+% the initial~$y$ and values of~$y$ can only increase. On the other
+% hand, the choice of~$j$ implies that $\sqrt{a} - y \leq
+% 5(\sqrt{a}+y)(\sqrt{a}-y) = 5(a - y^2) < 10^{9-4j}$. For $j=3$, the
+% first bound is better, while for larger~$j$, the second bound is
+% better. For all $j\in[3,6]$, we find $\sqrt{a}-y < 16\cdot
+% 10^{-2j}$. From this, we deduce that
+% \[
+% 10^{4j} (\sqrt{a}-y)
+% = \frac{10^{4j}\bigl(a-y^2-(\sqrt{a}-y)^2\bigr)}{2y}
+% \geq \frac{\bigl\lfloor 10^{4j}(a-y^2)\bigr\rfloor-257}
+% {2\cdot 10^{-8} \lfloor 10^{8}y+1\rfloor}
+% + \frac{1}{2}
+% \]
+% where we have replaced the bound $10^{4j}(16\cdot 10^{-2j}) = 256$
+% by~$257$ and extracted the corresponding term $1/\bigl(2\cdot
+% 10^{-8} \lfloor 10^{8}y+1\rfloor\bigr) \geq 1/2$. Given that
+% \eTeX{}'s integer division obeys $[b/c] \leq b/c + 1/2$, we deduce
+% that $10^{4j} z \leq 10^{4j} (\sqrt{a}-y)$, hence $y+z\leq\sqrt{a}$
+% is an underestimate of~$\sqrt{a}$, as claimed. One implementation
+% detail: because the computation involves |-#4*#4| |-| |2*#3*#5| |-|
+% |2*#2*#6| which may be as low as $-5\cdot 10^{8}$, we need to use
+% the \texttt{pack_big} functions, and the \texttt{big} shifts.
+% \begin{macrocode}
+\cs_new:Npn \@@_sqrt_auxii_o:NnnnnnnnN #1 #2#3#4#5#6 #7#8#9
+ {
+ \exp_after:wN #1
+ \int_value:w \@@_int_eval:w \c_@@_big_leading_shift_int
+ + #7 - #2 * #2
+ \exp_after:wN \@@_pack_big:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_big_middle_shift_int
+ - 2 * #2 * #3
+ \exp_after:wN \@@_pack_big:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_big_middle_shift_int
+ + #8 - #3 * #3 - 2 * #2 * #4
+ \exp_after:wN \@@_pack_big:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_big_middle_shift_int
+ - 2 * #3 * #4 - 2 * #2 * #5
+ \exp_after:wN \@@_pack_big:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_big_middle_shift_int
+ + #9 000 0000 - #4 * #4 - 2 * #3 * #5 - 2 * #2 * #6
+ \exp_after:wN \@@_pack_big:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_big_middle_shift_int
+ - 2 * #4 * #5 - 2 * #3 * #6
+ \exp_after:wN \@@_pack_big:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_big_middle_shift_int
+ - #5 * #5 - 2 * #4 * #6
+ \exp_after:wN \@@_pack_big:NNNNNNw
+ \int_value:w \@@_int_eval:w
+ \c_@@_big_middle_shift_int
+ - 2 * #5 * #6
+ \exp_after:wN \@@_pack_big:NNNNNNw
+ \int_value:w \@@_int_eval:w
+ \c_@@_big_trailing_shift_int
+ - #6 * #6 ;
+ % (
+ - 257 ) * 5000 0000 / (#2#3 + 1) + 10 0000 0000 ;
+ {#2}{#3}{#4}{#5}{#6} {#7}{#8}#9
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_sqrt_auxiii_o:wnnnnnnnn,
+% \@@_sqrt_auxiv_o:NNNNNw,
+% \@@_sqrt_auxv_o:NNNNNw,
+% \@@_sqrt_auxvi_o:NNNNNw,
+% \@@_sqrt_auxvii_o:NNNNNw
+% }
+% We receive here the difference $a-y^2=d=\sum_i d_i \cdot 10^{-4i}$,
+% as \meta{d_2} |;| \Arg{d_3} \ldots{} \Arg{d_{10}}, where each block
+% has~$4$ digits, except \meta{d_2}. This function finds the largest
+% $j\leq 6$ such that $10^{4j}(a-y^2) < 2\cdot 10^{8}$, then leaves an
+% open parenthesis and the integer
+% $\bigl\lfloor 10^{4j}(a-y^2)\bigr\rfloor$ in an integer
+% expression. The closing parenthesis is provided by the caller
+% \cs{@@_sqrt_auxii_o:NnnnnnnnN}, which completes the expression
+% \[
+% 10^{4j} z =
+% \Bigl[\bigl(\lfloor 10^{4j}(a-y^2)\rfloor - 257\bigr)
+% \cdot (0.5\cdot 10^{8})
+% \Bigm/ \lfloor 10^{8} y + 1\rfloor\Bigr]
+% \]
+% for an estimate of $10^{4j} (\sqrt{a} - y)$. If $d_2\geq 2$, $j=3$
+% and the \texttt{auxiv} auxiliary receives $10^{12} z$. If $d_2\leq
+% 1$ but $10^{4} d_2 + d_3 \geq 2$, $j=4$ and the \texttt{auxv}
+% auxiliary is called, and receives $10^{16} z$, and so on. In all
+% those cases, the \texttt{auxviii} auxiliary is set up to add~$z$
+% to~$y$, then go back to the \texttt{auxii} step with continuation
+% \texttt{auxiii} (the function we are currently describing). The
+% maximum value of $j$ is~$6$, regardless of whether $10^{12} d_2 +
+% 10^{8} d_3 + 10^{4} d_4 + d_5 \geq 1$. In this last case, we detect
+% when $10^{24} z < 10^{7}$, which essentially means $\sqrt{a} - y
+% \lesssim 10^{-17}$: once this threshold is reached, there is enough
+% information to find the correctly rounded~$\sqrt{a}$ with only one
+% more call to \cs{@@_sqrt_auxii_o:NnnnnnnnN}. Note that the
+% iteration cannot be stuck before reaching $j=6$, because for $j<6$,
+% one has $2\cdot 10^{8}\leq 10^{4(j+1)}(a-y^2)$, hence
+% \[
+% 10^{4j} z
+% \geq \frac{(20000-257)(0.5\cdot 10^{8})}{\lfloor 10^{8} y + 1\rfloor}
+% \geq (20000-257)\cdot 0.5 > 0 \,.
+% \]
+% \begin{macrocode}
+\cs_new:Npn \@@_sqrt_auxiii_o:wnnnnnnnn
+ #1; #2#3#4#5#6#7#8#9
+ {
+ \if_int_compare:w #1 > \c_one_int
+ \exp_after:wN \@@_sqrt_auxiv_o:NNNNNw
+ \int_value:w \@@_int_eval:w (#1#2 %)
+ \else:
+ \if_int_compare:w #1#2 > \c_one_int
+ \exp_after:wN \@@_sqrt_auxv_o:NNNNNw
+ \int_value:w \@@_int_eval:w (#1#2#3 %)
+ \else:
+ \if_int_compare:w #1#2#3 > \c_one_int
+ \exp_after:wN \@@_sqrt_auxvi_o:NNNNNw
+ \int_value:w \@@_int_eval:w (#1#2#3#4 %)
+ \else:
+ \exp_after:wN \@@_sqrt_auxvii_o:NNNNNw
+ \int_value:w \@@_int_eval:w (#1#2#3#4#5 %)
+ \fi:
+ \fi:
+ \fi:
+ }
+\cs_new:Npn \@@_sqrt_auxiv_o:NNNNNw 1#1#2#3#4#5#6;
+ { \@@_sqrt_auxviii_o:nnnnnnn {#1#2#3#4#5#6} {00000000} }
+\cs_new:Npn \@@_sqrt_auxv_o:NNNNNw 1#1#2#3#4#5#6;
+ { \@@_sqrt_auxviii_o:nnnnnnn {000#1#2#3#4#5} {#60000} }
+\cs_new:Npn \@@_sqrt_auxvi_o:NNNNNw 1#1#2#3#4#5#6;
+ { \@@_sqrt_auxviii_o:nnnnnnn {0000000#1} {#2#3#4#5#6} }
+\cs_new:Npn \@@_sqrt_auxvii_o:NNNNNw 1#1#2#3#4#5#6;
+ {
+ \if_int_compare:w #1#2 = \c_zero_int
+ \exp_after:wN \@@_sqrt_auxx_o:Nnnnnnnn
+ \fi:
+ \@@_sqrt_auxviii_o:nnnnnnn {00000000} {000#1#2#3#4#5}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {\@@_sqrt_auxviii_o:nnnnnnn, \@@_sqrt_auxix_o:wnwnw}
+% Simply add the two $8$-digit blocks of~$z$, aligned to the last four
+% of the five $4$-digit blocks of~$y$, then call the \texttt{auxii}
+% auxiliary to evaluate $y'^{2} = (y+z)^{2}$.
+% \begin{macrocode}
+\cs_new:Npn \@@_sqrt_auxviii_o:nnnnnnn #1#2 #3#4#5#6#7
+ {
+ \exp_after:wN \@@_sqrt_auxix_o:wnwnw
+ \int_value:w \@@_int_eval:w #3
+ \exp_after:wN \@@_basics_pack_low:NNNNNw
+ \int_value:w \@@_int_eval:w #1 + 1#4#5
+ \exp_after:wN \@@_basics_pack_low:NNNNNw
+ \int_value:w \@@_int_eval:w #2 + 1#6#7 ;
+ }
+\cs_new:Npn \@@_sqrt_auxix_o:wnwnw #1; #2#3; #4#5;
+ {
+ \@@_sqrt_auxii_o:NnnnnnnnN
+ \@@_sqrt_auxiii_o:wnnnnnnnn {#1}{#2}{#3}{#4}{#5}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {\@@_sqrt_auxx_o:Nnnnnnnn, \@@_sqrt_auxxi_o:wwnnN}
+% At this stage, $j=6$ and $10^{24} z < 10^{7}$, hence
+% \[
+% 10^{7} + 1/2 > 10^{24} z + 1/2 \geq
+% \bigl(10^{24}(a-y^2) - 258\bigr) \cdot (0.5\cdot 10^{8})
+% \Bigm/ (10^{8} y + 1) \,,
+% \]
+% then $10^{24}(a-y^2) - 258 < 2 (10^{7} + 1/2) (y + 10^{-8})$, and
+% \[
+% 10^{24}(a-y^2)
+% < (10^{7} + 1290.5) (1 + 10^{-8}/y) (2y)
+% < (10^{7} + 1290.5) (1 + 10^{-7}) (y + \sqrt{a}) \,,
+% \]
+% which finally implies $0\leq\sqrt{a}-y < 0.2\cdot 10^{-16}$. In
+% particular, $y$~is an underestimate of~$\sqrt{a}$ and $y+0.5\cdot
+% 10^{-16}$ is a (strict) overestimate. There is at exactly one
+% multiple $m$~of $0.5\cdot 10^{-16}$ in the interval $[y, y+0.5\cdot
+% 10^{-16})$. If $m^2>a$, then the square root is inexact and is
+% obtained by rounding $m-\epsilon$ to a multiple of $10^{-16}$ (the
+% precise shift $0<\epsilon<0.5\cdot 10^{-16}$ is irrelevant for
+% rounding). If $m^2=a$ then the square root is exactly~$m$, and
+% there is no rounding. If $m^2<a$ then we round $m+\epsilon$. For
+% now, discard a few irrelevant arguments |#1|, |#2|, |#3|, and find
+% the multiple of $0.5\cdot 10^{-16}$ within $[y, y+0.5\cdot
+% 10^{-16})$; rather, only the last $4$~digits |#8| of~$y$ are
+% considered, and we do not perform any carry yet. The \texttt{auxxi}
+% auxiliary sets up \texttt{auxii} with a continuation function
+% \texttt{auxxii} instead of \texttt{auxiii} as before. To prevent
+% \texttt{auxii} from giving a negative results $a-m^2$, we compute
+% $a+10^{-16}-m^2$ instead, always positive since $m<\sqrt{a}+0.5\cdot
+% 10^{-16}$ and $a\leq 1-10^{-16}$.
+% \begin{macrocode}
+\cs_new:Npn \@@_sqrt_auxx_o:Nnnnnnnn #1#2#3 #4#5#6#7#8
+ {
+ \exp_after:wN \@@_sqrt_auxxi_o:wwnnN
+ \int_value:w \@@_int_eval:w
+ (#8 + 2499) / 5000 * 5000 ;
+ {#4} {#5} {#6} {#7} ;
+ }
+\cs_new:Npn \@@_sqrt_auxxi_o:wwnnN #1; #2; #3#4#5
+ {
+ \@@_sqrt_auxii_o:NnnnnnnnN
+ \@@_sqrt_auxxii_o:nnnnnnnnw
+ #2 {#1}
+ {#3} { #4 + 1 } #5
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {\@@_sqrt_auxxii_o:nnnnnnnnw, \@@_sqrt_auxxiii_o:w}
+% The difference $0\leq a+10^{-16}-m^2\leq
+% 10^{-16}+(\sqrt{a}-m)(\sqrt{a}+m)\leq 2\cdot 10^{-16}$ was just
+% computed: its first $8$~digits vanish, as do the next four,~|#1|,
+% and most of the following four,~|#2|. The guess~$m$ is an
+% overestimate if $a+10^{-16}-m^2 < 10^{-16}$, that is, |#1#2|
+% vanishes. Otherwise it is an underestimate, unless
+% $a+10^{-16}-m^2=10^{-16}$ exactly. For an underestimate, call the
+% \texttt{auxxiv} function with argument~$9998$. For an exact result
+% call it with~$9999$, and for an overestimate call it with~$10000$.
+% \begin{macrocode}
+\cs_new:Npn \@@_sqrt_auxxii_o:nnnnnnnnw 0; #1#2#3#4#5#6#7#8 #9;
+ {
+ \if_int_compare:w #1#2 > \c_zero_int
+ \if_int_compare:w #1#2 = \c_one_int
+ \if_int_compare:w #3#4 = \c_zero_int
+ \if_int_compare:w #5#6 = \c_zero_int
+ \if_int_compare:w #7#8 = \c_zero_int
+ \@@_sqrt_auxxiii_o:w
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \exp_after:wN \@@_sqrt_auxxiv_o:wnnnnnnnN
+ \int_value:w 9998
+ \else:
+ \exp_after:wN \@@_sqrt_auxxiv_o:wnnnnnnnN
+ \int_value:w 10000
+ \fi:
+ ;
+ }
+\cs_new:Npn \@@_sqrt_auxxiii_o:w \fi: \fi: \fi: \fi: #1 \fi: ;
+ {
+ \fi: \fi: \fi: \fi: \fi:
+ \@@_sqrt_auxxiv_o:wnnnnnnnN 9999 ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_sqrt_auxxiv_o:wnnnnnnnN}
+% This receives $9998$, $9999$ or $10000$ as~|#1| when $m$~is an
+% underestimate, exact, or an overestimate, respectively. Then
+% comes~$m$ as five blocks of~$4$ digits, but where the last
+% block~|#6| may be $0$, $5000$, or~$10000$. In the latter case, we
+% need to add a carry, unless $m$~is an overestimate (|#1|~is then
+% $10000$). Then comes~$a$ as three arguments. Rounding is done by
+% \cs{@@_round:NNN}, whose first argument is the final sign~$0$
+% (square roots are positive). We fake its second argument. It
+% should be the last digit kept, but this is only used when ties are
+% \enquote{rounded to even}, and only when the result is exactly
+% half-way between two representable numbers rational square roots of
+% numbers with $16$~significant digits have: this situation never
+% arises for the square root, as any exact square root of a $16$~digit
+% number has at most $8$~significant digits. Finally, the last
+% argument is the next digit, possibly shifted by~$1$ when there are
+% further nonzero digits. This is achieved by \cs{@@_round_digit:Nw},
+% which receives (after removal of the $10000$'s digit) one of $0000$,
+% $0001$, $4999$, $5000$, $5001$, or~$9999$, which it converts to $0$,
+% $1$, $4$, $5$, $6$, and~$9$, respectively.
+% \begin{macrocode}
+\cs_new:Npn \@@_sqrt_auxxiv_o:wnnnnnnnN #1; #2#3#4#5#6 #7#8#9
+ {
+ \exp_after:wN \@@_basics_pack_high:NNNNNw
+ \int_value:w \@@_int_eval:w 1 0000 0000 + #2#3
+ \exp_after:wN \@@_basics_pack_low:NNNNNw
+ \int_value:w \@@_int_eval:w 1 0000 0000
+ + #4#5
+ \if_int_compare:w #6 > #1 \exp_stop_f: + 1 \fi:
+ + \exp_after:wN \@@_round:NNN
+ \exp_after:wN 0
+ \exp_after:wN 0
+ \int_value:w
+ \exp_after:wN \use_i:nn
+ \exp_after:wN \@@_round_digit:Nw
+ \int_value:w \@@_int_eval:w #6 + 19999 - #1 ;
+ \exp_after:wN ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{About the sign and exponent}
+%
+% \begin{macro}[EXP]{\@@_logb_o:w, \@@_logb_aux_o:w}
+% The exponent of a normal number is its \meta{exponent} minus one.
+% \begin{macrocode}
+\cs_new:Npn \@@_logb_o:w ? \s_@@ \@@_chk:w #1#2; @
+ {
+ \if_case:w #1 \exp_stop_f:
+ \@@_case_use:nw
+ { \@@_division_by_zero_o:Nnw \c_minus_inf_fp { logb } }
+ \or: \exp_after:wN \@@_logb_aux_o:w
+ \or: \@@_case_return_o:Nw \c_inf_fp
+ \else: \@@_case_return_same_o:w
+ \fi:
+ \s_@@ \@@_chk:w #1 #2;
+ }
+\cs_new:Npn \@@_logb_aux_o:w \s_@@ \@@_chk:w #1 #2 #3 #4 ;
+ {
+ \exp_after:wN \@@_parse:n \exp_after:wN
+ { \int_value:w \int_eval:w #3 - 1 \exp_after:wN }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_sign_o:w}
+% \begin{macro}[EXP]{\@@_sign_aux_o:w}
+% Find the sign of the floating point: \texttt{nan}, |+0|, |-0|, |+1| or |-1|.
+% \begin{macrocode}
+\cs_new:Npn \@@_sign_o:w ? \s_@@ \@@_chk:w #1#2; @
+ {
+ \if_case:w #1 \exp_stop_f:
+ \@@_case_return_same_o:w
+ \or: \exp_after:wN \@@_sign_aux_o:w
+ \or: \exp_after:wN \@@_sign_aux_o:w
+ \else: \@@_case_return_same_o:w
+ \fi:
+ \s_@@ \@@_chk:w #1 #2;
+ }
+\cs_new:Npn \@@_sign_aux_o:w \s_@@ \@@_chk:w #1 #2 #3 ;
+ { \exp_after:wN \@@_set_sign_o:w \exp_after:wN #2 \c_one_fp @ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_set_sign_o:w}
+% This function is used for the unary minus and for \texttt{abs}. It
+% leaves the sign of \texttt{nan} invariant, turns negative numbers
+% (sign~$2$) to positive numbers (sign~$0$) and positive numbers
+% (sign~$0$) to positive or negative numbers depending on~|#1|. It
+% also expands after itself in the input stream, just like
+% \cs{@@_+_o:ww}.
+% \begin{macrocode}
+\cs_new:Npn \@@_set_sign_o:w #1 \s_@@ \@@_chk:w #2#3#4; @
+ {
+ \exp_after:wN \@@_exp_after_o:w
+ \exp_after:wN \s_@@
+ \exp_after:wN \@@_chk:w
+ \exp_after:wN #2
+ \int_value:w
+ \if_case:w #3 \exp_stop_f: #1 \or: 1 \or: 0 \fi: \exp_stop_f:
+ #4;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Operations on tuples}
+%
+% \begin{macro}[EXP]{\@@_tuple_set_sign_o:w}
+% \begin{macro}[EXP]{\@@_tuple_set_sign_aux_o:Nnw, \@@_tuple_set_sign_aux_o:w}
+% Two cases: |abs(|\meta{tuple}|)| for which |#1| is $0$ (invalid for
+% tuples) and |-|\meta{tuple} for which |#1| is $2$. In that case,
+% map over all items in the tuple an auxiliary that dispatches to the
+% type-appropriate sign-flipping function.
+% \begin{macrocode}
+\cs_new:Npn \@@_tuple_set_sign_o:w #1#2 @
+ {
+ \if_meaning:w 2 #1
+ \exp_after:wN \@@_tuple_set_sign_aux_o:Nnw
+ \fi:
+ \@@_invalid_operation_o:nw { abs }
+ #2
+ }
+\cs_new:Npn \@@_tuple_set_sign_aux_o:Nnw #1#2
+ { \@@_tuple_map_o:nw \@@_tuple_set_sign_aux_o:w }
+\cs_new:Npn \@@_tuple_set_sign_aux_o:w #1#2 ;
+ {
+ \@@_change_func_type:NNN #1 \@@_set_sign_o:w
+ \@@_parse_apply_unary_error:NNw
+ 2 #1 #2 ; @
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_*_tuple_o:ww, \@@_tuple_*_o:ww, \@@_tuple_/_o:ww}
+% For \meta{number}|*|\meta{tuple} and \meta{tuple}|*|\meta{number}
+% and \meta{tuple}|/|\meta{number}, loop through the \meta{tuple} some
+% code that multiplies or divides by the appropriate \meta{number}.
+% Importantly we need to dispatch according to the type, and we make
+% sure to apply the operator in the correct order.
+% \begin{macrocode}
+\cs_new:cpn { @@_*_tuple_o:ww } #1 ;
+ { \@@_tuple_map_o:nw { \@@_binary_type_o:Nww * #1 ; } }
+\cs_new:cpn { @@_tuple_*_o:ww } #1 ; #2 ;
+ { \@@_tuple_map_o:nw { \@@_binary_rev_type_o:Nww * #2 ; } #1 ; }
+\cs_new:cpn { @@_tuple_/_o:ww } #1 ; #2 ;
+ { \@@_tuple_map_o:nw { \@@_binary_rev_type_o:Nww / #2 ; } #1 ; }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_tuple_+_tuple_o:ww, \@@_tuple_-_tuple_o:ww}
+% Check the two tuples have the same number of items and map through
+% these a helper that dispatches appropriately depending on the types.
+% This means |(1,2)+((1,1),2)| gives |(nan,4)|.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_new:cpn { @@_tuple_#1_tuple_o:ww }
+ \s_@@_tuple \@@_tuple_chk:w ##1 ;
+ \s_@@_tuple \@@_tuple_chk:w ##2 ;
+ {
+ \int_compare:nNnTF
+ { \@@_array_count:n {##1} } = { \@@_array_count:n {##2} }
+ { \@@_tuple_mapthread_o:nww { \@@_binary_type_o:Nww #1 } }
+ { \@@_invalid_operation_o:nww #1 }
+ \s_@@_tuple \@@_tuple_chk:w {##1} ;
+ \s_@@_tuple \@@_tuple_chk:w {##2} ;
+ }
+ }
+\@@_tmp:w +
+\@@_tmp:w -
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-convert.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-convert.dtx
new file mode 100644
index 00000000000..a0536c12995
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-convert.dtx
@@ -0,0 +1,630 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-convert.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3fp-convert} module\\ Floating point conversion^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\texttt{l3fp-convert} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% \subsection{Dealing with tuples}
+%
+% \begin{macro}[EXP]
+% {\@@_tuple_convert:Nw, \@@_tuple_convert_loop:nNw, \@@_tuple_convert_end:w}
+% The first argument is for instance \cs{@@_to_tl_dispatch:w}, which
+% converts any floating point object to the appropriate
+% representation. We loop through all items, putting |,~| between all
+% of them and making sure to remove the leading |,~|.
+% \begin{macrocode}
+\cs_new:Npn \@@_tuple_convert:Nw #1 \s_@@_tuple \@@_tuple_chk:w #2 ;
+ {
+ \int_case:nnF { \@@_array_count:n {#2} }
+ {
+ { 0 } { ( ) }
+ { 1 } { \@@_tuple_convert_end:w @ { #1 #2 , } }
+ }
+ {
+ \@@_tuple_convert_loop:nNw { } #1
+ #2 { ? \@@_tuple_convert_end:w } ;
+ @ { \use_none:nn }
+ }
+ }
+\cs_new:Npn \@@_tuple_convert_loop:nNw #1#2#3#4; #5 @ #6
+ {
+ \use_none:n #3
+ \exp_args:Nf \@@_tuple_convert_loop:nNw { #2 #3#4 ; } #2 #5
+ @ { #6 , ~ #1 }
+ }
+\cs_new:Npn \@@_tuple_convert_end:w #1 @ #2
+ { \exp_after:wN ( \exp:w \exp_end_continue_f:w #2 ) }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Trimming trailing zeros}
+%
+% \begin{macro}[EXP]{\@@_trim_zeros:w}
+% \begin{macro}[EXP]
+% {\@@_trim_zeros_loop:w, \@@_trim_zeros_dot:w, \@@_trim_zeros_end:w}
+% If |#1| ends with a $0$, the \texttt{loop} auxiliary takes that zero
+% as an end-delimiter for its first argument, and the second argument
+% is the same \texttt{loop} auxiliary. Once the last trailing zero is
+% reached, the second argument is the \texttt{dot} auxiliary,
+% which removes a trailing dot if any. We then clean-up with the
+% \texttt{end} auxiliary, keeping only the number.
+% \begin{macrocode}
+\cs_new:Npn \@@_trim_zeros:w #1 ;
+ {
+ \@@_trim_zeros_loop:w #1
+ ; \@@_trim_zeros_loop:w 0; \@@_trim_zeros_dot:w .; \s_@@_stop
+ }
+\cs_new:Npn \@@_trim_zeros_loop:w #1 0; #2 { #2 #1 ; #2 }
+\cs_new:Npn \@@_trim_zeros_dot:w #1 .; { \@@_trim_zeros_end:w #1 ; }
+\cs_new:Npn \@@_trim_zeros_end:w #1 ; #2 \s_@@_stop { #1 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Scientific notation}
+%
+% \begin{macro}[EXP]
+% {\fp_to_scientific:N, \fp_to_scientific:c, \fp_to_scientific:n}
+% The three public functions evaluate their argument, then pass it to
+% \cs{@@_to_scientific_dispatch:w}.
+% \begin{macrocode}
+\cs_new:Npn \fp_to_scientific:N #1
+ { \exp_after:wN \@@_to_scientific_dispatch:w #1 }
+\cs_generate_variant:Nn \fp_to_scientific:N { c }
+\cs_new:Npn \fp_to_scientific:n
+ {
+ \exp_after:wN \@@_to_scientific_dispatch:w
+ \exp:w \exp_end_continue_f:w \@@_parse:n
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_to_scientific_dispatch:w, \@@_to_scientific_recover:w, \@@_tuple_to_scientific:w}
+% We allow tuples.
+% \begin{macrocode}
+\cs_new:Npn \@@_to_scientific_dispatch:w #1
+ {
+ \@@_change_func_type:NNN
+ #1 \@@_to_scientific:w \@@_to_scientific_recover:w
+ #1
+ }
+\cs_new:Npn \@@_to_scientific_recover:w #1 #2 ;
+ {
+ \@@_error:nffn { unknown-type } { \tl_to_str:n { #2 ; } } { } { }
+ nan
+ }
+\cs_new:Npn \@@_tuple_to_scientific:w
+ { \@@_tuple_convert:Nw \@@_to_scientific_dispatch:w }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_to_scientific:w,
+% \@@_to_scientific_normal:wnnnnn,
+% \@@_to_scientific_normal:wNw
+% }
+% Expressing an internal floating point number in scientific notation
+% is quite easy: no rounding, and the format is very well defined.
+% First cater for the sign: negative numbers ($|#2|=2$) start
+% with~|-|; we then only need to care about positive numbers and
+% \texttt{nan}. Then filter the special cases: $\pm0$~are represented
+% as~|0|; infinities are converted to a number slightly larger than
+% the largest after an \enquote{invalid_operation} exception;
+% \texttt{nan} is represented as~|0| after an
+% \enquote{invalid_operation} exception. In the normal case,
+% decrement the exponent and unbrace the $4$ brace groups, then in a
+% second step grab the first digit (previously hidden in braces) to
+% order the various parts correctly.
+% \begin{macrocode}
+\cs_new:Npn \@@_to_scientific:w \s_@@ \@@_chk:w #1#2
+ {
+ \if_meaning:w 2 #2 \exp_after:wN - \exp:w \exp_end_continue_f:w \fi:
+ \if_case:w #1 \exp_stop_f:
+ \@@_case_return:nw { 0.000000000000000e0 }
+ \or: \exp_after:wN \@@_to_scientific_normal:wnnnnn
+ \or:
+ \@@_case_use:nw
+ {
+ \@@_invalid_operation:nnw
+ { \fp_to_scientific:N \c_@@_overflowing_fp }
+ { fp_to_scientific }
+ }
+ \or:
+ \@@_case_use:nw
+ {
+ \@@_invalid_operation:nnw
+ { \fp_to_scientific:N \c_zero_fp }
+ { fp_to_scientific }
+ }
+ \fi:
+ \s_@@ \@@_chk:w #1 #2
+ }
+\cs_new:Npn \@@_to_scientific_normal:wnnnnn
+ \s_@@ \@@_chk:w 1 #1 #2 #3#4#5#6 ;
+ {
+ \exp_after:wN \@@_to_scientific_normal:wNw
+ \exp_after:wN e
+ \int_value:w \@@_int_eval:w #2 - 1
+ ; #3 #4 #5 #6 ;
+ }
+\cs_new:Npn \@@_to_scientific_normal:wNw #1 ; #2#3;
+ { #2.#3 #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Decimal representation}
+%
+% \begin{macro}[EXP]
+% {\fp_to_decimal:N, \fp_to_decimal:c, \fp_to_decimal:n}
+% All three public variants are based on the same
+% \cs{@@_to_decimal_dispatch:w}
+% after evaluating their argument to an internal floating point.
+% \begin{macrocode}
+\cs_new:Npn \fp_to_decimal:N #1
+ { \exp_after:wN \@@_to_decimal_dispatch:w #1 }
+\cs_generate_variant:Nn \fp_to_decimal:N { c }
+\cs_new:Npn \fp_to_decimal:n
+ {
+ \exp_after:wN \@@_to_decimal_dispatch:w
+ \exp:w \exp_end_continue_f:w \@@_parse:n
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_to_decimal_dispatch:w, \@@_to_decimal_recover:w, \@@_tuple_to_decimal:w}
+% We allow tuples.
+% \begin{macrocode}
+\cs_new:Npn \@@_to_decimal_dispatch:w #1
+ {
+ \@@_change_func_type:NNN
+ #1 \@@_to_decimal:w \@@_to_decimal_recover:w
+ #1
+ }
+\cs_new:Npn \@@_to_decimal_recover:w #1 #2 ;
+ {
+ \@@_error:nffn { unknown-type } { \tl_to_str:n { #2 ; } } { } { }
+ nan
+ }
+\cs_new:Npn \@@_tuple_to_decimal:w
+ { \@@_tuple_convert:Nw \@@_to_decimal_dispatch:w }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_to_decimal:w,
+% \@@_to_decimal_normal:wnnnnn,
+% \@@_to_decimal_large:Nnnw,
+% \@@_to_decimal_huge:wnnnn,
+% }
+% The structure is similar to \cs{@@_to_scientific:w}.
+% Insert |-| for
+% negative numbers. Zero gives $0$, $\pm\infty$ and \nan{} yield an
+% \enquote{invalid operation} exception; note that $\pm\infty$
+% produces a very large output, which we don't expand now since it
+% most likely won't be needed. Normal numbers with an exponent in the
+% range $[1,15]$ have that number of digits before the decimal
+% separator: \enquote{decimate} them, and remove leading zeros with
+% \cs{int_value:w}, then trim trailing zeros and dot. Normal
+% numbers with an exponent $16$ or larger have no decimal separator,
+% we only need to add trailing zeros. When the exponent is
+% non-positive, the result should be $0.\meta{zeros}\meta{digits}$,
+% trimmed.
+% \begin{macrocode}
+\cs_new:Npn \@@_to_decimal:w \s_@@ \@@_chk:w #1#2
+ {
+ \if_meaning:w 2 #2 \exp_after:wN - \exp:w \exp_end_continue_f:w \fi:
+ \if_case:w #1 \exp_stop_f:
+ \@@_case_return:nw { 0 }
+ \or: \exp_after:wN \@@_to_decimal_normal:wnnnnn
+ \or:
+ \@@_case_use:nw
+ {
+ \@@_invalid_operation:nnw
+ { \fp_to_decimal:N \c_@@_overflowing_fp }
+ { fp_to_decimal }
+ }
+ \or:
+ \@@_case_use:nw
+ {
+ \@@_invalid_operation:nnw
+ { 0 }
+ { fp_to_decimal }
+ }
+ \fi:
+ \s_@@ \@@_chk:w #1 #2
+ }
+\cs_new:Npn \@@_to_decimal_normal:wnnnnn
+ \s_@@ \@@_chk:w 1 #1 #2 #3#4#5#6 ;
+ {
+ \int_compare:nNnTF {#2} > 0
+ {
+ \int_compare:nNnTF {#2} < \c_@@_prec_int
+ {
+ \@@_decimate:nNnnnn { \c_@@_prec_int - #2 }
+ \@@_to_decimal_large:Nnnw
+ }
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_to_decimal_huge:wnnnn
+ \prg_replicate:nn { #2 - \c_@@_prec_int } { 0 } ;
+ }
+ {#3} {#4} {#5} {#6}
+ }
+ {
+ \exp_after:wN \@@_trim_zeros:w
+ \exp_after:wN 0
+ \exp_after:wN .
+ \exp:w \exp_end_continue_f:w \prg_replicate:nn { - #2 } { 0 }
+ #3#4#5#6 ;
+ }
+ }
+\cs_new:Npn \@@_to_decimal_large:Nnnw #1#2#3#4;
+ {
+ \exp_after:wN \@@_trim_zeros:w \int_value:w
+ \if_int_compare:w #2 > \c_zero_int
+ #2
+ \fi:
+ \exp_stop_f:
+ #3.#4 ;
+ }
+\cs_new:Npn \@@_to_decimal_huge:wnnnn #1; #2#3#4#5 { #2#3#4#5 #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Token list representation}
+%
+% \begin{macro}[EXP]{\fp_to_tl:N, \fp_to_tl:c, \fp_to_tl:n}
+% These three public functions evaluate their argument, then pass it
+% to \cs{@@_to_tl_dispatch:w}.
+% \begin{macrocode}
+\cs_new:Npn \fp_to_tl:N #1 { \exp_after:wN \@@_to_tl_dispatch:w #1 }
+\cs_generate_variant:Nn \fp_to_tl:N { c }
+\cs_new:Npn \fp_to_tl:n
+ {
+ \exp_after:wN \@@_to_tl_dispatch:w
+ \exp:w \exp_end_continue_f:w \@@_parse:n
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_to_tl_dispatch:w, \@@_to_tl_recover:w, \@@_tuple_to_tl:w}
+% We allow tuples.
+% \begin{macrocode}
+\cs_new:Npn \@@_to_tl_dispatch:w #1
+ { \@@_change_func_type:NNN #1 \@@_to_tl:w \@@_to_tl_recover:w #1 }
+\cs_new:Npn \@@_to_tl_recover:w #1 #2 ;
+ {
+ \@@_error:nffn { unknown-type } { \tl_to_str:n { #2 ; } } { } { }
+ nan
+ }
+\cs_new:Npn \@@_tuple_to_tl:w
+ { \@@_tuple_convert:Nw \@@_to_tl_dispatch:w }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_to_tl:w, \@@_to_tl_normal:nnnnn,
+% \@@_to_tl_scientific:wnnnnn, \@@_to_tl_scientific:wNw
+% }
+% A structure similar to \cs{@@_to_scientific_dispatch:w} and
+% \cs{@@_to_decimal_dispatch:w}, but without the \enquote{invalid operation}
+% exception. First filter special cases. We express normal numbers
+% in decimal notation if the exponent is in the range $[-2,16]$, and
+% otherwise use scientific notation.
+% \begin{macrocode}
+\cs_new:Npn \@@_to_tl:w \s_@@ \@@_chk:w #1#2
+ {
+ \if_meaning:w 2 #2 \exp_after:wN - \exp:w \exp_end_continue_f:w \fi:
+ \if_case:w #1 \exp_stop_f:
+ \@@_case_return:nw { 0 }
+ \or: \exp_after:wN \@@_to_tl_normal:nnnnn
+ \or: \@@_case_return:nw { inf }
+ \else: \@@_case_return:nw { nan }
+ \fi:
+ }
+\cs_new:Npn \@@_to_tl_normal:nnnnn #1
+ {
+ \int_compare:nTF
+ { -2 <= #1 <= \c_@@_prec_int }
+ { \@@_to_decimal_normal:wnnnnn }
+ { \@@_to_tl_scientific:wnnnnn }
+ \s_@@ \@@_chk:w 1 0 {#1}
+ }
+\cs_new:Npn \@@_to_tl_scientific:wnnnnn
+ \s_@@ \@@_chk:w 1 #1 #2 #3#4#5#6 ;
+ {
+ \exp_after:wN \@@_to_tl_scientific:wNw
+ \exp_after:wN e
+ \int_value:w \@@_int_eval:w #2 - 1
+ ; #3 #4 #5 #6 ;
+ }
+\cs_new:Npn \@@_to_tl_scientific:wNw #1 ; #2#3;
+ { \@@_trim_zeros:w #2.#3 ; #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Formatting}
+%
+% This is not implemented yet, as it is not yet clear what a correct
+% interface would be, for this kind of structured conversion from a
+% floating point (or other types of variables) to a string. Ideas
+% welcome.
+%
+% \subsection{Convert to dimension or integer}
+%
+% \begin{macro}[EXP]{\fp_to_dim:N, \fp_to_dim:c, \fp_to_dim:n}
+% \begin{macro}[EXP]{\@@_to_dim_dispatch:w, \@@_to_dim_recover:w, \@@_to_dim:w}
+% All three public variants are based on the same
+% \cs{@@_to_dim_dispatch:w} after evaluating their argument to an
+% internal floating point.
+% We only allow floating point numbers, not tuples.
+% \begin{macrocode}
+\cs_new:Npn \fp_to_dim:N #1
+ { \exp_after:wN \@@_to_dim_dispatch:w #1 }
+\cs_generate_variant:Nn \fp_to_dim:N { c }
+\cs_new:Npn \fp_to_dim:n
+ {
+ \exp_after:wN \@@_to_dim_dispatch:w
+ \exp:w \exp_end_continue_f:w \@@_parse:n
+ }
+\cs_new:Npn \@@_to_dim_dispatch:w #1#2 ;
+ {
+ \@@_change_func_type:NNN #1 \@@_to_dim:w \@@_to_dim_recover:w
+ #1 #2 ;
+ }
+\cs_new:Npn \@@_to_dim_recover:w #1
+ { \@@_invalid_operation:nnw { 0pt } { fp_to_dim } }
+\cs_new:Npn \@@_to_dim:w #1 ; { \@@_to_decimal:w #1 ; pt }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\fp_to_int:N, \fp_to_int:c, \fp_to_int:n}
+% \begin{macro}[EXP]{\@@_to_int_dispatch:w, \@@_to_int_recover:w}
+% For the most part identical to \cs{fp_to_dim:N} but without |pt|,
+% and where \cs{@@_to_int:w} does more work.
+% To convert to an integer, first round to $0$ places (to the nearest
+% integer), then express the result as a decimal number: the
+% definition of \cs{@@_to_decimal_dispatch:w} is such that there are no
+% trailing dot nor zero.
+% \begin{macrocode}
+\cs_new:Npn \fp_to_int:N #1 { \exp_after:wN \@@_to_int_dispatch:w #1 }
+\cs_generate_variant:Nn \fp_to_int:N { c }
+\cs_new:Npn \fp_to_int:n
+ {
+ \exp_after:wN \@@_to_int_dispatch:w
+ \exp:w \exp_end_continue_f:w \@@_parse:n
+ }
+\cs_new:Npn \@@_to_int_dispatch:w #1#2 ;
+ {
+ \@@_change_func_type:NNN #1 \@@_to_int:w \@@_to_int_recover:w
+ #1 #2 ;
+ }
+\cs_new:Npn \@@_to_int_recover:w #1
+ { \@@_invalid_operation:nnw { 0 } { fp_to_int } }
+\cs_new:Npn \@@_to_int:w #1;
+ {
+ \exp_after:wN \@@_to_decimal:w \exp:w \exp_end_continue_f:w
+ \@@_round:Nwn \@@_round_to_nearest:NNN #1; { 0 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Convert from a dimension}
+%
+% \begin{macro}[EXP]{\dim_to_fp:n}
+% \begin{macro}[EXP]
+% {
+% \@@_from_dim_test:ww,
+% \@@_from_dim:wNw,
+% \@@_from_dim:wNNnnnnnn,
+% \@@_from_dim:wnnnnwNw,
+% }
+% The dimension expression (which can in fact be a glue expression) is
+% evaluated, converted to a number (\emph{i.e.}, expressed in scaled
+% points), then multiplied by $2^{-16} = 0.0000152587890625$ to give a
+% value expressed in points. The auxiliary \cs{@@_mul_npos_o:Nww}
+% expects the desired \meta{final sign} and two floating point
+% operands (of the form \cs{s_@@} \ldots{} |;|) as arguments.
+% This set of functions is also used to convert dimension registers to
+% floating points while parsing expressions: in this context there is
+% an additional exponent, which is the first argument of
+% \cs{@@_from_dim_test:ww}, and is combined with the exponent $-4$
+% of $2^{-16}$. There is also a need to expand afterwards: this is
+% performed by \cs{@@_mul_npos_o:Nww}, and cancelled by
+% \cs{prg_do_nothing:} here.
+% \begin{macrocode}
+\cs_new:Npn \dim_to_fp:n #1
+ {
+ \exp_after:wN \@@_from_dim_test:ww
+ \exp_after:wN 0
+ \exp_after:wN ,
+ \int_value:w \tex_glueexpr:D #1 ;
+ }
+\cs_new:Npn \@@_from_dim_test:ww #1, #2
+ {
+ \if_meaning:w 0 #2
+ \@@_case_return:nw { \exp_after:wN \c_zero_fp }
+ \else:
+ \exp_after:wN \@@_from_dim:wNw
+ \int_value:w \@@_int_eval:w #1 - 4
+ \if_meaning:w - #2
+ \exp_after:wN , \exp_after:wN 2 \int_value:w
+ \else:
+ \exp_after:wN , \exp_after:wN 0 \int_value:w #2
+ \fi:
+ \fi:
+ }
+\cs_new:Npn \@@_from_dim:wNw #1,#2#3;
+ {
+ \@@_pack_twice_four:wNNNNNNNN \@@_from_dim:wNNnnnnnn ;
+ #3 000 0000 00 {10}987654321; #2 {#1}
+ }
+\cs_new:Npn \@@_from_dim:wNNnnnnnn #1; #2#3#4#5#6#7#8#9
+ { \@@_from_dim:wnnnnwNn #1 {#2#300} {0000} ; }
+\cs_new:Npn \@@_from_dim:wnnnnwNn #1; #2#3#4#5#6; #7#8
+ {
+ \@@_mul_npos_o:Nww #7
+ \s_@@ \@@_chk:w 1 #7 {#5} #1 ;
+ \s_@@ \@@_chk:w 1 0 {#8} {1525} {8789} {0625} {0000} ;
+ \prg_do_nothing:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Use and eval}
+%
+% \begin{macro}[EXP]{\fp_use:N, \fp_use:c, \fp_eval:n}
+% Those public functions are simple copies of the decimal conversions.
+% \begin{macrocode}
+\cs_new_eq:NN \fp_use:N \fp_to_decimal:N
+\cs_generate_variant:Nn \fp_use:N { c }
+\cs_new_eq:NN \fp_eval:n \fp_to_decimal:n
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\fp_sign:n}
+% Trivial but useful. See the implementation of \cs{fp_add:Nn} for an
+% explanation of why to use \cs{@@_parse:n}, namely, for better error
+% reporting.
+% \begin{macrocode}
+\cs_new:Npn \fp_sign:n #1
+ { \fp_to_decimal:n { sign \@@_parse:n {#1} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\fp_abs:n}
+% Trivial but useful. See the implementation of \cs{fp_add:Nn} for an
+% explanation of why to use \cs{@@_parse:n}, namely, for better error
+% reporting.
+% \begin{macrocode}
+\cs_new:Npn \fp_abs:n #1
+ { \fp_to_decimal:n { abs \@@_parse:n {#1} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\fp_max:nn, \fp_min:nn}
+% Similar to \cs{fp_abs:n}, for consistency with \cs{int_max:nn}, \emph{etc.}
+% \begin{macrocode}
+\cs_new:Npn \fp_max:nn #1#2
+ { \fp_to_decimal:n { max ( \@@_parse:n {#1} , \@@_parse:n {#2} ) } }
+\cs_new:Npn \fp_min:nn #1#2
+ { \fp_to_decimal:n { min ( \@@_parse:n {#1} , \@@_parse:n {#2} ) } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Convert an array of floating points to a comma list}
+%
+% \begin{macro}[EXP]{\@@_array_to_clist:n}
+% \begin{macro}[EXP]{\@@_array_to_clist_loop:Nw}
+% Converts an array of floating point numbers to a comma-list. If
+% speed here ends up irrelevant, we can simplify the code for the
+% auxiliary to become
+% \begin{verbatim}
+% \cs_new:Npn \__fp_array_to_clist_loop:Nw #1#2;
+% {
+% \use_none:n #1
+% { , ~ } \fp_to_tl:n { #1 #2 ; }
+% \__fp_array_to_clist_loop:Nw
+% }
+% \end{verbatim}
+% The \cs{use_ii:nn} function is expanded after \cs{@@_expand:n} is
+% done, and it removes |,~| from the start of the representation.
+% \begin{macrocode}
+\cs_new:Npn \@@_array_to_clist:n #1
+ {
+ \tl_if_empty:nF {#1}
+ {
+ \exp_last_unbraced:Ne \use_ii:nn
+ {
+ \@@_array_to_clist_loop:Nw #1 { ? \prg_break: } ;
+ \prg_break_point:
+ }
+ }
+ }
+\cs_new:Npn \@@_array_to_clist_loop:Nw #1#2;
+ {
+ \use_none:n #1
+ , ~
+ \exp_not:f { \@@_to_tl_dispatch:w #1 #2 ; }
+ \@@_array_to_clist_loop:Nw
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-expo.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-expo.dtx
new file mode 100644
index 00000000000..6fbc9de52c7
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-expo.dtx
@@ -0,0 +1,1380 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-expo.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3fp-expo} module\\
+% Floating point exponential-related functions^^A
+% }
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-expo} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_word_exp:N ,
+% \@@_parse_word_ln:N ,
+% \@@_parse_word_fact:N,
+% }
+% Unary functions.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_word_exp:N
+ { \@@_parse_unary_function:NNN \@@_exp_o:w ? }
+\cs_new:Npn \@@_parse_word_ln:N
+ { \@@_parse_unary_function:NNN \@@_ln_o:w ? }
+\cs_new:Npn \@@_parse_word_fact:N
+ { \@@_parse_unary_function:NNN \@@_fact_o:w ? }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Logarithm}
+%
+% \subsubsection{Work plan}
+%
+% As for many other functions, we filter out special cases in
+% \cs{@@_ln_o:w}. Then \cs{@@_ln_npos_o:w} receives a positive normal
+% number, which we write in the form $a\cdot 10^{b}$ with $a\in[0.1,1)$.
+%
+% \emph{The rest of this section is actually not in sync with the code.
+% Or is the code not in sync with the section? In the current code,
+% $c\in [1,10]$ is such that $0.7\leq ac < 1.4$.}
+%
+% We are given a positive normal number, of the form $a\cdot 10^{b}$
+% with $a\in[0.1,1)$. To compute its logarithm, we find a small integer
+% $5\leq c < 50$ such that $0.91 \leq a c / 5 < 1.1$, and use the
+% relation
+% \begin{equation*}
+% \ln (a \cdot 10^b) = b \cdot \ln (10) - \ln (c/5) + \ln (ac/5).
+% \end{equation*}
+% The logarithms $\ln(10)$ and $\ln(c/5)$ are looked up in a table. The
+% last term is computed using the following Taylor series of $\ln$ near
+% $1$:
+% \begin{equation*}
+% \ln\left(\frac{ac}{5}\right)
+% = \ln\left(\frac{1+t}{1-t}\right)
+% = 2t\left(1 + t^2 \left(\frac{1}{3} + t^2 \left(\frac{1}{5}
+% + t^2 \left(\frac{1}{7} + t^2 \left( \frac{1}{9} + \cdots
+% \right)\right)\right)\right)\right)
+% \end{equation*}
+% where $t=1-10/(ac+5)$. We can now see one reason for the choice of
+% $ac\sim 5$: then $ac+5=10(1-\epsilon)$ with $-0.05<\epsilon\leq
+% 0.045$, hence
+% \begin{equation*}
+% t = \frac{\epsilon}{1-\epsilon}
+% = \epsilon (1+\epsilon)(1+\epsilon^2)(1+\epsilon^4)\ldots,
+% \end{equation*}
+% is not too difficult to compute.
+%
+% \subsubsection{Some constants}
+%
+% \begin{variable}
+% {
+% \c_@@_ln_i_fixed_tl ,
+% \c_@@_ln_ii_fixed_tl ,
+% \c_@@_ln_iii_fixed_tl ,
+% \c_@@_ln_iv_fixed_tl ,
+% \c_@@_ln_vi_fixed_tl ,
+% \c_@@_ln_vii_fixed_tl ,
+% \c_@@_ln_viii_fixed_tl ,
+% \c_@@_ln_ix_fixed_tl ,
+% \c_@@_ln_x_fixed_tl,
+% }
+% A few values of the logarithm as extended fixed point numbers.
+% Those are needed in the implementation. It turns out that we don't
+% need the value of $\ln(5)$.
+% \begin{macrocode}
+\tl_const:Nn \c_@@_ln_i_fixed_tl { {0000}{0000}{0000}{0000}{0000}{0000};}
+\tl_const:Nn \c_@@_ln_ii_fixed_tl { {6931}{4718}{0559}{9453}{0941}{7232};}
+\tl_const:Nn \c_@@_ln_iii_fixed_tl {{10986}{1228}{8668}{1096}{9139}{5245};}
+\tl_const:Nn \c_@@_ln_iv_fixed_tl {{13862}{9436}{1119}{8906}{1883}{4464};}
+\tl_const:Nn \c_@@_ln_vi_fixed_tl {{17917}{5946}{9228}{0550}{0081}{2477};}
+\tl_const:Nn \c_@@_ln_vii_fixed_tl {{19459}{1014}{9055}{3133}{0510}{5353};}
+\tl_const:Nn \c_@@_ln_viii_fixed_tl{{20794}{4154}{1679}{8359}{2825}{1696};}
+\tl_const:Nn \c_@@_ln_ix_fixed_tl {{21972}{2457}{7336}{2193}{8279}{0490};}
+\tl_const:Nn \c_@@_ln_x_fixed_tl {{23025}{8509}{2994}{0456}{8401}{7991};}
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Sign, exponent, and special numbers}
+%
+% \begin{macro}[EXP]{\@@_ln_o:w}
+% The logarithm of negative numbers (including $-\infty$ and $-0$)
+% raises the \enquote{invalid} exception. The logarithm of $+0$ is
+% $-\infty$, raising a division by zero exception. The logarithm of
+% $+\infty$ or a \texttt{nan} is itself. Positive normal numbers call
+% \cs{@@_ln_npos_o:w}.
+% \begin{macrocode}
+\cs_new:Npn \@@_ln_o:w #1 \s_@@ \@@_chk:w #2#3#4; @
+ {
+ \if_meaning:w 2 #3
+ \@@_case_use:nw { \@@_invalid_operation_o:nw { ln } }
+ \fi:
+ \if_case:w #2 \exp_stop_f:
+ \@@_case_use:nw
+ { \@@_division_by_zero_o:Nnw \c_minus_inf_fp { ln } }
+ \or:
+ \else:
+ \@@_case_return_same_o:w
+ \fi:
+ \@@_ln_npos_o:w \s_@@ \@@_chk:w #2#3#4;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Absolute ln}
+%
+% \begin{macro}[EXP]{\@@_ln_npos_o:w}
+% We catch the case of a significand very close to $0.1$ or to $1$.
+% In all other cases, the final result is at least $10^{-4}$, and
+% then an error of $0.5\cdot 10^{-20}$ is acceptable.
+% \begin{macrocode}
+\cs_new:Npn \@@_ln_npos_o:w \s_@@ \@@_chk:w 10#1#2#3;
+ { %^^A todo: ln(1) should be "exact zero", not "underflow"
+ \exp_after:wN \@@_sanitize:Nw
+ \int_value:w % for the overall sign
+ \if_int_compare:w #1 < \c_one_int
+ 2
+ \else:
+ 0
+ \fi:
+ \exp_after:wN \exp_stop_f:
+ \int_value:w \@@_int_eval:w % for the exponent
+ \@@_ln_significand:NNNNnnnN #2#3
+ \@@_ln_exponent:wn {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_ln_significand:NNNNnnnN}
+% \begin{syntax}
+% \cs{@@_ln_significand:NNNNnnnN} \meta{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} \meta{continuation}
+% \end{syntax}
+% This function expands to
+% \begin{syntax}
+% \meta{continuation} \Arg{Y_1} \Arg{Y_2} \Arg{Y_3} \Arg{Y_4} \Arg{Y_5} \Arg{Y_6} |;|
+% \end{syntax}
+% where $Y = - \ln(X)$ as an extended fixed point.
+% \begin{macrocode}
+\cs_new:Npn \@@_ln_significand:NNNNnnnN #1#2#3#4
+ {
+ \exp_after:wN \@@_ln_x_ii:wnnnn
+ \int_value:w
+ \if_case:w #1 \exp_stop_f:
+ \or:
+ \if_int_compare:w #2 < 4 \exp_stop_f:
+ \@@_int_eval:w 10 - #2
+ \else:
+ 6
+ \fi:
+ \or: 4
+ \or: 3
+ \or: 2
+ \or: 2
+ \or: 2
+ \else: 1
+ \fi:
+ ; { #1 #2 #3 #4 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_ln_x_ii:wnnnn}
+% We have thus found $c \in [1,10]$ such that $0.7\leq ac < 1.4$
+% in all cases. Compute $ 1 + x = 1 + ac \in [1.7,2.4)$.
+% \begin{macrocode}
+\cs_new:Npn \@@_ln_x_ii:wnnnn #1; #2#3#4#5
+ {
+ \exp_after:wN \@@_ln_div_after:Nw
+ \cs:w c_@@_ln_ \@@_int_to_roman:w #1 _fixed_tl \exp_after:wN \cs_end:
+ \int_value:w
+ \exp_after:wN \@@_ln_x_iv:wnnnnnnnn
+ \int_value:w \@@_int_eval:w
+ \exp_after:wN \@@_ln_x_iii_var:NNNNNw
+ \int_value:w \@@_int_eval:w 9999 9990 + #1*#2#3 +
+ \exp_after:wN \@@_ln_x_iii:NNNNNNw
+ \int_value:w \@@_int_eval:w 10 0000 0000 + #1*#4#5 ;
+ {20000} {0000} {0000} {0000}
+ } %^^A todo: reoptimize (a generalization attempt failed).
+\cs_new:Npn \@@_ln_x_iii:NNNNNNw #1#2 #3#4#5#6 #7;
+ { #1#2; {#3#4#5#6} {#7} }
+\cs_new:Npn \@@_ln_x_iii_var:NNNNNw #1 #2#3#4#5 #6;
+ {
+ #1#2#3#4#5 + 1 ;
+ {#1#2#3#4#5} {#6}
+ }
+% \end{macrocode}
+% The Taylor series to be used is expressed in terms of
+% $t = (x-1)/(x+1) = 1 - 2/(x+1)$. We now compute the
+% quotient with extended precision, reusing some code
+% from \cs{@@_/_o:ww}. Note that $1+x$ is known exactly.
+%
+% To reuse notations from \pkg{l3fp-basics}, we want to
+% compute $ A / Z $ with $ A = 2 $ and $ Z = x + 1 $.
+% In \pkg{l3fp-basics}, we considered the case where
+% both $A$ and $Z$ are arbitrary, in the range $[0.1,1)$,
+% and we had to monitor the growth of the sequence of
+% remainders $A$, $B$, $C$, etc. to ensure that no overflow
+% occurred during the computation of the next quotient.
+% The main source of risk was our choice to define the
+% quotient as roughly $10^9 \cdot A / 10^5 \cdot Z$: then
+% $A$ was bound to be below $2.147\cdots$, and this limit
+% was never far.
+%
+% In our case, we can simply work with $10^8 \cdot A$ and
+% $10^4 \cdot Z$, because our reason to work with higher
+% powers has gone: we needed the integer $y \simeq 10^5 \cdot Z$
+% to be at least $10^4$, and now, the definition
+% $y \simeq 10^4 \cdot Z$ suffices.
+%
+% Let us thus define $y = \left\lfloor 10^4 \cdot Z \right\rfloor + 1
+% \in ( 1.7 \cdot 10^4, 2.4 \cdot 10^4 ] $, and
+% \[
+% Q_{1}
+% =
+% \left\lfloor
+% \frac {\left\lfloor 10^8 \cdot A\right\rfloor} {y} - \frac{1}{2}
+% \right\rfloor.
+% \]
+% (The $1/2$ comes from how \eTeX{} rounds.) As for division, it is
+% easy to see that $Q_{1} \leq 10^4 A / Z$, \emph{i.e.}, $Q_{1}$
+% is an underestimate.
+%
+% Exactly as we did for division, we set $B = 10^4 A - Q_{1}Z$. Then
+% \begin{align*}
+% 10^4 B
+% & \leq
+% A_{1}A_{2}.A_{3}A_{4}
+% - \left( \frac{A_{1}A_{2}}{y} - \frac{3}{2} \right) 10^4 Z
+% \\
+% & \leq
+% A_{1}A_{2} \left( 1 - \frac{10^4 Z}{y} \right) + 1 + \frac{3}{2} y
+% \\
+% & \leq
+% 10^8 \frac{A}{y} + 1 + \frac{3}{2} y
+% \end{align*}
+% In the same way, and using $1.7\cdot 10^4\leq y\leq 2.4\cdot 10^4$,
+% and convexity, we get
+% \begin{align*}
+% 10^4 A &= 2\cdot 10^4 \\
+% 10^4 B &\leq 10^8 \frac{A}{y} + 1.6 y \leq 4.7\cdot 10^4\\
+% 10^4 C &\leq 10^8 \frac{B}{y} + 1.6 y \leq 5.8\cdot 10^4\\
+% 10^4 D &\leq 10^8 \frac{C}{y} + 1.6 y \leq 6.3\cdot 10^4\\
+% 10^4 E &\leq 10^8 \frac{D}{y} + 1.6 y \leq 6.5\cdot 10^4\\
+% 10^4 F &\leq 10^8 \frac{E}{y} + 1.6 y \leq 6.6\cdot 10^4\\
+% \end{align*}
+% Note that we compute more steps than for division: since $t$ is
+% not the end result, we need to know it with more accuracy
+% (on the other hand, the ending is much simpler, as we don't
+% need an exact rounding for transcendental functions, but just
+% a faithful rounding).
+% ^^A todo: doc
+%
+% \begin{syntax}
+% \cs{@@_ln_x_iv:wnnnnnnnn} \meta{1 or 2} \meta{8d} |;| \Arg{4d} \Arg{4d} \meta{fixed-tl}
+% \end{syntax}
+% The number is $x$. Compute $y$ by adding 1 to the five first digits.
+% \begin{macrocode}
+\cs_new:Npn \@@_ln_x_iv:wnnnnnnnn #1; #2#3#4#5 #6#7#8#9
+ {
+ \exp_after:wN \@@_div_significand_pack:NNN
+ \int_value:w \@@_int_eval:w
+ \@@_ln_div_i:w #1 ;
+ #6 #7 ; {#8} {#9}
+ {#2} {#3} {#4} {#5}
+ { \exp_after:wN \@@_ln_div_ii:wwn \int_value:w #1 }
+ { \exp_after:wN \@@_ln_div_ii:wwn \int_value:w #1 }
+ { \exp_after:wN \@@_ln_div_ii:wwn \int_value:w #1 }
+ { \exp_after:wN \@@_ln_div_ii:wwn \int_value:w #1 }
+ { \exp_after:wN \@@_ln_div_vi:wwn \int_value:w #1 }
+ }
+\cs_new:Npn \@@_ln_div_i:w #1;
+ {
+ \exp_after:wN \@@_div_significand_calc:wwnnnnnnn
+ \int_value:w \@@_int_eval:w 999999 + 2 0000 0000 / #1 ; % Q1
+ }
+\cs_new:Npn \@@_ln_div_ii:wwn #1; #2;#3 % y; B1;B2 <- for k=1
+ {
+ \exp_after:wN \@@_div_significand_pack:NNN
+ \int_value:w \@@_int_eval:w
+ \exp_after:wN \@@_div_significand_calc:wwnnnnnnn
+ \int_value:w \@@_int_eval:w 999999 + #2 #3 / #1 ; % Q2
+ #2 #3 ;
+ }
+\cs_new:Npn \@@_ln_div_vi:wwn #1; #2;#3#4#5 #6#7#8#9 %y;F1;F2F3F4x1x2x3x4
+ {
+ \exp_after:wN \@@_div_significand_pack:NNN
+ \int_value:w \@@_int_eval:w 1000000 + #2 #3 / #1 ; % Q6
+ }
+% \end{macrocode}
+% We now have essentially
+% ^^A todo: determine error on $Q_{6}$ (probably $6.7$),
+% ^^A todo: conclude the final result is off by $<10^{-23}$
+% \begin{syntax}
+% \cs{@@_ln_div_after:Nw} \meta{fixed tl}
+% \cs{@@_div_significand_pack:NNN} $10^6 + Q_{1}$
+% \cs{@@_div_significand_pack:NNN} $10^6 + Q_{2}$
+% \cs{@@_div_significand_pack:NNN} $10^6 + Q_{3}$
+% \cs{@@_div_significand_pack:NNN} $10^6 + Q_{4}$
+% \cs{@@_div_significand_pack:NNN} $10^6 + Q_{5}$
+% \cs{@@_div_significand_pack:NNN} $10^6 + Q_{6}$ |;|
+% \meta{exponent} |;| \meta{continuation}
+% \end{syntax}
+% where \meta{fixed tl} holds the logarithm of a number
+% in $[1,10]$, and \meta{exponent} is
+% the exponent. Also, the expansion is done backwards. Then
+% \cs{@@_div_significand_pack:NNN} puts things in the
+% correct order to add the $Q_{i}$ together and put semicolons
+% between each piece. Once those have been expanded, we get
+% \begin{syntax}
+% \cs{@@_ln_div_after:Nw} \meta{fixed-tl} \meta{1d} |;| \meta{4d} |;| \meta{4d} |;|
+% ~~\meta{4d} |;| \meta{4d} |;| \meta{4d} |;| \meta{4d} |;| \meta{exponent} |;|
+% \end{syntax}
+% ^^A todo: redoc.
+% Just as with division, we know that the first two digits
+% are |1| and |0| because of bounds on the final result of
+% the division $2/(x+1)$, which is between roughly $0.8$ and $1.2$.
+% We then compute $1-2/(x+1)$, after testing whether $2/(x+1)$ is
+% greater than or smaller than $1$.
+% \begin{macrocode}
+\cs_new:Npn \@@_ln_div_after:Nw #1#2;
+ {
+ \if_meaning:w 0 #2
+ \exp_after:wN \@@_ln_t_small:Nw
+ \else:
+ \exp_after:wN \@@_ln_t_large:NNw
+ \exp_after:wN -
+ \fi:
+ #1
+ }
+\cs_new:Npn \@@_ln_t_small:Nw #1 #2; #3; #4; #5; #6; #7;
+ {
+ \exp_after:wN \@@_ln_t_large:NNw
+ \exp_after:wN + % <sign>
+ \exp_after:wN #1
+ \int_value:w \@@_int_eval:w 9999 - #2 \exp_after:wN ;
+ \int_value:w \@@_int_eval:w 9999 - #3 \exp_after:wN ;
+ \int_value:w \@@_int_eval:w 9999 - #4 \exp_after:wN ;
+ \int_value:w \@@_int_eval:w 9999 - #5 \exp_after:wN ;
+ \int_value:w \@@_int_eval:w 9999 - #6 \exp_after:wN ;
+ \int_value:w \@@_int_eval:w 1 0000 - #7 ;
+ }
+% \end{macrocode}
+%
+% \begin{syntax}
+% \cs{@@_ln_t_large:NNw} \meta{sign} \meta{fixed tl}
+% ~~\meta{t_1}|;| \meta{t_2} |;| \meta{t_3}|;| \meta{t_4}|;| \meta{t_5} |;| \meta{t_6}|;|
+% ~~\meta{exponent} |;| \meta{continuation}
+% \end{syntax}
+% Compute the square $|t|^2$, and keep $|t|$ at the end with its
+% sign. We know that $|t|<0.1765$, so every piece has at most $4$
+% digits. However, since we were not careful in \cs{@@_ln_t_small:w},
+% they can have less than $4$ digits.
+% \begin{macrocode}
+\cs_new:Npn \@@_ln_t_large:NNw #1 #2 #3; #4; #5; #6; #7; #8;
+ {
+ \exp_after:wN \@@_ln_square_t_after:w
+ \int_value:w \@@_int_eval:w 9999 0000 + #3*#3
+ \exp_after:wN \@@_ln_square_t_pack:NNNNNw
+ \int_value:w \@@_int_eval:w 9999 0000 + 2*#3*#4
+ \exp_after:wN \@@_ln_square_t_pack:NNNNNw
+ \int_value:w \@@_int_eval:w 9999 0000 + 2*#3*#5 + #4*#4
+ \exp_after:wN \@@_ln_square_t_pack:NNNNNw
+ \int_value:w \@@_int_eval:w 9999 0000 + 2*#3*#6 + 2*#4*#5
+ \exp_after:wN \@@_ln_square_t_pack:NNNNNw
+ \int_value:w \@@_int_eval:w
+ 1 0000 0000 + 2*#3*#7 + 2*#4*#6 + #5*#5
+ + (2*#3*#8 + 2*#4*#7 + 2*#5*#6) / 1 0000
+ % ; ; ;
+ \exp_after:wN \@@_ln_twice_t_after:w
+ \int_value:w \@@_int_eval:w -1 + 2*#3
+ \exp_after:wN \@@_ln_twice_t_pack:Nw
+ \int_value:w \@@_int_eval:w 9999 + 2*#4
+ \exp_after:wN \@@_ln_twice_t_pack:Nw
+ \int_value:w \@@_int_eval:w 9999 + 2*#5
+ \exp_after:wN \@@_ln_twice_t_pack:Nw
+ \int_value:w \@@_int_eval:w 9999 + 2*#6
+ \exp_after:wN \@@_ln_twice_t_pack:Nw
+ \int_value:w \@@_int_eval:w 9999 + 2*#7
+ \exp_after:wN \@@_ln_twice_t_pack:Nw
+ \int_value:w \@@_int_eval:w 10000 + 2*#8 ; ;
+ { \@@_ln_c:NwNw #1 }
+ #2
+ }
+\cs_new:Npn \@@_ln_twice_t_pack:Nw #1 #2; { + #1 ; {#2} }
+\cs_new:Npn \@@_ln_twice_t_after:w #1; { ;;; {#1} }
+\cs_new:Npn \@@_ln_square_t_pack:NNNNNw #1 #2#3#4#5 #6;
+ { + #1#2#3#4#5 ; {#6} }
+\cs_new:Npn \@@_ln_square_t_after:w 1 0 #1#2#3 #4;
+ { \@@_ln_Taylor:wwNw {0#1#2#3} {#4} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_ln_Taylor:wwNw}
+% Denoting $T=t^2$, we get
+% \begin{syntax}
+% \cs{@@_ln_Taylor:wwNw}
+% ~~\Arg{T_1} \Arg{T_2} \Arg{T_3} \Arg{T_4} \Arg{T_5} \Arg{T_6} |;| |;|
+% ~~\Arg{(2t)_1} \Arg{(2t)_2} \Arg{(2t)_3} \Arg{(2t)_4} \Arg{(2t)_5} \Arg{(2t)_6} |;|
+% ~~|{| \cs{@@_ln_c:NwNw} \meta{sign} |}|
+% ~~\meta{fixed tl} \meta{exponent} |;| \meta{continuation}
+% \end{syntax}
+% And we want to compute
+% \[
+% \ln\left(\frac{1+t}{1-t}\right)
+% = 2t\left(1 + T \left(\frac{1}{3} + T \left(\frac{1}{5}
+% + T \left(\frac{1}{7} + T \left( \frac{1}{9} + \cdots
+% \right)\right)\right)\right)\right)
+% \]
+% The process looks as follows
+% \begin{verbatim}
+% \loop 5; A;
+% \div_int 5; 1.0; \add A; \mul T; {\loop \eval 5-2;}
+% \add 0.2; A; \mul T; {\loop \eval 5-2;}
+% \mul B; T; {\loop 3;}
+% \loop 3; C;
+% \end{verbatim}
+% ^^A todo: doc
+%
+% This uses the routine for dividing a number by a small integer
+% (${}<10^4$).
+% \begin{macrocode}
+\cs_new:Npn \@@_ln_Taylor:wwNw
+ { \@@_ln_Taylor_loop:www 21 ; {0000}{0000}{0000}{0000}{0000}{0000} ; }
+\cs_new:Npn \@@_ln_Taylor_loop:www #1; #2; #3;
+ {
+ \if_int_compare:w #1 = \c_one_int
+ \@@_ln_Taylor_break:w
+ \fi:
+ \exp_after:wN \@@_fixed_div_int:wwN \c_@@_one_fixed_tl #1;
+ \@@_fixed_add:wwn #2;
+ \@@_fixed_mul:wwn #3;
+ {
+ \exp_after:wN \@@_ln_Taylor_loop:www
+ \int_value:w \@@_int_eval:w #1 - 2 ;
+ }
+ #3;
+ }
+\cs_new:Npn \@@_ln_Taylor_break:w \fi: #1 \@@_fixed_add:wwn #2#3; #4 ;;
+ {
+ \fi:
+ \exp_after:wN \@@_fixed_mul:wwn
+ \exp_after:wN { \int_value:w \@@_int_eval:w 10000 + #2 } #3;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_ln_c:NwNw}
+% \begin{syntax}
+% \cs{@@_ln_c:NwNw} \meta{sign}
+% ~~\Arg{r_1} \Arg{r_2} \Arg{r_3} \Arg{r_4} \Arg{r_5} \Arg{r_6} |;|
+% ~~\meta{fixed tl} \meta{exponent} |;| \meta{continuation}
+% \end{syntax}
+% We are now reduced to finding $\ln(c)$ and $\meta{exponent}\ln(10)$
+% in a table, and adding it to the mixture. The first step is to
+% get $\ln(c) - \ln(x) = - \ln(a)$, then we get $|b|\ln(10)$ and add
+% or subtract.
+%
+% For now, $\ln(x)$ is given as $\cdot 10^0$. Unless both the exponent
+% is $1$ and $c=1$, we shift to working in units of $\cdot 10^4$,
+% since the final result is at least $\ln(10/7) \simeq 0.35$.
+% \begin{macrocode}
+\cs_new:Npn \@@_ln_c:NwNw #1 #2; #3
+ {
+ \if_meaning:w + #1
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_fixed_sub:wwn
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_fixed_add:wwn
+ \fi:
+ #3 #2 ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_ln_exponent:wn}
+% \begin{syntax}
+% \cs{@@_ln_exponent:wn}
+% ~~\Arg{s_1} \Arg{s_2} \Arg{s_3} \Arg{s_4} \Arg{s_5} \Arg{s_6} |;|
+% ~~\Arg{exponent}
+% \end{syntax}
+% Compute \meta{exponent} times $\ln(10)$. Apart from the cases where
+% \meta{exponent} is $0$ or $1$, the result is necessarily at
+% least $\ln(10) \simeq 2.3$ in magnitude. We can thus drop the least
+% significant $4$ digits. In the case of a very large (positive or
+% negative) exponent, we can (and we need to) drop $4$ additional
+% digits, since the result is of order $10^4$. Naively, one would
+% think that in both cases we can drop $4$ more digits than we do,
+% but that would be slightly too tight for rounding to happen correctly.
+% Besides, we already have addition and subtraction for $24$ digits
+% fixed point numbers.
+% \begin{macrocode}
+\cs_new:Npn \@@_ln_exponent:wn #1; #2
+ {
+ \if_case:w #2 \exp_stop_f:
+ 0 \@@_case_return:nw { \@@_fixed_to_float_o:Nw 2 }
+ \or:
+ \exp_after:wN \@@_ln_exponent_one:ww \int_value:w
+ \else:
+ \if_int_compare:w #2 > \c_zero_int
+ \exp_after:wN \@@_ln_exponent_small:NNww
+ \exp_after:wN 0
+ \exp_after:wN \@@_fixed_sub:wwn \int_value:w
+ \else:
+ \exp_after:wN \@@_ln_exponent_small:NNww
+ \exp_after:wN 2
+ \exp_after:wN \@@_fixed_add:wwn \int_value:w -
+ \fi:
+ \fi:
+ #2; #1;
+ }
+% \end{macrocode}
+% Now we painfully write all the cases.\footnote{Bruno: do rounding.}
+% No overflow nor underflow can happen, except when computing \texttt{ln(1)}.
+% \begin{macrocode}
+\cs_new:Npn \@@_ln_exponent_one:ww 1; #1;
+ {
+ 0
+ \exp_after:wN \@@_fixed_sub:wwn \c_@@_ln_x_fixed_tl #1;
+ \@@_fixed_to_float_o:wN 0
+ }
+% \end{macrocode}
+% For small exponents, we just drop one block of digits, and set the
+% exponent of the log to $4$ (minus any shift coming from leading zeros
+% in the conversion from fixed point to floating point). Note that here
+% the exponent has been made positive.
+% \begin{macrocode}
+\cs_new:Npn \@@_ln_exponent_small:NNww #1#2#3; #4#5#6#7#8#9;
+ {
+ 4
+ \exp_after:wN \@@_fixed_mul:wwn
+ \c_@@_ln_x_fixed_tl
+ {#3}{0000}{0000}{0000}{0000}{0000} ;
+ #2
+ {0000}{#4}{#5}{#6}{#7}{#8};
+ \@@_fixed_to_float_o:wN #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Exponential}
+%
+% \subsubsection{Sign, exponent, and special numbers}
+%
+% \begin{macro}[EXP]{\@@_exp_o:w}
+% \begin{macrocode}
+\cs_new:Npn \@@_exp_o:w #1 \s_@@ \@@_chk:w #2#3#4; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \@@_case_return_o:Nw \c_one_fp
+ \or:
+ \exp_after:wN \@@_exp_normal_o:w
+ \or:
+ \if_meaning:w 0 #3
+ \exp_after:wN \@@_case_return_o:Nw
+ \exp_after:wN \c_inf_fp
+ \else:
+ \exp_after:wN \@@_case_return_o:Nw
+ \exp_after:wN \c_zero_fp
+ \fi:
+ \or:
+ \@@_case_return_same_o:w
+ \fi:
+ \s_@@ \@@_chk:w #2#3#4;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_exp_normal_o:w, \@@_exp_pos_o:Nnwnw, \@@_exp_overflow:NN}
+% \begin{macrocode}
+\cs_new:Npn \@@_exp_normal_o:w \s_@@ \@@_chk:w 1#1
+ {
+ \if_meaning:w 0 #1
+ \@@_exp_pos_o:NNwnw + \@@_fixed_to_float_o:wN
+ \else:
+ \@@_exp_pos_o:NNwnw - \@@_fixed_inv_to_float_o:wN
+ \fi:
+ }
+\cs_new:Npn \@@_exp_pos_o:NNwnw #1#2#3 \fi: #4#5;
+ {
+ \fi:
+ \if_int_compare:w #4 > \c_@@_max_exp_exponent_int
+ \token_if_eq_charcode:NNTF + #1
+ { \@@_exp_overflow:NN \@@_overflow:w \c_inf_fp }
+ { \@@_exp_overflow:NN \@@_underflow:w \c_zero_fp }
+ \exp:w
+ \else:
+ \exp_after:wN \@@_sanitize:Nw
+ \exp_after:wN 0
+ \int_value:w #1 \@@_int_eval:w
+ \if_int_compare:w #4 < \c_zero_int
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ {
+ 0
+ \@@_decimate:nNnnnn { - #4 }
+ \@@_exp_Taylor:Nnnwn
+ }
+ {
+ \@@_decimate:nNnnnn { \c_@@_prec_int - #4 }
+ \@@_exp_pos_large:NnnNwn
+ }
+ #5
+ {#4}
+ #1 #2 0
+ \exp:w
+ \fi:
+ \exp_after:wN \exp_end:
+ }
+\cs_new:Npn \@@_exp_overflow:NN #1#2
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN #1
+ \exp_after:wN #2
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_exp_Taylor:Nnnwn}
+% \begin{macro}[EXP]{\@@_exp_Taylor_loop:www, \@@_exp_Taylor_break:Nww}
+% This function is called for numbers in the range $[10^{-9},
+% 10^{-1})$. We compute $10$ terms of the Taylor series. The
+% first argument is irrelevant (rounding digit used by some other
+% functions). The next three arguments, at least $16$ digits,
+% delimited by a semicolon, form a fixed point number, so we pack it
+% in blocks of $4$ digits.
+% \begin{macrocode}
+\cs_new:Npn \@@_exp_Taylor:Nnnwn #1#2#3 #4; #5 #6
+ {
+ #6
+ \@@_pack_twice_four:wNNNNNNNN
+ \@@_pack_twice_four:wNNNNNNNN
+ \@@_pack_twice_four:wNNNNNNNN
+ \@@_exp_Taylor_ii:ww
+ ; #2#3#4 0000 0000 ;
+ }
+\cs_new:Npn \@@_exp_Taylor_ii:ww #1; #2;
+ { \@@_exp_Taylor_loop:www 10 ; #1 ; #1 ; \s_@@_stop }
+\cs_new:Npn \@@_exp_Taylor_loop:www #1; #2; #3;
+ {
+ \if_int_compare:w #1 = \c_one_int
+ \exp_after:wN \@@_exp_Taylor_break:Nww
+ \fi:
+ \@@_fixed_div_int:wwN #3 ; #1 ;
+ \@@_fixed_add_one:wN
+ \@@_fixed_mul:wwn #2 ;
+ {
+ \exp_after:wN \@@_exp_Taylor_loop:www
+ \int_value:w \@@_int_eval:w #1 - 1 ;
+ #2 ;
+ }
+ }
+\cs_new:Npn \@@_exp_Taylor_break:Nww #1 #2; #3 \s_@@_stop
+ { \@@_fixed_add_one:wN #2 ; }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\c_@@_exp_intarray}
+% The integer array has $6\times 9\times 4=216$ items encoding the
+% values of $\exp(j\times 10^i)$ for $j=1,\dots,9$ and $i=-1,\dots,4$.
+% Each value is expressed as $\simeq 10^p \times 0.m_1m_2m_3$ with
+% three $8$-digit blocks $m_1$, $m_2$, $m_3$ and an integer
+% exponent~$p$ (one more than the scientific exponent), and these are
+% stored in the integer array as four items: $p$, $10^8+m_1$,
+% $10^8+m_2$, $10^8+m_3$. The various exponentials are stored in
+% increasing order of $j\times 10^i$.
+%
+% Storing this data in an integer array makes it slightly harder to
+% access (slower, too), but uses $16$ bytes of memory per exponential
+% stored, while storing as tokens used around $40$ tokens; tokens have
+% an especially large footprint in Unicode-aware engines.
+% \begin{macrocode}
+\intarray_const_from_clist:Nn \c_@@_exp_intarray
+ {
+ 1 , 1 1105 1709 , 1 1807 5647 , 1 6248 1171 ,
+ 1 , 1 1221 4027 , 1 5816 0169 , 1 8339 2107 ,
+ 1 , 1 1349 8588 , 1 0757 6003 , 1 1039 8374 ,
+ 1 , 1 1491 8246 , 1 9764 1270 , 1 3178 2485 ,
+ 1 , 1 1648 7212 , 1 7070 0128 , 1 1468 4865 ,
+ 1 , 1 1822 1188 , 1 0039 0508 , 1 9748 7537 ,
+ 1 , 1 2013 7527 , 1 0747 0476 , 1 5216 2455 ,
+ 1 , 1 2225 5409 , 1 2849 2467 , 1 6045 7954 ,
+ 1 , 1 2459 6031 , 1 1115 6949 , 1 6638 0013 ,
+ 1 , 1 2718 2818 , 1 2845 9045 , 1 2353 6029 ,
+ 1 , 1 7389 0560 , 1 9893 0650 , 1 2272 3043 ,
+ 2 , 1 2008 5536 , 1 9231 8766 , 1 7740 9285 ,
+ 2 , 1 5459 8150 , 1 0331 4423 , 1 9078 1103 ,
+ 3 , 1 1484 1315 , 1 9102 5766 , 1 0342 1116 ,
+ 3 , 1 4034 2879 , 1 3492 7351 , 1 2260 8387 ,
+ 4 , 1 1096 6331 , 1 5842 8458 , 1 5992 6372 ,
+ 4 , 1 2980 9579 , 1 8704 1728 , 1 2747 4359 ,
+ 4 , 1 8103 0839 , 1 2757 5384 , 1 0077 1000 ,
+ 5 , 1 2202 6465 , 1 7948 0671 , 1 6516 9579 ,
+ 9 , 1 4851 6519 , 1 5409 7902 , 1 7796 9107 ,
+ 14 , 1 1068 6474 , 1 5815 2446 , 1 2146 9905 ,
+ 18 , 1 2353 8526 , 1 6837 0199 , 1 8540 7900 ,
+ 22 , 1 5184 7055 , 1 2858 7072 , 1 4640 8745 ,
+ 27 , 1 1142 0073 , 1 8981 5684 , 1 2836 6296 ,
+ 31 , 1 2515 4386 , 1 7091 9167 , 1 0062 6578 ,
+ 35 , 1 5540 6223 , 1 8439 3510 , 1 0525 7117 ,
+ 40 , 1 1220 4032 , 1 9431 7840 , 1 8020 0271 ,
+ 44 , 1 2688 1171 , 1 4181 6135 , 1 4484 1263 ,
+ 87 , 1 7225 9737 , 1 6812 5749 , 1 2581 7748 ,
+ 131 , 1 1942 4263 , 1 9524 1255 , 1 9365 8421 ,
+ 174 , 1 5221 4696 , 1 8976 4143 , 1 9505 8876 ,
+ 218 , 1 1403 5922 , 1 1785 2837 , 1 4107 3977 ,
+ 261 , 1 3773 0203 , 1 0092 9939 , 1 8234 0143 ,
+ 305 , 1 1014 2320 , 1 5473 5004 , 1 5094 5533 ,
+ 348 , 1 2726 3745 , 1 7211 2566 , 1 5673 6478 ,
+ 391 , 1 7328 8142 , 1 2230 7421 , 1 7051 8866 ,
+ 435 , 1 1970 0711 , 1 1401 7046 , 1 9938 8888 ,
+ 869 , 1 3881 1801 , 1 9428 4368 , 1 5764 8232 ,
+ 1303 , 1 7646 2009 , 1 8905 4704 , 1 8893 1073 ,
+ 1738 , 1 1506 3559 , 1 7005 0524 , 1 9009 7592 ,
+ 2172 , 1 2967 6283 , 1 8402 3667 , 1 0689 6630 ,
+ 2606 , 1 5846 4389 , 1 5650 2114 , 1 7278 5046 ,
+ 3041 , 1 1151 7900 , 1 5080 6878 , 1 2914 4154 ,
+ 3475 , 1 2269 1083 , 1 0850 6857 , 1 8724 4002 ,
+ 3909 , 1 4470 3047 , 1 3316 5442 , 1 6408 6591 ,
+ 4343 , 1 8806 8182 , 1 2566 2921 , 1 5872 6150 ,
+ 8686 , 1 7756 0047 , 1 2598 6861 , 1 0458 3204 ,
+ 13029 , 1 6830 5723 , 1 7791 4884 , 1 1932 7351 ,
+ 17372 , 1 6015 5609 , 1 3095 3052 , 1 3494 7574 ,
+ 21715 , 1 5297 7951 , 1 6443 0315 , 1 3251 3576 ,
+ 26058 , 1 4665 6719 , 1 0099 3379 , 1 5527 2929 ,
+ 30401 , 1 4108 9724 , 1 3326 3186 , 1 5271 5665 ,
+ 34744 , 1 3618 6973 , 1 3140 0875 , 1 3856 4102 ,
+ 39087 , 1 3186 9209 , 1 6113 3900 , 1 6705 9685 ,
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_exp_pos_large:NnnNwn ,
+% \@@_exp_large_after:wwn ,
+% \@@_exp_large:NwN ,
+% \@@_exp_intarray:w ,
+% \@@_exp_intarray_aux:w ,
+% }
+% The first two arguments are irrelevant (a rounding digit, and a
+% brace group with $8$ zeros). The third argument is the integer part
+% of our number, then we have the decimal part delimited by a
+% semicolon, and finally the exponent, in the range $[0,5]$. Remove
+% leading zeros from the integer part: putting |#4| in there too
+% ensures that an integer part of $0$ is also removed. Then read
+% digits one by one, looking up $\exp(\meta{digit}\cdot
+% 10^{\meta{exponent}})$ in a table, and multiplying that to the
+% current total. The loop is done by \cs{@@_exp_large:NwN}, whose
+% |#1| is the \meta{exponent}, |#2| is the current mantissa, and |#3|
+% is the \meta{digit}. At the end, \cs{@@_exp_large_after:wwn} moves
+% on to the Taylor series, eventually multiplied with the mantissa
+% that we have just computed.
+% \begin{macrocode}
+\cs_new:Npn \@@_exp_pos_large:NnnNwn #1#2#3 #4#5; #6
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_exp_large:NwN
+ \exp_after:wN \exp_after:wN \exp_after:wN #6
+ \exp_after:wN \c_@@_one_fixed_tl
+ \int_value:w #3 #4 \exp_stop_f:
+ #5 00000 ;
+ }
+\cs_new:Npn \@@_exp_large:NwN #1#2; #3
+ {
+ \if_case:w #3 ~
+ \exp_after:wN \@@_fixed_continue:wn
+ \else:
+ \exp_after:wN \@@_exp_intarray:w
+ \int_value:w \@@_int_eval:w 36 * #1 + 4 * #3 \exp_after:wN ;
+ \fi:
+ #2;
+ {
+ \if_meaning:w 0 #1
+ \exp_after:wN \@@_exp_large_after:wwn
+ \else:
+ \exp_after:wN \@@_exp_large:NwN
+ \int_value:w \@@_int_eval:w #1 - 1 \exp_after:wN \scan_stop:
+ \fi:
+ }
+ }
+\cs_new:Npn \@@_exp_intarray:w #1 ;
+ {
+ +
+ \__kernel_intarray_item:Nn \c_@@_exp_intarray
+ { \@@_int_eval:w #1 - 3 \scan_stop: }
+ \exp_after:wN \use_i:nnn
+ \exp_after:wN \@@_fixed_mul:wwn
+ \int_value:w 0
+ \exp_after:wN \@@_exp_intarray_aux:w
+ \int_value:w \__kernel_intarray_item:Nn
+ \c_@@_exp_intarray { \@@_int_eval:w #1 - 2 }
+ \exp_after:wN \@@_exp_intarray_aux:w
+ \int_value:w \__kernel_intarray_item:Nn
+ \c_@@_exp_intarray { \@@_int_eval:w #1 - 1 }
+ \exp_after:wN \@@_exp_intarray_aux:w
+ \int_value:w \__kernel_intarray_item:Nn \c_@@_exp_intarray {#1} ; ;
+ }
+\cs_new:Npn \@@_exp_intarray_aux:w 1 #1#2#3#4#5 ; { ; {#1#2#3#4} {#5} }
+\cs_new:Npn \@@_exp_large_after:wwn #1; #2; #3
+ {
+ \@@_exp_Taylor:Nnnwn ? { } { } 0 #2; {} #3
+ \@@_fixed_mul:wwn #1;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Power}
+%
+% Raising a number $a$ to a power $b$ leads to many distinct situations.
+% \begin{center}\def\abs#1{\lvert #1\rvert}
+% \begin{tabular}{>{$}c<{$}|*8{>{$}l<{$}}}
+% a^b &-\infty &(-\infty,-0) &-\text{integer} &\pm 0 &+\text{integer} &(0,\infty) &+\infty &\nan \\ \hline
+% +\infty &+0 &\multicolumn{2}{c}{$+0$} &+1 &\multicolumn{2}{c}{$+\infty$} &+\infty &\nan \\
+% (1,\infty) &+0 &\multicolumn{2}{c}{$+\abs{a}^{b}$} &+1 &\multicolumn{2}{c}{$+\abs{a}^{b}$} &+\infty &\nan \\
+% +1 &+1 &\multicolumn{2}{c}{$+1$} &+1 &\multicolumn{2}{c}{$+1$} &+1 &+1 \\
+% (0,1) &+\infty &\multicolumn{2}{c}{$+\abs{a}^{b}$} &+1 &\multicolumn{2}{c}{$+\abs{a}^{b}$} &+0 &\nan \\
+% +0 &+\infty &\multicolumn{2}{c}{$+\infty$} &+1 &\multicolumn{2}{c}{$+0$} &+0 &\nan \\
+% -0 &+\infty &\nan &(-1)^b\infty &+1 &(-1)^b 0 &+0 &+0 &\nan \\
+% (-1,0) &+\infty &\nan &(-1)^b\abs{a}^{b} &+1 &(-1)^b\abs{a}^{b} &\nan &+0 &\nan \\
+% -1 &+1 &\nan &(-1)^b &+1 &(-1)^b &\nan &+1 &\nan \\
+% (-\infty,-1) &+0 &\nan &(-1)^b\abs{a}^{b} &+1 &(-1)^b\abs{a}^{b} &\nan &+\infty &\nan \\
+% -\infty &+0 &+0 &(-1)^b 0 &+1 &(-1)^b\infty &\nan &+\infty &\nan \\
+% \nan &\nan &\nan &\nan &+1 &\nan &\nan &\nan &\nan \\
+% \end{tabular}
+% \end{center}
+% We distinguished in this table the cases of finite (positive or
+% negative) integer exponents, as $(-1)^b$ is defined in that case.
+% One peculiarity of this operation is that $\nan^0 = 1^\nan = 1$,
+% because this relation is obeyed for any number, even $\pm\infty$.
+%
+% \begin{macro}[EXP]+\@@_^_o:ww+
+% We cram most of the tests into a single function to save csnames.
+% First treat the case $b=0$: $a^0=1$ for any $a$, even \texttt{nan}.
+% Then test the sign of $a$.
+% \begin{itemize}
+% \item If it is positive, and $a$ is a normal number, call
+% \cs{@@_pow_normal_o:ww} followed by the two \texttt{fp} $a$ and $b$.
+% For $a=+0$ or $+\inf$, call \cs{@@_pow_zero_or_inf:ww} instead, to
+% return either $+0$ or $+\infty$ as appropriate.
+% \item If $a$ is a \texttt{nan}, then skip to the next semicolon
+% (which happens to be conveniently the end of $b$) and return
+% \texttt{nan}.
+% \item Finally, if $a$ is negative, compute $|a|^b$
+% (\cs{@@_pow_normal_o:ww} which ignores the sign of its first
+% operand), and keep an extra copy of $a$ and $b$ (the second brace
+% group, containing \{~$b$~$a$~\}, is inserted between $a$ and $b$).
+% Then do some tests to find the final sign of the result if it
+% exists.
+% \end{itemize}
+% \begin{macrocode}
+\cs_new:cpn { @@_ \iow_char:N \^ _o:ww }
+ \s_@@ \@@_chk:w #1#2#3; \s_@@ \@@_chk:w #4#5#6;
+ {
+ \if_meaning:w 0 #4
+ \@@_case_return_o:Nw \c_one_fp
+ \fi:
+ \if_case:w #2 \exp_stop_f:
+ \exp_after:wN \use_i:nn
+ \or:
+ \@@_case_return_o:Nw \c_nan_fp
+ \else:
+ \exp_after:wN \@@_pow_neg:www
+ \exp:w \exp_end_continue_f:w \exp_after:wN \use:nn
+ \fi:
+ {
+ \if_meaning:w 1 #1
+ \exp_after:wN \@@_pow_normal_o:ww
+ \else:
+ \exp_after:wN \@@_pow_zero_or_inf:ww
+ \fi:
+ \s_@@ \@@_chk:w #1#2#3;
+ }
+ { \s_@@ \@@_chk:w #4#5#6; \s_@@ \@@_chk:w #1#2#3; }
+ \s_@@ \@@_chk:w #4#5#6;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_pow_zero_or_inf:ww}
+% Raising $-0$ or $-\infty$ to \texttt{nan} yields \texttt{nan}. For
+% other powers, the result is $+0$ if $0$ is raised to a positive
+% power or $\infty$ to a negative power, and $+\infty$ otherwise.
+% Thus, if the type of $a$ and the sign of $b$ coincide, the result
+% is~$0$, since those conveniently take the same possible values, $0$
+% and~$2$. Otherwise, either $a=\pm\infty$ and $b>0$ and the result
+% is $+\infty$, or $a=\pm 0$ with $b<0$ and we have a division by zero
+% unless $b=-\infty$.
+% \begin{macrocode}
+\cs_new:Npn \@@_pow_zero_or_inf:ww
+ \s_@@ \@@_chk:w #1#2; \s_@@ \@@_chk:w #3#4
+ {
+ \if_meaning:w 1 #4
+ \@@_case_return_same_o:w
+ \fi:
+ \if_meaning:w #1 #4
+ \@@_case_return_o:Nw \c_zero_fp
+ \fi:
+ \if_meaning:w 2 #1
+ \@@_case_return_o:Nw \c_inf_fp
+ \fi:
+ \if_meaning:w 2 #3
+ \@@_case_return_o:Nw \c_inf_fp
+ \else:
+ \@@_case_use:nw
+ {
+ \@@_division_by_zero_o:NNww \c_inf_fp ^
+ \s_@@ \@@_chk:w #1 #2 ;
+ }
+ \fi:
+ \s_@@ \@@_chk:w #3#4
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_pow_normal_o:ww}
+% We have in front of us $a$, and $b\neq 0$, we know that $a$ is a
+% normal number, and we wish to compute $\lvert a\rvert^{b}$. If
+% $\lvert a\rvert=1$, we return $1$, unless $a=-1$ and $b$ is
+% \texttt{nan}. Indeed, returning $1$ at this point would wrongly
+% raise \enquote{invalid} when the sign is considered. If $\lvert
+% a\rvert\neq 1$, test the type of $b$:
+% \begin{itemize}
+% \item[0] Impossible, we already filtered $b=\pm 0$.
+% \item[1] Call \cs{@@_pow_npos_o:Nww}.
+% \item[2] Return $+\infty$ or $+0$ depending on the sign of $b$ and
+% whether the exponent of $a$ is positive or not.
+% \item[3] Return $b$.
+% \end{itemize}
+% \begin{macrocode}
+\cs_new:Npn \@@_pow_normal_o:ww
+ \s_@@ \@@_chk:w 1 #1#2#3; \s_@@ \@@_chk:w #4#5
+ {
+ \if:w 0 \@@_str_if_eq:nn { #2 #3 } { 1 {1000} {0000} {0000} {0000} }
+ \if_int_compare:w #4 #1 = 32 \exp_stop_f:
+ \exp_after:wN \@@_case_return_ii_o:ww
+ \fi:
+ \@@_case_return_o:Nww \c_one_fp
+ \fi:
+ \if_case:w #4 \exp_stop_f:
+ \or:
+ \exp_after:wN \@@_pow_npos_o:Nww
+ \exp_after:wN #5
+ \or:
+ \if_meaning:w 2 #5 \exp_after:wN \reverse_if:N \fi:
+ \if_int_compare:w #2 > \c_zero_int
+ \exp_after:wN \@@_case_return_o:Nww
+ \exp_after:wN \c_inf_fp
+ \else:
+ \exp_after:wN \@@_case_return_o:Nww
+ \exp_after:wN \c_zero_fp
+ \fi:
+ \or:
+ \@@_case_return_ii_o:ww
+ \fi:
+ \s_@@ \@@_chk:w 1 #1 {#2} #3 ;
+ \s_@@ \@@_chk:w #4 #5
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% ^^A todo: check that we compute ln to 21 digits!
+% \begin{macro}[EXP]{\@@_pow_npos_o:Nww}
+% We now know that $a\neq\pm 1$ is a normal number, and $b$ is a
+% normal number too. We want to compute $\lvert a\rvert^{b} = (\lvert
+% x\rvert\cdot 10^{n})^{y\cdot 10^{p}} = \exp((\ln\lvert x\rvert + n
+% \ln(10))\cdot y \cdot 10^{p}) = \exp(z)$. To compute the
+% exponential accurately, we need to know the digits of $z$ up to the
+% $16$-th position. Since the exponential of $10^{5}$ is infinite, we
+% only need at most $21$ digits, hence the fixed point result of
+% \cs{@@_ln_o:w} is precise enough for our needs. Start an integer
+% expression for the decimal exponent of $e^{\lvert z\rvert}$. If $z$
+% is negative, negate that decimal exponent, and prepare to take the
+% inverse when converting from the fixed point to the floating point result.
+% \begin{macrocode}
+\cs_new:Npn \@@_pow_npos_o:Nww #1 \s_@@ \@@_chk:w 1#2#3
+ {
+ \exp_after:wN \@@_sanitize:Nw
+ \exp_after:wN 0
+ \int_value:w
+ \if:w #1 \if_int_compare:w #3 > \c_zero_int 0 \else: 2 \fi:
+ \exp_after:wN \@@_pow_npos_aux:NNnww
+ \exp_after:wN +
+ \exp_after:wN \@@_fixed_to_float_o:wN
+ \else:
+ \exp_after:wN \@@_pow_npos_aux:NNnww
+ \exp_after:wN -
+ \exp_after:wN \@@_fixed_inv_to_float_o:wN
+ \fi:
+ {#3}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%^^A begin[todo]
+% \begin{macro}[EXP]{\@@_pow_npos_aux:NNnww}
+% The first argument is the conversion function from fixed point to
+% float. Then comes an exponent and the $4$ brace groups of $x$,
+% followed by $b$. Compute $-\ln(x)$.
+% \begin{macrocode}
+\cs_new:Npn \@@_pow_npos_aux:NNnww #1#2#3#4#5; \s_@@ \@@_chk:w 1#6#7#8;
+ {
+ #1
+ \@@_int_eval:w
+ \@@_ln_significand:NNNNnnnN #4#5
+ \@@_pow_exponent:wnN {#3}
+ \@@_fixed_mul:wwn #8 {0000}{0000} ;
+ \@@_pow_B:wwN #7;
+ #1 #2 0 % fixed_to_float_o:wN
+ }
+\cs_new:Npn \@@_pow_exponent:wnN #1; #2
+ {
+ \if_int_compare:w #2 > \c_zero_int
+ \exp_after:wN \@@_pow_exponent:Nwnnnnnw % n\ln(10) - (-\ln(x))
+ \exp_after:wN +
+ \else:
+ \exp_after:wN \@@_pow_exponent:Nwnnnnnw % -(|n|\ln(10) + (-\ln(x)))
+ \exp_after:wN -
+ \fi:
+ #2; #1;
+ }
+\cs_new:Npn \@@_pow_exponent:Nwnnnnnw #1#2; #3#4#5#6#7#8;
+ { %^^A todo: use that in ln.
+ \exp_after:wN \@@_fixed_mul_after:wwn
+ \int_value:w \@@_int_eval:w \c_@@_leading_shift_int
+ \exp_after:wN \@@_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_middle_shift_int
+ #1#2*23025 - #1 #3
+ \exp_after:wN \@@_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_middle_shift_int
+ #1 #2*8509 - #1 #4
+ \exp_after:wN \@@_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_middle_shift_int
+ #1 #2*2994 - #1 #5
+ \exp_after:wN \@@_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_middle_shift_int
+ #1 #2*0456 - #1 #6
+ \exp_after:wN \@@_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_trailing_shift_int
+ #1 #2*8401 - #1 #7
+ #1 ( #2*7991 - #8 ) / 1 0000 ; ;
+ }
+\cs_new:Npn \@@_pow_B:wwN #1#2#3#4#5#6; #7;
+ {
+ \if_int_compare:w #7 < \c_zero_int
+ \exp_after:wN \@@_pow_C_neg:w \int_value:w -
+ \else:
+ \if_int_compare:w #7 < 22 \exp_stop_f:
+ \exp_after:wN \@@_pow_C_pos:w \int_value:w
+ \else:
+ \exp_after:wN \@@_pow_C_overflow:w \int_value:w
+ \fi:
+ \fi:
+ #7 \exp_after:wN ;
+ \int_value:w \@@_int_eval:w 10 0000 + #1 \@@_int_eval_end:
+ #2#3#4#5#6 0000 0000 0000 0000 0000 0000 ; %^^A todo: how many 0?
+ }
+\cs_new:Npn \@@_pow_C_overflow:w #1; #2; #3
+ {
+ + 2 * \c_@@_max_exponent_int
+ \exp_after:wN \@@_fixed_continue:wn \c_@@_one_fixed_tl
+ }
+\cs_new:Npn \@@_pow_C_neg:w #1 ; 1
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_pow_C_pack:w
+ \prg_replicate:nn {#1} {0}
+ }
+\cs_new:Npn \@@_pow_C_pos:w #1; 1
+ { \@@_pow_C_pos_loop:wN #1; }
+\cs_new:Npn \@@_pow_C_pos_loop:wN #1; #2
+ {
+ \if_meaning:w 0 #1
+ \exp_after:wN \@@_pow_C_pack:w
+ \exp_after:wN #2
+ \else:
+ \if_meaning:w 0 #2
+ \exp_after:wN \@@_pow_C_pos_loop:wN \int_value:w
+ \else:
+ \exp_after:wN \@@_pow_C_overflow:w \int_value:w
+ \fi:
+ \@@_int_eval:w #1 - 1 \exp_after:wN ;
+ \fi:
+ }
+\cs_new:Npn \@@_pow_C_pack:w
+ {
+ \exp_after:wN \@@_exp_large:NwN
+ \exp_after:wN 5
+ \c_@@_one_fixed_tl
+ }
+% \end{macrocode}
+% \end{macro}
+%^^A end[todo]
+%
+% \begin{macro}[EXP]{\@@_pow_neg:www, \@@_pow_neg_aux:wNN}
+% This function is followed by three floating point numbers: $|a|^b$,
+% $a\in[-\infty,-0]$, and $b$. If $b$ is an even integer (case $-1$),
+% $a^b=|a|^b$. If $b$ is an odd integer (case $0$), $a^b=-|a|^b$,
+% obtained by a call to \cs{@@_pow_neg_aux:wNN}. Otherwise, the sign is
+% undefined. This is invalid, unless $|a|^b$ turns out to be $+0$ or
+% \texttt{nan}, in which case we return that as $a^b$. In particular,
+% since the underflow detection occurs before \cs{@@_pow_neg:www} is
+% called, |(-0.1)**(12345.67)| gives $+0$ rather than complaining
+% that the sign is not defined.
+% \begin{macrocode}
+\cs_new:Npn \@@_pow_neg:www \s_@@ \@@_chk:w #1#2; #3; #4;
+ {
+ \if_case:w \@@_pow_neg_case:w #4 ;
+ \exp_after:wN \@@_pow_neg_aux:wNN
+ \or:
+ \if_int_compare:w \@@_int_eval:w #1 / 2 = \c_one_int
+ \@@_invalid_operation_o:Nww ^ #3; #4;
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_use_none_until_s:w
+ \fi:
+ \fi:
+ \@@_exp_after_o:w
+ \s_@@ \@@_chk:w #1#2;
+ }
+\cs_new:Npn \@@_pow_neg_aux:wNN #1 \s_@@ \@@_chk:w #2#3
+ {
+ \exp_after:wN \@@_exp_after_o:w
+ \exp_after:wN \s_@@
+ \exp_after:wN \@@_chk:w
+ \exp_after:wN #2
+ \int_value:w \@@_int_eval:w 2 - #3 \@@_int_eval_end:
+ }
+% \end{macrocode}
+% ^^A todo: is this \@@_exp_after_o:w necessary? Appropriate?
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_pow_neg_case:w, \@@_pow_neg_case_aux:nnnnn,
+% \@@_pow_neg_case_aux:Nnnw
+% }
+% This function expects a floating point number, and determines its
+% \enquote{parity}. It should be used after \cs{if_case:w} or in an
+% integer expression. It gives $-1$ if the number is an even integer,
+% $0$~if the number is an odd integer, and $1$~otherwise. Zeros and
+% $\pm\infty$ are even (because very large finite floating points are
+% even), while \texttt{nan} is a non-integer. The sign of normal
+% numbers is irrelevant to parity. After \cs{@@_decimate:nNnnnn} the
+% argument |#1| of \cs{@@_pow_neg_case_aux:Nnnw} is a rounding digit,
+% |0|~if and only if the number was an integer, and |#3| is the $8$
+% least significant digits of that integer.
+% \begin{macrocode}
+\cs_new:Npn \@@_pow_neg_case:w \s_@@ \@@_chk:w #1#2#3;
+ {
+ \if_case:w #1 \exp_stop_f:
+ -1
+ \or: \@@_pow_neg_case_aux:nnnnn #3
+ \or: -1
+ \else: 1
+ \fi:
+ \exp_stop_f:
+ }
+\cs_new:Npn \@@_pow_neg_case_aux:nnnnn #1#2#3#4#5
+ {
+ \if_int_compare:w #1 > \c_@@_prec_int
+ -1
+ \else:
+ \@@_decimate:nNnnnn { \c_@@_prec_int - #1 }
+ \@@_pow_neg_case_aux:Nnnw
+ {#2} {#3} {#4} {#5}
+ \fi:
+ }
+\cs_new:Npn \@@_pow_neg_case_aux:Nnnw #1#2#3#4 ;
+ {
+ \if_meaning:w 0 #1
+ \if_int_odd:w #3 \exp_stop_f:
+ 0
+ \else:
+ -1
+ \fi:
+ \else:
+ 1
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Factorial}
+%
+% \begin{variable}{\c_@@_fact_max_arg_int}
+% The maximum integer whose factorial fits in the exponent range is
+% $3248$, as $3249!\sim 10^{10000.8}$
+% \begin{macrocode}
+\int_const:Nn \c_@@_fact_max_arg_int { 3248 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_fact_o:w}
+% First detect $\pm 0$ and $+\infty$ and \texttt{nan}. Then note that
+% factorial of anything with a negative sign (except $-0$) is
+% undefined. Then call \cs{@@_small_int:wTF} to get an integer as the
+% argument, and start a loop. This is not the most efficient way of
+% computing the factorial, but it works all right. Of course we work
+% with $24$ digits instead of~$16$. It is easy to check that
+% computing factorials with this precision is enough.
+% \begin{macrocode}
+\cs_new:Npn \@@_fact_o:w #1 \s_@@ \@@_chk:w #2#3#4; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \@@_case_return_o:Nw \c_one_fp
+ \or:
+ \or:
+ \if_meaning:w 0 #3
+ \exp_after:wN \@@_case_return_same_o:w
+ \fi:
+ \or:
+ \@@_case_return_same_o:w
+ \fi:
+ \if_meaning:w 2 #3
+ \@@_case_use:nw { \@@_invalid_operation_o:fw { fact } }
+ \fi:
+ \@@_fact_pos_o:w
+ \s_@@ \@@_chk:w #2 #3 #4 ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_fact_pos_o:w, \@@_fact_int_o:w}
+% Then check the input is an integer, and call
+% \cs{@@_facorial_int_o:n} with that \texttt{int} as an argument. If
+% it's too big the factorial overflows. Otherwise call
+% \cs{@@_sanitize:Nw} with a positive sign marker~|0| and an integer
+% expression that will mop up any exponent in the calculation.
+% \begin{macrocode}
+\cs_new:Npn \@@_fact_pos_o:w #1;
+ {
+ \@@_small_int:wTF #1;
+ { \@@_fact_int_o:n }
+ { \@@_invalid_operation_o:fw { fact } #1; }
+ }
+\cs_new:Npn \@@_fact_int_o:n #1
+ {
+ \if_int_compare:w #1 > \c_@@_fact_max_arg_int
+ \@@_case_return:nw
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_overflow:w
+ \exp_after:wN \c_inf_fp
+ }
+ \fi:
+ \exp_after:wN \@@_sanitize:Nw
+ \exp_after:wN 0
+ \int_value:w \@@_int_eval:w
+ \@@_fact_loop_o:w #1 . 4 , { 1 } { } { } { } { } { } ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_fact_loop_o:w}
+% The loop receives an integer |#1| whose factorial we want to
+% compute, which we progressively decrement, and the result so far as
+% an extended-precision number |#2| in the form
+% \meta{exponent}|,|\meta{mantissa}|;|. The loop goes in steps of two
+% because we compute |#1*#1-1| as an integer expression (it must fit
+% since |#1| is at most $3248$), then multiply with the result so far.
+% We don't need to fill in most of the mantissa with zeros because
+% \cs{@@_ep_mul:wwwwn} first normalizes the extended precision number
+% to avoid loss of precision. When reaching a small enough number
+% simply use a table of factorials less than $10^8$. This limit is
+% chosen because the normalization step cannot deal with larger
+% integers.
+% \begin{macrocode}
+\cs_new:Npn \@@_fact_loop_o:w #1 . #2 ;
+ {
+ \if_int_compare:w #1 < 12 \exp_stop_f:
+ \@@_fact_small_o:w #1
+ \fi:
+ \exp_after:wN \@@_ep_mul:wwwwn
+ \exp_after:wN 4 \exp_after:wN ,
+ \exp_after:wN { \int_value:w \@@_int_eval:w #1 * (#1 - 1) }
+ { } { } { } { } { } ;
+ #2 ;
+ {
+ \exp_after:wN \@@_fact_loop_o:w
+ \int_value:w \@@_int_eval:w #1 - 2 .
+ }
+ }
+\cs_new:Npn \@@_fact_small_o:w #1 \fi: #2 ; #3 ; #4
+ {
+ \fi:
+ \exp_after:wN \@@_ep_mul:wwwwn
+ \exp_after:wN 4 \exp_after:wN ,
+ \exp_after:wN
+ {
+ \int_value:w
+ \if_case:w #1 \exp_stop_f:
+ 1 \or: 1 \or: 2 \or: 6 \or: 24 \or: 120 \or: 720 \or: 5040
+ \or: 40320 \or: 362880 \or: 3628800 \or: 39916800
+ \fi:
+ } { } { } { } { } { } ;
+ #3 ;
+ \@@_ep_to_float_o:wwN 0
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-extended.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-extended.dtx
new file mode 100644
index 00000000000..356c32b2328
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-extended.dtx
@@ -0,0 +1,1275 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-extended.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3fp-extended} module\\
+% Manipulating numbers with extended precision, for internal use^^A
+% }
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-extended} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% \subsection{Description of fixed point numbers}
+%
+% This module provides a few functions to manipulate positive floating
+% point numbers with extended precision ($24$ digits), but mostly
+% provides functions for fixed-point numbers with this precision ($24$
+% digits). Those are used in the computation of
+% Taylor series for the logarithm, exponential, and trigonometric
+% functions. Since we eventually only care about the $16$ first digits
+% of the final result, some of the calculations are not performed with
+% the full $24$-digit precision. In other words, the last two blocks of
+% each fixed point number may be wrong as long as the error is small
+% enough to be rounded away when converting back to a floating point
+% number. The fixed point numbers are expressed as
+% \begin{quote}
+% \Arg{a_1} \Arg{a_2} \Arg{a_3} \Arg{a_4} \Arg{a_5} \Arg{a_6} |;|
+% \end{quote}
+% where each \meta{a_i} is exactly $4$ digits (ranging from |0000| to
+% |9999|), except \meta{a_1}, which may be any \enquote{not-too-large}
+% non-negative integer, with or without leading zeros. Here,
+% \enquote{not-too-large} depends on the specific function (see the
+% corresponding comments for details). Checking for overflow is the
+% responsibility of the code calling those functions. The fixed point
+% number $a$ corresponding to the representation above is $a =
+% \sum_{i=1}^{6} \meta{a_i} \cdot 10^{-4i}$.
+%
+% Most functions we define here have the form
+% \begin{syntax}
+% \cs{@@_fixed_\meta{calculation}:wwn} \meta{operand_1} |;| \meta{operand_2} |;| \Arg{continuation}
+% \end{syntax}
+% They perform the \meta{calculation} on the two \meta{operands}, then
+% feed the result ($6$ brace groups followed by a semicolon) to the
+% \meta{continuation}, responsible for the next step of the calculation.
+% Some functions only accept an \texttt{N}-type \meta{continuation}.
+% This allows constructions such as
+% \begin{quote}
+% \cs{@@_fixed_add:wwn} \meta{X_1} |;| \meta{X_2} |;| \\
+% \cs{@@_fixed_mul:wwn} \meta{X_3} |;| \\
+% \cs{@@_fixed_add:wwn} \meta{X_4} |;| \\
+% \end{quote}
+% to compute $(X_1+X_2)\cdot X_3 + X_4$. This turns out to be very
+% appropriate for computing continued fractions and Taylor series.
+%
+% At the end of the calculation, the result is turned back to a floating
+% point number using \cs{@@_fixed_to_float_o:wN}. This function has to
+% change the exponent of the floating point number: it must be used
+% after starting an integer expression for the overall exponent of the
+% result.
+%
+% \subsection{Helpers for numbers with extended precision}
+%
+% \begin{variable}{\c_@@_one_fixed_tl}
+% The fixed-point number~$1$, used in \pkg{l3fp-expo}.
+% \begin{macrocode}
+\tl_const:Nn \c_@@_one_fixed_tl
+ { {10000} {0000} {0000} {0000} {0000} {0000} ; }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_fixed_continue:wn}
+% This function simply calls the next function.
+% \begin{macrocode}
+\cs_new:Npn \@@_fixed_continue:wn #1; #2 { #2 #1; }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_fixed_add_one:wN}
+% \begin{syntax}
+% \cs{@@_fixed_add_one:wN} \meta{a} |;| \meta{continuation}
+% \end{syntax}
+% This function adds $1$ to the fixed point \meta{a}, by changing
+% $a_1$ to $10000+a_1$, then calls the \meta{continuation}. This
+% requires $a_1 + 10000 < 2^{31}$.
+% \begin{macrocode}
+\cs_new:Npn \@@_fixed_add_one:wN #1#2; #3
+ {
+ \exp_after:wN #3 \exp_after:wN
+ { \int_value:w \@@_int_eval:w \c_@@_myriad_int + #1 } #2 ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_fixed_div_myriad:wn}
+% Divide a fixed point number by $10000$. This is a little bit more
+% subtle than just removing the last group and adding a leading group
+% of zeros: the first group~|#1| may have any number of digits, and we
+% must split~|#1| into the new first group and a second group of
+% exactly $4$~digits. The choice of shifts allows~|#1| to be in the
+% range $[0, 5\cdot 10^{8}-1]$.
+% \begin{macrocode}
+\cs_new:Npn \@@_fixed_div_myriad:wn #1#2#3#4#5#6;
+ {
+ \exp_after:wN \@@_fixed_mul_after:wwn
+ \int_value:w \@@_int_eval:w \c_@@_leading_shift_int
+ \exp_after:wN \@@_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_trailing_shift_int
+ + #1 ; {#2}{#3}{#4}{#5};
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_fixed_mul_after:wwn}
+% The fixed point operations which involve multiplication end by
+% calling this auxiliary. It braces the last block of digits, and
+% places the \meta{continuation} |#3| in front.
+% \begin{macrocode}
+\cs_new:Npn \@@_fixed_mul_after:wwn #1; #2; #3 { #3 {#1} #2; }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Multiplying a fixed point number by a short one}
+%
+% \begin{macro}[EXP]{\@@_fixed_mul_short:wwn}
+% \begin{syntax}\parskip=0pt\obeylines
+% \cs{@@_fixed_mul_short:wwn}
+% | |\Arg{a_1} \Arg{a_2} \Arg{a_3} \Arg{a_4} \Arg{a_5} \Arg{a_6} |;|
+% | |\Arg{b_0} \Arg{b_1} \Arg{b_2} |;| \Arg{continuation}
+% \end{syntax}
+% Computes the product $c=ab$ of $a=\sum_i \meta{a_i} 10^{-4i}$ and
+% $b=\sum_i \meta{b_i} 10^{-4i}$, rounds it to the closest multiple of
+% $10^{-24}$, and leaves \meta{continuation} \Arg{c_1} \ldots{}
+% \Arg{c_6} |;| in the input stream, where each of the \meta{c_i} are
+% blocks of $4$~digits, except \meta{c_1}, which is any \TeX{}
+% integer. Note that indices for \meta{b} start at~$0$: for instance
+% a second operand of |{0001}{0000}{0000}| leaves the first operand
+% unchanged (rather than dividing it by $10^{4}$, as
+% \cs{@@_fixed_mul:wwn} would).
+% \begin{macrocode}
+\cs_new:Npn \@@_fixed_mul_short:wwn #1#2#3#4#5#6; #7#8#9;
+ {
+ \exp_after:wN \@@_fixed_mul_after:wwn
+ \int_value:w \@@_int_eval:w \c_@@_leading_shift_int
+ + #1*#7
+ \exp_after:wN \@@_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_middle_shift_int
+ + #1*#8 + #2*#7
+ \exp_after:wN \@@_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_middle_shift_int
+ + #1*#9 + #2*#8 + #3*#7
+ \exp_after:wN \@@_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_middle_shift_int
+ + #2*#9 + #3*#8 + #4*#7
+ \exp_after:wN \@@_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_middle_shift_int
+ + #3*#9 + #4*#8 + #5*#7
+ \exp_after:wN \@@_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_trailing_shift_int
+ + #4*#9 + #5*#8 + #6*#7
+ + ( #5*#9 + #6*#8 + #6*#9 / \c_@@_myriad_int )
+ / \c_@@_myriad_int ; ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Dividing a fixed point number by a small integer}
+%
+% \begin{macro}[EXP]{\@@_fixed_div_int:wwN}
+% \begin{macro}[EXP]
+% {
+% \@@_fixed_div_int:wnN, \@@_fixed_div_int_auxi:wnn,
+% \@@_fixed_div_int_auxii:wnn, \@@_fixed_div_int_pack:Nw,
+% \@@_fixed_div_int_after:Nw
+% }
+% \begin{syntax}
+% \cs{@@_fixed_div_int:wwN} \meta{a} |;| \meta{n} |;| \meta{continuation}
+% \end{syntax}
+% Divides the fixed point number \meta{a} by the (small) integer
+% $0<\meta{n}<10^4$ and feeds the result to the \meta{continuation}.
+% There is no bound on $a_1$.
+%
+% The arguments of the \texttt{i} auxiliary are 1: one of the $a_{i}$,
+% 2: $n$, 3: the \texttt{ii} or the \texttt{iii} auxiliary. It
+% computes a (somewhat tight) lower bound $Q_{i}$ for the ratio
+% $a_{i}/n$.
+%
+% The \texttt{ii} auxiliary receives $Q_{i}$, $n$, and $a_{i}$ as
+% arguments. It adds $Q_{i}$ to a surrounding integer expression, and
+% starts a new one with the initial value $9999$, which ensures that
+% the result of this expression has $5$ digits. The auxiliary
+% also computes $a_{i}-n\cdot Q_{i}$, placing the result in front of
+% the $4$ digits of $a_{i+1}$. The resulting $a'_{i+1} = 10^{4}
+% (a_{i} - n \cdot Q_{i}) + a_{i+1}$ serves as the first argument for
+% a new call to the \texttt{i} auxiliary.
+%
+% When the \texttt{iii} auxiliary is called, the situation looks like
+% this:
+% \begin{quote}
+% \cs{@@_fixed_div_int_after:Nw} \meta{continuation} \\
+% $-1 + Q_{1}$ \\
+% \cs{@@_fixed_div_int_pack:Nw} $9999 + Q_{2}$ \\
+% \cs{@@_fixed_div_int_pack:Nw} $9999 + Q_{3}$ \\
+% \cs{@@_fixed_div_int_pack:Nw} $9999 + Q_{4}$ \\
+% \cs{@@_fixed_div_int_pack:Nw} $9999 + Q_{5}$ \\
+% \cs{@@_fixed_div_int_pack:Nw} $9999$ \\
+% \cs{@@_fixed_div_int_auxii:wnn} $Q_{6}$ |;| \Arg{n} \Arg{a_{6}}
+% \end{quote}
+% where expansion is happening from the last line up. The
+% \texttt{iii} auxiliary adds $Q_{6} + 2 \simeq a_{6}/n + 1$ to the
+% last $9999$, giving the integer closest to $10000 + a_{6}/n$.
+%
+% Each \texttt{pack} auxiliary receives $5$ digits followed by a
+% semicolon. The first digit is added as a carry to the integer
+% expression above, and the $4$ other digits are braced. Each call to
+% the \texttt{pack} auxiliary thus produces one brace group. The last
+% brace group is produced by the \texttt{after} auxiliary, which
+% places the \meta{continuation} as appropriate.
+% \begin{macrocode}
+\cs_new:Npn \@@_fixed_div_int:wwN #1#2#3#4#5#6 ; #7 ; #8
+ {
+ \exp_after:wN \@@_fixed_div_int_after:Nw
+ \exp_after:wN #8
+ \int_value:w \@@_int_eval:w - 1
+ \@@_fixed_div_int:wnN
+ #1; {#7} \@@_fixed_div_int_auxi:wnn
+ #2; {#7} \@@_fixed_div_int_auxi:wnn
+ #3; {#7} \@@_fixed_div_int_auxi:wnn
+ #4; {#7} \@@_fixed_div_int_auxi:wnn
+ #5; {#7} \@@_fixed_div_int_auxi:wnn
+ #6; {#7} \@@_fixed_div_int_auxii:wnn ;
+ }
+\cs_new:Npn \@@_fixed_div_int:wnN #1; #2 #3
+ {
+ \exp_after:wN #3
+ \int_value:w \@@_int_eval:w #1 / #2 - 1 ;
+ {#2}
+ {#1}
+ }
+\cs_new:Npn \@@_fixed_div_int_auxi:wnn #1; #2 #3
+ {
+ + #1
+ \exp_after:wN \@@_fixed_div_int_pack:Nw
+ \int_value:w \@@_int_eval:w 9999
+ \exp_after:wN \@@_fixed_div_int:wnN
+ \int_value:w \@@_int_eval:w #3 - #1*#2 \@@_int_eval_end:
+ }
+\cs_new:Npn \@@_fixed_div_int_auxii:wnn #1; #2 #3 { + #1 + 2 ; }
+\cs_new:Npn \@@_fixed_div_int_pack:Nw #1 #2; { + #1; {#2} }
+\cs_new:Npn \@@_fixed_div_int_after:Nw #1 #2; { #1 {#2} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Adding and subtracting fixed points}
+%
+% \begin{macro}[EXP]{\@@_fixed_add:wwn, \@@_fixed_sub:wwn}
+% \begin{macro}[EXP]
+% {
+% \@@_fixed_add:Nnnnnwnn,
+% \@@_fixed_add:nnNnnnwn,
+% \@@_fixed_add_pack:NNNNNwn,
+% \@@_fixed_add_after:NNNNNwn
+% }
+% \begin{syntax}
+% \cs{@@_fixed_add:wwn} \meta{a} |;| \meta{b} |;| \Arg{continuation}
+% \end{syntax}
+% Computes $a+b$ (resp.\ $a-b$) and feeds the result to the
+% \meta{continuation}. This function requires $0\leq a_{1},b_{1}\leq
+% 114748$, its result must be positive (this happens automatically for
+% addition) and its first group must have at most~$5$ digits: $(a\pm
+% b)_{1}<100000$. The two functions only differ by
+% a sign, hence use a common auxiliary. It would be nice to grab the
+% $12$ brace groups in one go; only $9$ parameters are allowed. Start
+% by grabbing the sign, $a_{1}, \ldots, a_{4}$, the rest of $a$,
+% and $b_{1}$ and $b_{2}$. The second auxiliary receives the rest of
+% $a$, the sign multiplying $b$, the rest of $b$, and the
+% \meta{continuation} as arguments. After going down through the
+% various level, we go back up, packing digits and bringing the
+% \meta{continuation} (|#8|, then |#7|) from the end of the argument
+% list to its start.
+% \begin{macrocode}
+\cs_new:Npn \@@_fixed_add:wwn { \@@_fixed_add:Nnnnnwnn + }
+\cs_new:Npn \@@_fixed_sub:wwn { \@@_fixed_add:Nnnnnwnn - }
+\cs_new:Npn \@@_fixed_add:Nnnnnwnn #1 #2#3#4#5 #6; #7#8
+ {
+ \exp_after:wN \@@_fixed_add_after:NNNNNwn
+ \int_value:w \@@_int_eval:w 9 9999 9998 + #2#3 #1 #7#8
+ \exp_after:wN \@@_fixed_add_pack:NNNNNwn
+ \int_value:w \@@_int_eval:w 1 9999 9998 + #4#5
+ \@@_fixed_add:nnNnnnwn #6 #1
+ }
+\cs_new:Npn \@@_fixed_add:nnNnnnwn #1#2 #3 #4#5 #6#7 ; #8
+ {
+ #3 #4#5
+ \exp_after:wN \@@_fixed_add_pack:NNNNNwn
+ \int_value:w \@@_int_eval:w 2 0000 0000 #3 #6#7 + #1#2 ; {#8} ;
+ }
+\cs_new:Npn \@@_fixed_add_pack:NNNNNwn #1 #2#3#4#5 #6; #7
+ { + #1 ; {#7} {#2#3#4#5} {#6} }
+\cs_new:Npn \@@_fixed_add_after:NNNNNwn 1 #1 #2#3#4#5 #6; #7
+ { #7 {#1#2#3#4#5} {#6} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Multiplying fixed points}
+%
+% ^^A todo: may a_1 or b_1 be = 10000? Used in ediv_epsi later.
+% \begin{macro}[EXP]{\@@_fixed_mul:wwn}
+% \begin{macro}[EXP]{\@@_fixed_mul:nnnnnnnw}
+% \begin{syntax}
+% \cs{@@_fixed_mul:wwn} \meta{a} |;| \meta{b} |;| \Arg{continuation}
+% \end{syntax}
+% Computes $a\times b$ and feeds the result to \meta{continuation}.
+% This function requires $0\leq a_{1}, b_{1} < 10000$. Once more, we
+% need to play around the limit of $9$ arguments for \TeX{} macros.
+% Note that we don't need to obtain an exact rounding, contrarily to
+% the |*| operator, so things could be harder. We wish to perform
+% carries in
+% \begin{align*}
+% a \times b =
+% & a_{1} \cdot b_{1} \cdot 10^{-8} \\
+% & + (a_{1} \cdot b_{2} + a_{2} \cdot b_{1}) \cdot 10^{-12} \\
+% & + (a_{1} \cdot b_{3} + a_{2} \cdot b_{2}
+% + a_{3} \cdot b_{1}) \cdot 10^{-16} \\
+% & + (a_{1} \cdot b_{4} + a_{2} \cdot b_{3}
+% + a_{3} \cdot b_{2} + a_{4} \cdot b_{1}) \cdot 10^{-20} \\
+% & + \Bigl(a_{2} \cdot b_{4} + a_{3} \cdot b_{3} + a_{4} \cdot b_{2}
+% \\ & \qquad
+% + \frac{a_{3} \cdot b_{4} + a_{4} \cdot b_{3}
+% + a_{1} \cdot b_{6} + a_{2} \cdot b_{5}
+% + a_{5} \cdot b_{2} + a_{6} \cdot b_{1}}{10^{4}}
+% \\ & \qquad
+% + a_{1} \cdot b_{5} + a_{5} \cdot b_{1}\Bigr) \cdot 10^{-24}
+% + O(10^{-24}),
+% \end{align*}
+% where the $O(10^{-24})$ stands for terms which are at most $5\cdot
+% 10^{-24}$; ignoring those leads to an error of at most
+% $5$~\texttt{ulp}. Note how the first $15$~terms only depend on
+% $a_{1},\ldots{},a_{4}$ and $b_{1},\ldots,b_{4}$, while the last
+% $6$~terms only depend on $a_{1},a_{2},a_{5},a_{6}$, and the
+% corresponding parts of~$b$. Hence, the first function grabs
+% $a_{1},\ldots,a_{4}$, the rest of $a$, and $b_{1},\ldots,b_{4}$, and
+% writes the $15$ first terms of the expression, including a left
+% parenthesis for the fraction. The \texttt{i} auxiliary receives
+% $a_{5}$, $a_{6}$, $b_{1}$, $b_{2}$, $a_{1}$, $a_{2}$, $b_{5}$,
+% $b_{6}$ and finally the \meta{continuation} as arguments. It writes
+% the end of the expression, including the right parenthesis and the
+% denominator of the fraction. The \meta{continuation}
+% is finally placed in front of the $6$ brace groups by
+% \cs{@@_fixed_mul_after:wwn}.
+% \begin{macrocode}
+\cs_new:Npn \@@_fixed_mul:wwn #1#2#3#4 #5; #6#7#8#9
+ {
+ \exp_after:wN \@@_fixed_mul_after:wwn
+ \int_value:w \@@_int_eval:w \c_@@_leading_shift_int
+ \exp_after:wN \@@_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_middle_shift_int
+ + #1*#6
+ \exp_after:wN \@@_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_middle_shift_int
+ + #1*#7 + #2*#6
+ \exp_after:wN \@@_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_middle_shift_int
+ + #1*#8 + #2*#7 + #3*#6
+ \exp_after:wN \@@_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_middle_shift_int
+ + #1*#9 + #2*#8 + #3*#7 + #4*#6
+ \exp_after:wN \@@_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_trailing_shift_int
+ + #2*#9 + #3*#8 + #4*#7
+ + ( #3*#9 + #4*#8
+ + \@@_fixed_mul:nnnnnnnw #5 {#6}{#7} {#1}{#2}
+ }
+\cs_new:Npn \@@_fixed_mul:nnnnnnnw #1#2 #3#4 #5#6 #7#8 ;
+ {
+ #1*#4 + #2*#3 + #5*#8 + #6*#7 ) / \c_@@_myriad_int
+ + #1*#3 + #5*#7 ; ;
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Combining product and sum of fixed points}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_fixed_mul_add:wwwn,
+% \@@_fixed_mul_sub_back:wwwn,
+% \@@_fixed_mul_one_minus_mul:wwn,
+% }
+% \begin{syntax}
+% \cs{@@_fixed_mul_add:wwwn} \meta{a} |;| \meta{b} |;| \meta{c} |;| \Arg{continuation}
+% \cs{@@_fixed_mul_sub_back:wwwn} \meta{a} |;| \meta{b} |;| \meta{c} |;| \Arg{continuation}
+% \cs{@@_fixed_one_minus_mul:wwn} \meta{a} |;| \meta{b} |;| \Arg{continuation}
+% \end{syntax}
+% Sometimes called |FMA| (fused multiply-add), these functions
+% compute $a\times b + c$, $c - a\times b$, and $1 - a\times b$ and
+% feed the result to the \meta{continuation}. Those functions require
+% $0\leq a_{1}, b_{1}, c_{1} \leq 10000$. Since those functions are
+% at the heart of the computation of Taylor expansions, we
+% over-optimize them a bit, and in particular we do not factor out the
+% common parts of the three functions.
+%
+% For definiteness, consider the task of computing $a\times b + c$.
+% We perform carries in
+% \begin{align*}
+% a \times b + c =
+% & (a_{1} \cdot b_{1} + c_{1} c_{2})\cdot 10^{-8} \\
+% & + (a_{1} \cdot b_{2} + a_{2} \cdot b_{1}) \cdot 10^{-12} \\
+% & + (a_{1} \cdot b_{3} + a_{2} \cdot b_{2} + a_{3} \cdot b_{1}
+% + c_{3} c_{4}) \cdot 10^{-16} \\
+% & + (a_{1} \cdot b_{4} + a_{2} \cdot b_{3} + a_{3} \cdot b_{2}
+% + a_{4} \cdot b_{1}) \cdot 10^{-20} \\
+% & + \Big(a_{2} \cdot b_{4} + a_{3} \cdot b_{3} + a_{4} \cdot b_{2}
+% \\ & \qquad
+% + \frac{a_{3} \cdot b_{4} + a_{4} \cdot b_{3}
+% + a_{1} \cdot b_{6} + a_{2} \cdot b_{5}
+% + a_{5} \cdot b_{2} + a_{6} \cdot b_{1}}{10^{4}}
+% \\ & \qquad
+% + a_{1} \cdot b_{5} + a_{5} \cdot b_{1}
+% + c_{5} c_{6} \Big) \cdot 10^{-24}
+% + O(10^{-24}),
+% \end{align*}
+% where $c_{1} c_{2}$, $c_{3} c_{4}$, $c_{5} c_{6}$ denote the
+% $8$-digit number obtained by juxtaposing the two blocks of digits of
+% $c$, and $\cdot$ denotes multiplication. The task is obviously
+% tough because we have $18$ brace groups in front of us.
+%
+% Each of the three function starts the first two levels (the first,
+% corresponding to $10^{-4}$, is empty), with $c_{1} c_{2}$ in the
+% first level, calls the \texttt{i} auxiliary with arguments described
+% later, and adds a trailing ${} + c_{5}c_{6}$ |;|
+% \Arg{continuation}~|;|. The ${} + c_{5}c_{6}$ piece, which is
+% omitted for \cs{@@_fixed_one_minus_mul:wwn}, is taken in the
+% integer expression for the $10^{-24}$ level.
+% \begin{macrocode}
+\cs_new:Npn \@@_fixed_mul_add:wwwn #1; #2; #3#4#5#6#7#8;
+ {
+ \exp_after:wN \@@_fixed_mul_after:wwn
+ \int_value:w \@@_int_eval:w \c_@@_big_leading_shift_int
+ \exp_after:wN \@@_pack_big:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_big_middle_shift_int + #3 #4
+ \@@_fixed_mul_add:Nwnnnwnnn +
+ + #5 #6 ; #2 ; #1 ; #2 ; +
+ + #7 #8 ; ;
+ }
+\cs_new:Npn \@@_fixed_mul_sub_back:wwwn #1; #2; #3#4#5#6#7#8;
+ {
+ \exp_after:wN \@@_fixed_mul_after:wwn
+ \int_value:w \@@_int_eval:w \c_@@_big_leading_shift_int
+ \exp_after:wN \@@_pack_big:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_big_middle_shift_int + #3 #4
+ \@@_fixed_mul_add:Nwnnnwnnn -
+ + #5 #6 ; #2 ; #1 ; #2 ; -
+ + #7 #8 ; ;
+ }
+\cs_new:Npn \@@_fixed_one_minus_mul:wwn #1; #2;
+ {
+ \exp_after:wN \@@_fixed_mul_after:wwn
+ \int_value:w \@@_int_eval:w \c_@@_big_leading_shift_int
+ \exp_after:wN \@@_pack_big:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_big_middle_shift_int +
+ 1 0000 0000
+ \@@_fixed_mul_add:Nwnnnwnnn -
+ ; #2 ; #1 ; #2 ; -
+ ; ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_fixed_mul_add:Nwnnnwnnn}
+% \begin{syntax}
+% \cs{@@_fixed_mul_add:Nwnnnwnnn} \meta{op} |+| \meta{c_3} \meta{c_4} |;|
+% ~~\meta{b} |;| \meta{a} |;| \meta{b} |;| \meta{op}
+% ~~|+| \meta{c_5} \meta{c_6} |;|
+% \end{syntax}
+% Here, \meta{op} is either |+| or |-|. Arguments |#3|, |#4|, |#5|
+% are \meta{b_1}, \meta{b_2}, \meta{b_3}; arguments |#7|, |#8|, |#9|
+% are \meta{a_1}, \meta{a_2}, \meta{a_3}. We can build three levels:
+% $a_{1} \cdot b_{1}$ for $10^{-8}$, $(a_{1} \cdot b_{2} + a_{2} \cdot
+% b_{1})$ for $10^{-12}$, and $(a_{1} \cdot b_{3} + a_{2} \cdot b_{2}
+% + a_{3} \cdot b_{1} + c_{3} c_{4})$ for $10^{-16}$. The $a$--$b$
+% products use the sign |#1|. Note that |#2| is empty for
+% \cs{@@_fixed_one_minus_mul:wwn}. We call the \texttt{ii} auxiliary
+% for levels $10^{-20}$ and $10^{-24}$, keeping the pieces of \meta{a}
+% we've read, but not \meta{b}, since there is another copy later in
+% the input stream.
+% \begin{macrocode}
+\cs_new:Npn \@@_fixed_mul_add:Nwnnnwnnn #1 #2; #3#4#5#6; #7#8#9
+ {
+ #1 #7*#3
+ \exp_after:wN \@@_pack_big:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_big_middle_shift_int
+ #1 #7*#4 #1 #8*#3
+ \exp_after:wN \@@_pack_big:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_big_middle_shift_int
+ #1 #7*#5 #1 #8*#4 #1 #9*#3 #2
+ \exp_after:wN \@@_pack_big:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_big_middle_shift_int
+ #1 \@@_fixed_mul_add:nnnnwnnnn {#7}{#8}{#9}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_fixed_mul_add:nnnnwnnnn}
+% \begin{syntax}
+% \cs{@@_fixed_mul_add:nnnnwnnnn} \meta{a} |;| \meta{b} |;| \meta{op}
+% ~~|+| \meta{c_5} \meta{c_6} |;|
+% \end{syntax}
+% Level $10^{-20}$ is $(a_{1} \cdot b_{4} + a_{2} \cdot b_{3} + a_{3}
+% \cdot b_{2} + a_{4} \cdot b_{1})$, multiplied by the sign, which was
+% inserted by the \texttt{i} auxiliary. Then we prepare level
+% $10^{-24}$. We don't have access to all parts of \meta{a} and
+% \meta{b} needed to make all products. Instead, we prepare the
+% partial expressions
+% \begin{align*}
+% & b_{1} + a_{4} \cdot b_{2} + a_{3} \cdot b_{3} + a_{2} \cdot b_{4} + a_{1} \\
+% & b_{2} + a_{4} \cdot b_{3} + a_{3} \cdot b_{4} + a_{2} .
+% \end{align*}
+% Obviously, those expressions make no mathematical sense: we
+% complete them with $a_{5} \cdot {}$ and ${} \cdot b_{5}$, and with
+% $a_{6} \cdot b_{1} + a_{5} \cdot {}$ and ${} \cdot b_{5} + a_{1}
+% \cdot b_{6}$, and of course with the trailing ${} + c_{5} c_{6}$.
+% To do all this, we keep $a_{1}$, $a_{5}$, $a_{6}$, and the
+% corresponding pieces of \meta{b}.
+% \begin{macrocode}
+\cs_new:Npn \@@_fixed_mul_add:nnnnwnnnn #1#2#3#4#5; #6#7#8#9
+ {
+ ( #1*#9 + #2*#8 + #3*#7 + #4*#6 )
+ \exp_after:wN \@@_pack_big:NNNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_big_trailing_shift_int
+ \@@_fixed_mul_add:nnnnwnnwN
+ { #6 + #4*#7 + #3*#8 + #2*#9 + #1 }
+ { #7 + #4*#8 + #3*#9 + #2 }
+ {#1} #5;
+ {#6}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_fixed_mul_add:nnnnwnnwN}
+% \begin{syntax}
+% \cs{@@_fixed_mul_add:nnnnwnnwN} \Arg{partial_1} \Arg{partial_2}
+% ~~\Arg{a_1} \Arg{a_5} \Arg{a_6} |;| \Arg{b_1} \Arg{b_5} \Arg{b_6} |;|
+% ~~\meta{op} |+| \meta{c_5} \meta{c_6} |;|
+% \end{syntax}
+% Complete the \meta{partial_1} and \meta{partial_2} expressions as
+% explained for the \texttt{ii} auxiliary. The second one is divided
+% by $10000$: this is the carry from level $10^{-28}$. The trailing
+% ${} + c_{5} c_{6}$ is taken into the expression for level
+% $10^{-24}$. Note that the total of level $10^{-24}$ is in the
+% interval $[-5\cdot 10^{8}, 6\cdot 10^{8}$ (give or take a couple of
+% $10000$), hence adding it to the shift gives a $10$-digit number, as
+% expected by the packing auxiliaries. See \pkg{l3fp-aux} for the
+% definition of the shifts and packing auxiliaries.
+% \begin{macrocode}
+\cs_new:Npn \@@_fixed_mul_add:nnnnwnnwN #1#2 #3#4#5; #6#7#8; #9
+ {
+ #9 (#4* #1 *#7)
+ #9 (#5*#6+#4* #2 *#7+#3*#8) / \c_@@_myriad_int
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Extended-precision floating point numbers}
+%
+% In this section we manipulate floating point numbers with roughly $24$
+% significant figures (\enquote{extended-precision} numbers, in short,
+% \enquote{ep}), which take the form of an integer exponent, followed by a
+% comma, then six groups of digits, ending with a semicolon. The first
+% group of digit may be any non-negative integer, while other groups of
+% digits have $4$~digits. In other words, an extended-precision number
+% is an exponent ending in a comma, then a fixed point number. The
+% corresponding value is $0.\meta{digits}\cdot 10^{\meta{exponent}}$.
+% This convention differs from floating points.
+%
+% \begin{macro}[EXP]{\@@_ep_to_fixed:wwn}
+% \begin{macro}[EXP]
+% {\@@_ep_to_fixed_auxi:www, \@@_ep_to_fixed_auxii:nnnnnnnwn}
+% Converts an extended-precision number with an exponent at most~$4$
+% and a first block less than $10^{8}$ to a fixed point number whose
+% first block has $12$~digits, hopefully starting with many zeros.
+% \begin{macrocode}
+\cs_new:Npn \@@_ep_to_fixed:wwn #1,#2
+ {
+ \exp_after:wN \@@_ep_to_fixed_auxi:www
+ \int_value:w \@@_int_eval:w 1 0000 0000 + #2 \exp_after:wN ;
+ \exp:w \exp_end_continue_f:w
+ \prg_replicate:nn { 4 - \int_max:nn {#1} { -32 } } { 0 } ;
+ }
+\cs_new:Npn \@@_ep_to_fixed_auxi:www 1#1; #2; #3#4#5#6#7;
+ {
+ \@@_pack_eight:wNNNNNNNN
+ \@@_pack_twice_four:wNNNNNNNN
+ \@@_pack_twice_four:wNNNNNNNN
+ \@@_pack_twice_four:wNNNNNNNN
+ \@@_ep_to_fixed_auxii:nnnnnnnwn ;
+ #2 #1#3#4#5#6#7 0000 !
+ }
+\cs_new:Npn \@@_ep_to_fixed_auxii:nnnnnnnwn #1#2#3#4#5#6#7; #8! #9
+ { #9 {#1#2}{#3}{#4}{#5}{#6}{#7}; }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% ^^A todo: make it work when the arg is zero.
+% \begin{macro}[EXP]{\@@_ep_to_ep:wwN}
+% \begin{macro}[rEXP]{\@@_ep_to_ep_loop:N, \@@_ep_to_ep_end:www}
+% \begin{macro}[EXP]{\@@_ep_to_ep_zero:ww}
+% Normalize an extended-precision number. More precisely, leading
+% zeros are removed from the mantissa of the argument, decreasing its
+% exponent as appropriate. Then the digits are packed into $6$~groups
+% of~$4$ (discarding any remaining digit, not rounding). Finally, the
+% continuation~|#8| is placed before the resulting exponent--mantissa
+% pair. The input exponent may in fact be given as an integer
+% expression. The \texttt{loop} auxiliary grabs a digit: if it
+% is~$0$, decrement the exponent and continue looping, and otherwise
+% call the \texttt{end} auxiliary, which places all digits in the
+% right order (the digit that was not~$0$, and any remaining digits),
+% followed by some~$0$, then packs them up neatly in $3\times2=6$
+% blocks of four. At the end of the day, remove with \cs{@@_use_i:ww}
+% any digit that did not make it in the final mantissa (typically only
+% zeros, unless the original first block has more than~$4$ digits).
+% \begin{macrocode}
+\cs_new:Npn \@@_ep_to_ep:wwN #1,#2#3#4#5#6#7; #8
+ {
+ \exp_after:wN #8
+ \int_value:w \@@_int_eval:w #1 + 4
+ \exp_after:wN \use_i:nn
+ \exp_after:wN \@@_ep_to_ep_loop:N
+ \int_value:w \@@_int_eval:w 1 0000 0000 + #2 \@@_int_eval_end:
+ #3#4#5#6#7 ; ; !
+ }
+\cs_new:Npn \@@_ep_to_ep_loop:N #1
+ {
+ \if_meaning:w 0 #1
+ - 1
+ \else:
+ \@@_ep_to_ep_end:www #1
+ \fi:
+ \@@_ep_to_ep_loop:N
+ }
+\cs_new:Npn \@@_ep_to_ep_end:www
+ #1 \fi: \@@_ep_to_ep_loop:N #2; #3!
+ {
+ \fi:
+ \if_meaning:w ; #1
+ - 2 * \c_@@_max_exponent_int
+ \@@_ep_to_ep_zero:ww
+ \fi:
+ \@@_pack_twice_four:wNNNNNNNN
+ \@@_pack_twice_four:wNNNNNNNN
+ \@@_pack_twice_four:wNNNNNNNN
+ \@@_use_i:ww , ;
+ #1 #2 0000 0000 0000 0000 0000 0000 ;
+ }
+\cs_new:Npn \@@_ep_to_ep_zero:ww \fi: #1; #2; #3;
+ { \fi: , {1000}{0000}{0000}{0000}{0000}{0000} ; }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_ep_compare:wwww}
+% \begin{macro}[EXP]{\@@_ep_compare_aux:wwww}
+% In \pkg{l3fp-trig} we need to compare two extended-precision
+% numbers. This is based on the same function for positive floating
+% point numbers, with an extra test if comparing only $16$ decimals is
+% not enough to distinguish the numbers. Note that this function only
+% works if the numbers are normalized so that their first block is
+% in~$[1000,9999]$.
+% \begin{macrocode}
+\cs_new:Npn \@@_ep_compare:wwww #1,#2#3#4#5#6#7;
+ { \@@_ep_compare_aux:wwww {#1}{#2}{#3}{#4}{#5}; #6#7; }
+\cs_new:Npn \@@_ep_compare_aux:wwww #1;#2;#3,#4#5#6#7#8#9;
+ {
+ \if_case:w
+ \@@_compare_npos:nwnw #1; {#3}{#4}{#5}{#6}{#7}; \exp_stop_f:
+ \if_int_compare:w #2 = #8#9 \exp_stop_f:
+ 0
+ \else:
+ \if_int_compare:w #2 < #8#9 - \fi: 1
+ \fi:
+ \or: 1
+ \else: -1
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% ^^A todo: doc that neither operand may be zero (or fix ep_to_ep above)
+% \begin{macro}[EXP]{\@@_ep_mul:wwwwn, \@@_ep_mul_raw:wwwwN}
+% Multiply two extended-precision numbers: first normalize them to
+% avoid losing too much precision, then multiply the mantissas |#2|
+% and~|#4| as fixed point numbers, and sum the exponents |#1|
+% and~|#3|. The result's first block is in $[100,9999]$.
+% \begin{macrocode}
+\cs_new:Npn \@@_ep_mul:wwwwn #1,#2; #3,#4;
+ {
+ \@@_ep_to_ep:wwN #3,#4;
+ \@@_fixed_continue:wn
+ {
+ \@@_ep_to_ep:wwN #1,#2;
+ \@@_ep_mul_raw:wwwwN
+ }
+ \@@_fixed_continue:wn
+ }
+\cs_new:Npn \@@_ep_mul_raw:wwwwN #1,#2; #3,#4; #5
+ {
+ \@@_fixed_mul:wwn #2; #4;
+ { \exp_after:wN #5 \int_value:w \@@_int_eval:w #1 + #3 , }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Dividing extended-precision numbers}
+%
+% \newcommand{\eTeXfrac}[2]{\left[\frac{#1}{#2}\right]}
+%
+% Divisions of extended-precision numbers are difficult to perform with
+% exact rounding: the technique used in \pkg{l3fp-basics} for $16$-digit
+% floating point numbers does not generalize easily to $24$-digit
+% numbers. Thankfully, there is no need for exact rounding.
+%
+% Let us call \meta{n} the numerator and \meta{d} the denominator.
+% After a simple normalization step, we can assume that
+% $\meta{n}\in[0.1,1)$ and $\meta{d}\in[0.1,1)$, and compute
+% $\meta{n}/(10\meta{d})\in(0.01,1)$. In terms of the $6$~blocks of
+% digits $\meta{n_1}\cdots\meta{n_6}$ and the $6$~blocks
+% $\meta{d_1}\cdots\meta{d_6}$, the condition translates to
+% $\meta{n_1},\meta{d_1}\in[1000,9999]$.
+%
+% We first find an integer estimate $a \simeq 10^{8} / \meta{d}$ by
+% computing
+% \begin{align*}
+% \alpha &= \eTeXfrac{10^{9}}{\meta{d_1}+1} \\
+% \beta &= \eTeXfrac{10^{9}}{\meta{d_1}} \\
+% a &= 10^{3} \alpha + (\beta-\alpha) \cdot
+% \left(10^{3}-\eTeXfrac{\meta{d_2}}{10}\right) - 1250,
+% \end{align*}
+% where $\eTeXfrac{\bullet}{\bullet}$ denotes \eTeX{}'s rounding
+% division, which rounds ties away from zero. The idea is to
+% interpolate between $10^{3}\alpha$ and $10^{3}\beta$ with a parameter
+% $\meta{d_2}/10^{4}$, so that when $\meta{d_2}=0$ one gets $a =
+% 10^{3}\beta-1250 \simeq 10^{12} / \meta{d_1} \simeq 10^{8} /
+% \meta{d}$, while when $\meta{d_2}=9999$ one gets $a =
+% 10^{3}\alpha-1250 \simeq 10^{12} / (\meta{d_1} + 1) \simeq 10^{8} /
+% \meta{d}$. The shift by $1250$ helps to ensure that $a$ is an
+% underestimate of the correct value. We shall prove that
+% \[
+% 1 - 1.755\cdot 10^{-5} < \frac{\meta{d}a}{10^{8}} < 1 .
+% \]
+% We can then compute the inverse of $\meta{d}a/10^{8} = 1 - \epsilon$
+% using the relation $1/(1-\epsilon) \simeq (1+\epsilon)(1+\epsilon^{2})
+% + \epsilon^{4}$, which is correct up to a relative error of
+% $\epsilon^5 < 1.6\cdot 10^{-24}$. This allows us to find the desired
+% ratio as
+% \[
+% \frac{\meta{n}}{\meta{d}}
+% = \frac{\meta{n}a}{10^{8}}
+% \bigl( (1+\epsilon)(1+\epsilon^{2}) + \epsilon^{4}\bigr) .
+% \]
+%
+% Let us prove the upper bound first (multiplied by $10^{15}$). Note
+% that $10^{7} \meta{d} < 10^{3} \meta{d_1} + 10^{-1} (\meta{d_2} + 1)$,
+% and that \eTeX{}'s division $\eTeXfrac{\meta{d_2}}{10}$ underestimates
+% $10^{-1}(\meta{d_2} + 1)$ by $0.5$ at most, as can be checked
+% for each possible last digit of \meta{d_2}. Then,
+% \begin{align}
+% 10^{7} \meta{d}a
+% & <
+% \left(10^{3}\meta{d_1}
+% + \eTeXfrac{\meta{d_2}}{10} + \frac{1}{2}\right)
+% \left(\left(10^{3}-\eTeXfrac{\meta{d_2}}{10}\right) \beta
+% + \eTeXfrac{\meta{d_2}}{10} \alpha - 1250\right)
+% \\
+% & <
+% \left(10^{3}\meta{d_1}
+% + \eTeXfrac{\meta{d_2}}{10} + \frac{1}{2}\right)
+% \\ & \qquad
+% \left(
+% \left(10^{3}-\eTeXfrac{\meta{d_2}}{10}\right)
+% \left(\frac{10^{9}}{\meta{d_1}} + \frac{1}{2} \right)
+% + \eTeXfrac{\meta{d_2}}{10}
+% \left(\frac{10^{9}}{\meta{d_1}+1} + \frac{1}{2} \right)
+% - 1250
+% \right)
+% \\
+% & <
+% \left(10^{3} \meta{d_1}
+% + \eTeXfrac{\meta{d_2}}{10} + \frac{1}{2}\right)
+% \left(\frac{10^{12}}{\meta{d_1}}
+% - \eTeXfrac{\meta{d_2}}{10}
+% \frac{10^{9}}{\meta{d_1}(\meta{d_1}+1)}
+% - 750\right)
+% \end{align}
+% We recognize a quadratic polynomial in $[\meta{d_2}/10]$ with a
+% negative leading coefficient: this polynomial is bounded above,
+% according to $([\meta{d_2}/10]+a)(b-c[\meta{d_2}/10]) \leq
+% (b+ca)^2/(4c)$. Hence,
+% \[
+% 10^{7} \meta{d}a
+% < \frac{10^{15}}{\meta{d_1}(\meta{d_1}+1)} \left(
+% \meta{d_1} + \frac{1}{2} + \frac{1}{4} 10^{-3}
+% - \frac{3}{8} \cdot 10^{-9} \meta{d_1}(\meta{d_1}+1) \right)^2
+% \]
+% Since \meta{d_1} takes integer values within $[1000,9999]$, it is a
+% simple programming exercise to check that the squared expression is
+% always less than $\meta{d_1}(\meta{d_1}+1)$, hence $10^{7} \meta{d} a
+% < 10^{15}$. The upper bound is proven. We also find that
+% $\frac{3}{8}$ can be replaced by slightly smaller numbers, but nothing
+% less than $0.374563\ldots$, and going back through the derivation of
+% the upper bound, we find that $1250$ is as small a shift as we can
+% obtain without breaking the bound.
+%
+% Now, the lower bound. The same computation as for the upper bound
+% implies
+% \[
+% 10^{7} \meta{d}a
+% > \left(10^{3} \meta{d_1} + \eTeXfrac{\meta{d_2}}{10}
+% - \frac{1}{2}\right)
+% \left(\frac{10^{12}}{\meta{d_1}}
+% - \eTeXfrac{\meta{d_2}}{10} \frac{10^{9}}{\meta{d_1}(\meta{d_1}+1)}
+% - 1750\right)
+% \]
+% This time, we want to find the minimum of this quadratic polynomial.
+% Since the leading coefficient is still negative, the minimum is
+% reached for one of the extreme values $[y/10]=0$ or $[y/10]=100$, and
+% we easily check the bound for those values.
+%
+% We have proven that the algorithm gives us a precise enough
+% answer. Incidentally, the upper bound that we derived tells us that
+% $a < 10^{8}/\meta{d} \leq 10^{9}$, hence we can compute $a$ safely as
+% a \TeX{} integer, and even add $10^{9}$ to it to ease grabbing of all
+% the digits. The lower bound implies $10^{8} - 1755 < a$, which we do
+% not care about.
+%
+% ^^A todo: provide ep_inv, not ep_div?
+% ^^A todo: make extra sure that the result's first block cannot be 99
+% ^^A todo: doc that neither operand may be zero (or fix ep_to_ep)
+% \begin{macro}[EXP]{\@@_ep_div:wwwwn}
+% Compute the ratio of two extended-precision numbers. The result is
+% an extended-precision number whose first block lies in the range
+% $[100,9999]$, and is placed after the \meta{continuation} once we
+% are done. First normalize the inputs so that both first block lie
+% in $[1000,9999]$, then call \cs{@@_ep_div_esti:wwwwn}
+% \meta{denominator} \meta{numerator}, responsible for estimating the
+% inverse of the denominator.
+% \begin{macrocode}
+\cs_new:Npn \@@_ep_div:wwwwn #1,#2; #3,#4;
+ {
+ \@@_ep_to_ep:wwN #1,#2;
+ \@@_fixed_continue:wn
+ {
+ \@@_ep_to_ep:wwN #3,#4;
+ \@@_ep_div_esti:wwwwn
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_ep_div_esti:wwwwn,
+% \@@_ep_div_estii:wwnnwwn,
+% \@@_ep_div_estiii:NNNNNwwwn
+% }
+% The \texttt{esti} function evaluates $\alpha=10^{9} / (\meta{d_1} +
+% 1)$, which is used twice in the expression for $a$, and combines the
+% exponents |#1| and~|#4| (with a shift by~$1$ because we later compute
+% $\meta{n}/(10\meta{d})$. Then the \texttt{estii} function evaluates
+% $10^{9} + a$, and puts the exponent~|#2| after the
+% continuation~|#7|: from there on we can forget exponents and focus
+% on the mantissa. The \texttt{estiii} function multiplies the
+% denominator~|#7| by $10^{-8}a$ (obtained as $a$ split into the
+% single digit~|#1| and two blocks of $4$~digits, |#2#3#4#5|
+% and~|#6|). The result $10^{-8}a\meta{d}=(1-\epsilon)$, and a
+% partially packed $10^{-9}a$ (as a block of four digits, and five
+% individual digits, not packed by lack of available macro parameters
+% here) are passed to \cs{@@_ep_div_epsi:wnNNNNn}, which computes
+% $10^{-9}a/(1-\epsilon)$, that is, $1/(10\meta{d})$ and we finally
+% multiply this by the numerator~|#8|.
+% \begin{macrocode}
+\cs_new:Npn \@@_ep_div_esti:wwwwn #1,#2#3; #4,
+ {
+ \exp_after:wN \@@_ep_div_estii:wwnnwwn
+ \int_value:w \@@_int_eval:w 10 0000 0000 / ( #2 + 1 )
+ \exp_after:wN ;
+ \int_value:w \@@_int_eval:w #4 - #1 + 1 ,
+ {#2} #3;
+ }
+\cs_new:Npn \@@_ep_div_estii:wwnnwwn #1; #2,#3#4#5; #6; #7
+ {
+ \exp_after:wN \@@_ep_div_estiii:NNNNNwwwn
+ \int_value:w \@@_int_eval:w 10 0000 0000 - 1750
+ + #1 000 + (10 0000 0000 / #3 - #1) * (1000 - #4 / 10) ;
+ {#3}{#4}#5; #6; { #7 #2, }
+ }
+\cs_new:Npn \@@_ep_div_estiii:NNNNNwwwn 1#1#2#3#4#5#6; #7;
+ {
+ \@@_fixed_mul_short:wwn #7; {#1}{#2#3#4#5}{#6};
+ \@@_ep_div_epsi:wnNNNNNn {#1#2#3#4}#5#6
+ \@@_fixed_mul:wwn
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_ep_div_epsi:wnNNNNNn,
+% \@@_ep_div_eps_pack:NNNNNw,
+% \@@_ep_div_epsii:wwnNNNNNn,
+% }
+% The bounds shown above imply that the \texttt{epsi} function's first
+% operand is $(1-\epsilon)$ with $\epsilon\in[0,1.755\cdot 10^{-5}]$.
+% The \texttt{epsi} function computes $\epsilon$ as $1-(1-\epsilon)$.
+% Since $\epsilon<10^{-4}$, its first block vanishes and there is no
+% need to explicitly use~|#1| (which is $9999$). Then \texttt{epsii}
+% evaluates $10^{-9}a/(1-\epsilon)$ as
+% $(1+\epsilon^2)(1+\epsilon)(10^{-9}a \epsilon) + 10^{-9}a$.
+% Importantly, we compute $10^{-9}a \epsilon$ before multiplying it
+% with the rest, rather than multiplying by $\epsilon$ and then
+% $10^{-9}a$, as this second option loses more precision. Also, the
+% combination of \texttt{short_mul} and \texttt{div_myriad} is both
+% faster and more precise than a simple \texttt{mul}.
+% \begin{macrocode}
+\cs_new:Npn \@@_ep_div_epsi:wnNNNNNn #1#2#3#4#5#6;
+ {
+ \exp_after:wN \@@_ep_div_epsii:wwnNNNNNn
+ \int_value:w \@@_int_eval:w 1 9998 - #2
+ \exp_after:wN \@@_ep_div_eps_pack:NNNNNw
+ \int_value:w \@@_int_eval:w 1 9999 9998 - #3#4
+ \exp_after:wN \@@_ep_div_eps_pack:NNNNNw
+ \int_value:w \@@_int_eval:w 2 0000 0000 - #5#6 ; ;
+ }
+\cs_new:Npn \@@_ep_div_eps_pack:NNNNNw #1#2#3#4#5#6;
+ { + #1 ; {#2#3#4#5} {#6} }
+\cs_new:Npn \@@_ep_div_epsii:wwnNNNNNn 1#1; #2; #3#4#5#6#7#8
+ {
+ \@@_fixed_mul:wwn {0000}{#1}#2; {0000}{#1}#2;
+ \@@_fixed_add_one:wN
+ \@@_fixed_mul:wwn {10000} {#1} #2 ;
+ {
+ \@@_fixed_mul_short:wwn {0000}{#1}#2; {#3}{#4#5#6#7}{#8000};
+ \@@_fixed_div_myriad:wn
+ \@@_fixed_mul:wwn
+ }
+ \@@_fixed_add:wwn {#3}{#4#5#6#7}{#8000}{0000}{0000}{0000};
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Inverse square root of extended precision numbers}
+%
+% The idea here is similar to division. Normalize the input,
+% multiplying by powers of $100$ until we have $x\in[0.01,1)$. Then
+% find an integer approximation $r \in [101, 1003]$ of
+% $10^{2}/\sqrt{x}$, as the fixed point of iterations of the Newton
+% method: essentially $r \mapsto (r + 10^{8} / (x_{1} r)) / 2$, starting
+% from a guess that optimizes the number of steps before convergence.
+% In fact, just as there is a slight shift when computing divisions to
+% ensure that some inequalities hold, we replace $10^{8}$ by a
+% slightly larger number which ensures that $r^2 x \geq 10^{4}$.
+% This also causes $r \in [101, 1003]$. Another correction to the above
+% is that the input is actually normalized to $[0.1,1)$, and we use
+% either $10^{8}$ or $10^{9}$ in the Newton method, depending on the
+% parity of the exponent. Skipping those technical hurdles, once we
+% have the approximation~$r$, we set $y = 10^{-4} r^{2} x$ (or rather,
+% the correct power of~$10$ to get $y\simeq 1$) and compute $y^{-1/2}$
+% through another application of Newton's method. This time, the
+% starting value is $z=1$, each step maps $z \mapsto z(1.5-0.5yz^2)$,
+% and we perform a fixed number of steps. Our final result combines~$r$
+% with $y^{-1/2}$ as $x^{-1/2} = 10^{-2} r y^{-1/2}$.
+%
+% ^^A todo: doc that the operand may not be zero (or fix ep_to_ep above)
+% \begin{macro}[EXP]{\@@_ep_isqrt:wwn}
+% \begin{macro}[EXP]
+% {\@@_ep_isqrt_aux:wwn, \@@_ep_isqrt_auxii:wwnnnwn}
+% First normalize the input, then check the parity of the
+% exponent~|#1|. If it is even, the result's exponent will be
+% $-|#1|/2$, otherwise it will be $(|#1|-1)/2$ (except in the case
+% where the input was an exact power of $100$). The \texttt{auxii}
+% function receives as~|#1| the result's exponent just computed, as
+% |#2| the starting value for the iteration giving~$r$ (the
+% values~$168$ and~$535$ lead to the least number of iterations before
+% convergence, on average), as |#3| and~|#4| one empty argument and
+% one~|0|, depending on the parity of the original exponent, as |#5|
+% and~|#6| the normalized mantissa ($|#5|\in[1000,9999]$), and as |#7|
+% the continuation. It sets up the iteration giving~$r$: the
+% \texttt{esti} function thus receives the initial two guesses |#2|
+% and~$0$, an approximation~|#5| of~$10^{4}x$ (its first block of
+% digits), and the empty/zero arguments |#3| and~|#4|, followed by the
+% mantissa and an altered continuation where we have stored the
+% result's exponent.
+% \begin{macrocode}
+\cs_new:Npn \@@_ep_isqrt:wwn #1,#2;
+ {
+ \@@_ep_to_ep:wwN #1,#2;
+ \@@_ep_isqrt_auxi:wwn
+ }
+\cs_new:Npn \@@_ep_isqrt_auxi:wwn #1,
+ {
+ \exp_after:wN \@@_ep_isqrt_auxii:wwnnnwn
+ \int_value:w \@@_int_eval:w
+ \int_if_odd:nTF {#1}
+ { (1 - #1) / 2 , 535 , { 0 } { } }
+ { 1 - #1 / 2 , 168 , { } { 0 } }
+ }
+\cs_new:Npn \@@_ep_isqrt_auxii:wwnnnwn #1, #2, #3#4 #5#6; #7
+ {
+ \@@_ep_isqrt_esti:wwwnnwn #2, 0, #5, {#3} {#4}
+ {#5} #6 ; { #7 #1 , }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_ep_isqrt_esti:wwwnnwn,
+% \@@_ep_isqrt_estii:wwwnnwn,
+% \@@_ep_isqrt_estiii:NNNNNwwwn
+% }
+% If the last two approximations gave the same result, we are done:
+% call the \texttt{estii} function to clean up. Otherwise, evaluate
+% $(\meta{prev} + 1.005 \cdot 10^{\text{$8$ or $9$}} / (\meta{prev}
+% \cdot x)) / 2$, as the next approximation: omitting the $1.005$
+% factor, this would be Newton's method. We can check by brute force
+% that if |#4| is empty (the original exponent was even), the process
+% computes an integer slightly larger than $100 / \sqrt{x}$, while if
+% |#4| is~$0$ (the original exponent was odd), the result is an
+% integer slightly larger than $100 / \sqrt{x/10}$. Once we are done,
+% we evaluate $100 r^2 / 2$ or $10 r^2 / 2$ (when the exponent is even
+% or odd, respectively) and feed that to \texttt{estiii}. This third
+% auxiliary finds $y_{\text{even}} / 2 = 10^{-4} r^2 x / 2$ or
+% $y_{\text{odd}} / 2 = 10^{-5} r^2 x / 2$ (again, depending on
+% earlier parity). A simple program shows that $y\in [1, 1.0201]$.
+% The number $y/2$ is fed to \cs{@@_ep_isqrt_epsi:wN}, which computes
+% $1/\sqrt{y}$, and we finally multiply the result by~$r$.
+% \begin{macrocode}
+\cs_new:Npn \@@_ep_isqrt_esti:wwwnnwn #1, #2, #3, #4
+ {
+ \if_int_compare:w #1 = #2 \exp_stop_f:
+ \exp_after:wN \@@_ep_isqrt_estii:wwwnnwn
+ \fi:
+ \exp_after:wN \@@_ep_isqrt_esti:wwwnnwn
+ \int_value:w \@@_int_eval:w
+ (#1 + 1 0050 0000 #4 / (#1 * #3)) / 2 ,
+ #1, #3, {#4}
+ }
+\cs_new:Npn \@@_ep_isqrt_estii:wwwnnwn #1, #2, #3, #4#5
+ {
+ \exp_after:wN \@@_ep_isqrt_estiii:NNNNNwwwn
+ \int_value:w \@@_int_eval:w 1000 0000 + #2 * #2 #5 * 5
+ \exp_after:wN , \int_value:w \@@_int_eval:w 10000 + #2 ;
+ }
+\cs_new:Npn \@@_ep_isqrt_estiii:NNNNNwwwn 1#1#2#3#4#5#6, 1#7#8; #9;
+ {
+ \@@_fixed_mul_short:wwn #9; {#1} {#2#3#4#5} {#600} ;
+ \@@_ep_isqrt_epsi:wN
+ \@@_fixed_mul_short:wwn {#7} {#80} {0000} ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_ep_isqrt_epsi:wN, \@@_ep_isqrt_epsii:wwN}
+% Here, we receive a fixed point number $y/2$ with $y\in[1,1.0201]$.
+% Starting from $z = 1$ we iterate $z \mapsto z(3/2 - z^2 y/2)$. In
+% fact, we start from the first iteration $z=3/2-y/2$ to avoid useless
+% multiplications. The \texttt{epsii} auxiliary receives $z$ as~|#1|
+% and $y$ as~|#2|.
+% \begin{macrocode}
+\cs_new:Npn \@@_ep_isqrt_epsi:wN #1;
+ {
+ \@@_fixed_sub:wwn {15000}{0000}{0000}{0000}{0000}{0000}; #1;
+ \@@_ep_isqrt_epsii:wwN #1;
+ \@@_ep_isqrt_epsii:wwN #1;
+ \@@_ep_isqrt_epsii:wwN #1;
+ }
+\cs_new:Npn \@@_ep_isqrt_epsii:wwN #1; #2;
+ {
+ \@@_fixed_mul:wwn #1; #1;
+ \@@_fixed_mul_sub_back:wwwn #2;
+ {15000}{0000}{0000}{0000}{0000}{0000};
+ \@@_fixed_mul:wwn #1;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Converting from fixed point to floating point}
+% ^^A todo: doc
+%
+% After computing Taylor series, we wish to convert the result from
+% extended precision (with or without an exponent) to the public
+% floating point format. The functions here should be called within an
+% integer expression for the overall exponent of the floating point.
+%
+% \begin{macro}[rEXP]{\@@_ep_to_float_o:wwN, \@@_ep_inv_to_float_o:wwN}
+% An extended-precision number is simply a comma-delimited exponent
+% followed by a fixed point number. Leave the exponent in the current
+% integer expression then convert the fixed point number.
+% \begin{macrocode}
+\cs_new:Npn \@@_ep_to_float_o:wwN #1,
+ { + \@@_int_eval:w #1 \@@_fixed_to_float_o:wN }
+\cs_new:Npn \@@_ep_inv_to_float_o:wwN #1,#2;
+ {
+ \@@_ep_div:wwwwn 1,{1000}{0000}{0000}{0000}{0000}{0000}; #1,#2;
+ \@@_ep_to_float_o:wwN
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_fixed_inv_to_float_o:wN}
+% Another function which reduces to converting an extended precision
+% number to a float.
+% \begin{macrocode}
+\cs_new:Npn \@@_fixed_inv_to_float_o:wN
+ { \@@_ep_inv_to_float_o:wwN 0, }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_fixed_to_float_rad_o:wN}
+% Converts the fixed point number~|#1| from degrees to radians then to
+% a floating point number. This could perhaps remain in
+% \pkg{l3fp-trig}.
+% \begin{macrocode}
+\cs_new:Npn \@@_fixed_to_float_rad_o:wN #1;
+ {
+ \@@_fixed_mul:wwn #1; {5729}{5779}{5130}{8232}{0876}{7981};
+ { \@@_ep_to_float_o:wwN 2, }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% ^^A todo: make exponents end in ',' consistently throughout l3fp
+% \begin{macro}[rEXP]
+% {\@@_fixed_to_float_o:wN, \@@_fixed_to_float_o:Nw}
+% \begin{syntax}
+% \ldots{} \cs{@@_int_eval:w} \meta{exponent} \cs{@@_fixed_to_float_o:wN} \Arg{a_1} \Arg{a_2} \Arg{a_3} \Arg{a_4} \Arg{a_5} \Arg{a_6} |;| \meta{sign}
+% \end{syntax}
+% yields
+% \begin{quote}
+% \meta{exponent'} |;| \Arg{a'_1} \Arg{a'_2} \Arg{a'_3} \Arg{a'_4} |;|
+% \end{quote}
+% And the \texttt{to_fixed} version gives six brace groups instead of
+% $4$, ensuring that $1000\leq\meta{a'_1}\leq 9999$. At this stage, we
+% know that \meta{a_1} is positive (otherwise, it is sign of an error
+% before), and we assume that it is less than $10^8$.\footnote{Bruno:
+% I must double check this assumption.}
+%
+%^^A todo: round properly when rounding to infinity: I need the sign.
+% \begin{macrocode}
+\cs_new:Npn \@@_fixed_to_float_o:Nw #1#2;
+ { \@@_fixed_to_float_o:wN #2; #1 }
+\cs_new:Npn \@@_fixed_to_float_o:wN #1#2#3#4#5#6; #7
+ { % for the 8-digit-at-the-start thing
+ + \@@_int_eval:w \c_@@_block_int
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_fixed_to_loop:N
+ \exp_after:wN \use_none:n
+ \int_value:w \@@_int_eval:w
+ 1 0000 0000 + #1 \exp_after:wN \@@_use_none_stop_f:n
+ \int_value:w 1#2 \exp_after:wN \@@_use_none_stop_f:n
+ \int_value:w 1#3#4 \exp_after:wN \@@_use_none_stop_f:n
+ \int_value:w 1#5#6
+ \exp_after:wN ;
+ \exp_after:wN ;
+ }
+\cs_new:Npn \@@_fixed_to_loop:N #1
+ {
+ \if_meaning:w 0 #1
+ - 1
+ \exp_after:wN \@@_fixed_to_loop:N
+ \else:
+ \exp_after:wN \@@_fixed_to_loop_end:w
+ \exp_after:wN #1
+ \fi:
+ }
+\cs_new:Npn \@@_fixed_to_loop_end:w #1 #2 ;
+ {
+ \if_meaning:w ; #1
+ \exp_after:wN \@@_fixed_to_float_zero:w
+ \else:
+ \exp_after:wN \@@_pack_twice_four:wNNNNNNNN
+ \exp_after:wN \@@_pack_twice_four:wNNNNNNNN
+ \exp_after:wN \@@_fixed_to_float_pack:ww
+ \exp_after:wN ;
+ \fi:
+ #1 #2 0000 0000 0000 0000 ;
+ }
+\cs_new:Npn \@@_fixed_to_float_zero:w ; 0000 0000 0000 0000 ;
+ {
+ - 2 * \c_@@_max_exponent_int ;
+ {0000} {0000} {0000} {0000} ;
+ }
+\cs_new:Npn \@@_fixed_to_float_pack:ww #1 ; #2#3 ; ;
+ {
+ \if_int_compare:w #2 > 4 \exp_stop_f:
+ \exp_after:wN \@@_fixed_to_float_round_up:wnnnnw
+ \fi:
+ ; #1 ;
+ }
+\cs_new:Npn \@@_fixed_to_float_round_up:wnnnnw ; #1#2#3#4 ;
+ {
+ \exp_after:wN \@@_basics_pack_high:NNNNNw
+ \int_value:w \@@_int_eval:w 1 #1#2
+ \exp_after:wN \@@_basics_pack_low:NNNNNw
+ \int_value:w \@@_int_eval:w 1 #3#4 + 1 ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-functions.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-functions.dtx
new file mode 100644
index 00000000000..f6c9ec346fe
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-functions.dtx
@@ -0,0 +1,288 @@
+% \iffalse
+%
+%% File l3fp-functions.dtx (C) Copyright 2012-2018,2020,2021,2023 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% http://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full]{l3doc}
+\usepackage{amsmath}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3fp-functions} module\\ Floating point functions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-functions} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% \subsection{Declaring functions}
+%
+% \begin{macro}{\fp_new_function:n}
+% \begin{macro}{\@@_new_function:n}
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_new_function:n #1
+ { \exp_args:No \@@_new_function:n { \tl_to_str:n {#1} } }
+\cs_new_protected:Npn \@@_new_function:n #1
+ {
+ \@@_id_if_invalid:nTF {#1}
+ { \msg_error:nnn { fp } { id-invalid } {#1} }
+ {
+ \cs_if_exist:cT { @@_parse_word_#1:N }
+ {
+ \msg_error:nnn
+ { fp } { id-already-defined } {#1}
+ \cs_undefine:c { @@_parse_word_#1:N }
+ \cs_undefine:c { @@_#1_o:w }
+ }
+ \@@_function_set_parsing:Nn \cs_gset_eq:NN {#1}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_function_set_parsing:Nn, \@@_function_set_parsing_aux:NNn}
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_function_set_parsing:Nn #1#2
+ {
+ \exp_args:NNc \@@_function_set_parsing_aux:NNn #1
+ { @@_parse_word_#2:N } {#2}
+ }
+\cs_new_protected:Npn \@@_function_set_parsing_aux:NNn #1#2#3
+ {
+ \cs_set:Npe \@@_tmp:w
+ {
+ \exp_not:N \@@_parse_function:NNN
+ \exp_not:N \@@_function_o:w
+ \exp_not:c { @@_#3_o:w }
+ }
+ \cs_if_eq:NNF #2 \@@_tmp:w
+ {
+ \cs_if_exist:NTF #2
+ {
+ \msg_warning:nnnn
+ { fp } { id-used-elsewhere } {#3} { function }
+ #1 #2 \@@_tmp:w
+ }
+ {
+ \cs_new_eq:NN #2 \scan_stop: % to declare the function
+ #1 #2 \@@_tmp:w
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_function_o:w}
+% \begin{macrocode}
+\cs_new:Npn \@@_function_o:w #1#2 @
+ {
+ \cs_if_exist:NTF #1
+ { #1 #2 @ }
+ {
+ \exp_after:wN \s_@@_symbolic
+ \exp_after:wN \@@_symbolic_chk:w
+ \exp_after:wN \@@_function_o:w
+ \exp_after:wN #1
+ \exp_after:wN ,
+ \exp_after:wN {
+ \exp:w \exp_end_continue_f:w
+ \@@_exp_after_array_f:w #2 \s_@@_expr_stop
+ \exp_after:wN
+ }
+ \exp_after:wN ;
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Defining functions by their expression}
+%
+% \begin{variable}{\l_@@_function_arg_int}
+% Labels the arguments of a function being defined.
+% \begin{macrocode}
+\int_new:N \l_@@_function_arg_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\fp_set_function:nnn}
+% \begin{macro}{\@@_set_function:Nnnn}
+% \begin{syntax}
+% \cs{fp_set_function:nnn} \Arg{identifier}
+% \Arg{comma-list of variables} \Arg{expression}
+% \end{syntax}
+% Defines the \meta{identifier} to stand for a function which expects
+% some arguments defined by the \meta{comma-list of variables}, and
+% evaluates to the \meta{expression}.
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_set_function:nnn #1
+ {
+ \exp_args:NNo \@@_set_function:Nnnn \cs_set_eq:cN
+ { \tl_to_str:n {#1} }
+ }
+\cs_new_protected:Npn \@@_set_function:Nnnn #1#2#3#4
+ {
+ \@@_id_if_invalid:nTF {#2}
+ { \msg_error:nnn { fp } { id-invalid } {#2} }
+ {
+ \cs_if_exist:cF { @@_parse_word_#2:N }
+ { \@@_function_set_parsing:Nn \cs_set_eq:NN {#2} }
+ \group_begin:
+ \int_zero:N \l_@@_function_arg_int
+ \exp_args:No \clist_map_inline:nn { \tl_to_str:n {#3} }
+ {
+ \int_incr:N \l_@@_function_arg_int
+ \exp_args:Ne \@@_clear_variable_aux:n
+ {
+ \c_underscore_str \tex_romannumeral:D \l_@@_function_arg_int
+ }
+ \fp_clear_variable:n {##1}
+ \cs_set_nopar:cpe { l_@@_variable_##1_fp }
+ {
+ \exp_not:N \s__fp_symbolic
+ \exp_not:N \@@_symbolic_chk:w
+ \exp_not:N \@@_function_arg_o:w
+ \int_use:N \l_@@_function_arg_int
+ ########1 , { } ;
+ }
+ }
+ \cs_set:Npn \@@_function_arg_o:w ##1 @
+ {
+ \exp_after:wN \s_@@_symbolic
+ \exp_after:wN \@@_symbolic_chk:w
+ \exp_after:wN \@@_function_arg_o:w
+ \tex_romannumeral:D
+ \@@_exp_after_symbolic_loop:N ##1
+ { , \tex_romannumeral:D \use_none:nn }
+ \exp_after:wN \c_zero_int
+ \exp_after:wN { \exp_after:wN } \exp_after:wN ;
+ }
+ \fp_set:Nn \l_@@_symbolic_fp {#4}
+ \use:e
+ {
+ \exp_not:n { \cs_gset:Npn \@@_tmp:w ##1 }
+ { \exp_not:o { \l_@@_symbolic_fp } }
+ }
+ \use:e
+ {
+ \exp_not:n { \cs_gset:Npn \@@_tmp:w ##1 @ }
+ {
+ \exp_not:N \@@_exp_after_symbolic_f:nw
+ \exp_not:n { { \exp_after:wN \exp_stop_f: } }
+ \exp_not:o { \@@_tmp:w { . , {##1} } }
+ }
+ }
+ \group_end:
+ #1 { @@_#2_o:w } \@@_tmp:w
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_function_arg_o:w,
+% \@@_function_arg_few:w,
+% \@@_function_arg_get:w
+% }
+% \begin{macrocode}
+\cs_new:Npn \@@_function_arg_o:w #1. #2
+ {
+ \if_meaning:w @ #2
+ \exp_after:wN \@@_function_arg_few:w
+ \fi:
+ \if_int_compare:w #1 = \c_one_int
+ \exp_after:wN \@@_function_arg_get:w
+ \fi:
+ \@@_use_i_until_s:nw
+ {
+ \exp_after:wN \@@_function_arg_o:w
+ \int_value:w \int_eval:n { #1 - 1 } .
+ }
+ #2
+ }
+\cs_new:Npn \@@_function_arg_few:w #1 @ { \exp_after:wN \c_nan_fp }
+\cs_new:Npn \@@_function_arg_get:w #1#2#3; #4 @
+ {
+ \@@_exp_after_array_f:w #3; \s_@@_expr_stop
+ \exp_after:wN \exp_stop_f:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\fp_clear_function:n}
+% \begin{macro}{\@@_clear_function:n}
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_clear_function:n #1
+ { \exp_args:No \@@_clear_function:n { \tl_to_str:n {#1} } }
+\cs_new_protected:Npn \@@_clear_function:n #1
+ {
+ \@@_id_if_invalid:nTF {#1}
+ { \msg_error:nnn { fp } { id-invalid } {#1} }
+ {
+ \cs_set_eq:cN { @@_#1_o:w } \tex_undefine:D
+ \@@_function_set_parsing:Nn \cs_set_eq:NN {#1}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% ^^A todo: add check for number of args
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-logic.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-logic.dtx
new file mode 100644
index 00000000000..9df6a4730a4
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-logic.dtx
@@ -0,0 +1,750 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-logic.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3fp-logic} module\\
+% Floating point conditionals^^A
+% }
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-logic} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\@@_parse_word_max:N , \@@_parse_word_min:N}
+% Those functions may receive a variable number of arguments.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_word_max:N
+ { \@@_parse_function:NNN \@@_minmax_o:Nw 2 }
+\cs_new:Npn \@@_parse_word_min:N
+ { \@@_parse_function:NNN \@@_minmax_o:Nw 0 }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Syntax of internal functions}
+%
+% \begin{itemize}
+% \item \cs{@@_compare_npos:nwnw} \Arg{expo_1} \meta{body_1} |;|
+% \Arg{expo_2} \meta{body_2} |;|
+% \item \cs{@@_minmax_o:Nw} \meta{sign} \meta{floating point array}
+% \item \cs{@@_not_o:w} |?| \meta{floating point array} (with one floating point number only)
+% \item \cs{@@_&_o:ww} \meta{floating point} \meta{floating point}
+% \item \cs{@@_|_o:ww} \meta{floating point} \meta{floating point}
+% \item \cs{@@_ternary:NwwN}, \cs{@@_ternary_auxi:NwwN},
+% \cs{@@_ternary_auxii:NwwN} have to be understood.
+% \end{itemize}
+%
+% \subsection{Tests}
+%
+% \begin{macro}[pTF]{\fp_if_exist:N, \fp_if_exist:c}
+% Copies of the \texttt{cs} functions defined in \pkg{l3basics}.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \fp_if_exist:N \cs_if_exist:N { TF , T , F , p }
+\prg_new_eq_conditional:NNn \fp_if_exist:c \cs_if_exist:c { TF , T , F , p }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\fp_if_nan:n}
+% Evaluate and check if the result is a floating point of the same
+% kind as \nan{}.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \fp_if_nan:n #1 { TF , T , F , p }
+ {
+ \if:w 3 \exp_last_unbraced:Nf \@@_kind:w { \@@_parse:n {#1} }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Comparison}
+%
+% \begin{macro}[pTF, EXP]{\fp_compare:n}
+% \begin{macro}[EXP]{\@@_compare_return:w}
+% Within floating point expressions, comparison operators are treated
+% as operations, so we evaluate |#1|, then compare with $\pm 0$.
+% Tuples are \texttt{true}.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \fp_compare:n #1 { p , T , F , TF }
+ {
+ \exp_after:wN \@@_compare_return:w
+ \exp:w \exp_end_continue_f:w \@@_parse:n {#1}
+ }
+\cs_new:Npn \@@_compare_return:w #1#2#3;
+ {
+ \if_charcode:w 0
+ \@@_if_type_fp:NTwFw
+ #1 { \@@_use_i_delimit_by_s_stop:nw #3 \s_@@_stop }
+ \s_@@ 1 \s_@@_stop
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF, EXP]{\fp_compare:nNn}
+% \begin{macro}[EXP]{\@@_compare_aux:wn}
+% Evaluate |#1| and |#3|, using an auxiliary to expand both, and feed
+% the two floating point numbers swapped to \cs{@@_compare_back_any:ww},
+% defined below. Compare the result with |`#2-`=|, which is $-1$ for
+% |<|, $0$ for |=|, $1$ for |>| and $2$ for |?|.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \fp_compare:nNn #1#2#3 { p , T , F , TF }
+ {
+ \if_int_compare:w
+ \exp_after:wN \@@_compare_aux:wn
+ \exp:w \exp_end_continue_f:w \@@_parse:n {#1} {#3}
+ = \@@_int_eval:w `#2 - `= \@@_int_eval_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \@@_compare_aux:wn #1; #2
+ {
+ \exp_after:wN \@@_compare_back_any:ww
+ \exp:w \exp_end_continue_f:w \@@_parse:n {#2} #1;
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_compare_back:ww, \@@_bcmp:ww, \@@_compare_back_any:ww, \@@_compare_nan:w}
+% \begin{quote}
+% \cs{@@_compare_back_any:ww} \meta{y} |;| \meta{x} |;|
+% \end{quote}
+% Expands (in the same way as \cs{int_eval:n}) to $-1$ if $x<y$, $0$
+% if $x=y$, $1$ if $x>y$, and $2$ otherwise (denoted as $x?y$). If
+% either operand is \texttt{nan}, stop the comparison with
+% \cs{@@_compare_nan:w} returning $2$. If $x$ is negative, swap the
+% outputs $1$ and $-1$ (\emph{i.e.}, $>$ and $<$); we can henceforth
+% assume that $x\geq 0$. If $y\geq 0$, and they have the same type,
+% either they are normal and we compare them with
+% \cs{@@_compare_npos:nwnw}, or they are equal. If $y\geq 0$, but of
+% a different type, the highest type is a larger number. Finally, if
+% $y\leq 0$, then $x>y$, unless both are zero.
+% \begin{macrocode}
+\cs_new:Npn \@@_compare_back:ww #1#2; #3#4;
+ {
+ \cs:w
+ @@
+ \@@_type_from_scan:N #1
+ _bcmp
+ \@@_type_from_scan:N #3
+ :ww
+ \cs_end:
+ #1#2; #3#4;
+ }
+\cs_new:Npn \@@_compare_back_any:ww #1#2; #3
+ {
+ \@@_if_type_fp:NTwFw
+ #1 { \@@_if_type_fp:NTwFw #3 \use_i:nn \s_@@ \use_ii:nn \s_@@_stop }
+ \s_@@ \use_ii:nn \s_@@_stop
+ \@@_compare_back:ww
+ {
+ \cs:w
+ @@
+ \@@_type_from_scan:N #1
+ _compare_back
+ \@@_type_from_scan:N #3
+ :ww
+ \cs_end:
+ }
+ #1#2 ; #3
+ }
+\cs_new:Npn \@@_bcmp:ww
+ \s_@@ \@@_chk:w #1 #2 #3;
+ \s_@@ \@@_chk:w #4 #5 #6;
+ {
+ \int_value:w
+ \if_meaning:w 3 #1 \exp_after:wN \@@_compare_nan:w \fi:
+ \if_meaning:w 3 #4 \exp_after:wN \@@_compare_nan:w \fi:
+ \if_meaning:w 2 #5 - \fi:
+ \if_meaning:w #2 #5
+ \if_meaning:w #1 #4
+ \if_meaning:w 1 #1
+ \@@_compare_npos:nwnw #6; #3;
+ \else:
+ 0
+ \fi:
+ \else:
+ \if_int_compare:w #4 < #1 - \fi: 1
+ \fi:
+ \else:
+ \if_int_compare:w #1#4 = \c_zero_int
+ 0
+ \else:
+ 1
+ \fi:
+ \fi:
+ \exp_stop_f:
+ }
+\cs_new:Npn \@@_compare_nan:w #1 \fi: \exp_stop_f: { 2 \exp_stop_f: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_compare_back_tuple:ww, \@@_tuple_compare_back:ww, \@@_tuple_compare_back_tuple:ww}
+% \begin{macro}[EXP]{\@@_tuple_compare_back_loop:w}
+% Tuple and floating point numbers are not comparable so return $2$ in
+% mixed cases or when tuples have a different number of items.
+% Otherwise compare pairs of items with \cs{@@_compare_back_any:ww}
+% and if any don't match return~$2$ (as \cs{int_value:w} |02|
+% \cs{exp_stop_f:}).
+% \begin{macrocode}
+\cs_new:Npn \@@_compare_back_tuple:ww #1; #2; { 2 }
+\cs_new:Npn \@@_tuple_compare_back:ww #1; #2; { 2 }
+\cs_new:Npn \@@_tuple_compare_back_tuple:ww
+ \s_@@_tuple \@@_tuple_chk:w #1;
+ \s_@@_tuple \@@_tuple_chk:w #2;
+ {
+ \int_compare:nNnTF { \@@_array_count:n {#1} } =
+ { \@@_array_count:n {#2} }
+ {
+ \int_value:w 0
+ \@@_tuple_compare_back_loop:w
+ #1 { \s_@@ \prg_break: } ; @
+ #2 { \s_@@ \prg_break: } ;
+ \prg_break_point:
+ \exp_stop_f:
+ }
+ { 2 }
+ }
+\cs_new:Npn \@@_tuple_compare_back_loop:w #1#2 ; #3 @ #4#5 ;
+ {
+ \use_none:n #1
+ \use_none:n #4
+ \if_int_compare:w
+ \@@_compare_back_any:ww #1 #2 ; #4 #5 ; = \c_zero_int
+ \else:
+ 2 \exp_after:wN \prg_break:
+ \fi:
+ \@@_tuple_compare_back_loop:w #3 @
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_compare_npos:nwnw}
+% \begin{macro}[EXP]{\@@_compare_significand:nnnnnnnn}
+% \begin{quote}
+% \cs{@@_compare_npos:nwnw}
+% \Arg{expo_1} \meta{body_1} |;|
+% \Arg{expo_2} \meta{body_2} |;|
+% \end{quote}
+% Within an \cs{int_value:w} \ldots{} \cs{exp_stop_f:} construction,
+% this expands to $0$ if the two numbers are equal, $-1$ if the first
+% is smaller, and $1$ if the first is bigger. First compare the
+% exponents: the larger one denotes the larger number. If they are
+% equal, we must compare significands. If both the first $8$ digits and
+% the next $8$ digits coincide, the numbers are equal. If only the
+% first $8$ digits coincide, the next $8$ decide. Otherwise, the
+% first $8$ digits are compared.
+% \begin{macrocode}
+\cs_new:Npn \@@_compare_npos:nwnw #1#2; #3#4;
+ {
+ \if_int_compare:w #1 = #3 \exp_stop_f:
+ \@@_compare_significand:nnnnnnnn #2 #4
+ \else:
+ \if_int_compare:w #1 < #3 - \fi: 1
+ \fi:
+ }
+\cs_new:Npn \@@_compare_significand:nnnnnnnn #1#2#3#4#5#6#7#8
+ {
+ \if_int_compare:w #1#2 = #5#6 \exp_stop_f:
+ \if_int_compare:w #3#4 = #7#8 \exp_stop_f:
+ 0
+ \else:
+ \if_int_compare:w #3#4 < #7#8 - \fi: 1
+ \fi:
+ \else:
+ \if_int_compare:w #1#2 < #5#6 - \fi: 1
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Floating point expression loops}
+%
+% \begin{macro}[rEXP]
+% {
+% \fp_do_until:nn, \fp_do_while:nn,
+% \fp_until_do:nn, \fp_while_do:nn
+% }
+% These are quite easy given the above functions. The |do_until| and
+% |do_while| versions execute the body, then test. The |until_do| and
+% |while_do| do it the other way round.
+% \begin{macrocode}
+\cs_new:Npn \fp_do_until:nn #1#2
+ {
+ #2
+ \fp_compare:nF {#1}
+ { \fp_do_until:nn {#1} {#2} }
+ }
+\cs_new:Npn \fp_do_while:nn #1#2
+ {
+ #2
+ \fp_compare:nT {#1}
+ { \fp_do_while:nn {#1} {#2} }
+ }
+\cs_new:Npn \fp_until_do:nn #1#2
+ {
+ \fp_compare:nF {#1}
+ {
+ #2
+ \fp_until_do:nn {#1} {#2}
+ }
+ }
+\cs_new:Npn \fp_while_do:nn #1#2
+ {
+ \fp_compare:nT {#1}
+ {
+ #2
+ \fp_while_do:nn {#1} {#2}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {
+% \fp_do_until:nNnn, \fp_do_while:nNnn,
+% \fp_until_do:nNnn, \fp_while_do:nNnn
+% }
+% As above but not using the |nNn| syntax.
+% \begin{macrocode}
+\cs_new:Npn \fp_do_until:nNnn #1#2#3#4
+ {
+ #4
+ \fp_compare:nNnF {#1} #2 {#3}
+ { \fp_do_until:nNnn {#1} #2 {#3} {#4} }
+ }
+\cs_new:Npn \fp_do_while:nNnn #1#2#3#4
+ {
+ #4
+ \fp_compare:nNnT {#1} #2 {#3}
+ { \fp_do_while:nNnn {#1} #2 {#3} {#4} }
+ }
+\cs_new:Npn \fp_until_do:nNnn #1#2#3#4
+ {
+ \fp_compare:nNnF {#1} #2 {#3}
+ {
+ #4
+ \fp_until_do:nNnn {#1} #2 {#3} {#4}
+ }
+ }
+\cs_new:Npn \fp_while_do:nNnn #1#2#3#4
+ {
+ \fp_compare:nNnT {#1} #2 {#3}
+ {
+ #4
+ \fp_while_do:nNnn {#1} #2 {#3} {#4}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\fp_step_function:nnnN, \fp_step_function:nnnc}
+% \begin{macro}[EXP]{\@@_step:wwwN, \@@_step_fp:wwwN}
+% \begin{macro}[EXP]{\@@_step:NnnnnN, \@@_step:NfnnnN}
+% The approach here is somewhat similar to
+% \cs{int_step_function:nnnN}. There are two subtleties: we use the
+% internal parser \cs{@@_parse:n} to avoid converting back and forth
+% from the internal representation; and (due to rounding) even a
+% non-zero step does not guarantee that the loop counter increases.
+% \begin{macrocode}
+\cs_new:Npn \fp_step_function:nnnN #1#2#3
+ {
+ \exp_after:wN \@@_step:wwwN
+ \exp:w \exp_end_continue_f:w \@@_parse_o:n {#1}
+ \exp:w \exp_end_continue_f:w \@@_parse_o:n {#2}
+ \exp:w \exp_end_continue_f:w \@@_parse:n {#3}
+ }
+\cs_generate_variant:Nn \fp_step_function:nnnN { nnnc }
+% \end{macrocode}
+% Only floating point numbers (not tuples) are allowed arguments.
+% Only \enquote{normal} floating points (not $\pm 0$,
+% $\pm\texttt{inf}$, \texttt{nan}) can be used as step; if positive,
+% call \cs{@@_step:NnnnnN} with argument |>| otherwise~|<|. This
+% function has one more argument than its integer counterpart, namely
+% the previous value, to catch the case where the loop has made no
+% progress. Conversion to decimal is done just before calling the
+% user's function.
+% \begin{macrocode}
+\cs_new:Npn \@@_step:wwwN #1#2; #3#4; #5#6; #7
+ {
+ \@@_if_type_fp:NTwFw #1 { } \s_@@ \prg_break: \s_@@_stop
+ \@@_if_type_fp:NTwFw #3 { } \s_@@ \prg_break: \s_@@_stop
+ \@@_if_type_fp:NTwFw #5 { } \s_@@ \prg_break: \s_@@_stop
+ \use_i:nnnn { \@@_step_fp:wwwN #1#2; #3#4; #5#6; #7 }
+ \prg_break_point:
+ \use:n
+ {
+ \@@_error:nfff { step-tuple } { \fp_to_tl:n { #1#2 ; } }
+ { \fp_to_tl:n { #3#4 ; } } { \fp_to_tl:n { #5#6 ; } }
+ }
+ }
+\cs_new:Npn \@@_step_fp:wwwN #1 ; \s_@@ \@@_chk:w #2#3#4 ; #5; #6
+ {
+ \token_if_eq_meaning:NNTF #2 1
+ {
+ \token_if_eq_meaning:NNTF #3 0
+ { \@@_step:NnnnnN > }
+ { \@@_step:NnnnnN < }
+ }
+ {
+ \token_if_eq_meaning:NNTF #2 0
+ {
+ \msg_expandable_error:nnn { kernel }
+ { zero-step } {#6}
+ }
+ {
+ \@@_error:nnfn { bad-step } { }
+ { \fp_to_tl:n { \s_@@ \@@_chk:w #2#3#4 ; } } {#6}
+ }
+ \use_none:nnnnn
+ }
+ { #1 ; } { \c_nan_fp } { \s_@@ \@@_chk:w #2#3#4 ; } { #5 ; } #6
+ }
+\cs_new:Npn \@@_step:NnnnnN #1#2#3#4#5#6
+ {
+ \fp_compare:nNnTF {#2} = {#3}
+ {
+ \@@_error:nffn { tiny-step }
+ { \fp_to_tl:n {#3} } { \fp_to_tl:n {#4} } {#6}
+ }
+ {
+ \fp_compare:nNnF {#2} #1 {#5}
+ {
+ \exp_args:Nf #6 { \@@_to_decimal_dispatch:w #2 }
+ \@@_step:NfnnnN
+ #1 { \@@_parse:n { #2 + #4 } } {#2} {#4} {#5} #6
+ }
+ }
+ }
+\cs_generate_variant:Nn \@@_step:NnnnnN { Nf }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\fp_step_inline:nnnn, \fp_step_variable:nnnNn}
+% \begin{macro}{\@@_step:NNnnnn}
+% As for \cs{int_step_inline:nnnn}, create a global function and apply it,
+% following up with a break point.
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_step_inline:nnnn
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:NNc \@@_step:NNnnnn
+ \cs_gset_protected:Npn
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w }
+ }
+\cs_new_protected:Npn \fp_step_variable:nnnNn #1#2#3#4#5
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:NNc \@@_step:NNnnnn
+ \cs_gset_protected:Npe
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w }
+ {#1} {#2} {#3}
+ {
+ \tl_set:Nn \exp_not:N #4 {##1}
+ \exp_not:n {#5}
+ }
+ }
+\cs_new_protected:Npn \@@_step:NNnnnn #1#2#3#4#5#6
+ {
+ #1 #2 ##1 {#6}
+ \fp_step_function:nnnN {#3} {#4} {#5} #2
+ \prg_break_point:Nn \scan_stop: { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+\msg_new:nnn { fp } { step-tuple }
+ { Tuple~argument~in~fp_step_...~{#1}{#2}{#3}. }
+\msg_new:nnn { fp } { bad-step }
+ { Invalid~step~size~#2~for~function~#3. }
+\msg_new:nnn { fp } { tiny-step }
+ { Tiny~step~size~(#1+#2=#1)~for~function~#3. }
+% \end{macrocode}
+%
+% \subsection{Extrema}
+%
+% \begin{macro}[EXP]{\@@_minmax_o:Nw, \@@_minmax_aux_o:Nw}
+% First check all operands are floating point numbers.
+% The argument~|#1| is $2$~to find the maximum of an array~|#2| of
+% floating point numbers, and $0$~to find the minimum. We read
+% numbers sequentially, keeping track of the largest (smallest) number
+% found so far. If numbers are equal (for instance~$\pm0$), the first
+% is kept. We append $-\infty$ ($\infty$), for the case of an empty
+% array. Since no number is smaller (larger) than that, this
+% additional item only affects the maximum (minimum) in the case of
+% |max()| and |min()| with no argument. The weird
+% fp-like trailing marker breaks the loop correctly: see the precise
+% definition of \cs{@@_minmax_loop:Nww}.
+% \begin{macrocode}
+\cs_new:Npn \@@_minmax_o:Nw #1
+ {
+ \@@_parse_function_all_fp_o:fnw
+ { \token_if_eq_meaning:NNTF 0 #1 { min } { max } }
+ { \@@_minmax_aux_o:Nw #1 }
+ }
+\cs_new:Npn \@@_minmax_aux_o:Nw #1#2 @
+ {
+ \if_meaning:w 0 #1
+ \exp_after:wN \@@_minmax_loop:Nww \exp_after:wN +
+ \else:
+ \exp_after:wN \@@_minmax_loop:Nww \exp_after:wN -
+ \fi:
+ #2
+ \s_@@ \@@_chk:w 2 #1 \s_@@_exact ;
+ \s_@@ \@@_chk:w { 3 \@@_minmax_break_o:w } ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_minmax_loop:Nww}
+% The first argument is $-$ or $+$ to denote the case where the
+% currently largest (smallest) number found (first floating point
+% argument) should be replaced by the new number (second floating
+% point argument). If the new number is \texttt{nan}, keep that as
+% the extremum, unless that extremum is already a \texttt{nan}.
+% Otherwise, compare the two numbers. If the new number is larger (in
+% the case of |max|) or smaller (in the case of |min|), the test
+% yields \texttt{true}, and we keep the second number as a new
+% maximum; otherwise we keep the first number. Then loop.
+% \begin{macrocode}
+\cs_new:Npn \@@_minmax_loop:Nww
+ #1 \s_@@ \@@_chk:w #2#3; \s_@@ \@@_chk:w #4#5;
+ {
+ \if_meaning:w 3 #4
+ \if_meaning:w 3 #2
+ \@@_minmax_auxi:ww
+ \else:
+ \@@_minmax_auxii:ww
+ \fi:
+ \else:
+ \if_int_compare:w
+ \@@_compare_back:ww
+ \s_@@ \@@_chk:w #4#5;
+ \s_@@ \@@_chk:w #2#3;
+ = #1 1 \exp_stop_f:
+ \@@_minmax_auxii:ww
+ \else:
+ \@@_minmax_auxi:ww
+ \fi:
+ \fi:
+ \@@_minmax_loop:Nww #1
+ \s_@@ \@@_chk:w #2#3;
+ \s_@@ \@@_chk:w #4#5;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_minmax_auxi:ww, \@@_minmax_auxii:ww}
+% Keep the first/second number, and remove the other.
+% \begin{macrocode}
+\cs_new:Npn \@@_minmax_auxi:ww #1 \fi: \fi: #2 \s_@@ #3 ; \s_@@ #4;
+ { \fi: \fi: #2 \s_@@ #3 ; }
+\cs_new:Npn \@@_minmax_auxii:ww #1 \fi: \fi: #2 \s_@@ #3 ;
+ { \fi: \fi: #2 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_minmax_break_o:w}
+% This function is called from within an \cs{if_meaning:w} test. Skip
+% to the end of the tests, close the current test with \cs{fi:}, clean
+% up, and return the appropriate number with one post-expansion.
+% \begin{macrocode}
+\cs_new:Npn \@@_minmax_break_o:w #1 \fi: \fi: #2 \s_@@ #3; #4;
+ { \fi: \@@_exp_after_o:w \s_@@ #3; }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Boolean operations}
+%
+% \begin{macro}[EXP]{\@@_not_o:w, \@@_tuple_not_o:w}
+% Return \texttt{true} or \texttt{false}, with two expansions, one to
+% exit the conditional, and one to please \pkg{l3fp-parse}. The first
+% argument is provided by \pkg{l3fp-parse} and is ignored.
+% \begin{macrocode}
+\cs_new:Npn \@@_not_o:w #1 \s_@@ \@@_chk:w #2#3; @
+ {
+ \if_meaning:w 0 #2
+ \exp_after:wN \exp_after:wN \exp_after:wN \c_one_fp
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \c_zero_fp
+ \fi:
+ }
+\cs_new:Npn \@@_tuple_not_o:w #1 @ { \exp_after:wN \c_zero_fp }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_&_o:ww, \@@_tuple_&_o:ww, \@@_&_tuple_o:ww, \@@_tuple_&_tuple_o:ww}
+% \begin{macro}[EXP]{\@@_|_o:ww, \@@_tuple_|_o:ww, \@@_|_tuple_o:ww, \@@_tuple_|_tuple_o:ww}
+% \begin{macro}[EXP]{\@@_and_return:wNw}
+% For \texttt{and}, if the first number is zero, return it (with the
+% same sign). Otherwise, return the second one. For \texttt{or}, the
+% logic is reversed: if the first number is non-zero, return it,
+% otherwise return the second number: we achieve that by hi-jacking
+% \cs{@@_&_o:ww}, inserting an extra argument, \cs{else:}, before
+% \cs{s_@@}. In all cases, expand after the floating point number.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_letter:N &
+ \char_set_catcode_letter:N |
+ \cs_new:Npn \@@_&_o:ww #1 \s_@@ \@@_chk:w #2#3;
+ {
+ \if_meaning:w 0 #2 #1
+ \@@_and_return:wNw \s_@@ \@@_chk:w #2#3;
+ \fi:
+ \@@_exp_after_o:w
+ }
+ \cs_new:Npn \@@_&_tuple_o:ww #1 \s_@@ \@@_chk:w #2#3;
+ {
+ \if_meaning:w 0 #2 #1
+ \@@_and_return:wNw \s_@@ \@@_chk:w #2#3;
+ \fi:
+ \@@_exp_after_tuple_o:w
+ }
+ \cs_new:Npn \@@_tuple_&_o:ww #1; { \@@_exp_after_o:w }
+ \cs_new:Npn \@@_tuple_&_tuple_o:ww #1; { \@@_exp_after_tuple_o:w }
+ \cs_new:Npn \@@_|_o:ww { \@@_&_o:ww \else: }
+ \cs_new:Npn \@@_|_tuple_o:ww { \@@_&_tuple_o:ww \else: }
+ \cs_new:Npn \@@_tuple_|_o:ww #1; #2; { \@@_exp_after_tuple_o:w #1; }
+ \cs_new:Npn \@@_tuple_|_tuple_o:ww #1; #2;
+ { \@@_exp_after_tuple_o:w #1; }
+\group_end:
+\cs_new:Npn \@@_and_return:wNw #1; \fi: #2;
+ { \fi: \@@_exp_after_o:w #1; }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Ternary operator}
+%
+% \begin{macro}[EXP]
+% {\@@_ternary:NwwN, \@@_ternary_auxi:NwwN, \@@_ternary_auxii:NwwN}
+% The first function receives the test and the true branch of the |?:|
+% ternary operator. It calls \cs{@@_ternary_auxii:NwwN} if the test
+% branch is a floating point number $\pm 0$, and otherwise calls
+% \cs{@@_ternary_auxi:NwwN}. These functions select one of their two
+% arguments.
+% \begin{macrocode}
+\cs_new:Npn \@@_ternary:NwwN #1 #2#3@ #4@ #5
+ {
+ \if_meaning:w \@@_parse_infix_::N #5
+ \if_charcode:w 0
+ \@@_if_type_fp:NTwFw
+ #2 { \use_i:nn \@@_use_i_delimit_by_s_stop:nw #3 \s_@@_stop }
+ \s_@@ 1 \s_@@_stop
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_ternary_auxii:NwwN
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_ternary_auxi:NwwN
+ \fi:
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \@@_exp_after_array_f:w #4 \s_@@_expr_stop
+ \exp_after:wN @
+ \exp:w
+ \@@_parse_operand:Nw \c_@@_prec_colon_int
+ \@@_parse_expand:w
+ \else:
+ \msg_expandable_error:nnnn
+ { fp } { missing } { : } { ~for~?: }
+ \exp_after:wN \@@_parse_continue:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \@@_exp_after_array_f:w #4 \s_@@_expr_stop
+ \exp_after:wN #5
+ \exp_after:wN #1
+ \fi:
+ }
+\cs_new:Npn \@@_ternary_auxi:NwwN #1#2@#3@#4
+ {
+ \exp_after:wN \@@_parse_continue:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \@@_exp_after_array_f:w #2 \s_@@_expr_stop
+ #4 #1
+ }
+\cs_new:Npn \@@_ternary_auxii:NwwN #1#2@#3@#4
+ {
+ \exp_after:wN \@@_parse_continue:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \@@_exp_after_array_f:w #3 \s_@@_expr_stop
+ #4 #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-parse.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-parse.dtx
new file mode 100644
index 00000000000..ee2eedbc227
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-parse.dtx
@@ -0,0 +1,2899 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-parse.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3fp-parse} module\\
+% Floating point expression parsing^^A
+% }
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-parse} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% \subsection{Work plan}
+%
+% The task at hand is non-trivial, and some previous failed attempts
+% show that the code leads to unreadable logs, so we had better get it
+% (almost) right the first time. Let us first describe our goal, then
+% discuss the design precisely before writing any code.
+%
+% In this file at least, a \meta{floating point object} is a floating
+% point number or tuple. This can be extended to anything that starts
+% with \cs{s_@@} or \cs{s_@@_\meta{type}} and ends with |;| with some
+% internal structure that depends on the \meta{type}.
+%
+% \begin{macro}[EXP]{\@@_parse:n}
+% \begin{syntax}
+% \cs{@@_parse:n} \Arg{fp expr}
+% \end{syntax}
+% Evaluates the \meta{fp expr} and leaves the result
+% in the input stream as a floating point object. This
+% function forms the basis of almost all public \pkg{l3fp} functions.
+% During evaluation, each token is fully \texttt{f}-expanded.
+%
+% \cs{@@_parse_o:n} does the same but expands once after its result.
+% \begin{texnote}
+% Registers (integers, toks, etc.) are automatically unpacked,
+% without requiring a function such as \cs{int_use:N}. Invalid
+% tokens remaining after \texttt{f}-expansion lead to
+% unrecoverable low-level \TeX{} errors.
+% \end{texnote}
+% \end{macro}
+%
+% \begin{variable}
+% {
+% \c_@@_prec_func_int,
+% \c_@@_prec_hatii_int,
+% \c_@@_prec_hat_int,
+% \c_@@_prec_not_int,
+% \c_@@_prec_juxt_int,
+% \c_@@_prec_times_int,
+% \c_@@_prec_plus_int,
+% \c_@@_prec_comp_int,
+% \c_@@_prec_and_int,
+% \c_@@_prec_or_int,
+% \c_@@_prec_quest_int,
+% \c_@@_prec_colon_int,
+% \c_@@_prec_comma_int,
+% \c_@@_prec_tuple_int,
+% \c_@@_prec_end_int,
+% }
+% Floating point expressions are composed of numbers, given in various
+% forms, infix operators, such as |+|, |**|, or~|,| (which joins two
+% numbers into a list), and prefix operators, such as the unary~|-|,
+% functions, or opening parentheses. Here is a list of precedences
+% which control the order of evaluation (some distinctions are
+% irrelevant for the order of evaluation, but serve as signals), from
+% the tightest binding to the loosest binding.
+% \begin{itemize}
+% \item[16] Function calls.
+% \item[13/14] Binary |**| and~|^| (right to left).
+% \item[12] Unary |+|, |-|, |!| (right to left).
+% \item[11] Juxtaposition (implicit~|*|) with no parenthesis.
+% \item[10] Binary |*| and~|/|.
+% \item[9] Binary |+| and~|-|.
+% \item[7] Comparisons.
+% \item[6] Logical \texttt{and}, denoted by~|&&|.
+% \item[5] Logical \texttt{or}, denoted by~\verb*+||+.
+% \item[4] Ternary operator |?:|, piece~|?|.
+% \item[3] Ternary operator |?:|, piece~|:|.
+% \item[2] Commas.
+% \item[1] Place where a comma is allowed and generates a tuple.
+% \item[0] Start and end of the expression.
+% \end{itemize}
+% \begin{macrocode}
+\int_const:Nn \c_@@_prec_func_int { 16 }
+\int_const:Nn \c_@@_prec_hatii_int { 14 }
+\int_const:Nn \c_@@_prec_hat_int { 13 }
+\int_const:Nn \c_@@_prec_not_int { 12 }
+\int_const:Nn \c_@@_prec_juxt_int { 11 }
+\int_const:Nn \c_@@_prec_times_int { 10 }
+\int_const:Nn \c_@@_prec_plus_int { 9 }
+\int_const:Nn \c_@@_prec_comp_int { 7 }
+\int_const:Nn \c_@@_prec_and_int { 6 }
+\int_const:Nn \c_@@_prec_or_int { 5 }
+\int_const:Nn \c_@@_prec_quest_int { 4 }
+\int_const:Nn \c_@@_prec_colon_int { 3 }
+\int_const:Nn \c_@@_prec_comma_int { 2 }
+\int_const:Nn \c_@@_prec_tuple_int { 1 }
+\int_const:Nn \c_@@_prec_end_int { 0 }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Storing results}
+%
+% The main question in parsing expressions expandably is to decide where
+% to put the intermediate results computed for various subexpressions.
+%
+% One option is to store the values at the start of the expression, and
+% carry them together as the first argument of each macro. However, we
+% want to \texttt{f}-expand tokens one by one in the expression (as
+% \cs{int_eval:n} does), and with this approach, expanding the next
+% unread token forces us to jump with \cs{exp_after:wN} over every value
+% computed earlier in the expression. With this approach, the run-time
+% grows at least quadratically in the length of the expression, if
+% not as its cube (inserting the \cs{exp_after:wN} is tricky and slow).
+%
+% A second option is to place those values at the end of the expression.
+% Then expanding the next unread token is straightforward, but this
+% still hits a performance issue: for long expressions we would be
+% reaching all the way to the end of the expression at every step of the
+% calculation. The run-time is again quadratic.
+%
+% A variation of the above attempts to place the intermediate results
+% which appear when computing a parenthesized expression near the
+% closing parenthesis. This still lets us expand tokens as we go, and
+% avoids performance problems as long as there are enough parentheses.
+% However, it would be better to avoid requiring the closing
+% parenthesis to be present as soon as the corresponding opening
+% parenthesis is read: the closing parenthesis may still be hidden in a
+% macro yet to be expanded.
+%
+% Hence, we need to go for some fine expansion control: the result is
+% stored \emph{before} the start!
+%
+% Let us illustrate this idea in a simple model: adding positive
+% integers which may be resulting from the expansion of macros, or may
+% be values of registers. Assume that one number, say, $12345$, has
+% already been found, and that we want to parse the next number. The
+% current status of the code may look as follows.
+% \begin{syntax}
+% \cs{exp_after:wN} |\add:ww| \cs{int_value:w} 12345 \cs{exp_after:wN} ;
+% \cs{exp:w} |\operand:w| \meta{stuff}
+% \end{syntax}
+% One step of expansion expands \cs{exp_after:wN}, which triggers the
+% primitive \cs{int_value:w}, which reads the five digits we have
+% already found, |12345|. This integer is unfinished, causing the
+% second \cs{exp_after:wN} to expand, and to trigger the construction
+% \cs{exp:w}, which expands |\operand:w|, defined to read
+% what follows and make a number out of it, then leave \cs{exp_end:}, the
+% number, and a semicolon in the input stream. Once |\operand:w| is
+% done expanding, we obtain essentially
+% \begin{syntax}
+% \cs{exp_after:wN} |\add:ww| \cs{int_value:w} 12345 ;
+% \cs{exp:w} \cs{exp_end:} 333444 ;
+% \end{syntax}
+% where in fact \cs{exp_after:wN} has already been expanded,
+% \cs{int_value:w} has already seen |12345|, and
+% \cs{exp:w} is still looking for a number. It finds
+% \cs{exp_end:}, hence expands to nothing. Now, \cs{int_value:w} sees
+% the \texttt{;}, which cannot be part of a number. The expansion
+% stops, and we are left with
+% \begin{syntax}
+% |\add:ww| 12345 ; 333444 ;
+% \end{syntax}
+% which can safely perform the addition by grabbing two arguments
+% delimited by~|;|.
+%
+% If we were to continue parsing the expression, then the following
+% number should also be cleaned up before the next use of a binary
+% operation such as |\add:ww|. Just like \cs{int_value:w} |12345|
+% \cs{exp_after:wN}~|;| expanded what follows once, we need |\add:ww|
+% to do the calculation, and in the process to expand the following
+% once. This is also true in our real application: all the functions of
+% the form \cs[no-index]{@@_\ldots_o:ww} expand what follows once. This comes at the
+% cost of leaving tokens in the input stack, and we need to be
+% careful not to waste this memory. All of our discussion above is nice
+% but simplistic, as operations should not simply be performed in the
+% order they appear.
+%
+% \subsubsection{Precedence and infix operators}
+%
+% The various operators we will encounter have different precedences,
+% which influence the order of calculations: $1+2\times 3 = 1+(2\times
+% 3)$ because $\times$~has a higher precedence than~$+$. The true
+% analog of our macro |\operand:w| must thus take care of that. When
+% looking for an operand, it needs to perform calculations until
+% reaching an operator which has lower precedence than the one which
+% called |\operand:w|. This means that |\operand:w| must know what the
+% previous binary operator is, or rather, its precedence: we thus rename
+% it |\operand:Nw|. Let us describe as an example how we plan to do
+% the calculation |41-2^3*4+5|. More precisely we describe how to
+% perform the first operation in this expression. Here, we abuse
+% notations: the first argument of |\operand:Nw| should be an integer
+% constant (\cs{c_@@_prec_plus_int}, \ldots{}) equal to the precedence
+% of the given operator, not directly the operator itself.
+% \begin{itemize}
+% \item Clean up~|41| and find~|-|. We call |\operand:Nw|~|-| to find
+% the second operand.
+% \item Clean up~|2| and find~|^|.
+% \item Compare the precedences of |-| and~|^|. Since the latter is
+% higher, we need to compute the exponentiation. For this, find the
+% second operand with a nested call to |\operand:Nw|~|^|.
+% \item Clean up~|3| and find~|*|.
+% \item Compare the precedences of |^| and~|*|. Since the former is
+% higher, |\operand:Nw|~|^| has found the second operand of the
+% exponentiation, which is computed: $2^{3} = 8$.
+% \item We now have |41-8*4+5|, and |\operand:Nw|~|-| is still
+% looking for a second operand for the subtraction. Is it~$8$?
+% \item Compare the precedences of |-| and~|*|. Since the latter is
+% higher, we are not done with~$8$. Call |\operand:Nw|~|*| to find
+% the second operand of the multiplication.
+% \item Clean up~|4|, and find~|+|.
+% \item Compare the precedences of |*| and~|+|. Since the former is
+% higher, |\operand:Nw|~|*| has found the second operand of the
+% multiplication, which is computed: $8*4 = 32$.
+% \item We now have |41-32+5|, and |\operand:Nw|~|-| is still looking
+% for a second operand for the subtraction. Is it~$32$?
+% \item Compare the precedences of |-| and~|+|. Since they are equal,
+% |\operand:Nw|~|-| has found the second operand for the
+% subtraction, which is computed: $41-32=9$.
+% \item We now have |9+5|.
+% \end{itemize}
+% The procedure above stops short of performing all computations, but
+% adding a surrounding call to |\operand:Nw| with a very low precedence
+% ensures that all computations are performed before |\operand:Nw|
+% is done. Adding a trailing marker with the same very low precedence
+% prevents the surrounding |\operand:Nw| from going beyond the marker.
+%
+% The pattern above to find an operand for a given operator, is to find
+% one number and the next operator, then compare precedences to know if
+% the next computation should be done. If it should, then perform it
+% after finding its second operand, and look at the next operator, then
+% compare precedences to know if the next computation should be done.
+% This continues until we find that the next computation should not be
+% done. Then, we stop.
+%
+% We are now ready to get a bit more technical and describe which of the
+% \pkg{l3fp-parse} functions correspond to each step above.
+%
+% First, \cs{@@_parse_operand:Nw} is the |\operand:Nw| function above,
+% with small modifications due to expansion issues discussed later. We
+% denote by \meta{precedence} the argument of \cs{@@_parse_operand:Nw},
+% that is, the precedence of the binary operator whose operand we are
+% trying to find. The basic action is to read numbers from the input
+% stream. This is done by \cs{@@_parse_one:Nw}. A first approximation
+% of this function is that it reads one \meta{number}, performing no
+% computation, and finds the following binary \meta{operator}. Then it
+% expands to
+% \begin{quote}
+% \meta{number}\\
+% | \__fp_parse_infix_|\meta{operator}|:N| \meta{precedence}
+% \end{quote}
+% expanding the \texttt{infix} auxiliary before leaving the above in the
+% input stream.
+%
+% We now explain the \texttt{infix} auxiliaries. We need some
+% flexibility in how we treat the case of equal precedences: most often,
+% the first operation encountered should be performed, such as |1-2-3|
+% being computed as |(1-2)-3|, but |2^3^4| should be evaluated as
+% |2^(3^4)| instead. For this reason, and to support the equivalence
+% between |**| and~|^| more easily, each binary operator is converted to
+% a control sequence |\__fp_parse_infix_|\meta{operator}|:N| when it is
+% encountered for the first time. Instead of passing both precedences
+% to a test function to do the comparison steps above, we pass the
+% \meta{precedence} (of the earlier operator) to the \texttt{infix}
+% auxiliary for the following \meta{operator}, to know whether to
+% perform the computation of the \meta{operator}. If it should not be
+% performed, the \texttt{infix} auxiliary expands to
+% \begin{syntax}
+% |@| \cs{use_none:n} |\__fp_parse_infix_|\meta{operator}|:N|
+% \end{syntax}
+% and otherwise it calls \cs{@@_parse_operand:Nw} with the precedence of
+% the \meta{operator} to find its second operand \meta{number_2} and the
+% next \meta{operator_2}, and expands to
+% \begin{syntax}
+% |@| \cs{@@_parse_apply_binary:NwNwN}
+% ~~~~\meta{operator} \meta{number_2}
+% |@| |\__fp_parse_infix_|\meta{operator_2}|:N|
+% \end{syntax}
+% The \texttt{infix} function is responsible for comparing precedences,
+% but cannot directly call the computation functions, because the first
+% operand \meta{number} is before the \texttt{infix} function in the
+% input stream. This is why we stop the expansion here and give control
+% to another function to close the loop.
+%
+% A definition of \cs{@@_parse_operand:Nw} \meta{precedence} with some
+% of the expansion control removed is
+% \begin{syntax}
+% \cs{exp_after:wN} \cs{@@_parse_continue:NwN}
+% \cs{exp_after:wN} \meta{precedence}
+% \cs{exp:w} \cs{exp_end_continue_f:w}
+% ~~\cs{@@_parse_one:Nw} \meta{precedence}
+% \end{syntax}
+% This expands \cs{@@_parse_one:Nw} \meta{precedence} completely, which
+% finds a number, wraps the next \meta{operator} into an \texttt{infix}
+% function, feeds this function the \meta{precedence}, and expands it,
+% yielding either
+% \begin{syntax}
+% \cs{@@_parse_continue:NwN} \meta{precedence}
+% \meta{number} |@|
+% \cs{use_none:n} |\__fp_parse_infix_|\meta{operator}|:N|
+% \end{syntax}
+% or
+% \begin{syntax}
+% \cs{@@_parse_continue:NwN} \meta{precedence}
+% \meta{number} |@|
+% \cs{@@_parse_apply_binary:NwNwN}
+% ~~\meta{operator} \meta{number_2}
+% |@| |\__fp_parse_infix_|\meta{operator_2}|:N|
+% \end{syntax}
+% The definition of \cs{@@_parse_continue:NwN} is then very simple:
+% \begin{syntax}
+% |\cs_new:Npn \__fp_parse_continue:NwN #1#2@#3 { #3 #1 #2 @ }|
+% \end{syntax}
+% In the first case, |#3|~is \cs{use_none:n}, yielding
+% \begin{syntax}
+% \cs{use_none:n} \meta{precedence} \meta{number} |@|
+% |\__fp_parse_infix_|\meta{operator}|:N|
+% \end{syntax}
+% then \meta{number} |@| |\__fp_parse_infix_|\meta{operator}|:N|. In
+% the second case, |#3|~is \cs{@@_parse_apply_binary:NwNwN}, whose role
+% is to compute \meta{number} \meta{operator} \meta{number_2} and to
+% prepare for the next comparison of precedences: first we get
+% \begin{syntax}
+% \cs{@@_parse_apply_binary:NwNwN}
+% ~~\meta{precedence} \meta{number} |@|
+% ~~\meta{operator} \meta{number_2}
+% |@| |\__fp_parse_infix_|\meta{operator_2}|:N|
+% \end{syntax}
+% then
+% \begin{syntax}
+% \cs{exp_after:wN} \cs{@@_parse_continue:NwN}
+% \cs{exp_after:wN} \meta{precedence}
+% \cs{exp:w} \cs{exp_end_continue_f:w}
+% |\__fp_|\meta{operator}|_o:ww| \meta{number} \meta{number_2}
+% \cs{exp:w} \cs{exp_end_continue_f:w}
+% |\__fp_parse_infix_|\meta{operator_2}|:N| \meta{precedence}
+% \end{syntax}
+% where |\__fp_|\meta{operator}|_o:ww| computes \meta{number}
+% \meta{operator} \meta{number_2} and expands after the result, thus
+% triggers the comparison of the precedence of the \meta{operator_2} and
+% the \meta{precedence}, continuing the loop.
+%
+% We have introduced the most important functions here, and the next few
+% paragraphs we describe various subtleties.
+%
+% \subsubsection{Prefix operators, parentheses, and functions}
+%
+% Prefix operators (unary |-|, |+|,~|!|) and parentheses are taken care
+% of by the same mechanism, and functions (\texttt{sin}, \texttt{exp},
+% etc.) as well. Finding the argument of the unary~|-|, for instance,
+% is very similar to grabbing the second operand of a binary infix
+% operator, with a subtle precedence explained below. Once that operand
+% is found, the operator can be applied to it (for the unary~|-|, this
+% simply flips the sign). A left parenthesis is just a prefix operator
+% with a very low precedence equal to that of the closing parenthesis
+% (which is treated as an infix operator, since it normally appears just
+% after numbers), so that all computations are performed until the
+% closing parenthesis. The prefix operator associated to the left
+% parenthesis does not alter its argument, but it removes the closing
+% parenthesis (with some checks).
+%
+% Prefix operators are the reason why we only summarily described the
+% function \cs{@@_parse_one:Nw} earlier. This function is responsible
+% for reading in the input stream the first possible \meta{number} and
+% the next infix \meta{operator}. If what follows \cs{@@_parse_one:Nw}
+% \meta{precedence} is a prefix operator, then we must find the operand
+% of this prefix operator through a nested call to
+% \cs{@@_parse_operand:Nw} with the appropriate precedence, then apply
+% the operator to the operand found to yield the result of
+% \cs{@@_parse_one:Nw}. So far, all is simple.
+%
+% The unary operators |+|, |-|,~|!| complicate things a little bit:
+% |-3**2| should be $-(3^2)=-9$, and not $(-3)^2=9$. This would easily
+% be done by giving~|-| a lower precedence, equal to that of the infix
+% |+| and~|-|. Unfortunately, this fails in cases such as |3**-2*4|,
+% yielding $3^{-2\times 4}$ instead of the correct $3^{-2}\times 4$. A
+% second attempt would be to call \cs{@@_parse_operand:Nw} with the
+% \meta{precedence} of the previous operator, but |0>-2+3| is then
+% parsed as |0>-(2+3)|: the addition is performed because it binds more
+% tightly than the comparision which precedes~|-|. The correct approach
+% is for a unary~|-| to perform operations whose precedence is greater
+% than both that of the previous operation, and that of the unary~|-|
+% itself. The unary~|-| is given a precedence higher than
+% multiplication and division. This does not lead to any surprising
+% result, since $-(x/y) = (-x)/y$ and similarly for multiplication, and
+% it reduces the number of nested calls to \cs{@@_parse_operand:Nw}.
+%
+% Functions are implemented as prefix operators with very high
+% precedence, so that their argument is the first number that can
+% possibly be built.
+%
+% Note that contrarily to the \texttt{infix} functions discussed
+% earlier, the \texttt{prefix} functions do perform tests on the
+% previous \meta{precedence} to decide whether to find an argument or
+% not, since we know that we need a number, and must never stop there.
+%
+% \subsubsection{Numbers and reading tokens one by one}
+%
+% So far, we have glossed over one important point: what is a
+% \enquote{number}? A number is typically given in the form
+% \meta{significand}|e|\meta{exponent}, where the \meta{significand} is
+% any non-empty string composed of decimal digits and at most one
+% decimal separator (a period), the exponent
+% \enquote{\texttt{e}\meta{exponent}} is optional and is composed of an
+% exponent mark~|e| followed by a possibly empty string of signs
+% |+| or~|-| and a non-empty string of decimal digits. The
+% \meta{significand} can also be an integer, dimension, skip, or muskip
+% variable, in which case dimensions are converted from points (or mu
+% units) to floating points, and the \meta{exponent} can also be an
+% integer variable. Numbers can also be given as floating point
+% variables, or as named constants such as |nan|, |inf| or~|pi|. We may
+% add more types in the future.
+%
+% When \cs{@@_parse_one:Nw} is looking for a \enquote{number}, here is
+% what happens.
+% \begin{itemize}
+% \item If the next token is a control sequence with the meaning of
+% \cs{scan_stop:}, it can be: \cs{s_@@}, in which case our job is
+% done, as what follows is an internal floating point number, or
+% \cs{s_@@_expr_mark}, in which case the expression has come to an early
+% end, as we are still looking for a number here, or something else,
+% in which case we consider the control sequence to be a bad
+% variable resulting from \texttt{c}-expansion.
+% \item If the next token is a control sequence with a different
+% meaning, we assume that it is a register, unpack it with
+% \cs{tex_the:D}, and use its value (in \texttt{pt} for dimensions
+% and skips, \texttt{mu} for muskips) as the \meta{significand} of a
+% number: we look for an exponent.
+% \item If the next token is a digit, we remove any leading zeros,
+% then read a significand larger than~$1$ if the next character is a
+% digit, read a significand smaller than~$1$ if the next character
+% is a period, or we have found a significand equal to~$0$
+% otherwise, and look for an exponent.
+% \item If the next token is a letter, we collect more letters until
+% the first non-letter: the resulting word may denote a function
+% such as |asin|, a constant such as |pi| or be unknown. In the
+% first case, we call \cs{@@_parse_operand:Nw} to find the argument
+% of the function, then apply the function, before declaring that we
+% are done. Otherwise, we are done, either with the value of the
+% constant, or with the value |nan| for unknown words.
+% \item If the next token is anything else, we check whether it is a
+% known prefix operator, in which case \cs{@@_parse_operand:Nw}
+% finds its operand. If it is not known, then either a number is
+% missing (if the token is a known infix operator) or the token is
+% simply invalid in floating point expressions.
+% \end{itemize}
+% Once a number is found, \cs{@@_parse_one:Nw} also finds an infix
+% operator. This goes as follows.
+% \begin{itemize}
+% \item If the next token is a control sequence, it could be the
+% special marker \cs{s_@@_expr_mark}, and
+% otherwise it is a case of juxtaposing numbers, such as
+% |2\c_zero_int|, with an implied multiplication.
+% \item If the next token is a letter, it is also a case of
+% juxtaposition, as letters cannot be proper infix operators.
+% \item Otherwise (including in the case of digits), if the token is a
+% known infix operator, the appropriate
+% |\__fp_infix_|\meta{operator}|:N| function is built, and if it
+% does not exist, we complain. In particular, the juxtaposition
+% |\c_zero_int 2| is disallowed.
+% \end{itemize}
+%
+% In the above, we need to test whether a character token~|#1| is a
+% digit:
+% \begin{verbatim}
+% \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+% is a digit
+% \else:
+% not a digit
+% \fi:
+% \end{verbatim}
+% To exclude |0|, replace |9| by |10|. The use of
+% \cs{token_to_str:N} ensures that a digit with any catcode is detected.
+% To test if a character token is a letter, we need to work with its
+% character code, testing if |`#1| lies in $[65,90]$ (uppercase letters)
+% or $[97,112]$ (lowercase letters)
+% \begin{verbatim}
+% \if_int_compare:w \__fp_int_eval:w
+% ( `#1 \if_int_compare:w `#1 > `Z - 32 \fi: ) / 26 = 3 \exp_stop_f:
+% is a letter
+% \else:
+% not a letter
+% \fi:
+% \end{verbatim}
+% At all steps, we try to accept all category codes: when |#1|~is kept
+% to be used later, it is almost always converted to category code other
+% through \cs{token_to_str:N}. More precisely, catcodes $\{3, 6, 7, 8,
+% 11, 12\}$ should work without trouble, but not $\{1, 2, 4, 10, 13\}$,
+% and of course $\{0, 5, 9\}$ cannot become tokens.
+%
+% Floating point expressions should behave as much as possible like
+% \eTeX{}-based integer expressions and dimension expressions. In
+% particular, \texttt{f}-expansion should be performed as the expression
+% is read, token by token, forcing the expansion of protected macros,
+% and ignoring spaces. One advantage of expanding at every step is that
+% restricted expandable functions can then be used in floating point
+% expressions just as they can be in other kinds of expressions.
+% Problematically, spaces stop \texttt{f}-expansion: for instance, the
+% macro~|\X| below would not be expanded if we simply performed
+% \texttt{f}-expansion.
+% \begin{verbatim}
+% \DeclareDocumentCommand {\test} {m} { \fp_eval:n {#1} }
+% \ExplSyntaxOff
+% \test { 1 + \X }
+% \end{verbatim}
+% Of course, spaces typically do not appear in a code setting, but may very
+% easily come in document-level input, from which some expressions may
+% come. To avoid this problem, at every step, we do essentially what
+% \cs{use:f} would do: take an argument, put it back in the input
+% stream, then \texttt{f}-expand it. This is not a complete solution,
+% since a macro's expansion could contain leading spaces which would stop
+% the \texttt{f}-expansion before further macro calls are performed.
+% However, in practice it should be enough: in particular, floating
+% point numbers are correctly expanded to the underlying \cs{s_@@}
+% \ldots{} structure. The \texttt{f}-expansion is performed by
+% \cs{@@_parse_expand:w}.
+%
+% ^^A begin[todo]
+%
+% \subsection{Main auxiliary functions}
+%
+% \begin{macro}[rEXP]{\@@_parse_operand:Nw}
+% \begin{syntax}
+% \cs{exp:w} \cs{@@_parse_operand:Nw} \meta{precedence} \cs{@@_parse_expand:w}
+% \end{syntax}
+% Reads the \enquote{\ttfamily\ldots{}}, performing every computation
+% with a precedence higher than \meta{precedence}, then expands to
+% \begin{syntax}
+% \meta{result} |@| |\__fp_parse_infix_|\meta{operation}|:N| \ldots{}
+% \end{syntax}
+% where the \meta{operation} is the first operation with a lower
+% precedence, possibly \texttt{end}, and the
+% \enquote{\ttfamily\ldots{}} start just after the \meta{operation}.
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_infix_+:N}
+% \begin{syntax}
+% \cs{@@_parse_infix_+:N} \meta{precedence} \ldots{}
+% \end{syntax}
+% If |+|~has a precedence higher than the \meta{precedence}, cleans up
+% a second \meta{operand} and finds the \meta{operation_2} which
+% follows, and expands to
+% \begin{syntax}
+% |@| \cs{@@_parse_apply_binary:NwNwN} |+| \meta{operand} |@| \cs{@@_parse_infix_\meta{operation_2}:N} \ldots{}
+% \end{syntax}
+% Otherwise expands to
+% \begin{syntax}
+% |@| \cs{use_none:n} \cs{@@_parse_infix_+:N} \ldots{}
+% \end{syntax}
+% A similar function exists for each infix operator.
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_one:Nw}
+% \begin{syntax}
+% \cs{@@_parse_one:Nw} \meta{precedence} \ldots{}
+% \end{syntax}
+% Cleans up one or two operands depending on how the precedence of the
+% next operation compares to the \meta{precedence}. If the following
+% \meta{operation} has a precedence higher than \meta{precedence},
+% expands to
+% \begin{syntax}
+% \meta{operand_1} |@| \cs{@@_parse_apply_binary:NwNwN} \meta{operation} \meta{operand_2} |@| |\__fp_parse_infix_|\meta{operation_2}|:N| \ldots{}
+% \end{syntax}
+% and otherwise expands to
+% \begin{syntax}
+% \meta{operand} |@| \cs{use_none:n} |\__fp_parse_infix_|\meta{operation}|:N| \ldots{}
+% \end{syntax}
+% \end{macro}
+%
+% ^^A end[todo]
+%
+% \subsection{Helpers}
+%
+% \begin{macro}[rEXP]{\@@_parse_expand:w}
+% \begin{syntax}
+% \cs{exp:w} \cs{@@_parse_expand:w} \meta{tokens}
+% \end{syntax}
+% This function must always come within a \cs{exp:w} expansion.
+% The \meta{tokens} should be the part of the expression that we have
+% not yet read. This requires in particular closing all conditionals
+% properly before expanding.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_expand:w #1 { \exp_end_continue_f:w #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_return_semicolon:w}
+% This very odd function swaps its position with the following
+% \cs{fi:} and removes \cs{@@_parse_expand:w} normally responsible for
+% expansion. That turns out to be useful.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_return_semicolon:w
+ #1 \fi: \@@_parse_expand:w { \fi: ; #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_parse_digits_vii:N ,
+% \@@_parse_digits_vi:N ,
+% \@@_parse_digits_v:N ,
+% \@@_parse_digits_iv:N ,
+% \@@_parse_digits_iii:N ,
+% \@@_parse_digits_ii:N ,
+% \@@_parse_digits_i:N ,
+% \@@_parse_digits_:N
+% }
+% These functions must be called within an \cs{int_value:w} or
+% \cs{@@_int_eval:w} construction. The first token which follows must
+% be \texttt{f}-expanded prior to calling those functions. The
+% functions read tokens one by one, and output digits into the input
+% stream, until meeting a non-digit, or up to a number of digits equal
+% to their index. The full expansion is
+% \begin{syntax}
+% \meta{digits} |;| \meta{filling 0} |;| \meta{length}
+% \end{syntax}
+% where \meta{filling 0} is a string of zeros such that \meta{digits}
+% \meta{filling 0} has the length given by the index of the function,
+% and \meta{length} is the number of zeros in the \meta{filling 0}
+% string. Each function puts a digit into the input stream and calls
+% the next function, until we find a non-digit. We are careful to
+% pass the tested tokens through \cs{token_to_str:N} to normalize
+% their category code.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1 #2 #3
+ {
+ \cs_new:cpn { @@_parse_digits_ #1 :N } ##1
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N ##1 \exp_stop_f:
+ \token_to_str:N ##1 \exp_after:wN #2 \exp:w
+ \else:
+ \@@_parse_return_semicolon:w #3 ##1
+ \fi:
+ \@@_parse_expand:w
+ }
+ }
+\@@_tmp:w {vii} \@@_parse_digits_vi:N { 0000000 ; 7 }
+\@@_tmp:w {vi} \@@_parse_digits_v:N { 000000 ; 6 }
+\@@_tmp:w {v} \@@_parse_digits_iv:N { 00000 ; 5 }
+\@@_tmp:w {iv} \@@_parse_digits_iii:N { 0000 ; 4 }
+\@@_tmp:w {iii} \@@_parse_digits_ii:N { 000 ; 3 }
+\@@_tmp:w {ii} \@@_parse_digits_i:N { 00 ; 2 }
+\@@_tmp:w {i} \@@_parse_digits_:N { 0 ; 1 }
+\cs_new:Npn \@@_parse_digits_:N { ; ; 0 }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Parsing one number}
+%
+% \begin{macro}[EXP]{\@@_parse_one:Nw}
+% This function finds one number, and packs the symbol which follows
+% in an \cs[no-index]{@@_parse_infix_\ldots{}} csname.
+% |#1|~is the previous \meta{precedence},
+% and |#2|~the first token of the operand. We distinguish four cases:
+% |#2|~is equal to \cs{scan_stop:} in meaning, |#2|~is a different
+% control sequence, |#2|~is a digit, and |#2|~is something else (this
+% last case is split further later). Despite the earlier
+% \texttt{f}-expansion, |#2|~may still be expandable if it was
+% protected by \cs{exp_not:N}, as may happen with the \LaTeXe{} command
+% \tn{protect}. Using a well placed \cs{reverse_if:N}, this case is
+% sent to \cs{@@_parse_one_fp:NN} which deals with it robustly.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_one:Nw #1 #2
+ {
+ \if_catcode:w \scan_stop: \exp_not:N #2
+ \exp_after:wN \if_meaning:w \exp_not:N #2 #2 \else:
+ \exp_after:wN \reverse_if:N
+ \fi:
+ \if_meaning:w \scan_stop: #2
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_one_fp:NN
+ \else:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_one_register:NN
+ \fi:
+ \else:
+ \if_int_compare:w 9 < 1 \token_to_str:N #2 \exp_stop_f:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_one_digit:NN
+ \else:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_one_other:NN
+ \fi:
+ \fi:
+ #1 #2
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_one_fp:NN,
+% \@@_exp_after_expr_mark_f:nw,
+% \@@_exp_after_?_f:nw
+% }
+% This function receives a \meta{precedence} and a control sequence
+% equal to \cs{scan_stop:} in meaning. There are three cases.
+% \begin{itemize}
+% \item \cs{s_@@} starts a floating point number, and we call
+% \cs{@@_exp_after_f:nw}, which |f|-expands after the floating
+% point.
+% \item \cs{s_@@_expr_mark} is a premature end, we call
+% \cs{@@_exp_after_expr_mark_f:nw}, which triggers an |fp-early-end|
+% error.
+% \item For a control sequence not containing \cs[no-index]{s_@@}, we call
+% \cs{@@_exp_after_?_f:nw}, causing a |bad-variable| error.
+% \end{itemize}
+% This scheme is extensible: additional types can be added by starting
+% the variables with a scan mark of the form \cs[no-index]{s_@@_\meta{type}} and
+% defining |\__fp_exp_after_|\meta{type}|_f:nw|. In all cases, we
+% make sure that the second argument of \cs{@@_parse_infix:NN} is
+% correctly expanded.
+% A special case only enabled in \LaTeXe{} is that if \tn{protect} is
+% encountered then the error message mentions the control sequence
+% which follows it rather than \tn{protect} itself. The test for
+% \LaTeXe{} uses \tn{@unexpandable@protect} rather than \tn{protect}
+% because \tn{protect} is often \cs{scan_stop:} hence \enquote{does
+% not exist}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_one_fp:NN #1
+ {
+ \@@_exp_after_any_f:nw
+ {
+ \exp_after:wN \@@_parse_infix:NN
+ \exp_after:wN #1 \exp:w \@@_parse_expand:w
+ }
+ }
+\cs_new:Npn \@@_exp_after_expr_mark_f:nw #1
+ {
+ \int_case:nnF { \exp_after:wN \use_i:nnn \use_none:nnn #1 }
+ {
+ \c_@@_prec_comma_int { }
+ \c_@@_prec_tuple_int { }
+ \c_@@_prec_end_int
+ {
+ \exp_after:wN \c_@@_empty_tuple_fp
+ \exp:w \exp_end_continue_f:w
+ }
+ }
+ {
+ \msg_expandable_error:nn { fp } { early-end }
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w
+ }
+ #1
+ }
+\cs_new:cpn { @@_exp_after_?_f:nw } #1#2
+ {
+ \msg_expandable_error:nnn { kernel } { bad-variable }
+ {#2}
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w #1
+ }
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_if_exist:NT #1
+ {
+ \cs_gset:cpn { @@_exp_after_?_f:nw } ##1##2
+ {
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w ##1
+ \str_if_eq:nnTF {##2} { \protect }
+ {
+ \cs_if_eq:NNTF ##2 #1 { \use_i:nn } { \use:n }
+ {
+ \msg_expandable_error:nnn { fp }
+ { robust-cmd }
+ }
+ }
+ {
+ \msg_expandable_error:nnn { kernel }
+ { bad-variable } {##2}
+ }
+ }
+ }
+ }
+\exp_args:Nc \@@_tmp:w { @unexpandable@protect }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_one_register:NN,
+% \@@_parse_one_register_aux:Nw,
+% \@@_parse_one_register_auxii:wwwNw,
+% \@@_parse_one_register_int:www,
+% \@@_parse_one_register_mu:www,
+% \@@_parse_one_register_dim:ww,
+% }
+% This is called whenever~|#2| is a control sequence other than
+% \cs{scan_stop:} in meaning. We special-case \tn{wd}, \tn{ht}, \tn{dp}
+% (see later) and otherwise assume that it is a register, but
+% carefully unpack it with \cs{tex_the:D} within braces. First, we
+% find the exponent following~|#2|. Then we unpack~|#2| with
+% \cs{tex_the:D}, and the \texttt{auxii} auxiliary distinguishes
+% integer registers from dimensions/skips from muskips, according to
+% the presence of a period and/or of |pt|. For integers, simply
+% convert \meta{value}|e|\meta{exponent} to a floating point number
+% with \cs{@@_parse:n} (this is somewhat wasteful). For other
+% registers, the decimal rounding provided by \TeX{} does not
+% accurately represent the binary value that it manipulates, so we
+% extract this binary value as a number of scaled points with
+% \cs{int_value:w} \cs{dim_to_decimal_in_sp:n} |{| \meta{decimal value} |pt| |}|, and
+% use an auxiliary of \cs{dim_to_fp:n}, which performs the
+% multiplication by $2^{-16}$, correctly rounded.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_one_register:NN #1#2
+ {
+ \exp_after:wN \@@_parse_infix_after_operand:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \@@_parse_one_register_special:N #2
+ \exp_after:wN \@@_parse_one_register_aux:Nw
+ \exp_after:wN #2
+ \int_value:w
+ \exp_after:wN \@@_parse_exponent:N
+ \exp:w \@@_parse_expand:w
+ }
+\cs_new:Npe \@@_parse_one_register_aux:Nw #1
+ {
+ \exp_not:n
+ {
+ \exp_after:wN \use:nn
+ \exp_after:wN \@@_parse_one_register_auxii:wwwNw
+ }
+ \exp_not:N \exp_after:wN { \exp_not:N \tex_the:D #1 }
+ ; \exp_not:N \@@_parse_one_register_dim:ww
+ \tl_to_str:n { pt } ; \exp_not:N \@@_parse_one_register_mu:www
+ . \tl_to_str:n { pt } ; \exp_not:N \@@_parse_one_register_int:www
+ \s_@@_stop
+ }
+\exp_args:Nno \use:nn
+ { \cs_new:Npn \@@_parse_one_register_auxii:wwwNw #1 . #2 }
+ { \tl_to_str:n { pt } #3 ; #4#5 \s_@@_stop }
+ { #4 #1.#2; }
+\exp_args:Nno \use:nn
+ { \cs_new:Npn \@@_parse_one_register_mu:www #1 }
+ { \tl_to_str:n { mu } ; #2 ; }
+ { \@@_parse_one_register_dim:ww #1 ; }
+\cs_new:Npn \@@_parse_one_register_int:www #1; #2.; #3;
+ { \@@_parse:n { #1 e #3 } }
+\cs_new:Npn \@@_parse_one_register_dim:ww #1; #2;
+ {
+ \exp_after:wN \@@_from_dim_test:ww
+ \int_value:w #2 \exp_after:wN ,
+ \int_value:w \dim_to_decimal_in_sp:n { #1 pt } ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_parse_one_register_special:N,
+% \@@_parse_one_register_math:NNw,
+% \@@_parse_one_register_wd:w,
+% \@@_parse_one_register_wd:Nw
+% }
+% The \tn{wd}, \tn{dp}, \tn{ht} primitives expect an integer argument.
+% We abuse the exponent parser to find the integer argument: simply
+% include the exponent marker~|e|. Once that \enquote{exponent} is
+% found, use \cs{tex_the:D} to find the box dimension and then copy
+% what we did for dimensions.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_one_register_special:N #1
+ {
+ \if_meaning:w \box_wd:N #1 \@@_parse_one_register_wd:w \fi:
+ \if_meaning:w \box_ht:N #1 \@@_parse_one_register_wd:w \fi:
+ \if_meaning:w \box_dp:N #1 \@@_parse_one_register_wd:w \fi:
+ \if_meaning:w \infty #1
+ \@@_parse_one_register_math:NNw \infty #1
+ \fi:
+ \if_meaning:w \pi #1
+ \@@_parse_one_register_math:NNw \pi #1
+ \fi:
+ }
+\cs_new:Npn \@@_parse_one_register_math:NNw
+ #1#2#3#4 \@@_parse_expand:w
+ {
+ #3
+ \str_if_eq:nnTF {#1} {#2}
+ {
+ \msg_expandable_error:nnn
+ { fp } { infty-pi } {#1}
+ \c_nan_fp
+ }
+ { #4 \@@_parse_expand:w }
+ }
+\cs_new:Npn \@@_parse_one_register_wd:w
+ #1#2 \exp_after:wN #3#4 \@@_parse_expand:w
+ {
+ #1
+ \exp_after:wN \@@_parse_one_register_wd:Nw
+ #4 \@@_parse_expand:w e
+ }
+\cs_new:Npn \@@_parse_one_register_wd:Nw #1#2 ;
+ {
+ \exp_after:wN \@@_from_dim_test:ww
+ \exp_after:wN 0 \exp_after:wN ,
+ \int_value:w \dim_to_decimal_in_sp:n { #1 #2 } ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_one_digit:NN}
+% A digit marks the beginning of an explicit floating point number.
+% Once the number is found, we catch the case of overflow and
+% underflow with \cs{@@_sanitize:wN}, then
+% \cs{@@_parse_infix_after_operand:NwN} expands \cs{@@_parse_infix:NN}
+% after the number we find, to wrap the following infix operator as
+% required. Finding the number itself begins by removing leading
+% zeros: further steps are described later.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_one_digit:NN #1
+ {
+ \exp_after:wN \@@_parse_infix_after_operand:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN \@@_sanitize:wN
+ \int_value:w \@@_int_eval:w 0 \@@_parse_trim_zeros:N
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_one_other:NN}
+% For this function, |#2|~is a character token which is not a digit.
+% If it is an \textsc{ascii} letter, \cs{@@_parse_letters:N} beyond this one and give
+% the result to \cs{@@_parse_word:Nw}. Otherwise, the character is
+% assumed to be a prefix operator, and we build
+% |\__fp_parse_prefix_|\meta{operator}|:Nw|.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_one_other:NN #1 #2
+ {
+ \if_int_compare:w
+ \@@_int_eval:w
+ ( `#2 \if_int_compare:w `#2 > `Z - 32 \fi: ) / 26
+ = 3 \exp_stop_f:
+ \exp_after:wN \@@_parse_word:Nw
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp:w \exp_after:wN \@@_parse_letters:N
+ \exp:w
+ \else:
+ \exp_after:wN \@@_parse_prefix:NNN
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \cs:w
+ @@_parse_prefix_ \token_to_str:N #2 :Nw
+ \exp_after:wN
+ \cs_end:
+ \exp:w
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_word:Nw}
+% \begin{macro}[rEXP]{\@@_parse_letters:N}
+% Finding letters is a simple recursion. Once \cs{@@_parse_letters:N}
+% has done its job, we try to build a control sequence from the
+% word~|#2|. If it is a known word, then the corresponding action is
+% taken, and otherwise, we complain about an unknown word, yield
+% \cs{c_nan_fp}, and look for the following infix operator. Note that
+% the unknown word could be a mistyped function as well as a mistyped
+% constant, so there is no way to tell whether to look for arguments;
+% we do not.
+% The standard requires \enquote{inf} and \enquote{infinity} and
+% \enquote{nan} to be recognized regardless of case, but we probably
+% don't want to allow every \pkg{l3fp} word to have an arbitrary
+% mixture of lower and upper case, so we test and use a
+% differently-named control sequence.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_word:Nw #1#2;
+ {
+ \cs_if_exist_use:cF { @@_parse_word_#2:N }
+ {
+ \cs_if_exist_use:cF
+ { @@_parse_caseless_ \str_casefold:n {#2} :N }
+ {
+ \msg_expandable_error:nnn
+ { fp } { unknown-fp-word } {#2}
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w
+ \@@_parse_infix:NN
+ }
+ }
+ #1
+ }
+\cs_new:Npn \@@_parse_letters:N #1
+ {
+ \exp_end_continue_f:w
+ \if_int_compare:w
+ \if_catcode:w \scan_stop: \exp_not:N #1
+ 0
+ \else:
+ \@@_int_eval:w
+ ( `#1 \if_int_compare:w `#1 > `Z - 32 \fi: ) / 26
+ \fi:
+ = 3 \exp_stop_f:
+ \exp_after:wN #1
+ \exp:w \exp_after:wN \@@_parse_letters:N
+ \exp:w
+ \else:
+ \@@_parse_return_semicolon:w #1
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_parse_prefix:NNN, \@@_parse_prefix_unknown:NNN}
+% For this function, |#1|~is the previous \meta{precedence}, |#2|~is
+% the operator just seen, and |#3|~is a control sequence which
+% implements the operator if it is a known operator. If this control
+% sequence is \cs{scan_stop:}, then the operator is in fact unknown.
+% Either the expression is missing a number there (if the operator is
+% valid as an infix operator), and we put \texttt{nan}, wrapping the
+% infix operator in a csname as appropriate, or the character is
+% simply invalid in floating point expressions, and we continue
+% looking for a number, starting again from \cs{@@_parse_one:Nw}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_prefix:NNN #1#2#3
+ {
+ \if_meaning:w \scan_stop: #3
+ \exp_after:wN \@@_parse_prefix_unknown:NNN
+ \exp_after:wN #2
+ \fi:
+ #3 #1
+ }
+\cs_new:Npn \@@_parse_prefix_unknown:NNN #1#2#3
+ {
+ \cs_if_exist:cTF { @@_parse_infix_ \token_to_str:N #1 :N }
+ {
+ \msg_expandable_error:nnn
+ { fp } { missing-number } {#1}
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w
+ \@@_parse_infix:NN #3 #1
+ }
+ {
+ \msg_expandable_error:nnn
+ { fp } { unknown-symbol } {#1}
+ \@@_parse_one:Nw #3
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Numbers: trimming leading zeros}
+%
+% Numbers are parsed as follows: first we trim leading zeros, then
+% if the next character is a digit, start reading a significand $\geq 1$
+% with the set of functions |\__fp_parse_large|\ldots{}; if it is a
+% period, the significand is~$<1$; and otherwise it is zero. In the
+% second case, trim additional zeros after the period, counting them for
+% an exponent shift $\meta{exp_1}<0$, then read the significand with the
+% set of functions |\__fp_parse_small|\ldots{} Once the significand is
+% read, read the exponent if |e|~is present.
+%
+% \begin{macro}[rEXP]{\@@_parse_trim_zeros:N, \@@_parse_trim_end:w}
+% This function expects an already expanded token. It removes any
+% leading zero, then distinguishes three cases: if the first non-zero
+% token is a digit, then call \cs{@@_parse_large:N} (the significand
+% is $\geq 1$); if it is |.|, then continue trimming zeros with
+% \cs{@@_parse_strim_zeros:N}; otherwise, our number is exactly zero,
+% and we call \cs{@@_parse_zero:} to take care of that case.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_trim_zeros:N #1
+ {
+ \if:w 0 \exp_not:N #1
+ \exp_after:wN \@@_parse_trim_zeros:N
+ \exp:w
+ \else:
+ \if:w . \exp_not:N #1
+ \exp_after:wN \@@_parse_strim_zeros:N
+ \exp:w
+ \else:
+ \@@_parse_trim_end:w #1
+ \fi:
+ \fi:
+ \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_trim_end:w #1 \fi: \fi: \@@_parse_expand:w
+ {
+ \fi:
+ \fi:
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ \exp_after:wN \@@_parse_large:N
+ \else:
+ \exp_after:wN \@@_parse_zero:
+ \fi:
+ #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {\@@_parse_strim_zeros:N, \@@_parse_strim_end:w}
+% If we have removed all digits until a period (or if the body started
+% with a period), then enter the \enquote{\texttt{small_trim}} loop
+% which outputs $-1$ for each removed~$0$. Those $-1$ are added to an
+% integer expression waiting for the exponent. If the first non-zero
+% token is a digit, call \cs{@@_parse_small:N} (our significand is
+% smaller than~$1$), and otherwise, the number is an exact zero. The
+% name \texttt{strim} stands for \enquote{small trim}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_strim_zeros:N #1
+ {
+ \if:w 0 \exp_not:N #1
+ - 1
+ \exp_after:wN \@@_parse_strim_zeros:N \exp:w
+ \else:
+ \@@_parse_strim_end:w #1
+ \fi:
+ \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_strim_end:w #1 \fi: \@@_parse_expand:w
+ {
+ \fi:
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ \exp_after:wN \@@_parse_small:N
+ \else:
+ \exp_after:wN \@@_parse_zero:
+ \fi:
+ #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_zero:}
+% After reading a significand of~$0$, find any exponent, then put a
+% sign of~|1| for \cs{@@_sanitize:wN}, which removes everything
+% and leaves an exact zero.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_zero:
+ {
+ \exp_after:wN ; \exp_after:wN 1
+ \int_value:w \@@_parse_exponent:N
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Number: small significand}
+%
+% \begin{macro}[rEXP]{\@@_parse_small:N}
+% This function is called after we have passed the decimal separator
+% and removed all leading zeros from the significand. It is followed
+% by a non-zero digit (with any catcode). The goal is to read up to
+% $16$ digits. But we can't do that all at once, because
+% \cs{int_value:w} (which allows us to collect digits and continue
+% expanding) can only go up to $9$ digits. Hence we grab digits in
+% two steps of $8$ digits. Since |#1| is a digit, read seven more
+% digits using \cs{@@_parse_digits_vii:N}. The \texttt{small_leading}
+% auxiliary leaves those digits in the \cs{int_value:w}, and
+% grabs some more, or stops if there are no more digits. Then the
+% \texttt{pack_leading} auxiliary puts the various parts in the
+% appropriate order for the processing further up.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_small:N #1
+ {
+ \exp_after:wN \@@_parse_pack_leading:NNNNNww
+ \int_value:w \@@_int_eval:w 1 \token_to_str:N #1
+ \exp_after:wN \@@_parse_small_leading:wwNN
+ \int_value:w 1
+ \exp_after:wN \@@_parse_digits_vii:N
+ \exp:w \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_parse_small_leading:wwNN}
+% \begin{syntax}
+% \cs{@@_parse_small_leading:wwNN} |1| \meta{digits} |;| \meta{zeros} |;| \meta{number~of~zeros}
+% \end{syntax}
+% We leave \meta{digits} \meta{zeros} in the input stream: the
+% functions used to grab digits are such that this constitutes digits
+% $1$ through~$8$ of the significand. Then prepare to pack $8$~more
+% digits, with an exponent shift of zero (this shift is used in
+% the case of a large significand). If |#4|~is a digit, leave it
+% behind for the packing function, and read $6$~more digits to reach a
+% total of $15$~digits: further digits are involved in the rounding.
+% Otherwise put $8$~zeros in to complete the significand, then look
+% for an exponent.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_small_leading:wwNN 1 #1 ; #2; #3 #4
+ {
+ #1 #2
+ \exp_after:wN \@@_parse_pack_trailing:NNNNNNww
+ \exp_after:wN 0
+ \int_value:w \@@_int_eval:w 1
+ \if_int_compare:w 9 < 1 \token_to_str:N #4 \exp_stop_f:
+ \token_to_str:N #4
+ \exp_after:wN \@@_parse_small_trailing:wwNN
+ \int_value:w 1
+ \exp_after:wN \@@_parse_digits_vi:N
+ \exp:w
+ \else:
+ 0000 0000 \@@_parse_exponent:Nw #4
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_parse_small_trailing:wwNN}
+% \begin{syntax}
+% \cs{@@_parse_small_trailing:wwNN} |1| \meta{digits} |;| \meta{zeros} |;| \meta{number~of~zeros} \meta{next~token}
+% \end{syntax}
+% Leave digits $10$ to~$15$ (arguments |#1| and |#2|) in the input
+% stream. If the \meta{next~token} is a digit, it is the $16$th
+% digit, we keep it, then the \texttt{small_round} auxiliary considers
+% this digit and all further digits to perform the rounding: the
+% function expands to nothing, to |+0| or to |+1|.
+% Otherwise, there is no $16$-th digit, so we put a~$0$, and look for
+% an exponent.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_small_trailing:wwNN 1 #1 ; #2; #3 #4
+ {
+ #1 #2
+ \if_int_compare:w 9 < 1 \token_to_str:N #4 \exp_stop_f:
+ \token_to_str:N #4
+ \exp_after:wN \@@_parse_small_round:NN
+ \exp_after:wN #4
+ \exp:w
+ \else:
+ 0 \@@_parse_exponent:Nw #4
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_parse_pack_trailing:NNNNNNww ,
+% \@@_parse_pack_leading:NNNNNww ,
+% \@@_parse_pack_carry:w
+% }
+% Those functions are expanded after all the digits are found, we took
+% care of the rounding, as well as the exponent. The last argument is
+% the exponent. The previous five arguments are $8$~digits which we
+% pack in groups of~$4$, and the argument before that is~$1$, except
+% in the rare case where rounding lead to a carry, in which case the
+% argument is~$2$. The \texttt{trailing} function has an exponent
+% shift as its first argument, which we add to the exponent found in
+% the |e...| syntax. If the trailing digits cause a carry, the
+% integer expression for the leading digits is incremented (|+1|
+% in the code below). If the leading digits propagate this carry all
+% the way up, the function \cs{@@_parse_pack_carry:w} increments the
+% exponent, and changes the significand from |0000...| to |1000...|:
+% this is simple because such a carry can only occur to give rise to a
+% power of~$10$.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_pack_trailing:NNNNNNww #1 #2 #3#4#5#6 #7; #8 ;
+ {
+ \if_meaning:w 2 #2 + 1 \fi:
+ ; #8 + #1 ; {#3#4#5#6} {#7};
+ }
+\cs_new:Npn \@@_parse_pack_leading:NNNNNww #1 #2#3#4#5 #6; #7;
+ {
+ + #7
+ \if_meaning:w 2 #1 \@@_parse_pack_carry:w \fi:
+ ; 0 {#2#3#4#5} {#6}
+ }
+\cs_new:Npn \@@_parse_pack_carry:w \fi: ; 0 #1
+ { \fi: + 1 ; 0 {1000} }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Number: large significand}
+%
+% Parsing a significand larger than~$1$ is a little bit more difficult
+% than parsing small significands. We need to count the number of
+% digits before the decimal separator, and add that to the final
+% exponent. We also need to test for the presence of a dot each time we
+% run out of digits, and branch to the appropriate \texttt{parse_small}
+% function in those cases.
+%
+% \begin{macro}[EXP]{\@@_parse_large:N}
+% This function is followed by the first non-zero digit of a
+% \enquote{large} significand ($\geq 1$). It is called within an
+% integer expression for the exponent. Grab up to $7$~more digits,
+% for a total of $8$~digits.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_large:N #1
+ {
+ \exp_after:wN \@@_parse_large_leading:wwNN
+ \int_value:w 1 \token_to_str:N #1
+ \exp_after:wN \@@_parse_digits_vii:N
+ \exp:w \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_parse_large_leading:wwNN}
+% \begin{syntax}
+% \cs{@@_parse_large_leading:wwNN} |1| \meta{digits} |;| \meta{zeros} |;| \meta{number~of~zeros} \meta{next~token}
+% \end{syntax}
+% We shift the exponent by the number of digits in~|#1|, namely the
+% target number, $8$, minus the \meta{number of zeros} (number of
+% digits missing). Then prepare to pack the $8$~first digits. If the
+% \meta{next token} is a digit, read up to $6$~more digits (digits
+% $10$ to~$15$). If it is a period, try to grab the end of our
+% $8$~first digits, branching to the \texttt{small} functions since
+% the number of digit does not affect the exponent anymore. Finally,
+% if this is the end of the significand, insert the \meta{zeros} to
+% complete the $8$~first digits, insert $8$~more, and look for an
+% exponent.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_large_leading:wwNN 1 #1 ; #2; #3 #4
+ {
+ + \c_@@_half_prec_int - #3
+ \exp_after:wN \@@_parse_pack_leading:NNNNNww
+ \int_value:w \@@_int_eval:w 1 #1
+ \if_int_compare:w 9 < 1 \token_to_str:N #4 \exp_stop_f:
+ \exp_after:wN \@@_parse_large_trailing:wwNN
+ \int_value:w 1 \token_to_str:N #4
+ \exp_after:wN \@@_parse_digits_vi:N
+ \exp:w
+ \else:
+ \if:w . \exp_not:N #4
+ \exp_after:wN \@@_parse_small_leading:wwNN
+ \int_value:w 1
+ \cs:w
+ @@_parse_digits_
+ \@@_int_to_roman:w #3
+ :N \exp_after:wN
+ \cs_end:
+ \exp:w
+ \else:
+ #2
+ \exp_after:wN \@@_parse_pack_trailing:NNNNNNww
+ \exp_after:wN 0
+ \int_value:w 1 0000 0000
+ \@@_parse_exponent:Nw #4
+ \fi:
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_large_trailing:wwNN}
+% \begin{syntax}
+% \cs{@@_parse_large_trailing:wwNN} |1| \meta{digits} |;| \meta{zeros} |;| \meta{number~of~zeros} \meta{next~token}
+% \end{syntax}
+% We have just read $15$~digits. If the \meta{next token} is a digit,
+% then the exponent shift caused by this block of $8$~digits is~$8$,
+% first argument to the \texttt{pack_trailing} function. We keep the
+% \meta{digits} and this $16$-th digit, and find how this should be
+% rounded using \cs{@@_parse_large_round:NN}. Otherwise, the exponent
+% shift is the number of \meta{digits}, $7$~minus the \meta{number of
+% zeros}, and we test for a decimal point. This case happens in
+% |123451234512345.67| with exactly $15$ digits before the decimal
+% separator. Then branch to the appropriate \texttt{small} auxiliary,
+% grabbing a few more digits to complement the digits we already
+% grabbed. Finally, if this is truly the end of the significand, look
+% for an exponent after using the \meta{zeros} and providing a $16$-th
+% digit of~$0$.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_large_trailing:wwNN 1 #1 ; #2; #3 #4
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #4 \exp_stop_f:
+ \exp_after:wN \@@_parse_pack_trailing:NNNNNNww
+ \exp_after:wN \c_@@_half_prec_int
+ \int_value:w \@@_int_eval:w 1 #1 \token_to_str:N #4
+ \exp_after:wN \@@_parse_large_round:NN
+ \exp_after:wN #4
+ \exp:w
+ \else:
+ \exp_after:wN \@@_parse_pack_trailing:NNNNNNww
+ \int_value:w \@@_int_eval:w 7 - #3 \exp_stop_f:
+ \int_value:w \@@_int_eval:w 1 #1
+ \if:w . \exp_not:N #4
+ \exp_after:wN \@@_parse_small_trailing:wwNN
+ \int_value:w 1
+ \cs:w
+ @@_parse_digits_
+ \@@_int_to_roman:w #3
+ :N \exp_after:wN
+ \cs_end:
+ \exp:w
+ \else:
+ #2 0 \@@_parse_exponent:Nw #4
+ \fi:
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Number: beyond 16 digits, rounding}
+%
+% \begin{macro}[rEXP]{\@@_parse_round_loop:N, \@@_parse_round_up:N}
+% This loop is called when rounding a number (whether the mantissa is
+% small or large). It should appear in an integer expression. This
+% function reads digits one by one, until reaching a non-digit, and
+% adds~$1$ to the integer expression for each digit. If all digits
+% found are~$0$, the function ends the expression by |;0|,
+% otherwise by |;1|. This is done by switching the loop to
+% |round_up| at the first non-zero digit, thus we avoid to test
+% whether digits are~$0$ or not once we see a first non-zero digit.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_round_loop:N #1
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ + 1
+ \if:w 0 \token_to_str:N #1
+ \exp_after:wN \@@_parse_round_loop:N
+ \exp:w
+ \else:
+ \exp_after:wN \@@_parse_round_up:N
+ \exp:w
+ \fi:
+ \else:
+ \@@_parse_return_semicolon:w 0 #1
+ \fi:
+ \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_round_up:N #1
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ + 1
+ \exp_after:wN \@@_parse_round_up:N
+ \exp:w
+ \else:
+ \@@_parse_return_semicolon:w 1 #1
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_parse_round_after:wN}
+% After the loop \cs{@@_parse_round_loop:N}, this function fetches an
+% exponent with \cs{@@_parse_exponent:N}, and combines it with the
+% number of digits counted by \cs{@@_parse_round_loop:N}. At the same
+% time, the result |0| or |1| is added to the
+% surrounding integer expression.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_round_after:wN #1; #2
+ {
+ + #2 \exp_after:wN ;
+ \int_value:w \@@_int_eval:w #1 + \@@_parse_exponent:N
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {\@@_parse_small_round:NN, \@@_parse_round_after:wN}
+% Here, |#1|~is the digit that we are currently rounding (we only care
+% whether it is even or odd). If |#2|~is not a digit, then fetch an
+% exponent and expand to |;|\meta{exponent} only. Otherwise, we
+% expand to |+0| or |+1|, then |;|\meta{exponent}. To
+% decide which, call \cs{@@_round_s:NNNw} to know whether to round up,
+% giving it as arguments a sign~$0$ (all explicit numbers are
+% positive), the digit |#1|~to round, the first following digit~|#2|,
+% and either |+0| or |+1| depending on whether the
+% following digits are all zero or not. This last argument is
+% obtained by \cs{@@_parse_round_loop:N}, whose number of digits we
+% discard by multiplying it by~$0$. The exponent which follows the
+% number is also fetched by \cs{@@_parse_round_after:wN}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_small_round:NN #1#2
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #2 \exp_stop_f:
+ +
+ \exp_after:wN \@@_round_s:NNNw
+ \exp_after:wN 0
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \int_value:w \@@_int_eval:w
+ \exp_after:wN \@@_parse_round_after:wN
+ \int_value:w \@@_int_eval:w 0 * \@@_int_eval:w 0
+ \exp_after:wN \@@_parse_round_loop:N
+ \exp:w
+ \else:
+ \@@_parse_exponent:Nw #2
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_parse_large_round:NN,
+% \@@_parse_large_round_test:NN,
+% \@@_parse_large_round_aux:wNN,
+% }
+% Large numbers are harder to round, as there may be a period in the
+% way. Again, |#1|~is the digit that we are currently rounding (we
+% only care whether it is even or odd). If there are no more digits
+% (|#2|~is not a digit), then we must test for a period: if there is
+% one, then switch to the rounding function for small significands,
+% otherwise fetch an exponent. If there are more digits (|#2|~is a
+% digit), then round, checking with \cs{@@_parse_round_loop:N} if all
+% further digits vanish, or some are non-zero. This loop is not
+% enough, as it is stopped by a period. After the loop, the
+% \texttt{aux} function tests for a period: if it is present, then we
+% must continue looking for digits, this time discarding the number of
+% digits we find.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_large_round:NN #1#2
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #2 \exp_stop_f:
+ +
+ \exp_after:wN \@@_round_s:NNNw
+ \exp_after:wN 0
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \int_value:w \@@_int_eval:w
+ \exp_after:wN \@@_parse_large_round_aux:wNN
+ \int_value:w \@@_int_eval:w 1
+ \exp_after:wN \@@_parse_round_loop:N
+ \else: %^^A could be dot, or e, or other
+ \exp_after:wN \@@_parse_large_round_test:NN
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \fi:
+ }
+\cs_new:Npn \@@_parse_large_round_test:NN #1#2
+ {
+ \if:w . \exp_not:N #2
+ \exp_after:wN \@@_parse_small_round:NN
+ \exp_after:wN #1
+ \exp:w
+ \else:
+ \@@_parse_exponent:Nw #2
+ \fi:
+ \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_large_round_aux:wNN #1 ; #2 #3
+ {
+ + #2
+ \exp_after:wN \@@_parse_round_after:wN
+ \int_value:w \@@_int_eval:w #1
+ \if:w . \exp_not:N #3
+ + 0 * \@@_int_eval:w 0
+ \exp_after:wN \@@_parse_round_loop:N
+ \exp:w \exp_after:wN \@@_parse_expand:w
+ \else:
+ \exp_after:wN ;
+ \exp_after:wN 0
+ \exp_after:wN #3
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Number: finding the exponent}
+%
+% Expansion is a little bit tricky here, in part because we accept input
+% where multiplication is implicit.
+% \begin{syntax}
+% \cs{@@_parse:n} |{ 3.2 erf(0.1) }|
+% \cs{@@_parse:n} |{ 3.2 e\l_my_int }|
+% \cs{@@_parse:n} |{ 3.2 \c_pi_fp }|
+% \end{syntax}
+% The first case indicates that just looking one character ahead for an
+% \enquote{\texttt{e}} is not enough, since we would mistake the
+% function \texttt{erf} for an exponent of \enquote{\texttt{rf}}. An
+% alternative would be to look two tokens ahead and check if what
+% follows is a sign or a digit, considering in that case that we must be
+% finding an exponent. But taking care of the second case requires that
+% we unpack registers after \texttt{e}. However, blindly expanding the
+% two tokens ahead completely would break the third example (unpacking
+% is even worse). Indeed, in the course of reading $3.2$, \cs{c_pi_fp}
+% is expanded to \cs{s_@@} \cs{@@_chk:w} |1| |0| |{-1}| |{3141}|
+% $\cdots$ |;| and \cs{s_@@} stops the expansion. Expanding two tokens
+% ahead would then force the expansion of \cs{@@_chk:w} (despite it
+% being protected), and that function tries to produce an error.
+%
+% What can we do? Really, the reason why this last case breaks is that
+% just as \TeX{} does, we should read ahead as little as possible.
+% Here, the only case where there may be an exponent is if the first
+% token ahead is |e|. Then we expand (and possibly unpack) the second
+% token.
+%
+% \begin{macro}[rEXP]{\@@_parse_exponent:Nw}
+% This auxiliary is convenient to smuggle some material through
+% \cs{fi:} ending conditional processing. We place those \cs{fi:}
+% (argument~|#2|) at a very odd place because this allows us to insert
+% \cs{@@_int_eval:w} \ldots{} there if needed.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_exponent:Nw #1 #2 \@@_parse_expand:w
+ {
+ \exp_after:wN ;
+ \int_value:w #2 \@@_parse_exponent:N #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {\@@_parse_exponent:N, \@@_parse_exponent_aux:NN}
+% This function should be called within an \cs{int_value:w}
+% expansion (or within an integer expression). It leaves digits of the
+% exponent behind it in the input stream, and terminates the expansion
+% with a semicolon. If there is no~|e| (or~|E|), leave an exponent of~$0$. If
+% there is an~|e| or~|E|, expand the next token to run some tests on it. The
+% first rough test is that if the character code of~|#1| is greater
+% than that of~|9| (largest code valid for an exponent, less than any
+% code valid for an identifier), there was in fact no exponent;
+% otherwise, we search for the sign of the exponent.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_exponent:N #1
+ {
+ \if:w e \if:w E \exp_not:N #1 e \else: \exp_not:N #1 \fi:
+ \exp_after:wN \@@_parse_exponent_aux:NN
+ \exp_after:wN #1
+ \exp:w
+ \else:
+ 0 \@@_parse_return_semicolon:w #1
+ \fi:
+ \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_exponent_aux:NN #1#2
+ {
+ \if_int_compare:w \if_catcode:w \scan_stop: \exp_not:N #2
+ 0 \else: `#2 \fi: > `9 \exp_stop_f:
+ 0 \exp_after:wN ; \exp_after:wN #1
+ \else:
+ \exp_after:wN \@@_parse_exponent_sign:N
+ \fi:
+ #2
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_parse_exponent_sign:N}
+% Read signs one by one (if there is any).
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_exponent_sign:N #1
+ {
+ \if:w + \if:w - \exp_not:N #1 + \fi: \token_to_str:N #1
+ \exp_after:wN \@@_parse_exponent_sign:N
+ \exp:w \exp_after:wN \@@_parse_expand:w
+ \else:
+ \exp_after:wN \@@_parse_exponent_body:N
+ \exp_after:wN #1
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_parse_exponent_body:N}
+% An exponent can be an explicit integer (most common case), or
+% various other things (most of which are invalid).
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_exponent_body:N #1
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ \token_to_str:N #1
+ \exp_after:wN \@@_parse_exponent_digits:N
+ \exp:w
+ \else:
+ \@@_parse_exponent_keep:NTF #1
+ { \@@_parse_return_semicolon:w #1 }
+ {
+ \exp_after:wN ;
+ \exp:w
+ }
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_parse_exponent_digits:N}
+% Read digits one by one, and leave them behind in the input stream.
+% When finding a non-digit, stop, and insert a semicolon. Note that
+% we do not check for overflow of the exponent, hence there can be a
+% \TeX{} error. It is mostly harmless, except when parsing
+% |0e9876543210|, which should be a valid representation of $0$, but
+% is not.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_exponent_digits:N #1
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ \token_to_str:N #1
+ \exp_after:wN \@@_parse_exponent_digits:N
+ \exp:w
+ \else:
+ \@@_parse_return_semicolon:w #1
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_parse_exponent_keep:NTF}
+% This is the last building block for parsing exponents. The
+% argument~|#1| is already fully expanded, and neither |+| nor~|-| nor
+% a digit. It can be:
+% \begin{itemize}
+% \item \cs{s_@@}, marking the start of an internal floating point,
+% invalid here;
+% \item another control sequence equal to \tn{relax}, probably a bad
+% variable;
+% \item a register: in this case we make sure that it is an integer
+% register, not a dimension;
+% \item a character other than |+|, |-| or digits, again, an error.
+% \end{itemize}
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_parse_exponent_keep:N #1 { TF }
+ {
+ \if_catcode:w \scan_stop: \exp_not:N #1
+ \if_meaning:w \scan_stop: #1
+ \if:w 0 \@@_str_if_eq:nn { \s_@@ } { \exp_not:N #1 }
+ 0
+ \msg_expandable_error:nnn
+ { fp } { after-e } { floating~point~ }
+ \prg_return_true:
+ \else:
+ 0
+ \msg_expandable_error:nnn
+ { kernel } { bad-variable } {#1}
+ \prg_return_false:
+ \fi:
+ \else:
+ \if:w 0 \@@_str_if_eq:nn { \int_value:w #1 } { \tex_the:D #1 }
+ \int_value:w #1
+ \else:
+ 0
+ \msg_expandable_error:nnn
+ { fp } { after-e } { dimension~#1 }
+ \fi:
+ \prg_return_false:
+ \fi:
+ \else:
+ 0
+ \msg_expandable_error:nnn
+ { fp } { missing } { exponent }
+ \prg_return_true:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Constants, functions and prefix operators}
+%
+% \subsubsection{Prefix operators}
+%
+% \begin{macro}[EXP]{\@@_parse_prefix_+:Nw}
+% A unary~|+| does nothing: we should continue looking for a number.
+% \begin{macrocode}
+\cs_new_eq:cN { @@_parse_prefix_+:Nw } \@@_parse_one:Nw
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_apply_function:NNNwN}
+% Here, |#1| is a precedence, |#2| is some extra data used by some
+% functions, |#3| is \emph{e.g.}, \cs{@@_sin_o:w}, and expands once
+% after the calculation, |#4| is the operand, and |#5| is a
+% \cs[no-index]{@@_parse_infix_\ldots{}:N} function. We feed the data~|#2|, and the
+% argument~|#4|, to the function~|#3|, which expands
+% \cs{exp:w} thus the \texttt{infix} function~|#5|.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_apply_function:NNNwN #1#2#3#4@#5
+ {
+ #3 #2 #4 @
+ \exp:w \exp_end_continue_f:w #5 #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_apply_unary:NNNwN}
+% \begin{macro}[EXP]{\@@_parse_apply_unary_chk:NwNw, \@@_parse_apply_unary_chk:nNNNw}
+% \begin{macro}[EXP]{\@@_parse_apply_unary_type:NNN, \@@_parse_apply_unary_error:NNw}
+% In contrast to \cs{@@_parse_apply_function:NNNwN}, this checks that
+% the operand |#4| is a single argument (namely there is a single
+% |;|). We use the fact that any floating point starts with a
+% \enquote{safe} token like \cs{s_@@}. If there is no argument
+% produce the |fp-no-arg| error; if there are at least two produce
+% |fp-multi-arg|. For the error message extract the mathematical
+% function name (such as |sin|) from the \pkg{expl3} function that
+% computes it, such as \cs{@@_sin_o:w}.
+%
+% In addition, since there is a single argument we can dispatch on
+% type and check that the resulting function exists. This catches
+% things like |sin((1,2))| where it does not make sense to take the
+% sine of a tuple.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_apply_unary:NNNwN #1#2#3#4@#5
+ {
+ \@@_parse_apply_unary_chk:NwNw #4 @ ; . \s_@@_stop
+ \@@_parse_apply_unary_type:NNN
+ #3 #2 #4 @
+ \exp:w \exp_end_continue_f:w #5 #1
+ }
+\cs_new:Npn \@@_parse_apply_unary_chk:NwNw #1#2 ; #3#4 \s_@@_stop
+ {
+ \if_meaning:w @ #3 \else:
+ \token_if_eq_meaning:NNTF . #3
+ { \@@_parse_apply_unary_chk:nNNNNw { no } }
+ { \@@_parse_apply_unary_chk:nNNNNw { multi } }
+ \fi:
+ }
+\cs_new:Npn \@@_parse_apply_unary_chk:nNNNNw #1#2#3#4#5#6 @
+ {
+ #2
+ \@@_error:nffn { #1-arg } { \@@_func_to_name:N #4 } { } { }
+ \exp_after:wN #4 \exp_after:wN #5 \c_nan_fp @
+ }
+\cs_new:Npn \@@_parse_apply_unary_type:NNN #1#2#3
+ {
+ \@@_change_func_type:NNN #3 #1 \@@_parse_apply_unary_error:NNw
+ #2 #3
+ }
+\cs_new:Npn \@@_parse_apply_unary_error:NNw #1#2#3 @
+ { \@@_invalid_operation_o:fw { \@@_func_to_name:N #1 } #3 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_prefix_-:Nw, \@@_parse_prefix_!:Nw}
+% The unary~|-| and boolean not are harder: we parse the operand using
+% a precedence equal to the maximum of the previous precedence~|##1|
+% and the precedence \cs{c_@@_prec_not_int} of the unary operator, then call
+% the appropriate |\__fp_|\meta{operation}|_o:w| function,
+% where the \meta{operation} is |set_sign| or |not|.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3#4
+ {
+ \cs_new:cpn { @@_parse_prefix_ #1 :Nw } ##1
+ {
+ \exp_after:wN \@@_parse_apply_unary:NNNwN
+ \exp_after:wN ##1
+ \exp_after:wN #4
+ \exp_after:wN #3
+ \exp:w
+ \if_int_compare:w #2 < ##1
+ \@@_parse_operand:Nw ##1
+ \else:
+ \@@_parse_operand:Nw #2
+ \fi:
+ \@@_parse_expand:w
+ }
+ }
+\@@_tmp:w - \c_@@_prec_not_int \@@_set_sign_o:w 2
+\@@_tmp:w ! \c_@@_prec_not_int \@@_not_o:w ?
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_prefix_.:Nw}
+% Numbers which start with a decimal separator (a~period) end up here.
+% Of course, we do not look for an operand, but for the rest of the
+% number. This function is very similar to \cs{@@_parse_one_digit:NN}
+% but calls \cs{@@_parse_strim_zeros:N} to trim zeros after the
+% decimal point, rather than the \texttt{trim_zeros} function for
+% zeros before the decimal point.
+% \begin{macrocode}
+\cs_new:cpn { @@_parse_prefix_.:Nw } #1
+ {
+ \exp_after:wN \@@_parse_infix_after_operand:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN \@@_sanitize:wN
+ \int_value:w \@@_int_eval:w 0 \@@_parse_strim_zeros:N
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_parse_prefix_(:Nw, \@@_parse_lparen_after:NwN}
+% The left parenthesis is treated as a unary prefix operator because
+% it appears in exactly the same settings. If the previous precedence
+% is \cs{c_@@_prec_func_int} we are parsing arguments of a function
+% and commas should not build tuples; otherwise commas should build
+% tuples. We distinguish these cases by precedence:
+% \cs{c_@@_prec_comma_int} for the case of arguments,
+% \cs{c_@@_prec_tuple_int} for the case of tuples.
+% Once the operand is found, the \texttt{lparen_after} auxiliary makes
+% sure that there was a closing parenthesis (otherwise it complains),
+% and leaves in the input stream an operand,
+% fetching the following infix operator.
+% \begin{macrocode}
+\cs_new:cpn { @@_parse_prefix_(:Nw } #1
+ {
+ \exp_after:wN \@@_parse_lparen_after:NwN
+ \exp_after:wN #1
+ \exp:w
+ \if_int_compare:w #1 = \c_@@_prec_func_int
+ \@@_parse_operand:Nw \c_@@_prec_comma_int
+ \else:
+ \@@_parse_operand:Nw \c_@@_prec_tuple_int
+ \fi:
+ \@@_parse_expand:w
+ }
+\cs_new:Npe \@@_parse_lparen_after:NwN #1#2 @ #3
+ {
+ \exp_not:N \token_if_eq_meaning:NNTF #3
+ \exp_not:c { @@_parse_infix_):N }
+ {
+ \exp_not:N \@@_exp_after_array_f:w #2 \s_@@_expr_stop
+ \exp_not:N \exp_after:wN
+ \exp_not:N \@@_parse_infix_after_paren:NN
+ \exp_not:N \exp_after:wN #1
+ \exp_not:N \exp:w
+ \exp_not:N \@@_parse_expand:w
+ }
+ {
+ \exp_not:N \msg_expandable_error:nnn
+ { fp } { missing } { ) }
+ \exp_not:N \tl_if_empty:nT {#2} \exp_not:N \c_@@_empty_tuple_fp
+ #2 @
+ \exp_not:N \use_none:n #3
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_prefix_):Nw}
+% The right parenthesis can appear as a prefix in two similar cases:
+% in an empty tuple or tuple ending with a comma, or in an empty
+% argument list or argument list ending with a comma, such as in
+% |max(1,2,)| or in |rand()|.
+% \begin{macrocode}
+\cs_new:cpn { @@_parse_prefix_):Nw } #1
+ {
+ \if_int_compare:w #1 = \c_@@_prec_comma_int
+ \else:
+ \if_int_compare:w #1 = \c_@@_prec_tuple_int
+ \exp_after:wN \c_@@_empty_tuple_fp \exp:w
+ \else:
+ \msg_expandable_error:nnn
+ { fp } { missing-number } { ) }
+ \exp_after:wN \c_nan_fp \exp:w
+ \fi:
+ \exp_end_continue_f:w
+ \fi:
+ \@@_parse_infix_after_paren:NN #1 )
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Constants}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_word_inf:N , \@@_parse_word_nan:N ,
+% \@@_parse_word_pi:N , \@@_parse_word_deg:N ,
+% \@@_parse_word_true:N , \@@_parse_word_false:N ,
+% }
+% Some words correspond to constant floating points. The floating
+% point constant is left as a result of \cs{@@_parse_one:Nw} after
+% expanding \cs{@@_parse_infix:NN}.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1 #2
+ {
+ \cs_new:cpn { @@_parse_word_#1:N }
+ { \exp_after:wN #2 \exp:w \exp_end_continue_f:w \@@_parse_infix:NN }
+ }
+\@@_tmp:w { inf } \c_inf_fp
+\@@_tmp:w { nan } \c_nan_fp
+\@@_tmp:w { pi } \c_pi_fp
+\@@_tmp:w { deg } \c_one_degree_fp
+\@@_tmp:w { true } \c_one_fp
+\@@_tmp:w { false } \c_zero_fp
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_caseless_inf:N,
+% \@@_parse_caseless_infinity:N,
+% \@@_parse_caseless_nan:N
+% }
+% Copies of \cs[no-index]{@@_parse_word_\ldots{}:N} commands, to allow
+% arbitrary case as mandated by the standard.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_parse_caseless_inf:N \@@_parse_word_inf:N
+\cs_new_eq:NN \@@_parse_caseless_infinity:N \@@_parse_word_inf:N
+\cs_new_eq:NN \@@_parse_caseless_nan:N \@@_parse_word_nan:N
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_word_pt:N , \@@_parse_word_in:N ,
+% \@@_parse_word_pc:N , \@@_parse_word_cm:N , \@@_parse_word_mm:N ,
+% \@@_parse_word_dd:N , \@@_parse_word_cc:N , \@@_parse_word_nd:N ,
+% \@@_parse_word_nc:N , \@@_parse_word_bp:N , \@@_parse_word_sp:N ,
+% }
+% Dimension units are also floating point constants but their value is
+% not stored as a floating point constant. We give the values
+% explicitly here.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1 #2
+ {
+ \cs_new:cpn { @@_parse_word_#1:N }
+ {
+ \@@_exp_after_f:nw { \@@_parse_infix:NN }
+ \s_@@ \@@_chk:w 10 #2 ;
+ }
+ }
+\@@_tmp:w {pt} { {1} {1000} {0000} {0000} {0000} }
+\@@_tmp:w {in} { {2} {7227} {0000} {0000} {0000} }
+\@@_tmp:w {pc} { {2} {1200} {0000} {0000} {0000} }
+\@@_tmp:w {cm} { {2} {2845} {2755} {9055} {1181} }
+\@@_tmp:w {mm} { {1} {2845} {2755} {9055} {1181} }
+\@@_tmp:w {dd} { {1} {1070} {0085} {6496} {0630} }
+\@@_tmp:w {cc} { {2} {1284} {0102} {7795} {2756} }
+\@@_tmp:w {nd} { {1} {1066} {9783} {4645} {6693} }
+\@@_tmp:w {nc} { {2} {1280} {3740} {1574} {8031} }
+\@@_tmp:w {bp} { {1} {1003} {7500} {0000} {0000} }
+\@@_tmp:w {sp} { {-4} {1525} {8789} {0625} {0000} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_word_em:N, \@@_parse_word_ex:N}
+% The font-dependent units |em| and |ex| must be evaluated on the fly.
+% We reuse an auxiliary of \cs{dim_to_fp:n}.
+% \begin{macrocode}
+\tl_map_inline:nn { {em} {ex} }
+ {
+ \cs_new:cpn { @@_parse_word_#1:N }
+ {
+ \exp_after:wN \@@_from_dim_test:ww
+ \exp_after:wN 0 \exp_after:wN ,
+ \int_value:w \dim_to_decimal_in_sp:n { 1 #1 } \exp_after:wN ;
+ \exp:w \exp_end_continue_f:w \@@_parse_infix:NN
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Functions}
+%
+% ^^A begin[todo]
+%
+% \begin{macro}[EXP]
+% {\@@_parse_unary_function:NNN, \@@_parse_function:NNN}
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_unary_function:NNN #1#2#3
+ {
+ \exp_after:wN \@@_parse_apply_unary:NNNwN
+ \exp_after:wN #3
+ \exp_after:wN #2
+ \exp_after:wN #1
+ \exp:w
+ \@@_parse_operand:Nw \c_@@_prec_func_int \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_function:NNN #1#2#3
+ {
+ \exp_after:wN \@@_parse_apply_function:NNNwN
+ \exp_after:wN #3
+ \exp_after:wN #2
+ \exp_after:wN #1
+ \exp:w
+ \@@_parse_operand:Nw \c_@@_prec_func_int \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Main functions}
+%
+% \begin{macro}[EXP]{\@@_parse:n, \@@_parse_o:n}
+% \begin{macro}[EXP]{\@@_parse_after:ww}
+% Start an \cs{exp:w} expansion so that \cs{@@_parse:n} expands
+% in two steps. The \cs{@@_parse_operand:Nw} function performs
+% computations until reaching an operation with precedence
+% \cs{c_@@_prec_end_int} or less, namely, the end of the expression. The
+% marker \cs{s_@@_expr_mark} indicates that the next token is an already
+% parsed version of an infix operator, and \cs{@@_parse_infix_end:N}
+% has infinitely negative precedence. Finally, clean up a
+% (well-defined) set of extra tokens and stop the initial expansion
+% with \cs{exp_end:}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse:n #1
+ {
+ \exp:w
+ \exp_after:wN \@@_parse_after:ww
+ \exp:w
+ \@@_parse_operand:Nw \c_@@_prec_end_int
+ \@@_parse_expand:w #1
+ \s_@@_expr_mark \@@_parse_infix_end:N
+ \s_@@_expr_stop
+ \exp_end:
+ }
+\cs_new:Npn \@@_parse_after:ww
+ #1@ \@@_parse_infix_end:N \s_@@_expr_stop #2 { #2 #1 }
+\cs_new:Npn \@@_parse_o:n #1
+ {
+ \exp:w
+ \exp_after:wN \@@_parse_after:ww
+ \exp:w
+ \@@_parse_operand:Nw \c_@@_prec_end_int
+ \@@_parse_expand:w #1
+ \s_@@_expr_mark \@@_parse_infix_end:N
+ \s_@@_expr_stop
+ {
+ \exp_end_continue_f:w
+ \@@_exp_after_any_f:nw { \exp_after:wN \exp_stop_f: }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_operand:Nw}
+% \begin{macro}[EXP]{\@@_parse_continue:NwN}
+% This is just a shorthand which sets up both \cs{@@_parse_continue:NwN}
+% and \cs{@@_parse_one:Nw} with the same precedence. Note the
+% trailing \cs{exp:w}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_operand:Nw #1
+ {
+ \exp_end_continue_f:w
+ \exp_after:wN \@@_parse_continue:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN \@@_parse_one:Nw
+ \exp_after:wN #1
+ \exp:w
+ }
+\cs_new:Npn \@@_parse_continue:NwN #1 #2 @ #3 { #3 #1 #2 @ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_apply_binary:NwNwN}
+% \begin{macro}[EXP]
+% {\@@_parse_apply_binary_chk:NN, \@@_parse_apply_binary_error:NNN}
+% Receives \meta{precedence} \meta{operand_1} |@| \meta{operation}
+% \meta{operand_2} |@| \meta{infix command}. Builds the appropriate
+% call to the \meta{operation}~|#3|, dispatching on both types.
+% If the resulting control sequence does not exist, the operation is
+% not allowed.
+%
+% This is redefined in \pkg{l3fp-extras}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_apply_binary:NwNwN #1 #2#3@ #4 #5#6@ #7
+ {
+ \exp_after:wN \@@_parse_continue:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN \@@_parse_apply_binary_chk:NN
+ \cs:w
+ @@
+ \@@_type_from_scan:N #2
+ _#4
+ \@@_type_from_scan:N #5
+ _o:ww
+ \cs_end:
+ #4
+ #2#3 #5#6
+ \exp:w \exp_end_continue_f:w #7 #1
+ }
+\cs_new:Npn \@@_parse_apply_binary_chk:NN #1#2
+ {
+ \if_meaning:w \scan_stop: #1
+ \@@_parse_apply_binary_error:NNN #2
+ \fi:
+ #1
+ }
+\cs_new:Npn \@@_parse_apply_binary_error:NNN #1#2#3
+ {
+ #2
+ \@@_invalid_operation_o:Nww #1
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_binary_type_o:Nww, \@@_binary_rev_type_o:Nww}
+% Applies the operator |#1| to its two arguments, dispatching
+% according to their types, and expands once after the result.
+% The |rev| version swaps its arguments before doing this.
+% \begin{macrocode}
+\cs_new:Npn \@@_binary_type_o:Nww #1 #2#3 ; #4
+ {
+ \exp_after:wN \@@_parse_apply_binary_chk:NN
+ \cs:w
+ @@
+ \@@_type_from_scan:N #2
+ _ #1
+ \@@_type_from_scan:N #4
+ _o:ww
+ \cs_end:
+ #1
+ #2 #3 ; #4
+ }
+\cs_new:Npn \@@_binary_rev_type_o:Nww #1 #2#3 ; #4#5 ;
+ {
+ \exp_after:wN \@@_parse_apply_binary_chk:NN
+ \cs:w
+ @@
+ \@@_type_from_scan:N #4
+ _ #1
+ \@@_type_from_scan:N #2
+ _o:ww
+ \cs_end:
+ #1
+ #4 #5 ; #2 #3 ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Infix operators}
+%
+% \begin{macro}[EXP]{\@@_parse_infix_after_operand:NwN}
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_infix_after_operand:NwN #1 #2;
+ {
+ \@@_exp_after_f:nw { \@@_parse_infix:NN #1 }
+ #2;
+ }
+\cs_new:Npn \@@_parse_infix:NN #1 #2
+ {
+ \if_catcode:w \scan_stop: \exp_not:N #2
+ \if:w 0 \@@_str_if_eq:nn { \s_@@_expr_mark } { \exp_not:N #2 }
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_infix_mark:NNN
+ \else:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_infix_juxt:N
+ \fi:
+ \else:
+ \if_int_compare:w
+ \@@_int_eval:w
+ ( `#2 \if_int_compare:w `#2 > `Z - 32 \fi: ) / 26
+ = 3 \exp_stop_f:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_infix_juxt:N
+ \else:
+ \exp_after:wN \@@_parse_infix_check:NNN
+ \cs:w
+ @@_parse_infix_ \token_to_str:N #2 :N
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \cs_end:
+ \fi:
+ \fi:
+ #1
+ #2
+ }
+\cs_new:Npn \@@_parse_infix_check:NNN #1#2#3
+ {
+ \if_meaning:w \scan_stop: #1
+ \msg_expandable_error:nnn
+ { fp } { missing } { * }
+ \exp_after:wN \@@_parse_infix_mul:N
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \else:
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp:w \exp_after:wN \@@_parse_expand:w
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_parse_infix_after_paren:NN}
+% Variant of \cs{@@_parse_infix:NN} for use after a closing
+% parenthesis. The only difference is that \cs{@@_parse_infix_juxt:N}
+% is replaced by \cs{@@_parse_infix_mul:N}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_infix_after_paren:NN #1 #2
+ {
+ \if_catcode:w \scan_stop: \exp_not:N #2
+ \if:w 0 \@@_str_if_eq:nn { \s_@@_expr_mark } { \exp_not:N #2 }
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_infix_mark:NNN
+ \else:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_infix_mul:N
+ \fi:
+ \else:
+ \if_int_compare:w
+ \@@_int_eval:w
+ ( `#2 \if_int_compare:w `#2 > `Z - 32 \fi: ) / 26
+ = 3 \exp_stop_f:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_infix_mul:N
+ \else:
+ \exp_after:wN \@@_parse_infix_check:NNN
+ \cs:w
+ @@_parse_infix_ \token_to_str:N #2 :N
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \cs_end:
+ \fi:
+ \fi:
+ #1
+ #2
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Closing parentheses and commas}
+%
+% \begin{macro}[EXP]{\@@_parse_infix_mark:NNN}
+% As an infix operator, \cs{s_@@_expr_mark} means that the next
+% token~(|#3|) has already gone through \cs{@@_parse_infix:NN} and
+% should be provided the precedence~|#1|. The scan mark~|#2| is
+% discarded.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_infix_mark:NNN #1#2#3 { #3 #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_infix_end:N}
+% This one is a little bit odd: force every previous operator to end,
+% regardless of the precedence.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_infix_end:N #1
+ { @ \use_none:n \@@_parse_infix_end:N }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]+\@@_parse_infix_):N+
+% This is very similar to \cs{@@_parse_infix_end:N}, complaining about
+% an extra closing parenthesis if the previous operator was the
+% beginning of the expression, with precedence \cs{c_@@_prec_end_int}.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_new:Npn #1 ##1
+ {
+ \if_int_compare:w ##1 > \c_@@_prec_end_int
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN #1
+ \else:
+ \msg_expandable_error:nnn { fp } { extra } { ) }
+ \exp_after:wN \@@_parse_infix:NN
+ \exp_after:wN ##1
+ \exp:w \exp_after:wN \@@_parse_expand:w
+ \fi:
+ }
+ }
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_):N }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[verb, EXP]{\__fp_parse_infix_,:N}
+% \begin{macro}[EXP]{\@@_parse_infix_comma:w, \@@_parse_apply_comma:NwNwN}
+% As for other infix operations, if the previous operations has higher
+% precedence the comma waits. Otherwise we call
+% \cs{@@_parse_operand:Nw} to read more comma-delimited arguments that
+% \cs{@@_parse_infix_comma:w} simply concatenates into a |@|-delimited
+% array. The first comma in a tuple that is not a function argument
+% is distinguished: in that case call \cs{@@_parse_apply_comma:NwNwN}
+% whose job is to convert the first item of the tuple and an array of
+% the remaining items into a tuple. In contrast to
+% \cs{@@_parse_apply_binary:NwNwN} this function's operands are not
+% single-object arrays.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_new:Npn #1 ##1
+ {
+ \if_int_compare:w ##1 > \c_@@_prec_comma_int
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN #1
+ \else:
+ \if_int_compare:w ##1 < \c_@@_prec_comma_int
+ \exp_after:wN @
+ \exp_after:wN \@@_parse_apply_comma:NwNwN
+ \exp_after:wN ,
+ \exp:w
+ \else:
+ \exp_after:wN \@@_parse_infix_comma:w
+ \exp:w
+ \fi:
+ \@@_parse_operand:Nw \c_@@_prec_comma_int
+ \exp_after:wN \@@_parse_expand:w
+ \fi:
+ }
+ }
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_,:N }
+\cs_new:Npn \@@_parse_infix_comma:w #1 @
+ { #1 @ \use_none:n }
+\cs_new:Npn \@@_parse_apply_comma:NwNwN #1 #2@ #3 #4@ #5
+ {
+ \exp_after:wN \@@_parse_continue:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \@@_exp_after_tuple_f:nw { }
+ \s_@@_tuple \@@_tuple_chk:w { #2 #4 } ;
+ #5 #1
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Usual infix operators}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_infix_+:N, \@@_parse_infix_-:N,
+% \@@_parse_infix_juxt:N,
+% \@@_parse_infix_/:N, \@@_parse_infix_mul:N,
+% \@@_parse_infix_and:N, \@@_parse_infix_or:N,
+% }
+% \begin{macro}[EXP]+\@@_parse_infix_^:N+
+% As described in the \enquote{work plan}, each infix operator has an
+% associated |\..._infix_...| function, a computing function, and
+% precedence, given as arguments to \cs{@@_tmp:w}. Using the general
+% mechanism for arithmetic operations. The power operation must be
+% associative in the opposite order from all others. For this, we use
+% two distinct precedences.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3#4
+ {
+ \cs_new:Npn #1 ##1
+ {
+ \if_int_compare:w ##1 < #3
+ \exp_after:wN @
+ \exp_after:wN \@@_parse_apply_binary:NwNwN
+ \exp_after:wN #2
+ \exp:w
+ \@@_parse_operand:Nw #4
+ \exp_after:wN \@@_parse_expand:w
+ \else:
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN #1
+ \fi:
+ }
+ }
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_^:N } ^
+ \c_@@_prec_hatii_int \c_@@_prec_hat_int
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_juxt:N } *
+ \c_@@_prec_juxt_int \c_@@_prec_juxt_int
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_/:N } /
+ \c_@@_prec_times_int \c_@@_prec_times_int
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_mul:N } *
+ \c_@@_prec_times_int \c_@@_prec_times_int
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_-:N } -
+ \c_@@_prec_plus_int \c_@@_prec_plus_int
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_+:N } +
+ \c_@@_prec_plus_int \c_@@_prec_plus_int
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_and:N } &
+ \c_@@_prec_and_int \c_@@_prec_and_int
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_or:N } |
+ \c_@@_prec_or_int \c_@@_prec_or_int
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Juxtaposition}
+%
+% \begin{macro}[EXP]+\@@_parse_infix_(:N+
+% When an opening parenthesis appears where we expect an infix
+% operator, we compute the product of the previous operand and the
+% contents of the parentheses using \cs{@@_parse_infix_mul:N}.
+% \begin{macrocode}
+\cs_new:cpn { @@_parse_infix_(:N } #1
+ { \@@_parse_infix_mul:N #1 ( }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Multi-character cases}
+%
+% \begin{macro}[EXP]{\@@_parse_infix_*:N}
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_new:cpn { @@_parse_infix_*:N } ##1##2
+ {
+ \if:w * \exp_not:N ##2
+ \exp_after:wN #1
+ \exp_after:wN ##1
+ \else:
+ \exp_after:wN \@@_parse_infix_mul:N
+ \exp_after:wN ##1
+ \exp_after:wN ##2
+ \fi:
+ }
+ }
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_^:N }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]+\@@_parse_infix_|:Nw+
+% \begin{macro}[EXP]+\@@_parse_infix_&:Nw+
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3
+ {
+ \cs_new:Npn #1 ##1##2
+ {
+ \if:w #2 \exp_not:N ##2
+ \exp_after:wN #1
+ \exp_after:wN ##1
+ \exp:w \exp_after:wN \@@_parse_expand:w
+ \else:
+ \exp_after:wN #3
+ \exp_after:wN ##1
+ \exp_after:wN ##2
+ \fi:
+ }
+ }
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_|:N } | \@@_parse_infix_or:N
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_&:N } & \@@_parse_infix_and:N
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Ternary operator}
+%
+% \begin{macro}[EXP]{\@@_parse_infix_?:N, \@@_parse_infix_::N}
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3#4
+ {
+ \cs_new:Npn #1 ##1
+ {
+ \if_int_compare:w ##1 < \c_@@_prec_quest_int
+ #4
+ \exp_after:wN @
+ \exp_after:wN #2
+ \exp:w
+ \@@_parse_operand:Nw #3
+ \exp_after:wN \@@_parse_expand:w
+ \else:
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN #1
+ \fi:
+ }
+ }
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_?:N }
+ \@@_ternary:NwwN \c_@@_prec_quest_int { }
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_::N }
+ \@@_ternary_auxii:NwwN \c_@@_prec_colon_int
+ {
+ \msg_expandable_error:nnnn
+ { fp } { missing } { ? } { ~for~?: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Comparisons}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_infix_<:N, \@@_parse_infix_=:N,
+% \@@_parse_infix_>:N, \@@_parse_infix_!:N
+% }
+% \begin{macro}[EXP]
+% {
+% \@@_parse_excl_error:,
+% \@@_parse_compare:NNNNNNN,
+% \@@_parse_compare_auxi:NNNNNNN,
+% \@@_parse_compare_auxii:NNNNN,
+% \@@_parse_compare_end:NNNNw,
+% \@@_compare:wNNNNw,
+% }
+% \begin{macrocode}
+\cs_new:cpn { @@_parse_infix_<:N } #1
+ { \@@_parse_compare:NNNNNNN #1 1 0 0 0 0 < }
+\cs_new:cpn { @@_parse_infix_=:N } #1
+ { \@@_parse_compare:NNNNNNN #1 1 0 0 0 0 = }
+\cs_new:cpn { @@_parse_infix_>:N } #1
+ { \@@_parse_compare:NNNNNNN #1 1 0 0 0 0 > }
+\cs_new:cpn { @@_parse_infix_!:N } #1
+ {
+ \exp_after:wN \@@_parse_compare:NNNNNNN
+ \exp_after:wN #1
+ \exp_after:wN 0
+ \exp_after:wN 1
+ \exp_after:wN 1
+ \exp_after:wN 1
+ \exp_after:wN 1
+ }
+\cs_new:Npn \@@_parse_excl_error:
+ {
+ \msg_expandable_error:nnnn
+ { fp } { missing } { = } { ~after~!. }
+ }
+\cs_new:Npn \@@_parse_compare:NNNNNNN #1
+ {
+ \if_int_compare:w #1 < \c_@@_prec_comp_int
+ \exp_after:wN \@@_parse_compare_auxi:NNNNNNN
+ \exp_after:wN \@@_parse_excl_error:
+ \else:
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN \@@_parse_compare:NNNNNNN
+ \fi:
+ }
+\cs_new:Npn \@@_parse_compare_auxi:NNNNNNN #1#2#3#4#5#6#7
+ {
+ \if_case:w
+ \@@_int_eval:w \exp_after:wN ` \token_to_str:N #7 - `<
+ \@@_int_eval_end:
+ \@@_parse_compare_auxii:NNNNN #2#2#4#5#6
+ \or: \@@_parse_compare_auxii:NNNNN #2#3#2#5#6
+ \or: \@@_parse_compare_auxii:NNNNN #2#3#4#2#6
+ \or: \@@_parse_compare_auxii:NNNNN #2#3#4#5#2
+ \else: #1 \@@_parse_compare_end:NNNNw #3#4#5#6#7
+ \fi:
+ }
+\cs_new:Npn \@@_parse_compare_auxii:NNNNN #1#2#3#4#5
+ {
+ \exp_after:wN \@@_parse_compare_auxi:NNNNNNN
+ \exp_after:wN \prg_do_nothing:
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN #4
+ \exp_after:wN #5
+ \exp:w \exp_after:wN \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_compare_end:NNNNw #1#2#3#4#5 \fi:
+ {
+ \fi:
+ \exp_after:wN @
+ \exp_after:wN \@@_parse_apply_compare:NwNNNNNwN
+ \exp_after:wN \c_one_fp
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN #4
+ \exp:w
+ \@@_parse_operand:Nw \c_@@_prec_comp_int \@@_parse_expand:w #5
+ }
+\cs_new:Npn \@@_parse_apply_compare:NwNNNNNwN
+ #1 #2@ #3 #4#5#6#7 #8@ #9
+ {
+ \if_int_odd:w
+ \if_meaning:w \c_zero_fp #3
+ 0
+ \else:
+ \if_case:w \@@_compare_back_any:ww #8 #2 \exp_stop_f:
+ #5 \or: #6 \or: #7 \else: #4
+ \fi:
+ \fi:
+ \exp_stop_f:
+ \exp_after:wN \@@_parse_apply_compare_aux:NNwN
+ \exp_after:wN \c_one_fp
+ \else:
+ \exp_after:wN \@@_parse_apply_compare_aux:NNwN
+ \exp_after:wN \c_zero_fp
+ \fi:
+ #1 #8 #9
+ }
+\cs_new:Npn \@@_parse_apply_compare_aux:NNwN #1 #2 #3; #4
+ {
+ \if_meaning:w \@@_parse_compare:NNNNNNN #4
+ \exp_after:wN \@@_parse_continue_compare:NNwNN
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp:w \exp_end_continue_f:w
+ \@@_exp_after_o:w #3;
+ \exp:w \exp_end_continue_f:w
+ \else:
+ \exp_after:wN \@@_parse_continue:NwN
+ \exp_after:wN #2
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \fi:
+ #4 #2
+ }
+\cs_new:Npn \@@_parse_continue_compare:NNwNN #1#2 #3@ #4#5
+ { #4 #2 #3@ #1 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Tools for functions}
+%
+% \begin{macro}[EXP]{\@@_parse_function_all_fp_o:fnw}
+% Followed by \Arg{function name} \Arg{code} \meta{float array} |@|
+% this checks all floats are floating point numbers (no tuples).
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_function_all_fp_o:fnw #1#2#3 @
+ {
+ \@@_array_if_all_fp:nTF {#3}
+ { #2 #3 @ }
+ {
+ \@@_error:nffn { bad-args }
+ {#1}
+ { \fp_to_tl:n { \s_@@_tuple \@@_tuple_chk:w {#3} ; } }
+ { }
+ \exp_after:wN \c_nan_fp
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_function_one_two:nnw}
+% \begin{macro}[EXP]
+% {
+% \@@_parse_function_one_two_error_o:w,
+% \@@_parse_function_one_two_aux:nnw,
+% \@@_parse_function_one_two_auxii:nnw
+% }
+% This is followed by \Arg{function name} \Arg{code} \meta{float
+% array} |@|. It checks that the \meta{float array} consists of one
+% or two floating point numbers (not tuples), then leaves the
+% \meta{code} (if there is one float) or its tail (if there are two
+% floats) followed by the \meta{float array}. The \meta{code} should
+% start with a single token such as \cs{@@_atan_default:w} that deals
+% with the single-float case.
+%
+% The first \cs{@@_if_type_fp:NTwFw} test catches the case of no
+% argument and the case of a tuple argument. The next one
+% distinguishes the case of a single argument (no error, just add
+% \cs{c_one_fp}) from a tuple second argument. Finally check there is
+% no further argument.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_function_one_two:nnw #1#2#3
+ {
+ \@@_if_type_fp:NTwFw
+ #3 { } \s_@@ \@@_parse_function_one_two_error_o:w \s_@@_stop
+ \@@_parse_function_one_two_aux:nnw {#1} {#2} #3
+ }
+\cs_new:Npn \@@_parse_function_one_two_error_o:w #1#2#3#4 @
+ {
+ \@@_error:nffn { bad-args }
+ {#2}
+ { \fp_to_tl:n { \s_@@_tuple \@@_tuple_chk:w {#4} ; } }
+ { }
+ \exp_after:wN \c_nan_fp
+ }
+\cs_new:Npn \@@_parse_function_one_two_aux:nnw #1#2 #3; #4
+ {
+ \@@_if_type_fp:NTwFw
+ #4 { }
+ \s_@@
+ {
+ \if_meaning:w @ #4
+ \exp_after:wN \use_iv:nnnn
+ \fi:
+ \@@_parse_function_one_two_error_o:w
+ }
+ \s_@@_stop
+ \@@_parse_function_one_two_auxii:nnw {#1} {#2} #3; #4
+ }
+\cs_new:Npn \@@_parse_function_one_two_auxii:nnw #1#2#3; #4; #5
+ {
+ \if_meaning:w @ #5 \else:
+ \exp_after:wN \@@_parse_function_one_two_error_o:w
+ \fi:
+ \use_ii:nn {#1} { \use_none:n #2 } #3; #4; #5
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_tuple_map_o:nw, \@@_tuple_map_loop_o:nw}
+% Apply |#1| to all items in the following tuple and expand once
+% afterwards. The code |#1| should itself expand once after its
+% result.
+% \begin{macrocode}
+\cs_new:Npn \@@_tuple_map_o:nw #1 \s_@@_tuple \@@_tuple_chk:w #2 ;
+ {
+ \exp_after:wN \s_@@_tuple
+ \exp_after:wN \@@_tuple_chk:w
+ \exp_after:wN {
+ \exp:w \exp_end_continue_f:w
+ \@@_tuple_map_loop_o:nw {#1} #2
+ { \s_@@ \prg_break: } ;
+ \prg_break_point:
+ \exp_after:wN } \exp_after:wN ;
+ }
+\cs_new:Npn \@@_tuple_map_loop_o:nw #1#2#3 ;
+ {
+ \use_none:n #2
+ #1 #2 #3 ;
+ \exp:w \exp_end_continue_f:w
+ \@@_tuple_map_loop_o:nw {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_tuple_mapthread_o:nww, \@@_tuple_mapthread_loop_o:nw}
+% Apply |#1| to pairs of items in the two following tuples and expand once
+% afterwards.
+% \begin{macrocode}
+\cs_new:Npn \@@_tuple_mapthread_o:nww #1
+ \s_@@_tuple \@@_tuple_chk:w #2 ;
+ \s_@@_tuple \@@_tuple_chk:w #3 ;
+ {
+ \exp_after:wN \s_@@_tuple
+ \exp_after:wN \@@_tuple_chk:w
+ \exp_after:wN {
+ \exp:w \exp_end_continue_f:w
+ \@@_tuple_mapthread_loop_o:nw {#1}
+ #2 { \s_@@ \prg_break: } ; @
+ #3 { \s_@@ \prg_break: } ;
+ \prg_break_point:
+ \exp_after:wN } \exp_after:wN ;
+ }
+\cs_new:Npn \@@_tuple_mapthread_loop_o:nw #1#2#3 ; #4 @ #5#6 ;
+ {
+ \use_none:n #2
+ \use_none:n #5
+ #1 #2 #3 ; #5 #6 ;
+ \exp:w \exp_end_continue_f:w
+ \@@_tuple_mapthread_loop_o:nw {#1} #4 @
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% ^^A end[todo]
+%
+% \subsection{Messages}
+%
+% \begin{macrocode}
+\msg_new:nnn { fp } { deprecated }
+ { '#1'~deprecated;~use~'#2' }
+\msg_new:nnn { fp } { unknown-fp-word }
+ { Unknown~fp~word~#1. }
+\msg_new:nnn { fp } { missing }
+ { Missing~#1~inserted #2. }
+\msg_new:nnn { fp } { extra }
+ { Extra~#1~ignored. }
+\msg_new:nnn { fp } { early-end }
+ { Premature~end~in~fp~expression. }
+\msg_new:nnn { fp } { after-e }
+ { Cannot~use~#1 after~'e'. }
+\msg_new:nnn { fp } { missing-number }
+ { Missing~number~before~'#1'. }
+\msg_new:nnn { fp } { unknown-symbol }
+ { Unknown~symbol~#1~ignored. }
+\msg_new:nnn { fp } { extra-comma }
+ { Unexpected~comma~turned~to~nan~result. }
+\msg_new:nnn { fp } { no-arg }
+ { #1~got~no~argument;~used~nan. }
+\msg_new:nnn { fp } { multi-arg }
+ { #1~got~more~than~one~argument;~used~nan. }
+\msg_new:nnn { fp } { num-args }
+ { #1~expects~between~#2~and~#3~arguments. }
+\msg_new:nnn { fp } { bad-args }
+ { Arguments~in~#1#2~are~invalid. }
+\msg_new:nnn { fp } { infty-pi }
+ { Math~command~#1 is~not~an~fp }
+\cs_if_exist:cT { @unexpandable@protect }
+ {
+ \msg_new:nnn { fp } { robust-cmd }
+ { Robust~command~#1 invalid~in~fp~expression! }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-random.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-random.dtx
new file mode 100644
index 00000000000..ce5a0c48f70
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-random.dtx
@@ -0,0 +1,629 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-random.dtx
+%
+% Copyright (C) 2016-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3fp-random} module\\
+% Floating point random numbers
+% }
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-random} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\@@_parse_word_rand:N , \@@_parse_word_randint:N}
+% Those functions may receive a variable number of arguments. We
+% won't use the argument~|?|.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_word_rand:N
+ { \@@_parse_function:NNN \@@_rand_o:Nw ? }
+\cs_new:Npn \@@_parse_word_randint:N
+ { \@@_parse_function:NNN \@@_randint_o:Nw ? }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Engine support}
+%
+% Obviously, every word \enquote{random} below means
+% \enquote{pseudo-random}, as we have no access to entropy (except a
+% very unreliable source of entropy: the time it takes to run some
+% code).
+%
+% The primitive random number generator (RNG) is provided as
+% \cs{tex_uniformdeviate:D}. Under the hood, it maintains an array of
+% $55$ $28$-bit numbers, updated with a linear recursion relation
+% (similar to Fibonacci numbers) modulo $2^{28}$. When
+% \cs{tex_uniformdeviate:D} \meta{integer} is called (for brevity denote
+% by~$N$ the \meta{integer}), the next $28$-bit number is read from the
+% array, scaled by $N/2^{28}$, and rounded. To prevent $0$ and $N$ from
+% appearing half as often as other numbers, they are both mapped to the
+% result~$0$.
+%
+% This process means that \cs{tex_uniformdeviate:D} only gives a uniform
+% distribution from $0$ to $N-1$ if $N$ is a divisor of $2^{28}$, so we
+% will mostly call the RNG with such power of~$2$ arguments. If $N$
+% does not divide $2^{28}$, then the relative non-uniformity (difference
+% between probabilities of getting different numbers) is about
+% $N/2^{28}$. This implies that detecting deviation from $1/N$ of the
+% probability of a fixed value X requires about $2^{56}/N$ random
+% trials. But collective patterns can reduce this to about
+% $2^{56}/N^2$. For instance with $N=3\times 2^{k}$, the modulo~$3$
+% repartition of such random numbers is biased with a non-uniformity
+% about $2^k/2^{28}$ (which is much worse than the circa $3/2^{28}$
+% non-uniformity from taking directly $N=3$). This is detectable after
+% about $2^{56}/2^{2k} = 9\cdot 2^{56}/N^2$ random numbers. For $k=15$,
+% $N=98304$, this means roughly $2^{26}$ calls to the RNG
+% (experimentally this takes at the very least 16 seconds on a 2 giga-hertz
+% processor). While this bias is not quite problematic, it is
+% uncomfortably close to being so, and it becomes worse as $N$ is
+% increased. In our code, we shall thus combine several results from
+% the RNG\@.
+%
+% The RNG has three types of unexpected correlations. First, everything
+% is linear modulo~$2^{28}$, hence the lowest $k$ bits of the random
+% numbers only depend on the lowest $k$ bits of the seed (and of course
+% the number of times the RNG was called since setting the seed). The
+% recommended way to get a number from $0$ to $N-1$ is thus to scale the
+% raw $28$-bit integer, as the engine's RNG does. We will go further
+% and in fact typically we discard some of the lowest bits.
+%
+% Second, suppose that we call the RNG with the same argument~$N$ to get
+% a set of $K$ integers in $[0,N-1]$ (throwing away repeats), and
+% suppose that $N>K^3$ and $K>55$. The recursion used to construct more
+% $28$-bit numbers from previous ones is linear:
+% $x_n = x_{n-55} - x_{n-24}$ or $x_n = x_{n-55}-x_{n-24}+2^{28}$.
+% After rescaling and rounding we find that the result $N_n\in[0,N-1]$
+% is among $N_{n-55} - N_{n-24} + \{-1,0,1\}$ modulo~$N$ (a more
+% detailed analysis shows that $0$ appears with frequency close
+% to~$3/4$). The resulting set thus has more triplets $(a,b,c)$ than
+% expected obeying $a=b+c$ modulo~$N$. Namely it will have of order
+% $(K-55)\times 3/4$ such triplets, when one would expect $K^3/(6N)$.
+% This starts to be detectable around $N=2^{18}>55^3$ (earlier if one
+% keeps track of positions too, but this is more subtle than it looks
+% because the array of $28$-bit integers is read backwards by the
+% engine). Hopefully the correlation is subtle enough to not affect
+% realistic documents so we do not specifically mitigate against this.
+% Since we typically use two calls to the RNG per \cs{int_rand:nn} we
+% would need to investigate linear relations between the $x_{2n}$ on the
+% one hand and between the $x_{2n+1}$ on the other hand. Such relations
+% will have more complicated coefficients than $\pm 1$, which alleviates
+% the issue.
+%
+% Third, consider successive batches of $165$ calls to the RNG (with
+% argument $2^{28}$ or with argument~$2$ for instance), then most
+% batches have more odd than even numbers. Note that this does not mean
+% that there are more odd than even numbers overall. Similar issues are
+% discussed in Knuth's TAOCP volume 2 near exercise 3.3.2-31. We do not
+% have any mitigation strategy for this.
+%
+% Ideally, our algorithm should be:
+% \begin{itemize}
+% \item Uniform. The result should be as uniform as possible assuming
+% that the RNG's underlying $28$-bit integers are uniform.
+% \item Uncorrelated. The result should not have detectable
+% correlations between different seeds, similar to the lowest-bit ones
+% mentioned earlier.
+% \item Quick. The algorithm should be fast in \TeX{}, so no
+% \enquote{bit twiddling}, but \enquote{digit twiddling} is ok.
+% \item Simple. The behaviour must be documentable precisely.
+% \item Predictable. The number of calls to the RNG should be the same
+% for any \cs{int_rand:nn}, because then the algorithm can be modified
+% later without changing the result of other uses of the RNG\@.
+% \item Robust. It should work even for \cs{int_rand:nn} |{| |-|
+% \cs{c_max_int} |}| |{| \cs{c_max_int} |}| where the range is not
+% representable as an integer. In fact, we also provide later a
+% floating-point |randint| whose range can go all the way up to
+% $2\times 10^{16}-1$ possible values.
+% \end{itemize}
+% Some of these requirements conflict. For instance, uniformity cannot
+% be achieved with a fixed number of calls to the RNG\@.
+%
+% Denote by $\operatorname{random}(N)$ one call to
+% \cs{tex_uniformdeviate:D} with argument~$N$, and by
+% $\operatorname{ediv}(p,q)$ the \eTeX{} rounding division giving
+% $\lfloor p/q+1/2\rfloor$. Denote by $\meta{min}$, $\meta{max}$ and
+% $R=\meta{max}-\meta{min}+1$ the arguments of \cs{int_min:nn} and the
+% number of possible outcomes. Note that $R\in [1,2^{32}-1]$ cannot
+% necessarily be represented as an integer (however, $R-2^{31}$ can).
+% Our strategy is to get two $28$-bit integers $X$ and $Y$ from the RNG,
+% split each into $14$-bit integers, as $X=X_1\times 2^{14}+X_0$ and
+% $Y=Y_1\times 2^{14}+Y_0$ then return essentially
+% $\meta{min} + \lfloor R (X_1\times 2^{-14} + Y_1\times 2^{-28} +
+% Y_0\times 2^{-42} + X_0\times 2^{-56})\rfloor$. For small~$R$ the
+% $X_0$ term has a tiny effect so we ignore it and we can compute
+% $R\times Y/2^{28}$ much more directly by $\operatorname{random}(R)$.
+% \begin{itemize}
+% \item If $R \leq 2^{17}-1$ then return
+% $\operatorname{ediv}(R\operatorname{random}(2^{14}) +
+% \operatorname{random}(R) + 2^{13}, 2^{14}) - 1 + \meta{min}$. The
+% shifts by $2^{13}$ and $-1$ convert \eTeX{} division to truncated
+% division. The bound on $R$ ensures that the number obtained after
+% the shift is less than \cs{c_max_int}. The non-uniformity is at
+% most of order $2^{17}/2^{42} = 2^{-25}$.
+% \item Split $R=R_2\times 2^{28}+R_1\times 2^{14}+R_0$, where
+% $R_2\in [0,15]$. Compute
+% $\meta{min} + R_2 X_1 2^{14} + (R_2 Y_1 + R_1 X_1) +
+% \operatorname{ediv}(R_2 Y_0 + R_1 Y_1 + R_0 X_1 +
+% \operatorname{ediv}(R_2 X_0 + R_0 Y_1 + \operatorname{ediv}((2^{14}
+% R_1 + R_0) (2^{14} Y_0 + X_0), 2^{28}), 2^{14}), 2^{14})$ then map a
+% result of $\meta{max}+1$ to $\meta{min}$. Writing each
+% $\operatorname{ediv}$ in terms of truncated division with a shift,
+% and using
+% $\lfloor(p+\lfloor r/s\rfloor)/q\rfloor =
+% \lfloor(ps+r)/(sq)\rfloor$, what we compute is equal to
+% $\lfloor\meta{exact}+2^{-29}+2^{-15}+2^{-1}\rfloor$ with
+% $\meta{exact}=\meta{min} + R \times 0.X_1Y_1Y_0X_0$. Given we map
+% $\meta{max}+1$ to $\meta{min}$, the shift has no effect on
+% uniformity. The non-uniformity is bounded by $R/2^{56}<2^{-24}$. It
+% may be possible to speed up the code by dropping tiny terms such as
+% $R_0 X_0$, but the analysis of non-uniformity proves too difficult.
+%
+% To avoid the overflow when the computation yields $\meta{max}+1$
+% with $\meta{max}=2^{31}-1$ (note that $R$ is then arbitrary), we
+% compute the result in two pieces. Compute
+% $\meta{first} = \meta{min} + R_2 X_1 2^{14}$ if $R_2<8$ or
+% $\meta{min} + 8 X_1 2^{14} + (R_2-8) X_1 2^{14}$ if $R_2\geq 8$, the
+% expressions being chosen to avoid overflow. Compute
+% $\meta{second} = R_2 Y_1 + R_1 X_1 + \operatorname{ediv}({\dots})$,
+% at most
+% $R_2 2^{14} + R_1 2^{14} + R_0\leq 2^{28} + 15\times 2^{14} - 1$,
+% not at risk of overflowing. We have
+% $\meta{first}+\meta{second}=\meta{max}+1=\meta{min}+R$ if and only
+% if $\meta{second} = R1 2^{14} + R_0 + R_2 2^{14}$ and
+% $2^{14} R_2 X_1 = 2^{28} R_2 - 2^{14} R_2$ (namely $R_2=0$ or
+% $X_1=2^{14}-1$). In that case, return \meta{min}, otherwise return
+% $\meta{first}+\meta{second}$, which is safe because it is at most
+% \meta{max}. Note that the decision of what to return does not need
+% \meta{first} explicitly so we don't actually compute it, just put it
+% in an integer expression in which \meta{second} is eventually added
+% (or not).
+% \item To get a floating point number in $[0,1)$ just call the
+% $R=10000\leq 2^{17}-1$ procedure above to produce four blocks of four
+% digits.
+% \item To get an integer floating point number in a range (whose size
+% can be up to $2\times 10^{16}-1$), work with fixed-point numbers:
+% get six times four digits to build a fixed point number, multiply by
+% $R$ and add $\meta{min}$. This requires some care because
+% \pkg{l3fp-extended} only supports non-negative numbers.
+% \end{itemize}
+%
+% \begin{variable}{\c__kernel_randint_max_int}
+% Constant equal to $2^{17}-1$, the maximal size of a range that
+% \cs{int_range:nn} can do with its \enquote{simple} algorithm.
+% \begin{macrocode}
+\int_const:Nn \c__kernel_randint_max_int { 131071 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\__kernel_randint:n}
+% Used in an integer expression, \cs{__kernel_randint:n} |{|$R$|}|
+% gives a random number
+% $1+\lfloor(R\operatorname{random}(2^{14}) +
+% \operatorname{random}(R))/2^{14}\rfloor$ that is in $[1,R]$.
+% Previous code was computing $\lfloor p/2^{14}\rfloor$ as
+% $\operatorname{ediv}(p-2^{13},2^{14})$ but that wrongly gives $-1$
+% for $p=0$.
+% \begin{macrocode}
+\cs_new:Npn \__kernel_randint:n #1
+ {
+ (#1 * \tex_uniformdeviate:D 16384
+ + \tex_uniformdeviate:D #1 + 8192 ) / 16384
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_rand_myriads:n, \@@_rand_myriads_loop:w, \@@_rand_myriads_get:w}
+% Used as \cs{@@_rand_myriads:n} |{XXX}| with one letter |X|
+% (specifically) per block of four digit we want; it expands to |;|
+% followed by the requested number of brace groups, each containing
+% four (pseudo-random) digits. Digits are produced as a random number
+% in $[10000,19999]$ for the usual reason of preserving leading zeros.
+% \begin{macrocode}
+\cs_new:Npn \@@_rand_myriads:n #1
+ { \@@_rand_myriads_loop:w #1 \prg_break: X \prg_break_point: ; }
+\cs_new:Npn \@@_rand_myriads_loop:w #1 X
+ {
+ #1
+ \exp_after:wN \@@_rand_myriads_get:w
+ \int_value:w \@@_int_eval:w 9999 +
+ \__kernel_randint:n { 10000 }
+ \@@_rand_myriads_loop:w
+ }
+\cs_new:Npn \@@_rand_myriads_get:w 1 #1 ; { ; {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Random floating point}
+%
+% \begin{macro}[EXP]{\@@_rand_o:Nw, \@@_rand_o:w}
+% First we check that |random| was called without argument. Then get
+% four blocks of four digits and convert that fixed point number to a
+% floating point number (this correctly sets the exponent). This has
+% a minor bug: if all of the random numbers are zero then the result
+% is correctly~$0$ but it raises the \texttt{underflow} flag; it
+% should not do that.
+% \begin{macrocode}
+\cs_new:Npn \@@_rand_o:Nw ? #1 @
+ {
+ \tl_if_empty:nTF {#1}
+ {
+ \exp_after:wN \@@_rand_o:w
+ \exp:w \exp_end_continue_f:w
+ \@@_rand_myriads:n { XXXX } { 0000 } { 0000 } ; 0
+ }
+ {
+ \msg_expandable_error:nnnnn
+ { fp } { num-args } { rand() } { 0 } { 0 }
+ \exp_after:wN \c_nan_fp
+ }
+ }
+\cs_new:Npn \@@_rand_o:w ;
+ {
+ \exp_after:wN \@@_sanitize:Nw
+ \exp_after:wN 0
+ \int_value:w \@@_int_eval:w \c_zero_int
+ \@@_fixed_to_float_o:wN
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Random integer}
+%
+% \begin{macro}[EXP]{\@@_randint_o:Nw}
+% \begin{macro}[EXP]
+% {
+% \@@_randint_default:w,
+% \@@_randint_badarg:w,
+% \@@_randint_o:w,
+% \@@_randint_auxi_o:ww,
+% \@@_randint_auxii:wn,
+% \@@_randint_auxiii_o:ww,
+% \@@_randint_auxiv_o:ww,
+% \@@_randint_auxv_o:w,
+% }
+% Enforce that there is one argument (then add first argument~$1$)
+% or two arguments. Call \cs{@@_randint_badarg:w} on each; this
+% function inserts |1| \cs{exp_stop_f:} to end the \cs{if_case:w}
+% statement if either the argument is not an integer or if its
+% absolute value is $\geq 10^{16}$. Also bail out if
+% \cs{@@_compare_back:ww} yields~|1|, meaning that the bounds are
+% not in the right order. Otherwise an auxiliary converts each
+% argument times $10^{-16}$ (hence the shift in exponent) to a
+% $24$-digit fixed point number (see \pkg{l3fp-extended}).
+% Then compute the number of choices, $\meta{max}+1-\meta{min}$.
+% Create a random $24$-digit fixed-point number with
+% \cs{@@_rand_myriads:n}, then use a fused multiply-add instruction
+% to multiply the number of choices to that random number and add it
+% to \meta{min}. Then truncate to $16$ digits (namely select the
+% integer part of $10^{16}$ times the result) before converting back
+% to a floating point number (\cs{@@_sanitize:Nw} takes care of zero).
+% To avoid issues with negative numbers, add $1$ to all fixed point
+% numbers (namely $10^{16}$ to the integers they represent), except
+% of course when it is time to convert back to a float.
+% \begin{macrocode}
+\cs_new:Npn \@@_randint_o:Nw ?
+ {
+ \@@_parse_function_one_two:nnw
+ { randint }
+ { \@@_randint_default:w \@@_randint_o:w }
+ }
+\cs_new:Npn \@@_randint_default:w #1 { \exp_after:wN #1 \c_one_fp }
+\cs_new:Npn \@@_randint_badarg:w \s_@@ \@@_chk:w #1#2#3;
+ {
+ \@@_int:wTF \s_@@ \@@_chk:w #1#2#3;
+ {
+ \if_meaning:w 1 #1
+ \if_int_compare:w
+ \@@_use_i_until_s:nw #3 ; > \c_@@_prec_int
+ \c_one_int
+ \fi:
+ \fi:
+ }
+ { \c_one_int }
+ }
+\cs_new:Npn \@@_randint_o:w #1; #2; @
+ {
+ \if_case:w
+ \@@_randint_badarg:w #1;
+ \@@_randint_badarg:w #2;
+ \if:w 1 \@@_compare_back:ww #2; #1; \c_one_int \fi:
+ \c_zero_int
+ \@@_randint_auxi_o:ww #1; #2;
+ \or:
+ \@@_invalid_operation_tl_o:ff
+ { randint } { \@@_array_to_clist:n { #1; #2; } }
+ \exp:w
+ \fi:
+ \exp_after:wN \exp_end:
+ }
+\cs_new:Npn \@@_randint_auxi_o:ww #1 ; #2 ; #3 \exp_end:
+ {
+ \fi:
+ \@@_randint_auxii:wn #2 ;
+ { \@@_randint_auxii:wn #1 ; \@@_randint_auxiii_o:ww }
+ }
+\cs_new:Npn \@@_randint_auxii:wn \s_@@ \@@_chk:w #1#2#3#4 ;
+ {
+ \if_meaning:w 0 #1
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ { \exp_after:wN \@@_fixed_continue:wn \c_@@_one_fixed_tl }
+ {
+ \exp_after:wN \@@_ep_to_fixed:wwn
+ \int_value:w \@@_int_eval:w
+ #3 - \c_@@_prec_int , #4 {0000} {0000} ;
+ {
+ \if_meaning:w 0 #2
+ \exp_after:wN \use_i:nnnn
+ \exp_after:wN \@@_fixed_add_one:wN
+ \fi:
+ \exp_after:wN \@@_fixed_sub:wwn \c_@@_one_fixed_tl
+ }
+ \@@_fixed_continue:wn
+ }
+ }
+\cs_new:Npn \@@_randint_auxiii_o:ww #1 ; #2 ;
+ {
+ \@@_fixed_add:wwn #2 ;
+ {0000} {0000} {0000} {0001} {0000} {0000} ;
+ \@@_fixed_sub:wwn #1 ;
+ {
+ \exp_after:wN \use_i:nn
+ \exp_after:wN \@@_fixed_mul_add:wwwn
+ \exp:w \exp_end_continue_f:w \@@_rand_myriads:n { XXXXXX } ;
+ }
+ #1 ;
+ \@@_randint_auxiv_o:ww
+ #2 ;
+ \@@_randint_auxv_o:w #1 ; @
+ }
+\cs_new:Npn \@@_randint_auxiv_o:ww #1#2#3#4#5 ; #6#7#8#9
+ {
+ \if_int_compare:w
+ \if_int_compare:w #1#2 > #6#7 \exp_stop_f: 1 \else:
+ \if_int_compare:w #1#2 < #6#7 \exp_stop_f: - \fi: \fi:
+ #3#4 > #8#9 \exp_stop_f:
+ \@@_use_i_until_s:nw
+ \fi:
+ \@@_randint_auxv_o:w {#1}{#2}{#3}{#4}#5
+ }
+\cs_new:Npn \@@_randint_auxv_o:w #1#2#3#4#5 ; #6 @
+ {
+ \exp_after:wN \@@_sanitize:Nw
+ \int_value:w
+ \if_int_compare:w #1 < 10000 \exp_stop_f:
+ 2
+ \else:
+ 0
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_reverse_args:Nww
+ \fi:
+ \exp_after:wN \@@_fixed_sub:wwn \c_@@_one_fixed_tl
+ {#1} {#2} {#3} {#4} {0000} {0000} ;
+ {
+ \exp_after:wN \exp_stop_f:
+ \int_value:w \@@_int_eval:w \c_@@_prec_int
+ \@@_fixed_to_float_o:wN
+ }
+ 0
+ \exp:w \exp_after:wN \exp_end:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\int_rand:nn, \@@_randint:ww}
+% Evaluate the argument and filter out the case where the lower
+% bound~|#1| is more than the upper bound~|#2|. Then determine
+% whether the range is narrower than \cs{c__kernel_randint_max_int};
+% |#2-#1| may overflow for very large positive~|#2| and negative~|#1|.
+% If the range is narrow, call \cs{__kernel_randint:n} \Arg{choices}
+% where \meta{choices} is the number of possible outcomes. If the
+% range is wide, use somewhat slower code.
+% \begin{macrocode}
+\cs_new:Npn \int_rand:nn #1#2
+ {
+ \int_eval:n
+ {
+ \exp_after:wN \@@_randint:ww
+ \int_value:w \int_eval:n {#1} \exp_after:wN ;
+ \int_value:w \int_eval:n {#2} ;
+ }
+ }
+\cs_new:Npn \@@_randint:ww #1; #2;
+ {
+ \if_int_compare:w #1 > #2 \exp_stop_f:
+ \msg_expandable_error:nnnn
+ { kernel } { randint-backward-range } {#1} {#2}
+ \@@_randint:ww #2; #1;
+ \else:
+ \if_int_compare:w \@@_int_eval:w #2
+ \if_int_compare:w #1 > \c_zero_int
+ - #1 < \@@_int_eval:w
+ \else:
+ < \@@_int_eval:w #1 +
+ \fi:
+ \c__kernel_randint_max_int
+ \@@_int_eval_end:
+ \__kernel_randint:n
+ { \@@_int_eval:w #2 - #1 + 1 \@@_int_eval_end: }
+ - 1 + #1
+ \else:
+ \__kernel_randint:nn {#1} {#2}
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \__kernel_randint:nn, \@@_randint_split_o:Nw, \@@_randint_split_aux:w,
+% \@@_randinat_wide_aux:w, \@@_randinat_wide_auxii:w,
+% }
+% Any $n\in[-2^{31}+1,2^{31}-1]$ is uniquely written as
+% $2^{14}n_1+n_2$ with $n_1\in[-2^{17},2^{17}-1]$ and
+% $n_2\in[0,2^{14}-1]$. Calling \cs{@@_randint_split_o:Nw} $n$ |;|
+% gives $n_1$|;| $n_2$|;| and expands the next token once. We do this
+% for two random numbers and apply \cs{@@_randint_split_o:Nw} twice to
+% fully decompose the range~$R$. One subtlety is that we compute
+% $R-2^{31}=\meta{max}-\meta{min}-(2^{31}-1)\in[-2^{31}+1,2^{31}-1]$
+% rather than $R$ to avoid overflow.
+%
+% Then we have \cs{@@_randint_wide_aux:w} \meta{X_1}|;|\meta{X_0}|;|
+% \meta{Y_1}|;|\meta{Y_0}|;| \meta{R_2}|;|\meta{R_1}|;|\meta{R_0}|;.|
+% and we apply the algorithm described earlier.
+% \begin{macrocode}
+\cs_new:Npn \__kernel_randint:nn #1#2
+ {
+ #1
+ \exp_after:wN \@@_randint_wide_aux:w
+ \int_value:w
+ \exp_after:wN \@@_randint_split_o:Nw
+ \tex_uniformdeviate:D 268435456 ;
+ \int_value:w
+ \exp_after:wN \@@_randint_split_o:Nw
+ \tex_uniformdeviate:D 268435456 ;
+ \int_value:w
+ \exp_after:wN \@@_randint_split_o:Nw
+ \int_value:w \@@_int_eval:w 131072 +
+ \exp_after:wN \@@_randint_split_o:Nw
+ \int_value:w
+ \__kernel_int_add:nnn {#2} { -#1 } { -\c_max_int } ;
+ .
+ }
+\cs_new:Npn \@@_randint_split_o:Nw #1#2 ;
+ {
+ \if_meaning:w 0 #1
+ 0 \exp_after:wN ; \int_value:w 0
+ \else:
+ \exp_after:wN \@@_randint_split_aux:w
+ \int_value:w \@@_int_eval:w (#1#2 - 8192) / 16384 ;
+ + #1#2
+ \fi:
+ \exp_after:wN ;
+ }
+\cs_new:Npn \@@_randint_split_aux:w #1 ;
+ {
+ #1 \exp_after:wN ;
+ \int_value:w \@@_int_eval:w - #1 * 16384
+ }
+\cs_new:Npn \@@_randint_wide_aux:w #1;#2; #3;#4; #5;#6;#7; .
+ {
+ \exp_after:wN \@@_randint_wide_auxii:w
+ \int_value:w \@@_int_eval:w #5 * #3 + #6 * #1 +
+ (#5 * #4 + #6 * #3 + #7 * #1 +
+ (#5 * #2 + #7 * #3 +
+ (16384 * #6 + #7) * (16384 * #4 + #2) / 268435456) / 16384
+ ) / 16384 \exp_after:wN ;
+ \int_value:w \@@_int_eval:w (#5 + #6) * 16384 + #7 ;
+ #1 ; #5 ;
+ }
+\cs_new:Npn \@@_randint_wide_auxii:w #1; #2; #3; #4;
+ {
+ \if_int_odd:w 0
+ \if_int_compare:w #1 = #2 \else: \exp_stop_f: \fi:
+ \if_int_compare:w #4 = \c_zero_int 1 \fi:
+ \if_int_compare:w #3 = 16383 ~ 1 \fi:
+ \exp_stop_f:
+ \exp_after:wN \prg_break:
+ \fi:
+ \if_int_compare:w #4 < 8 \exp_stop_f:
+ + #4 * #3 * 16384
+ \else:
+ + 8 * #3 * 16384 + (#4 - 8) * #3 * 16384
+ \fi:
+ + #1
+ \prg_break_point:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\int_rand:n, \@@_randint:n}
+% Similar to \cs{int_rand:nn}, but needs fewer checks.
+% \begin{macrocode}
+\cs_new:Npn \int_rand:n #1
+ {
+ \int_eval:n
+ { \exp_args:Nf \@@_randint:n { \int_eval:n {#1} } }
+ }
+\cs_new:Npn \@@_randint:n #1
+ {
+ \if_int_compare:w #1 < \c_one_int
+ \msg_expandable_error:nnnn
+ { kernel } { randint-backward-range } { 1 } {#1}
+ \@@_randint:ww #1; 1;
+ \else:
+ \if_int_compare:w #1 > \c__kernel_randint_max_int
+ \__kernel_randint:nn { 1 } {#1}
+ \else:
+ \__kernel_randint:n {#1}
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-round.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-round.dtx
new file mode 100644
index 00000000000..48ae82730c7
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-round.dtx
@@ -0,0 +1,592 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-round.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3fp-round} module\\ Rounding floating points^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-round} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% ^^A todo: provide an interface for rounding modes.
+% ^^A todo: provide a \l_@@_rounding_mode_int giving the current mode.
+% ^^A todo: make transcendental function obey the correct rounding mode.
+% ^^A todo: optimize all rounding functions for various rounding modes.
+% ^^A todo: reduce the number of almost identical functions.
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_word_trunc:N,
+% \@@_parse_word_floor:N,
+% \@@_parse_word_ceil:N
+% }
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_word_trunc:N
+ { \@@_parse_function:NNN \@@_round_o:Nw \@@_round_to_zero:NNN }
+\cs_new:Npn \@@_parse_word_floor:N
+ { \@@_parse_function:NNN \@@_round_o:Nw \@@_round_to_ninf:NNN }
+\cs_new:Npn \@@_parse_word_ceil:N
+ { \@@_parse_function:NNN \@@_round_o:Nw \@@_round_to_pinf:NNN }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_word_round:N, \@@_parse_round:Nw,
+% }
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_word_round:N #1#2
+ {
+ \@@_parse_function:NNN
+ \@@_round_o:Nw \@@_round_to_nearest:NNN #1
+ #2
+ }
+\cs_new:Npn \@@_parse_round:Nw #1 #2 \@@_round_to_nearest:NNN #3#4
+ { #2 #1 #3 }
+
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Rounding tools}
+%
+% \begin{variable}{\c_@@_five_int}
+% This is used as the half-point for which numbers are rounded
+% up/down.
+% \begin{macrocode}
+\int_const:Nn \c_@@_five_int { 5 }
+% \end{macrocode}
+% \end{variable}
+%
+% Floating point operations often yield a result that cannot be exactly
+% represented in a significand with $16$ digits. In that case, we need to
+% round the exact result to a representable number. The \textsc{ieee}
+% standard defines four rounding modes:
+% \begin{itemize}
+% \item Round to nearest: round to the representable floating point
+% number whose absolute difference with the exact result is the
+% smallest. If the exact result lies exactly at the mid-point
+% between two consecutive representable floating point numbers,
+% round to the floating point number whose last digit is even.
+% \item Round towards negative infinity: round to the greatest
+% floating point number not larger than the exact result.
+% \item Round towards zero: round to a floating point number with the
+% same sign as the exact result, with the largest absolute value not
+% larger than the absolute value of the exact result.
+% \item Round towards positive infinity: round to the least floating
+% point number not smaller than the exact result.
+% \end{itemize}
+% This is not fully implemented in \pkg{l3fp} yet, and transcendental
+% functions fall back on the \enquote{round to nearest} mode. All
+% rounding for basic algebra is done through the functions defined in
+% this module, which can be redefined to change their rounding behaviour
+% (but there is not interface for that yet).
+%
+% The rounding tools available in this module are many variations on a
+% base function \cs{@@_round:NNN}, which expands to |0\exp_stop_f:| or
+% |1\exp_stop_f:| depending on whether the final result should be rounded up
+% or down.
+% \begin{itemize}
+% \item \cs{@@_round:NNN} \meta{sign} \meta{digit_1} \meta{digit_2}
+% can expand to |0\exp_stop_f:| or |1\exp_stop_f:|.
+% \item \cs{@@_round_s:NNNw} \meta{sign} \meta{digit_1} \meta{digit_2}
+% \meta{more digits}|;| can expand to |0\exp_stop_f:;| or |1\exp_stop_f:;|.
+% \item \cs{@@_round_neg:NNN} \meta{sign} \meta{digit_1} \meta{digit_2}
+% can expand to |0\exp_stop_f:| or |1\exp_stop_f:|.
+% \end{itemize}
+% See implementation comments for details on the syntax.
+%
+% \begin{macro}[rEXP]{\@@_round:NNN}
+% \begin{macro}[rEXP]
+% {
+% \@@_round_to_nearest:NNN,
+% \@@_round_to_nearest_ninf:NNN,
+% \@@_round_to_nearest_zero:NNN,
+% \@@_round_to_nearest_pinf:NNN,
+% \@@_round_to_ninf:NNN,
+% \@@_round_to_zero:NNN,
+% \@@_round_to_pinf:NNN
+% }
+% \begin{syntax}
+% \cs{@@_round:NNN} \meta{final sign} \meta{digit_1} \meta{digit_2}
+% \end{syntax}
+% If rounding the number $\meta{final sign}
+% \meta{digit_1}.\meta{digit_2}$ to an integer rounds it towards zero
+% (truncates it), this function expands to |0\exp_stop_f:|, and otherwise
+% to |1\exp_stop_f:|. Typically used within the scope of an
+% \cs{@@_int_eval:w}, to add~$1$ if needed, and thereby round
+% correctly. The result depends on the rounding mode.
+%
+% It is very important that \meta{final sign} be the final sign of the
+% result. Otherwise, the result would be incorrect in the case of
+% rounding towards~$-\infty$ or towards~$+\infty$. Also recall that
+% \meta{final sign} is~$0$ for positive, and~$2$ for negative.
+%
+% By default, the functions below return |0\exp_stop_f:|, but this is
+% superseded by \cs{@@_round_return_one:}, which instead returns
+% |1\exp_stop_f:|, expanding everything and removing |0\exp_stop_f:| in the
+% process. In the case of rounding towards~$\pm\infty$ or
+% towards~$0$, this is not really useful, but it prepares us for the
+% \enquote{round to nearest, ties to even} mode.
+%
+% The \enquote{round to nearest} mode is the default. If the
+% \meta{digit_2} is larger than~$5$, then round up. If it is less
+% than~$5$, round down. If it is exactly $5$, then round such that
+% \meta{digit_1} plus the result is even. In other words, round up if
+% \meta{digit_1} is odd.
+%
+% The \enquote{round to nearest} mode has three variants, which differ
+% in how ties are rounded: down towards $-\infty$, truncated towards $0$,
+% or up towards $+\infty$.
+% \begin{macrocode}
+\cs_new:Npn \@@_round_return_one:
+ { \exp_after:wN 1 \exp_after:wN \exp_stop_f: \exp:w }
+\cs_new:Npn \@@_round_to_ninf:NNN #1 #2 #3
+ {
+ \if_meaning:w 2 #1
+ \if_int_compare:w #3 > \c_zero_int
+ \@@_round_return_one:
+ \fi:
+ \fi:
+ \c_zero_int
+ }
+\cs_new:Npn \@@_round_to_zero:NNN #1 #2 #3 { \c_zero_int }
+\cs_new:Npn \@@_round_to_pinf:NNN #1 #2 #3
+ {
+ \if_meaning:w 0 #1
+ \if_int_compare:w #3 > \c_zero_int
+ \@@_round_return_one:
+ \fi:
+ \fi:
+ \c_zero_int
+ }
+\cs_new:Npn \@@_round_to_nearest:NNN #1 #2 #3
+ {
+ \if_int_compare:w #3 > \c_@@_five_int
+ \@@_round_return_one:
+ \else:
+ \if_meaning:w 5 #3
+ \if_int_odd:w #2 \exp_stop_f:
+ \@@_round_return_one:
+ \fi:
+ \fi:
+ \fi:
+ \c_zero_int
+ }
+\cs_new:Npn \@@_round_to_nearest_ninf:NNN #1 #2 #3
+ {
+ \if_int_compare:w #3 > \c_@@_five_int
+ \@@_round_return_one:
+ \else:
+ \if_meaning:w 5 #3
+ \if_meaning:w 2 #1
+ \@@_round_return_one:
+ \fi:
+ \fi:
+ \fi:
+ \c_zero_int
+ }
+\cs_new:Npn \@@_round_to_nearest_zero:NNN #1 #2 #3
+ {
+ \if_int_compare:w #3 > \c_@@_five_int
+ \@@_round_return_one:
+ \fi:
+ \c_zero_int
+ }
+\cs_new:Npn \@@_round_to_nearest_pinf:NNN #1 #2 #3
+ {
+ \if_int_compare:w #3 > \c_@@_five_int
+ \@@_round_return_one:
+ \else:
+ \if_meaning:w 5 #3
+ \if_meaning:w 0 #1
+ \@@_round_return_one:
+ \fi:
+ \fi:
+ \fi:
+ \c_zero_int
+ }
+\cs_new_eq:NN \@@_round:NNN \@@_round_to_nearest:NNN
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_round_s:NNNw}
+% \begin{syntax}
+% \cs{@@_round_s:NNNw} \meta{final sign} \meta{digit} \meta{more digits} |;|
+% \end{syntax}
+% Similar to \cs{@@_round:NNN}, but with an extra semicolon, this
+% function expands to |0\exp_stop_f:;| if rounding $\meta{final sign}
+% \meta{digit}.\meta{more digits}$ to an integer truncates, and to
+% |1\exp_stop_f:;| otherwise. The \meta{more digits} part must be a digit,
+% followed by something that does not overflow a \cs{int_use:N}
+% \cs{@@_int_eval:w} construction. The only relevant information about
+% this piece is whether it is zero or not.
+% \begin{macrocode}
+\cs_new:Npn \@@_round_s:NNNw #1 #2 #3 #4;
+ {
+ \exp_after:wN \@@_round:NNN
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \int_value:w \@@_int_eval:w
+ \if_int_odd:w 0 \if_meaning:w 0 #3 1 \fi:
+ \if_meaning:w 5 #3 1 \fi:
+ \exp_stop_f:
+ \if_int_compare:w \@@_int_eval:w #4 > \c_zero_int
+ 1 +
+ \fi:
+ \fi:
+ #3
+ ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_round_digit:Nw}
+% \begin{syntax}
+% \cs{int_value:w} \cs{@@_round_digit:Nw} \meta{digit} \meta{int expr} |;|
+% \end{syntax}
+% This function should always be called within an \cs{int_value:w}
+% or \cs{@@_int_eval:w} expansion; it may add an extra
+% \cs{@@_int_eval:w}, which means that the integer or integer
+% expression should not be ended with a synonym of \tn{relax}, but
+% with a semi-colon for instance.
+% \begin{macrocode}
+\cs_new:Npn \@@_round_digit:Nw #1 #2;
+ {
+ \if_int_odd:w \if_meaning:w 0 #1 1 \else:
+ \if_meaning:w 5 #1 1 \else:
+ 0 \fi: \fi: \exp_stop_f:
+ \if_int_compare:w \@@_int_eval:w #2 > \c_zero_int
+ \@@_int_eval:w 1 +
+ \fi:
+ \fi:
+ #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_round_neg:NNN}
+% \begin{macro}[EXP]
+% {
+% \@@_round_to_nearest_neg:NNN,
+% \@@_round_to_nearest_ninf_neg:NNN,
+% \@@_round_to_nearest_zero_neg:NNN,
+% \@@_round_to_nearest_pinf_neg:NNN,
+% \@@_round_to_ninf_neg:NNN,
+% \@@_round_to_zero_neg:NNN,
+% \@@_round_to_pinf_neg:NNN
+% }
+% \begin{syntax}
+% \cs{@@_round_neg:NNN} \meta{final sign} \meta{digit_1} \meta{digit_2}
+% \end{syntax}
+% This expands to |0\exp_stop_f:| or |1\exp_stop_f:| after doing the following
+% test. Starting from a number of
+% the form $ \meta{final sign}0.\meta{15 digits}\meta{digit_1} $ with exactly
+% $15$ (non-all-zero) digits before \meta{digit_1}, subtract from it
+% $\meta{final sign}0.0\ldots{}0\meta{digit_2}$, where there are $16$~zeros.
+% If in the current rounding mode the result should be rounded down,
+% then this function returns |1\exp_stop_f:|. Otherwise, \emph{i.e.},
+% if the result is rounded back to the first operand, then this function
+% returns |0\exp_stop_f:|.
+%
+% It turns out that this negative \enquote{round to nearest}
+% is identical to the positive one. And this is the default mode.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_round_to_ninf_neg:NNN \@@_round_to_pinf:NNN
+\cs_new:Npn \@@_round_to_zero_neg:NNN #1 #2 #3
+ {
+ \if_int_compare:w #3 > \c_zero_int
+ \@@_round_return_one:
+ \fi:
+ \c_zero_int
+ }
+\cs_new_eq:NN \@@_round_to_pinf_neg:NNN \@@_round_to_ninf:NNN
+\cs_new_eq:NN \@@_round_to_nearest_neg:NNN \@@_round_to_nearest:NNN
+\cs_new_eq:NN \@@_round_to_nearest_ninf_neg:NNN
+ \@@_round_to_nearest_pinf:NNN
+\cs_new:Npn \@@_round_to_nearest_zero_neg:NNN #1 #2 #3
+ {
+ \if_int_compare:w #3 < \c_@@_five_int \else:
+ \@@_round_return_one:
+ \fi:
+ \c_zero_int
+ }
+\cs_new_eq:NN \@@_round_to_nearest_pinf_neg:NNN
+ \@@_round_to_nearest_ninf:NNN
+\cs_new_eq:NN \@@_round_neg:NNN \@@_round_to_nearest_neg:NNN
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{The \texttt{round} function}
+%
+% ^^A todo: This macro is intermingled with l3fp-parse.
+% ^^A todo: Add explanations.
+% \begin{macro}[EXP]{\@@_round_o:Nw, \@@_round_aux_o:Nw}
+% First check that all arguments are floating point numbers.
+% The |trunc|, |ceil| and |floor| functions expect one or two
+% arguments (the second is $0$ by default), and the |round| function
+% also accepts a third argument (\texttt{nan} by default), which
+% changes |#1| from \cs{@@_round_to_nearest:NNN} to one of its
+% analogues.
+% \begin{macrocode}
+\cs_new:Npn \@@_round_o:Nw #1
+ {
+ \@@_parse_function_all_fp_o:fnw
+ { \@@_round_name_from_cs:N #1 }
+ { \@@_round_aux_o:Nw #1 }
+ }
+\cs_new:Npn \@@_round_aux_o:Nw #1#2 @
+ {
+ \if_case:w
+ \@@_int_eval:w \@@_array_count:n {#2} \@@_int_eval_end:
+ \@@_round_no_arg_o:Nw #1 \exp:w
+ \or: \@@_round:Nwn #1 #2 {0} \exp:w
+ \or: \@@_round:Nww #1 #2 \exp:w
+ \else: \@@_round:Nwww #1 #2 @ \exp:w
+ \fi:
+ \exp_after:wN \exp_end:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_round_no_arg_o:Nw}
+% \begin{macrocode}
+\cs_new:Npn \@@_round_no_arg_o:Nw #1
+ {
+ \cs_if_eq:NNTF #1 \@@_round_to_nearest:NNN
+ { \@@_error:nnnn { num-args } { round () } { 1 } { 3 } }
+ {
+ \@@_error:nffn { num-args }
+ { \@@_round_name_from_cs:N #1 () } { 1 } { 2 }
+ }
+ \exp_after:wN \c_nan_fp
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_round:Nwww}
+% Having three arguments is only allowed for |round|, not |trunc|,
+% |ceil|, |floor|, so check for that case. If all is well, construct
+% one of \cs{@@_round_to_nearest:NNN}, \cs{@@_round_to_nearest_zero:NNN},
+% \cs{@@_round_to_nearest_ninf:NNN}, \cs{@@_round_to_nearest_pinf:NNN}
+% and act accordingly.
+% \begin{macrocode}
+\cs_new:Npn \@@_round:Nwww #1#2 ; #3 ; \s_@@ \@@_chk:w #4#5#6 ; #7 @
+ {
+ \cs_if_eq:NNTF #1 \@@_round_to_nearest:NNN
+ {
+ \tl_if_empty:nTF {#7}
+ {
+ \exp_args:Nc \@@_round:Nww
+ {
+ @@_round_to_nearest
+ \if_meaning:w 0 #4 _zero \else:
+ \if_case:w #5 \exp_stop_f: _pinf \or: \else: _ninf \fi: \fi:
+ :NNN
+ }
+ #2 ; #3 ;
+ }
+ {
+ \@@_error:nnnn { num-args } { round () } { 1 } { 3 }
+ \exp_after:wN \c_nan_fp
+ }
+ }
+ {
+ \@@_error:nffn { num-args }
+ { \@@_round_name_from_cs:N #1 () } { 1 } { 2 }
+ \exp_after:wN \c_nan_fp
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_round_name_from_cs:N}
+% \begin{macrocode}
+\cs_new:Npn \@@_round_name_from_cs:N #1
+ {
+ \cs_if_eq:NNTF #1 \@@_round_to_zero:NNN { trunc }
+ {
+ \cs_if_eq:NNTF #1 \@@_round_to_ninf:NNN { floor }
+ {
+ \cs_if_eq:NNTF #1 \@@_round_to_pinf:NNN { ceil }
+ { round }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_round:Nww, \@@_round:Nwn}
+% \begin{macro}[EXP]
+% {
+% \@@_round_normal:NwNNnw ,
+% \@@_round_normal:NnnwNNnn ,
+% \@@_round_pack:Nw ,
+% \@@_round_normal:NNwNnn ,
+% \@@_round_normal_end:wwNnn ,
+% \@@_round_special:NwwNnn ,
+% \@@_round_special_aux:Nw
+% }
+% If the number of digits to round to is an integer or infinity all is
+% good; if it is \texttt{nan} then just produce a \texttt{nan};
+% otherwise invalid as we have something like |round(1,3.14)| where
+% the number of digits is not an integer.
+% \begin{macrocode}
+\cs_new:Npn \@@_round:Nww #1#2 ; #3 ;
+ {
+ \@@_small_int:wTF #3; { \@@_round:Nwn #1#2; }
+ {
+ \if:w 3 \@@_kind:w #3 ;
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ { \exp_after:wN \c_nan_fp }
+ {
+ \@@_invalid_operation_tl_o:ff
+ { \@@_round_name_from_cs:N #1 }
+ { \@@_array_to_clist:n { #2; #3; } }
+ }
+ }
+ }
+\cs_new:Npn \@@_round:Nwn #1 \s_@@ \@@_chk:w #2#3#4; #5
+ {
+ \if_meaning:w 1 #2
+ \exp_after:wN \@@_round_normal:NwNNnw
+ \exp_after:wN #1
+ \int_value:w #5
+ \else:
+ \exp_after:wN \@@_exp_after_o:w
+ \fi:
+ \s_@@ \@@_chk:w #2#3#4;
+ }
+\cs_new:Npn \@@_round_normal:NwNNnw #1#2 \s_@@ \@@_chk:w 1#3#4#5;
+ {
+ \@@_decimate:nNnnnn { \c_@@_prec_int - #4 - #2 }
+ \@@_round_normal:NnnwNNnn #5 #1 #3 {#4} {#2}
+ }
+\cs_new:Npn \@@_round_normal:NnnwNNnn #1#2#3#4; #5#6
+ {
+ \exp_after:wN \@@_round_normal:NNwNnn
+ \int_value:w \@@_int_eval:w
+ \if_int_compare:w #2 > \c_zero_int
+ 1 \int_value:w #2
+ \exp_after:wN \@@_round_pack:Nw
+ \int_value:w \@@_int_eval:w 1#3 +
+ \else:
+ \if_int_compare:w #3 > \c_zero_int
+ 1 \int_value:w #3 +
+ \fi:
+ \fi:
+ \exp_after:wN #5
+ \exp_after:wN #6
+ \use_none:nnnnnnn #3
+ #1
+ \@@_int_eval_end:
+ 0000 0000 0000 0000 ; #6
+ }
+\cs_new:Npn \@@_round_pack:Nw #1
+ { \if_meaning:w 2 #1 + 1 \fi: \@@_int_eval_end: }
+\cs_new:Npn \@@_round_normal:NNwNnn #1 #2
+ {
+ \if_meaning:w 0 #2
+ \exp_after:wN \@@_round_special:NwwNnn
+ \exp_after:wN #1
+ \fi:
+ \@@_pack_twice_four:wNNNNNNNN
+ \@@_pack_twice_four:wNNNNNNNN
+ \@@_round_normal_end:wwNnn
+ ; #2
+ }
+\cs_new:Npn \@@_round_normal_end:wwNnn #1;#2;#3#4#5
+ {
+ \exp_after:wN \@@_exp_after_o:w \exp:w \exp_end_continue_f:w
+ \@@_sanitize:Nw #3 #4 ; #1 ;
+ }
+\cs_new:Npn \@@_round_special:NwwNnn #1#2;#3;#4#5#6
+ {
+ \if_meaning:w 0 #1
+ \@@_case_return:nw
+ { \exp_after:wN \@@_zero_fp:N \exp_after:wN #4 }
+ \else:
+ \exp_after:wN \@@_round_special_aux:Nw
+ \exp_after:wN #4
+ \int_value:w \@@_int_eval:w 1
+ \if_meaning:w 1 #1 -#6 \else: +#5 \fi:
+ \fi:
+ ;
+ }
+\cs_new:Npn \@@_round_special_aux:Nw #1#2;
+ {
+ \exp_after:wN \@@_exp_after_o:w \exp:w \exp_end_continue_f:w
+ \@@_sanitize:Nw #1#2; {1000}{0000}{0000}{0000};
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-symbolic.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-symbolic.dtx
new file mode 100644
index 00000000000..901ce7a27ca
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-symbolic.dtx
@@ -0,0 +1,688 @@
+% \iffalse
+%
+%% File l3fp-symbolic.dtx (C) Copyright 2012-2015,2017,2018,2020,2021,2023 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% http://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full]{l3doc}
+\usepackage{amsmath}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3fp-symbolic} module\\ Symbolic expressions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-symbolic} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% \subsection{Misc}
+%
+% \begin{variable}{\l_@@_symbolic_fp}
+% Scratch floating point.
+% \begin{macrocode}
+\fp_new:N \l_@@_symbolic_fp
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Building blocks for expressions}
+%
+% Every symbolic expression has the form \cs{s_@@_symbolic}
+% \cs{@@_symbolic_chk:w} \meta{operation} |,| \Arg{operands} \meta{junk}
+% |;| where the \meta{operation} is a list of \texttt{N}-type tokens,
+% the \meta{operands} is an array of floating point objects, and the
+% \meta{junk} is to be discarded. If the outermost operator (last to be
+% evaluated) is unary, the expression has the form
+% \begin{quote}
+% \cs{s_@@_symbolic} \cs{@@_symbolic_chk:w} \\
+% \cs{@@_types_unary:NNw} |\__fp_|\meta{op}|_o:w| \meta{token} |,| \\
+% |{| \meta{operand} |}| \meta{junk} |;|
+% \end{quote}
+% where the \meta{op} is a unary operation (|set_sign|, |cos|,
+% \ldots{}), and the \meta{token} and \meta{operand} are used as
+% arguments for \cs{@@_\meta{op}_o:w} (or the type-specific analog of
+% this function). If the outermost operator is binary, the expression
+% has the form
+% \begin{quote}
+% \cs{s_@@_symbolic} \cs{@@_symbolic_chk:w} \\
+% \cs{@@_types_binary:Nww} |\__fp_|\meta{op}|_o:ww| |,| \\
+% |{| \meta{operand_1} \meta{operand_2} |}| \meta{junk} |;|
+% \end{quote}
+% where the \meta{op} is an operation (|+|, |&|, \ldots{}), and
+% |\__fp_|\meta{op}|_o:ww| receives the \meta{operands} as arguments.
+% If the expression consists of a single variable, it is stored as
+% \begin{quote}
+% \cs{s_@@_symbolic} \cs{@@_symbolic_chk:w} \\
+% \cs{@@_variable_o:w} \meta{identifier} |,| \\
+% |{| |}| \meta{junk} |;|
+% \end{quote}
+%
+% Symbolic expressions are stored in a prefix form. When encountering a
+% symbolic expression in a floating point computation, we attempt to
+% evaluate the operands as much as possible, and if that yields floating
+% point numbers rather than expressions, we apply the operator which
+% follows (if the function is known).
+%
+% For instance, the expression |a + b * sin(c)| is stored as
+% \begin{verbatim}
+% \s__fp_symbolic \__fp_symbolic_chk:w
+% \__fp_types_binary:Nww \__fp_+_o:ww ,
+% {
+% \s__fp_symbolic \__fp_symbolic_chk:w
+% \__fp_variable_o:w a , { } ;
+% \s__fp_symbolic \__fp_symbolic_chk:w
+% \__fp_types_binary:Nww \__fp_*_o:ww ,
+% {
+% \s__fp_symbolic \__fp_symbolic_chk:w
+% \__fp_variable_o:w b , { } ;
+% \s__fp_symbolic \__fp_symbolic_chk:w
+% \__fp_types_unary:NNw \__fp_sin_o:w \use_i:nn ,
+% {
+% \s__fp_symbolic \__fp_symbolic_chk:w
+% \__fp_variable_o:w c , { } ;
+% } ;
+% } ;
+% } ;
+% \end{verbatim}
+%
+% \begin{variable}{\s_@@_symbolic}
+% Scan mark indicating the start of a symbolic expression.
+% \begin{macrocode}
+\scan_new:N \s_@@_symbolic
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_symbolic_chk:w}
+% Analog of \cs{@@_chk:w} for symbolic expressions.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_symbolic_chk:w #1,#2#3;
+ {
+ \msg_error:nne { fp } { misused-fp }
+ {
+ \@@_to_tl_dispatch:w
+ \s_@@_symbolic \@@_symbolic_chk:w #1,{#2};
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Expanding after a symbolic expression}
+%
+% \begin{macro}[EXP]
+% {\@@_if_has_symbolic:nTF, \@@_if_has_symbolic_aux:w}
+% Tests if |#1| contains \cs{s_@@_symbolic} at top-level. This test
+% should be precise enough to determine if a given array contains a
+% symbolic expression or only consists of floating points. Used in
+% \cs{@@_exp_after_symbolic_f:nw}.
+% \begin{macrocode}
+\cs_new:Npn \@@_if_has_symbolic:nTF #1
+ {
+ \@@_if_has_symbolic_aux:w
+ #1 \s_@@_mark \use_i:nn
+ \s_@@_symbolic \s_@@_mark \use_ii:nn
+ \s_@@_stop
+ }
+\cs_new:Npn \@@_if_has_symbolic_aux:w
+ #1 \s_@@_symbolic #2 \s_@@_mark #3#4 \s_@@_stop { #3 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_exp_after_symbolic_f:nw}
+% \begin{macro}[EXP]
+% {\@@_exp_after_symbolic_aux:w, \@@_exp_after_symbolic_loop:N}
+% This function does two things: trigger an \texttt{f}-expansion of
+% the argument~|#1| after the following symbolic expression, and
+% evaluate all pieces of the expression which can be evaluated.
+% \begin{macrocode}
+\cs_new:Npn \@@_exp_after_symbolic_f:nw
+ #1 \s_@@_symbolic \@@_symbolic_chk:w #2, #3#4;
+ {
+ \exp_after:wN \@@_exp_after_symbolic_aux:w
+ \exp:w
+ \@@_exp_after_symbolic_loop:N #2
+ { , \exp:w \use_none:nn }
+ \exp_after:wN \exp_end: \exp_after:wN
+ {
+ \exp:w \exp_end_continue_f:w
+ \@@_exp_after_array_f:w #3 \s_@@_expr_stop
+ \exp_after:wN
+ }
+ \exp_after:wN ;
+ \exp:w \exp_end_continue_f:w #1
+ }
+\cs_new:Npn \@@_exp_after_symbolic_aux:w #1, #2;
+ {
+ \@@_if_has_symbolic:nTF {#2}
+ { \s_@@_symbolic \@@_symbolic_chk:w #1, {#2} ; }
+ { #1 #2 @ \prg_do_nothing: }
+ }
+\cs_new:Npn \@@_exp_after_symbolic_loop:N #1
+ {
+ \exp_after:wN \exp_end:
+ \exp_after:wN #1
+ \exp:w
+ \@@_exp_after_symbolic_loop:N
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Applying infix operators to expressions}
+%
+% \begin{macro}[EXP]{\@@_symbolic_binary_o:Nww}
+% Used when applying infix operators to expressions.
+% \begin{macrocode}
+\cs_new:Npn \@@_symbolic_binary_o:Nww #1 #2; #3;
+ {
+ \@@_exp_after_symbolic_f:nw { \exp_after:wN \exp_stop_f: }
+ \s_@@_symbolic \@@_symbolic_chk:w
+ \@@_types_binary:Nww #1 , { #2; #3; } ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{@makeother}{^} %^^A Hack!
+% \begin{@makeother}{|} %^^A Hack!
+% \begin{@makeother}{&} %^^A Hack!
+% \begin{macro}[EXP]
+% {
+% \@@_symbolic_+_symbolic_o:ww,
+% \@@_symbolic_+_o:ww,
+% \@@_+_symbolic_o:ww,
+% \@@_symbolic_-_symbolic_o:ww,
+% \@@_symbolic_-_o:ww,
+% \@@_-_symbolic_o:ww,
+% \@@_symbolic_*_symbolic_o:ww,
+% \@@_symbolic_*_o:ww,
+% \@@_*_symbolic_o:ww,
+% \@@_symbolic_/_symbolic_o:ww,
+% \@@_symbolic_/_o:ww,
+% \@@_/_symbolic_o:ww,
+% \@@_symbolic_^_symbolic_o:ww,
+% \@@_symbolic_^_o:ww,
+% \@@_^_symbolic_o:ww,
+% \@@_symbolic_|_symbolic_o:ww,
+% \@@_symbolic_|_o:ww,
+% \@@_|_symbolic_o:ww,
+% \@@_symbolic_&_symbolic_o:ww,
+% \@@_symbolic_&_o:ww,
+% \@@_&_symbolic_o:ww,
+% }
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2
+ {
+ \cs_new:cpn
+ { @@_symbolic_#2_symbolic_o:ww }
+ { \@@_symbolic_binary_o:Nww #1 }
+ \cs_new_eq:cc
+ { @@_symbolic_#2 _o:ww }
+ { @@_symbolic_#2_symbolic_o:ww }
+ \cs_new_eq:cc
+ { @@ _#2_symbolic_o:ww }
+ { @@_symbolic_#2_symbolic_o:ww }
+ }
+\tl_map_inline:nn { + - * / ^ & | }
+ { \exp_args:Nc \@@_tmp:w { @@_#1_o:ww } {#1} }
+% \end{macrocode}
+% \end{macro}
+% \end{@makeother}
+% \end{@makeother}
+% \end{@makeother}
+%
+% \subsection{Applying prefix functions to expressions}
+%
+% \begin{macro}[EXP]{\@@_symbolic_unary_o:NNw}
+% Used when applying infix operators to expressions.
+% \begin{macrocode}
+\cs_new:Npn \@@_symbolic_unary_o:NNw #1#2#3; @
+ {
+ \@@_exp_after_symbolic_f:nw { \exp_after:wN \exp_stop_f: }
+ \s_@@_symbolic \@@_symbolic_chk:w
+ \@@_types_unary:NNw #1#2 , { #3; } ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_symbolic_acos_o:w ,
+% \@@_symbolic_acsc_o:w ,
+% \@@_symbolic_asec_o:w ,
+% \@@_symbolic_asin_o:w ,
+% \@@_symbolic_cos_o:w ,
+% \@@_symbolic_cot_o:w ,
+% \@@_symbolic_csc_o:w ,
+% \@@_symbolic_exp_o:w ,
+% \@@_symbolic_ln_o:w ,
+% \@@_symbolic_not_o:w ,
+% \@@_symbolic_sec_o:w ,
+% \@@_symbolic_set_sign_o:w ,
+% \@@_symbolic_sin_o:w ,
+% \@@_symbolic_tan_o:w ,
+% }
+% \begin{macrocode}
+\tl_map_inline:nn
+ {
+ {acos} {acsc} {asec} {asin} {cos} {cot} {csc} {exp} {ln}
+ {not} {sec} {set_sign} {sin} {sqrt} {tan}
+ }
+ {
+ \cs_new:cpe { @@_symbolic_#1_o:w }
+ {
+ \exp_not:N \@@_symbolic_unary_o:NNw
+ \exp_not:c { @@_#1_o:w }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Conversions}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_symbolic_to_decimal:w,
+% \@@_symbolic_to_int:w,
+% \@@_symbolic_to_scientific:w
+% }
+% \begin{macro}[EXP]{\@@_symbolic_convert:wnnN}
+% Symbolic expressions cannot be converted to decimal, integer, or
+% scientific notation unless they can be reduced to
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3
+ {
+ \cs_new:cpn { @@_symbolic_to_#1:w }
+ {
+ \exp_after:wN \@@_symbolic_convert:wnnN
+ \exp:w \exp_end_continue_f:w
+ \@@_exp_after_symbolic_f:nw { { #2 } { fp_to_#1 } #3 }
+ }
+ }
+\@@_tmp:w { decimal } { 0 } \@@_to_decimal_dispatch:w
+\@@_tmp:w { int } { 0 } \@@_to_int_dispatch:w
+\@@_tmp:w { scientific } { nan } \@@_to_scientific_dispatch:w
+\cs_new:Npn \@@_symbolic_convert:wnnN #1#2; #3#4#5
+ {
+ \str_if_eq:nnTF {#1} { \s_@@_symbolic }
+ { \@@_invalid_operation:nnw {#3} {#4} #1#2; }
+ { #5 #1#2; }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_symbolic_cs_arg_to_fn:NN, \@@_symbolic_op_arg_to_fn:nN}
+% \begin{macrocode}
+\cs_new:Npn \@@_symbolic_cs_arg_to_fn:NN #1
+ {
+ \exp_args:Nf \@@_symbolic_op_arg_to_fn:nN
+ { \@@_types_cs_to_op:N #1 }
+ }
+\cs_new:Npn \@@_symbolic_op_arg_to_fn:nN #1#2
+ {
+ \str_case:nnF { #1 #2 }
+ {
+ { not ? } { ! }
+ { set_sign 0 } { abs }
+ { set_sign 2 } { - }
+ }
+ {
+ \token_if_eq_meaning:NNTF #2 \use_ii:nn
+ { #1 d } {#1}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_symbolic_to_tl:w}
+% \begin{macro}[rEXP]
+% {
+% \@@_symbolic_unary_to_tl:NNw,
+% \@@_symbolic_binary_to_tl:Nww,
+% \@@_symbolic_function_to_tl:Nw
+% }
+% Converting a symbolic expression to a token list is possible.
+% \begin{macrocode}
+\cs_new:Npn \@@_symbolic_to_tl:w
+ \s_@@_symbolic \@@_symbolic_chk:w #1#2, #3#4;
+ {
+ \str_case:nnTF {#1}
+ {
+ { \@@_types_unary:NNw } { \@@_symbolic_unary_to_tl:NNw }
+ { \@@_types_binary:Nww } { \@@_symbolic_binary_to_tl:Nww }
+ { \@@_function_o:w } { \@@_symbolic_function_to_tl:Nw }
+ }
+ { #2, #3 @ }
+ { \tl_to_str:n {#2} }
+ }
+\cs_new:Npn \@@_symbolic_unary_to_tl:NNw #1#2 , #3 @
+ {
+ \use:e
+ {
+ \@@_symbolic_cs_arg_to_fn:NN #1#2
+ ( \@@_to_tl_dispatch:w #3 )
+ }
+ }
+\cs_new:Npn \@@_symbolic_binary_to_tl:Nww #1, #2; #3; @
+ {
+ \use:e
+ {
+ ( \@@_to_tl_dispatch:w #2; )
+ \@@_types_cs_to_op:N #1
+ ( \@@_to_tl_dispatch:w #3; )
+ }
+ }
+\cs_new:Npn \@@_symbolic_function_to_tl:Nw #1, #2@
+ {
+ \use:e
+ {
+ \@@_types_cs_to_op:N #1
+ ( \@@_array_to_clist:n {#2} )
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Identifiers}
+%
+% Functions defined here are not necessarily tied to symbolic
+% expressions.
+%
+% \begin{macro}[TF]{\@@_id_if_invalid:n}
+% \begin{macro}[EXP]{\@@_id_if_invalid_aux:N}
+% If |#1| contains a space, it is not a valid identifier. Otherwise,
+% loop through letters in |#1|: if it is not a letter, break the loop
+% and return \texttt{true}. If the end of the loop is reached
+% without finding any non-letter, return \texttt{false}.
+% Note |#1| must be a str (i.e., resulted from \cs{tl_to_str:n}).
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn
+ \@@_id_if_invalid:n #1 { T , F , TF }
+ {
+ \tl_if_empty:nTF {#1}
+ { \prg_return_true: }
+ {
+ \tl_if_in:nnTF { #1 } { ~ }
+ { \prg_return_true: }
+ {
+ \@@_id_if_invalid_aux:N #1
+ { ? \prg_break:n \prg_return_false: }
+ \prg_break_point:
+ }
+ }
+ }
+\cs_new:Npn \@@_id_if_invalid_aux:N #1
+ {
+ \use_none:n #1
+ \int_compare:nF { `a <= `#1 <= `z }
+ {
+ \int_compare:nF { `A <= `#1 <= `Z }
+ { \prg_break:n \prg_return_true: }
+ }
+ \@@_id_if_invalid_aux:N
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Declaring variables and assigning values}
+%
+% \begin{macro}[EXP]{\@@_variable_o:w}
+% We do not use \cs{exp_last_unbraced:Nv} to extract the value of
+% |\l__fp_variable_#1_fp| because in \cs{fp_set_variable:nn} we define
+% this \texttt{fp} variable to be something which \texttt{f}-expands
+% to an actual floating point, rather than a genuine floating point.
+% \begin{macrocode}
+\cs_new:Npn \@@_variable_o:w #1 @ #2
+ {
+ \fp_if_exist:cTF { l_@@_variable_#1_fp }
+ {
+ \exp_last_unbraced:Nf \@@_exp_after_array_f:w
+ { \use:c { l_@@_variable_#1_fp } } \s_@@_expr_stop
+ \exp_after:wN \exp_stop_f: #2
+ }
+ {
+ \token_if_eq_meaning:NNTF #2 \prg_do_nothing:
+ {
+ \s_@@_symbolic \@@_symbolic_chk:w
+ \@@_variable_o:w #1 , { } ;
+ }
+ {
+ \exp_after:wN \s_@@_symbolic
+ \exp_after:wN \@@_symbolic_chk:w
+ \exp_after:wN \@@_variable_o:w
+ \exp:w
+ \@@_exp_after_symbolic_loop:N #1
+ { , \exp:w \use_none:nn }
+ \exp_after:wN \exp_end:
+ \exp_after:wN { \exp_after:wN } \exp_after:wN ;
+ #2
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_variable_set_parsing:Nn, \@@_variable_set_parsing_aux:NNn}
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_variable_set_parsing:Nn #1#2
+ {
+ \cs_set:Npn \@@_tmp:w
+ {
+ \@@_exp_after_symbolic_f:nw { \@@_parse_infix:NN }
+ \s_@@_symbolic \@@_symbolic_chk:w
+ \@@_variable_o:w #2 , { } ;
+ }
+ \exp_args:NNc \@@_variable_set_parsing_aux:NNn #1
+ { @@_parse_word_#2:N } {#2}
+ }
+\cs_new_protected:Npn \@@_variable_set_parsing_aux:NNn #1#2#3
+ {
+ \cs_if_eq:NNF #2 \@@_tmp:w
+ {
+ \cs_if_exist:NTF #2
+ {
+ \msg_warning:nnnn
+ { fp } { id-used-elsewhere } {#3} { variable }
+ #1 #2 \@@_tmp:w
+ }
+ {
+ \cs_new_eq:NN #2 \scan_stop: % to declare the function
+ #1 #2 \@@_tmp:w
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \fp_clear_variable:n,
+% \@@_clear_variable:n, \@@_clear_variable_aux:n
+% }
+% We need local undefining, so have to do it low-level.
+% \cs{@@_clear_variable_aux:n} is needed by \cs{@@_set_function:Nnnn}
+% to skip \cs{@@_id_if_invalid:nTF}.
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_clear_variable:n #1
+ {
+ \exp_args:No \@@_clear_variable:n { \tl_to_str:n {#1} }
+ }
+\cs_new_protected:Npn \@@_clear_variable:n #1
+ {
+ \@@_id_if_invalid:nTF {#1}
+ { \msg_error:nnn { fp } { id-invalid } {#1} }
+ { \@@_clear_variable_aux:n {#1} }
+ }
+\cs_new_protected:Npn \@@_clear_variable_aux:n #1
+ {
+ \cs_set_eq:cN { l_@@_variable_#1_fp } \tex_undefined:D
+ \@@_variable_set_parsing:Nn \cs_set_eq:NN {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\fp_new_variable:n}
+% \begin{macro}{\@@_new_variable:n}
+% Check that |#1| is a valid identifier. If the identifier is already
+% in use, complain. Then set |\__fp_parse_word_#1:N| to use
+% |\__fp_variable_o:w|.
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_new_variable:n #1
+ {
+ \exp_args:No \@@_new_variable:n { \tl_to_str:n {#1} }
+ }
+\cs_new_protected:Npn \@@_new_variable:n #1
+ {
+ \@@_id_if_invalid:nTF {#1}
+ { \msg_error:nnn { fp } { id-invalid } {#1} }
+ {
+ \cs_if_exist:cT { @@_parse_word_#1:N }
+ {
+ \msg_error:nnn
+ { fp } { id-already-defined } {#1}
+ \cs_undefine:c { @@_parse_word_#1:N }
+ \cs_set_eq:cN { l_@@_variable_#1_fp } \tex_undefined:D
+ }
+ \@@_variable_set_parsing:Nn \cs_gset_eq:NN {#1}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_symbolic_flag}
+% \begin{macro}{\fp_set_variable:nn, \@@_set_variable:nn}
+% Refuse invalid identifiers. If the variable does not exist yet,
+% define it just as in \cs{fp_new_variable:n} (but without unnecessary
+% checks). Then evaluate~|#2|. If the result contains the
+% identifier~|#1|, we would later get a loop in cases such as
+% \begin{quote}
+% \cs{fp_set_variable:nn} |{A}| |{A}|\\
+% \cs{fp_show:n} |{A}|
+% \end{quote}
+% To detect this, define |\l__fp_variable_#1_fp| to raise an
+% internal flag and evaluate to \texttt{nan}. Then re-evaluate
+% \cs{l_@@_symbolic_fp}, and store the result in~|#1|. If the flag is
+% raised, |#1|~was present in \cs{l_@@_symbolic_fp}. In all cases,
+% the |#1|-free result ends up in |\l__fp_variable_#1_fp|.
+% \begin{macrocode}
+\flag_new:N \l_@@_symbolic_flag
+\cs_new_protected:Npn \fp_set_variable:nn #1
+ {
+ \exp_args:No \@@_set_variable:nn { \tl_to_str:n {#1} }
+ }
+\cs_new_protected:Npn \@@_set_variable:nn #1#2
+ {
+ \@@_id_if_invalid:nTF {#1}
+ { \msg_error:nnn { fp } { id-invalid } {#1} }
+ {
+ \@@_variable_set_parsing:Nn \cs_set_eq:NN {#1}
+ \fp_set:Nn \l_@@_symbolic_fp {#2}
+ \cs_set_nopar:cpn { l_@@_variable_#1_fp }
+ { \flag_ensure_raised:N \l_@@_symbolic_flag \c_nan_fp }
+ \flag_clear:N \l_@@_symbolic_flag
+ \fp_set:cn { l_@@_variable_#1_fp } { \l_@@_symbolic_fp }
+ \flag_if_raised:NT \l_@@_symbolic_flag
+ {
+ \msg_error:nneee { fp } { id-loop }
+ { #1 }
+ { \tl_to_str:n {#2} }
+ { \fp_to_tl:N \l_@@_symbolic_fp }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{variable}
+%
+% \subsection{Messages}
+%
+% \begin{macrocode}
+\msg_new:nnnn { fp } { id-invalid }
+ { Floating~point~identifier~'#1'~invalid. }
+ {
+ LaTeX~has~been~asked~to~create~a~new~floating~point~identifier~'#1'~
+ but~this~may~only~contain~ASCII~letters.
+ }
+\msg_new:nnnn { fp } { id-already-defined }
+ { Floating~point~identifier~'#1'~already~defined. }
+ {
+ LaTeX~has~been~asked~to~create~a~new~floating~point~identifier~'#1'~
+ but~this~name~has~already~been~used~elsewhere.
+ }
+\msg_new:nnnn { fp } { id-used-elsewhere }
+ { Floating~point~identifier~'#1'~already~used~for~something~else. }
+ {
+ LaTeX~has~been~asked~to~create~a~new~floating~point~identifier~'#1'~
+ but~this~name~is~used,~and~is~not~a~user-defined~#2.
+ }
+\msg_new:nnnn { fp } { id-loop }
+ { Variable~'#1'~used~in~the~definition~of~'#1'. }
+ {
+ LaTeX~has~been~asked~to~set~the~floating~point~identifier~'#1'~
+ to~the~expression~'#2'.~Evaluating~this~expression~yields~'#3',~
+ which~contains~'#1'~itself.
+ }
+% \end{macrocode}
+%
+% \subsection{Road-map}
+%
+% The following functions are not implemented: |min|, |max|, |?:|,
+% comparisons, |round|, |atan|, |acot|.
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-traps.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-traps.dtx
new file mode 100644
index 00000000000..5a4a3b41f9f
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-traps.dtx
@@ -0,0 +1,402 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-traps.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3fp-traps} module\\
+% Trapping floating-point exceptions^^A
+% }
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2024-04-11}
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-traps} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% Exceptions should be accessed by an \texttt{n}-type argument, among
+% \begin{itemize}
+% \item \texttt{invalid_operation}
+% \item \texttt{division_by_zero}
+% \item \texttt{overflow}
+% \item \texttt{underflow}
+% \item \texttt{inexact} (actually never used).
+% \end{itemize}
+%
+% \subsection{Flags}
+%
+% \begin{variable}[module = fp]
+% {
+% \l_fp_invalid_operation_flag,
+% \l_fp_division_by_zero_flag,
+% \l_fp_overflow_flag,
+% \l_fp_underflow_flag
+% }
+% Flags to denote exceptions.
+% \begin{macrocode}
+\flag_new:N \l_fp_invalid_operation_flag
+\flag_new:N \l_fp_division_by_zero_flag
+\flag_new:N \l_fp_overflow_flag
+\flag_new:N \l_fp_underflow_flag
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Traps}
+%
+% Exceptions can be trapped to obtain custom behaviour. When an invalid
+% operation or a division by zero is trapped, the trap receives as
+% arguments the result as an |N|-type floating point number, the
+% function name (multiple letters for prefix operations, or a single
+% symbol for infix operations), and the operand(s). When an overflow or
+% underflow is trapped, the trap receives the resulting overly large or
+% small floating point number if it is not too big, otherwise it
+% receives $+\infty$. Currently, the inexact exception is entirely
+% ignored.
+%
+% The behaviour when an exception occurs is controlled by the
+% definitions of the functions
+% \begin{itemize}
+% \item \cs{@@_invalid_operation:nnw},
+% \item \cs{@@_invalid_operation_o:Nww},
+% \item \cs{@@_invalid_operation_tl_o:ff},
+% \item \cs{@@_division_by_zero_o:Nnw},
+% \item \cs{@@_division_by_zero_o:NNww},
+% \item \cs{@@_overflow:w},
+% \item \cs{@@_underflow:w}.
+% \end{itemize}
+% Rather than changing them directly, we provide a user interface as
+% \cs{fp_trap:nn} \Arg{exception} \Arg{way of trapping}, where the
+% \meta{way of trapping} is one of \texttt{error}, \texttt{flag}, or
+% \texttt{none}.
+%
+% We also provide \cs{@@_invalid_operation_o:nw}, defined in terms of
+% \cs{@@_invalid_operation:nnw}.
+%
+% \begin{macro}{\fp_trap:nn}
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_trap:nn #1#2
+ {
+ \cs_if_exist_use:cF { @@_trap_#1_set_#2: }
+ {
+ \clist_if_in:nnTF
+ { invalid_operation , division_by_zero , overflow , underflow }
+ {#1}
+ {
+ \msg_error:nnee { fp }
+ { unknown-fpu-trap-type } {#1} {#2}
+ }
+ {
+ \msg_error:nne
+ { fp } { unknown-fpu-exception } {#1}
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_trap_invalid_operation_set_error: ,
+% \@@_trap_invalid_operation_set_flag: ,
+% \@@_trap_invalid_operation_set_none: ,
+% \@@_trap_invalid_operation_set:N ,
+% }
+% We provide three types of trapping for invalid operations: either
+% produce an error and raise the relevant flag; or only raise the
+% flag; or don't even raise the flag. In most cases, the function
+% produces as a result its first argument, possibly with
+% post-expansion.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_trap_invalid_operation_set_error:
+ { \@@_trap_invalid_operation_set:N \prg_do_nothing: }
+\cs_new_protected:Npn \@@_trap_invalid_operation_set_flag:
+ { \@@_trap_invalid_operation_set:N \use_none:nnnnn }
+\cs_new_protected:Npn \@@_trap_invalid_operation_set_none:
+ { \@@_trap_invalid_operation_set:N \use_none:nnnnnnn }
+\cs_new_protected:Npn \@@_trap_invalid_operation_set:N #1
+ {
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \@@_invalid_operation:nnw ##1##2##3; }
+ {
+ #1
+ \@@_error:nnfn { invalid } {##2} { \fp_to_tl:n { ##3; } } { }
+ \flag_ensure_raised:N \l_fp_invalid_operation_flag
+ ##1
+ }
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \@@_invalid_operation_o:Nww ##1##2; ##3; }
+ {
+ #1
+ \@@_error:nffn { invalid-ii }
+ { \fp_to_tl:n { ##2; } } { \fp_to_tl:n { ##3; } } {##1}
+ \flag_ensure_raised:N \l_fp_invalid_operation_flag
+ \exp_after:wN \c_nan_fp
+ }
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \@@_invalid_operation_tl_o:ff ##1##2 }
+ {
+ #1
+ \@@_error:nffn { invalid } {##1} {##2} { }
+ \flag_ensure_raised:N \l_fp_invalid_operation_flag
+ \exp_after:wN \c_nan_fp
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_trap_division_by_zero_set_error: ,
+% \@@_trap_division_by_zero_set_flag: ,
+% \@@_trap_division_by_zero_set_none: ,
+% \@@_trap_division_by_zero_set:N ,
+% }
+% We provide three types of trapping for invalid operations and
+% division by zero: either produce an error and raise the relevant
+% flag; or only raise the flag; or don't even raise the flag. In all
+% cases, the function must produce a result, namely its first
+% argument, $\pm\infty$ or \nan{}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_trap_division_by_zero_set_error:
+ { \@@_trap_division_by_zero_set:N \prg_do_nothing: }
+\cs_new_protected:Npn \@@_trap_division_by_zero_set_flag:
+ { \@@_trap_division_by_zero_set:N \use_none:nnnnn }
+\cs_new_protected:Npn \@@_trap_division_by_zero_set_none:
+ { \@@_trap_division_by_zero_set:N \use_none:nnnnnnn }
+\cs_new_protected:Npn \@@_trap_division_by_zero_set:N #1
+ {
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \@@_division_by_zero_o:Nnw ##1##2##3; }
+ {
+ #1
+ \@@_error:nnfn { zero-div } {##2} { \fp_to_tl:n { ##3; } } { }
+ \flag_ensure_raised:N \l_fp_division_by_zero_flag
+ \exp_after:wN ##1
+ }
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \@@_division_by_zero_o:NNww ##1##2##3; ##4; }
+ {
+ #1
+ \@@_error:nffn { zero-div-ii }
+ { \fp_to_tl:n { ##3; } } { \fp_to_tl:n { ##4; } } {##2}
+ \flag_ensure_raised:N \l_fp_division_by_zero_flag
+ \exp_after:wN ##1
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_trap_overflow_set_error: ,
+% \@@_trap_overflow_set_flag: ,
+% \@@_trap_overflow_set_none: ,
+% \@@_trap_overflow_set:N ,
+% }
+% \begin{macro}
+% {
+% \@@_trap_underflow_set_error: ,
+% \@@_trap_underflow_set_flag: ,
+% \@@_trap_underflow_set_none: ,
+% \@@_trap_underflow_set:N ,
+% }
+% \begin{macro}{\@@_trap_overflow_set:NnNn}
+% Just as for invalid operations and division by zero, the three
+% different behaviours are obtained by feeding \cs{prg_do_nothing:},
+% \cs{use_none:nnnnn} or \cs{use_none:nnnnnnn} to an auxiliary, with a
+% further auxiliary common to overflow and underflow functions. In
+% most cases, the argument of the \cs{@@_overflow:w} and
+% \cs{@@_underflow:w} functions will be an (almost) normal number
+% (with an exponent outside the allowed range), and the error message
+% thus displays that number together with the result to which it
+% overflowed or underflowed. For extreme cases such as \texttt{10 **
+% 1e9999}, the exponent would be too large for \TeX{}, and
+% \cs{@@_overflow:w} receives $\pm \infty$ (\cs{@@_underflow:w} would
+% receive $\pm 0$); then we cannot do better than simply say an
+% overflow or underflow occurred.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_trap_overflow_set_error:
+ { \@@_trap_overflow_set:N \prg_do_nothing: }
+\cs_new_protected:Npn \@@_trap_overflow_set_flag:
+ { \@@_trap_overflow_set:N \use_none:nnnnn }
+\cs_new_protected:Npn \@@_trap_overflow_set_none:
+ { \@@_trap_overflow_set:N \use_none:nnnnnnn }
+\cs_new_protected:Npn \@@_trap_overflow_set:N #1
+ { \@@_trap_overflow_set:NnNn #1 { overflow } \@@_inf_fp:N { inf } }
+\cs_new_protected:Npn \@@_trap_underflow_set_error:
+ { \@@_trap_underflow_set:N \prg_do_nothing: }
+\cs_new_protected:Npn \@@_trap_underflow_set_flag:
+ { \@@_trap_underflow_set:N \use_none:nnnnn }
+\cs_new_protected:Npn \@@_trap_underflow_set_none:
+ { \@@_trap_underflow_set:N \use_none:nnnnnnn }
+\cs_new_protected:Npn \@@_trap_underflow_set:N #1
+ { \@@_trap_overflow_set:NnNn #1 { underflow } \@@_zero_fp:N { 0 } }
+\cs_new_protected:Npn \@@_trap_overflow_set:NnNn #1#2#3#4
+ {
+ \exp_args:Nno \use:n
+ { \cs_set:cpn { @@_ #2 :w } \s_@@ \@@_chk:w ##1##2##3; }
+ {
+ #1
+ \@@_error:nffn
+ { flow \if_meaning:w 1 ##1 -to \fi: }
+ { \fp_to_tl:n { \s_@@ \@@_chk:w ##1##2##3; } }
+ { \token_if_eq_meaning:NNF 0 ##2 { - } #4 }
+ {#2}
+ \flag_ensure_raised:c { l_fp_#2_flag }
+ #3 ##2
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_invalid_operation:nnw, \@@_invalid_operation_o:Nww,
+% \@@_invalid_operation_tl_o:ff,
+% \@@_division_by_zero_o:Nnw, \@@_division_by_zero_o:NNww,
+% \@@_overflow:w , \@@_underflow:w
+% }
+% Initialize the control sequences (to log properly their
+% existence). Then set invalid operations to trigger an error, and
+% division by zero, overflow, and underflow to act silently on their
+% flag.
+% \begin{macrocode}
+\cs_new:Npn \@@_invalid_operation:nnw #1#2#3; { }
+\cs_new:Npn \@@_invalid_operation_o:Nww #1#2; #3; { }
+\cs_new:Npn \@@_invalid_operation_tl_o:ff #1 #2 { }
+\cs_new:Npn \@@_division_by_zero_o:Nnw #1#2#3; { }
+\cs_new:Npn \@@_division_by_zero_o:NNww #1#2#3; #4; { }
+\cs_new:Npn \@@_overflow:w { }
+\cs_new:Npn \@@_underflow:w { }
+\fp_trap:nn { invalid_operation } { error }
+\fp_trap:nn { division_by_zero } { flag }
+\fp_trap:nn { overflow } { flag }
+\fp_trap:nn { underflow } { flag }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_invalid_operation_o:nw, \@@_invalid_operation_o:fw}
+% Convenient short-hands for returning \cs{c_nan_fp} for a unary or
+% binary operation, and expanding after.
+% \begin{macrocode}
+\cs_new:Npn \@@_invalid_operation_o:nw
+ { \@@_invalid_operation:nnw { \exp_after:wN \c_nan_fp } }
+\cs_generate_variant:Nn \@@_invalid_operation_o:nw { f }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Errors}
+%
+% \begin{macro}[EXP]{\@@_error:nnnn, \@@_error:nnfn, \@@_error:nffn, \@@_error:nfff}
+% \begin{macrocode}
+\cs_new:Npn \@@_error:nnnn
+ { \msg_expandable_error:nnnnn { fp } }
+\cs_generate_variant:Nn \@@_error:nnnn { nnf, nff , nfff }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+% Some messages.
+% \begin{macrocode}
+\msg_new:nnnn { fp } { unknown-fpu-exception }
+ {
+ The~FPU~exception~'#1'~is~not~known:~
+ that~trap~will~never~be~triggered.
+ }
+ {
+ The~only~exceptions~to~which~traps~can~be~attached~are \\
+ \iow_indent:n
+ {
+ * ~ invalid_operation \\
+ * ~ division_by_zero \\
+ * ~ overflow \\
+ * ~ underflow
+ }
+ }
+\msg_new:nnnn { fp } { unknown-fpu-trap-type }
+ { The~FPU~trap~type~'#2'~is~not~known. }
+ {
+ The~trap~type~must~be~one~of \\
+ \iow_indent:n
+ {
+ * ~ error \\
+ * ~ flag \\
+ * ~ none
+ }
+ }
+\msg_new:nnn { fp } { flow }
+ { An ~ #3 ~ occurred. }
+\msg_new:nnn { fp } { flow-to }
+ { #1 ~ #3 ed ~ to ~ #2 . }
+\msg_new:nnn { fp } { zero-div }
+ { Division~by~zero~in~ #1 (#2) }
+\msg_new:nnn { fp } { zero-div-ii }
+ { Division~by~zero~in~ (#1) #3 (#2) }
+\msg_new:nnn { fp } { invalid }
+ { Invalid~operation~ #1 (#2) }
+\msg_new:nnn { fp } { invalid-ii }
+ { Invalid~operation~ (#1) #3 (#2) }
+\msg_new:nnn { fp } { unknown-type }
+ { Unknown~type~for~'#1' }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-trig.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-trig.dtx
new file mode 100644
index 00000000000..d8585ebe662
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-trig.dtx
@@ -0,0 +1,1700 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-trig.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3fp-trig} module\\
+% Floating point trigonometric functions^^A
+% }
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-trig} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_word_acos:N ,
+% \@@_parse_word_acosd:N ,
+% \@@_parse_word_acsc:N ,
+% \@@_parse_word_acscd:N ,
+% \@@_parse_word_asec:N ,
+% \@@_parse_word_asecd:N ,
+% \@@_parse_word_asin:N ,
+% \@@_parse_word_asind:N ,
+% \@@_parse_word_cos:N ,
+% \@@_parse_word_cosd:N ,
+% \@@_parse_word_cot:N ,
+% \@@_parse_word_cotd:N ,
+% \@@_parse_word_csc:N ,
+% \@@_parse_word_cscd:N ,
+% \@@_parse_word_sec:N ,
+% \@@_parse_word_secd:N ,
+% \@@_parse_word_sin:N ,
+% \@@_parse_word_sind:N ,
+% \@@_parse_word_tan:N ,
+% \@@_parse_word_tand:N ,
+% }
+% Unary functions.
+% \begin{macrocode}
+\tl_map_inline:nn
+ {
+ {acos} {acsc} {asec} {asin}
+ {cos} {cot} {csc} {sec} {sin} {tan}
+ }
+ {
+ \cs_new:cpe { @@_parse_word_#1:N }
+ {
+ \exp_not:N \@@_parse_unary_function:NNN
+ \exp_not:c { @@_#1_o:w }
+ \exp_not:N \use_i:nn
+ }
+ \cs_new:cpe { @@_parse_word_#1d:N }
+ {
+ \exp_not:N \@@_parse_unary_function:NNN
+ \exp_not:c { @@_#1_o:w }
+ \exp_not:N \use_ii:nn
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_word_acot:N , \@@_parse_word_acotd:N,
+% \@@_parse_word_atan:N , \@@_parse_word_atand:N,
+% }
+% Those functions may receive a variable number of arguments.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_word_acot:N
+ { \@@_parse_function:NNN \@@_acot_o:Nw \use_i:nn }
+\cs_new:Npn \@@_parse_word_acotd:N
+ { \@@_parse_function:NNN \@@_acot_o:Nw \use_ii:nn }
+\cs_new:Npn \@@_parse_word_atan:N
+ { \@@_parse_function:NNN \@@_atan_o:Nw \use_i:nn }
+\cs_new:Npn \@@_parse_word_atand:N
+ { \@@_parse_function:NNN \@@_atan_o:Nw \use_ii:nn }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Direct trigonometric functions}
+%
+% The approach for all trigonometric functions (sine, cosine, tangent,
+% cotangent, cosecant, and secant), with arguments given in radians or
+% in degrees, is the same.
+% \begin{itemize}
+% \item Filter out special cases ($\pm 0$, $\pm\inf$ and \nan{}).
+% \item Keep the sign for later, and work with the absolute value
+% $\lvert x\rvert$ of the argument.
+% \item Small numbers ($\lvert x\rvert<1$ in radians, $\lvert
+% x\rvert<10$ in degrees) are converted to fixed point numbers (and
+% to radians if $\lvert x\rvert$ is in degrees).
+% \item For larger numbers, we need argument reduction. Subtract a
+% multiple of $\pi/2$ (in degrees,~$90$) to bring the number to the
+% range to $[0, \pi/2)$ (in degrees, $[0,90)$).
+% \item Reduce further to $[0, \pi/4]$ (in degrees, $[0,45]$) using
+% $\sin x = \cos (\pi/2-x)$, and when working in degrees, convert to
+% radians.
+% \item Use the appropriate power series depending on the octant
+% $\lfloor\frac{|x|}{\pi/4}\rfloor \mod 8$ (in degrees, the same
+% formula with $\pi/4\to 45$), the sign, and the function to
+% compute.
+% \end{itemize}
+%
+% \subsubsection{Filtering special cases}
+%
+% \begin{macro}[EXP]{\@@_sin_o:w}
+% This function, and its analogs for \texttt{cos}, \texttt{csc},
+% \texttt{sec}, \texttt{tan}, and \texttt{cot} instead of
+% \texttt{sin}, are followed either by \cs{use_i:nn} and a float in
+% radians or by \cs{use_ii:nn} and a float in degrees. The sine of
+% $\pm 0$ or \nan{} is the same float. The sine of $\pm\infty$ raises
+% an invalid operation exception with the appropriate function name.
+% Otherwise, call the \texttt{trig} function to perform argument
+% reduction and if necessary convert the reduced argument to radians.
+% Then, \cs{@@_sin_series_o:NNwwww} is called to compute the
+% Taylor series: this function receives a sign~|#3|, an initial octant
+% of~$0$, and the function \cs{@@_ep_to_float_o:wwN} which converts the
+% result of the series to a floating point directly rather than taking
+% its inverse, since $\sin(x) = \#3 \sin\lvert x\rvert$.
+% \begin{macrocode}
+\cs_new:Npn \@@_sin_o:w #1 \s_@@ \@@_chk:w #2#3#4; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \@@_case_return_same_o:w
+ \or: \@@_case_use:nw
+ {
+ \@@_trig:NNNNNwn #1 \@@_sin_series_o:NNwwww
+ \@@_ep_to_float_o:wwN #3 0
+ }
+ \or: \@@_case_use:nw
+ { \@@_invalid_operation_o:fw { #1 { sin } { sind } } }
+ \else: \@@_case_return_same_o:w
+ \fi:
+ \s_@@ \@@_chk:w #2 #3 #4;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_cos_o:w}
+% The cosine of $\pm 0$ is $1$. The cosine of $\pm\infty$ raises an
+% invalid operation exception. The cosine of \nan{} is itself.
+% Otherwise, the \texttt{trig} function reduces the argument to at
+% most half a right-angle and converts if necessary to radians. We
+% then call the same series as for sine, but using a positive
+% sign~|0| regardless of the sign of~$x$, and with an initial octant
+% of~$2$, because $\cos(x) = + \sin(\pi/2 + \lvert x\rvert)$.
+% \begin{macrocode}
+\cs_new:Npn \@@_cos_o:w #1 \s_@@ \@@_chk:w #2#3; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \@@_case_return_o:Nw \c_one_fp
+ \or: \@@_case_use:nw
+ {
+ \@@_trig:NNNNNwn #1 \@@_sin_series_o:NNwwww
+ \@@_ep_to_float_o:wwN 0 2
+ }
+ \or: \@@_case_use:nw
+ { \@@_invalid_operation_o:fw { #1 { cos } { cosd } } }
+ \else: \@@_case_return_same_o:w
+ \fi:
+ \s_@@ \@@_chk:w #2 #3;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_csc_o:w}
+% The cosecant of $\pm 0$ is $\pm \infty$ with the same sign, with a
+% division by zero exception (see \cs{@@_cot_zero_o:Nfw} defined
+% below), which requires the function name. The cosecant of
+% $\pm\infty$ raises an invalid operation exception. The cosecant of
+% \nan{} is itself. Otherwise, the \texttt{trig} function performs
+% the argument reduction, and converts if necessary to radians before
+% calling the same series as for sine, using the sign~|#3|, a starting
+% octant of~$0$, and inverting during the conversion from the fixed
+% point sine to the floating point result, because $\csc(x) = \#3
+% \big( \sin\lvert x\rvert\big)^{-1}$.
+% \begin{macrocode}
+\cs_new:Npn \@@_csc_o:w #1 \s_@@ \@@_chk:w #2#3#4; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \@@_cot_zero_o:Nfw #3 { #1 { csc } { cscd } }
+ \or: \@@_case_use:nw
+ {
+ \@@_trig:NNNNNwn #1 \@@_sin_series_o:NNwwww
+ \@@_ep_inv_to_float_o:wwN #3 0
+ }
+ \or: \@@_case_use:nw
+ { \@@_invalid_operation_o:fw { #1 { csc } { cscd } } }
+ \else: \@@_case_return_same_o:w
+ \fi:
+ \s_@@ \@@_chk:w #2 #3 #4;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_sec_o:w}
+% The secant of $\pm 0$ is $1$. The secant of $\pm \infty$ raises an
+% invalid operation exception. The secant of \nan{} is itself.
+% Otherwise, the \texttt{trig} function reduces the argument and turns
+% it to radians before calling the same series as for sine, using a
+% positive sign~$0$, a starting octant of~$2$, and inverting upon
+% conversion, because $\sec(x) = + 1 / \sin(\pi/2 + \lvert x\rvert)$.
+% \begin{macrocode}
+\cs_new:Npn \@@_sec_o:w #1 \s_@@ \@@_chk:w #2#3; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \@@_case_return_o:Nw \c_one_fp
+ \or: \@@_case_use:nw
+ {
+ \@@_trig:NNNNNwn #1 \@@_sin_series_o:NNwwww
+ \@@_ep_inv_to_float_o:wwN 0 2
+ }
+ \or: \@@_case_use:nw
+ { \@@_invalid_operation_o:fw { #1 { sec } { secd } } }
+ \else: \@@_case_return_same_o:w
+ \fi:
+ \s_@@ \@@_chk:w #2 #3;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_tan_o:w}
+% The tangent of $\pm 0$ or \nan{} is the same floating point number.
+% The tangent of $\pm\infty$ raises an invalid operation exception.
+% Once more, the \texttt{trig} function does the argument reduction
+% step and conversion to radians before calling
+% \cs{@@_tan_series_o:NNwwww}, with a sign~|#3| and an initial octant
+% of~$1$ (this shift is somewhat arbitrary). See \cs{@@_cot_o:w} for
+% an explanation of the $0$~argument.
+% \begin{macrocode}
+\cs_new:Npn \@@_tan_o:w #1 \s_@@ \@@_chk:w #2#3#4; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \@@_case_return_same_o:w
+ \or: \@@_case_use:nw
+ {
+ \@@_trig:NNNNNwn #1
+ \@@_tan_series_o:NNwwww 0 #3 1
+ }
+ \or: \@@_case_use:nw
+ { \@@_invalid_operation_o:fw { #1 { tan } { tand } } }
+ \else: \@@_case_return_same_o:w
+ \fi:
+ \s_@@ \@@_chk:w #2 #3 #4;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_cot_o:w}
+% \begin{macro}[EXP]{\@@_cot_zero_o:Nfw}
+% The cotangent of $\pm 0$ is $\pm \infty$ with the same sign, with a
+% division by zero exception (see \cs{@@_cot_zero_o:Nfw}. The
+% cotangent of $\pm\infty$ raises an invalid operation exception. The
+% cotangent of \nan{} is itself. We use $\cot x = - \tan (\pi/2 +
+% x)$, and the initial octant for the tangent was chosen to be $1$, so
+% the octant here starts at $3$. The change in sign is obtained by
+% feeding \cs{@@_tan_series_o:NNwwww} two signs rather than just the
+% sign of the argument: the first of those indicates whether we
+% compute tangent or cotangent. Those signs are eventually combined.
+% \begin{macrocode}
+\cs_new:Npn \@@_cot_o:w #1 \s_@@ \@@_chk:w #2#3#4; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \@@_cot_zero_o:Nfw #3 { #1 { cot } { cotd } }
+ \or: \@@_case_use:nw
+ {
+ \@@_trig:NNNNNwn #1
+ \@@_tan_series_o:NNwwww 2 #3 3
+ }
+ \or: \@@_case_use:nw
+ { \@@_invalid_operation_o:fw { #1 { cot } { cotd } } }
+ \else: \@@_case_return_same_o:w
+ \fi:
+ \s_@@ \@@_chk:w #2 #3 #4;
+ }
+\cs_new:Npn \@@_cot_zero_o:Nfw #1#2#3 \fi:
+ {
+ \fi:
+ \token_if_eq_meaning:NNTF 0 #1
+ { \exp_args:NNf \@@_division_by_zero_o:Nnw \c_inf_fp }
+ { \exp_args:NNf \@@_division_by_zero_o:Nnw \c_minus_inf_fp }
+ {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Distinguishing small and large arguments}
+%
+% \begin{macro}[EXP]{\@@_trig:NNNNNwn}
+% The first argument is \cs{use_i:nn} if the operand is in radians and
+% \cs{use_ii:nn} if it is in degrees. Arguments |#2| to~|#5| control
+% what trigonometric function we compute, and |#6| to~|#8| are pieces
+% of a normal floating point number. Call the \texttt{_series}
+% function~|#2|, with arguments |#3|, either a conversion function
+% (\cs{@@_ep_to_float_o:wN} or \cs{@@_ep_inv_to_float_o:wN}) or a sign $0$
+% or~$2$ when computing tangent or cotangent; |#4|, a sign $0$ or~$2$;
+% the octant, computed in an integer expression starting with~|#5| and
+% stopped by a period; and a fixed point number obtained from the
+% floating point number by argument reduction (if necessary) and
+% conversion to radians (if necessary). Any argument reduction
+% adjusts the octant accordingly by leaving a (positive) shift into
+% its integer expression. Let us explain the integer comparison. Two
+% of the four \cs{exp_after:wN} are expanded, the expansion hits the
+% test, which is true if the float is at least~$1$ when working in
+% radians, and at least $10$ when working in degrees. Then one of the
+% remaining \cs{exp_after:wN} hits |#1|, which picks the \texttt{trig}
+% or \texttt{trigd} function in whichever branch of the conditional
+% was taken. The final \cs{exp_after:wN} closes the conditional. At
+% the end of the day, a number is \texttt{large} if it is $\geq 1$ in
+% radians or $\geq 10$ in degrees, and \texttt{small} otherwise. All
+% four \texttt{trig}/\texttt{trigd} auxiliaries receive the operand as
+% an extended-precision number.
+% \begin{macrocode}
+\cs_new:Npn \@@_trig:NNNNNwn #1#2#3#4#5 \s_@@ \@@_chk:w 1#6#7#8;
+ {
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN #4
+ \int_value:w \@@_int_eval:w #5
+ \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
+ \if_int_compare:w #7 > #1 0 1 \exp_stop_f:
+ #1 \@@_trig_large:ww \@@_trigd_large:ww
+ \else:
+ #1 \@@_trig_small:ww \@@_trigd_small:ww
+ \fi:
+ #7,#8{0000}{0000};
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Small arguments}
+%
+% \begin{macro}[EXP]{\@@_trig_small:ww}
+% This receives a small extended-precision number in radians and
+% converts it to a fixed point number. Some trailing digits may be
+% lost in the conversion, so we keep the original floating point
+% number around: when computing sine or tangent (or their inverses),
+% the last step is to multiply by the floating point number (as
+% an extended-precision number) rather than the fixed point number.
+% The period serves to end the integer expression for the octant.
+% \begin{macrocode}
+\cs_new:Npn \@@_trig_small:ww #1,#2;
+ { \@@_ep_to_fixed:wwn #1,#2; . #1,#2; }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_trigd_small:ww}
+% Convert the extended-precision number to radians, then call
+% \cs{@@_trig_small:ww} to massage it in the form appropriate for the
+% \texttt{_series} auxiliary.
+% \begin{macrocode}
+\cs_new:Npn \@@_trigd_small:ww #1,#2;
+ {
+ \@@_ep_mul_raw:wwwwN
+ -1,{1745}{3292}{5199}{4329}{5769}{2369}; #1,#2;
+ \@@_trig_small:ww
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Argument reduction in degrees}
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_trigd_large:ww, \@@_trigd_large_auxi:nnnnwNNNN,
+% \@@_trigd_large_auxii:wNw, \@@_trigd_large_auxiii:www
+% }
+% Note that $25\times 360 = 9000$, so $10^{k+1} \equiv 10^{k}
+% \pmod{360}$ for $k\geq 3$. When the exponent~|#1| is very large, we
+% can thus safely replace it by~$22$ (or even~$19$). We turn the
+% floating point number into a fixed point number with two blocks of
+% $8$~digits followed by five blocks of $4$~digits. The original
+% float is $100\times\meta{block_1}\cdots\meta{block_3}.
+% \meta{block_4}\cdots\meta{block_7}$, or is equal to it modulo~$360$
+% if the exponent~|#1| is very large. The first auxiliary finds
+% $\meta{block_1} + \meta{block_2} \pmod{9}$, a single digit, and
+% prepends it to the $4$~digits of \meta{block_3}. It also unpacks
+% \meta{block_4} and grabs the $4$~digits of \meta{block_7}. The
+% second auxiliary grabs the \meta{block_3} plus any contribution from
+% the first two blocks as~|#1|, the first digit of \meta{block_4}
+% (just after the decimal point in hundreds of degrees) as~|#2|, and
+% the three other digits as~|#3|. It finds the quotient and remainder
+% of |#1#2| modulo~$9$, adds twice the quotient to the integer
+% expression for the octant, and places the remainder (between $0$
+% and~$8$) before |#3| to form a new \meta{block_4}. The resulting
+% fixed point number is $x\in [0, 0.9]$. If $x\geq 0.45$, we add~$1$
+% to the octant and feed $0.9-x$ with an exponent of~$2$ (to
+% compensate the fact that we are working in units of hundreds of
+% degrees rather than degrees) to \cs{@@_trigd_small:ww}. Otherwise,
+% we feed it~$x$ with an exponent of~$2$. The third auxiliary also
+% discards digits which were not packed into the various
+% \meta{blocks}. Since the original exponent~|#1| is at least~$2$,
+% those are all~$0$ and no precision is lost (|#6| and~|#7| are
+% four~$0$ each).
+% \begin{macrocode}
+\cs_new:Npn \@@_trigd_large:ww #1, #2#3#4#5#6#7;
+ {
+ \exp_after:wN \@@_pack_eight:wNNNNNNNN
+ \exp_after:wN \@@_pack_eight:wNNNNNNNN
+ \exp_after:wN \@@_pack_twice_four:wNNNNNNNN
+ \exp_after:wN \@@_pack_twice_four:wNNNNNNNN
+ \exp_after:wN \@@_trigd_large_auxi:nnnnwNNNN
+ \exp_after:wN ;
+ \exp:w \exp_end_continue_f:w
+ \prg_replicate:nn { \int_max:nn { 22 - #1 } { 0 } } { 0 }
+ #2#3#4#5#6#7 0000 0000 0000 !
+ }
+\cs_new:Npn \@@_trigd_large_auxi:nnnnwNNNN #1#2#3#4#5; #6#7#8#9
+ {
+ \exp_after:wN \@@_trigd_large_auxii:wNw
+ \int_value:w \@@_int_eval:w #1 + #2
+ - (#1 + #2 - 4) / 9 * 9 \@@_int_eval_end:
+ #3;
+ #4; #5{#6#7#8#9};
+ }
+\cs_new:Npn \@@_trigd_large_auxii:wNw #1; #2#3;
+ {
+ + (#1#2 - 4) / 9 * 2
+ \exp_after:wN \@@_trigd_large_auxiii:www
+ \int_value:w \@@_int_eval:w #1#2
+ - (#1#2 - 4) / 9 * 9 \@@_int_eval_end: #3 ;
+ }
+\cs_new:Npn \@@_trigd_large_auxiii:www #1; #2; #3!
+ {
+ \if_int_compare:w #1 < 4500 \exp_stop_f:
+ \exp_after:wN \@@_use_i_until_s:nw
+ \exp_after:wN \@@_fixed_continue:wn
+ \else:
+ + 1
+ \fi:
+ \@@_fixed_sub:wwn {9000}{0000}{0000}{0000}{0000}{0000};
+ {#1}#2{0000}{0000};
+ { \@@_trigd_small:ww 2, }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Argument reduction in radians}
+%
+% Arguments greater or equal to~$1$ need to be reduced to a range where
+% we only need a few terms of the Taylor series. We reduce to the range
+% $[0,2\pi]$ by subtracting multiples of~$2\pi$, then to the smaller
+% range $[0,\pi/2]$ by subtracting multiples of~$\pi/2$ (keeping track
+% of how many times~$\pi/2$ is subtracted), then to $[0,\pi/4]$ by
+% mapping $x\to \pi/2 - x$ if appropriate. When the argument is very
+% large, say, $10^{100}$, an equally large multiple of~$2\pi$ must be
+% subtracted, hence we must work with a very good approximation
+% of~$2\pi$ in order to get a sensible remainder modulo~$2\pi$.
+%
+% Specifically, we multiply the argument by an approximation
+% of~$1/(2\pi)$ with $\ExplSyntaxOn\int_eval:n { \c__fp_max_exponent_int
+% + 48 }\ExplSyntaxOff$~digits, then discard the integer part of the
+% result, keeping $52$~digits of the fractional part. From the
+% fractional part of $x/(2\pi)$ we deduce the octant (quotient of the
+% first three digits by~$125$). We then multiply by $8$ or~$-8$ (the
+% latter when the octant is odd), ignore any integer part (related to
+% the octant), and convert the fractional part to an extended precision
+% number, before multiplying by~$\pi/4$ to convert back to a value in
+% radians in $[0,\pi/4]$.
+%
+% It is possible to prove that given the precision of floating points
+% and their range of exponents, the $52$~digits may start at most with
+% $24$~zeros. The $5$~last digits are affected by carries from
+% computations which are not done, hence we are left with at least $52 -
+% 24 - 5 = 23$ significant digits, enough to round correctly up to
+% $0.6\cdot\text{ulp}$ in all cases.
+%
+% \begin{variable}[EXP]{\c_@@_trig_intarray}
+% This integer array stores blocks of $8$~decimals of
+% $10^{-16}/(2\pi)$. Each entry is $10^8$ plus an $8$~digit number
+% storing $8$ decimals. In total we store $10112$~decimals of
+% $10^{-16}/(2\pi)$. The number of decimals we really need is the
+% maximum exponent plus the number of digits we later need,~$52$,
+% plus~$12$ ($4-1$~groups of $4$~digits). The memory footprint ($1/2$
+% byte per digit) is the same as an earlier method of storing the data
+% as a control sequence name, but the major advantage is that we can
+% unpack specific subsets of the digits without unpacking the $10112$
+% decimals.
+% \begin{macrocode}
+\intarray_const_from_clist:Nn \c_@@_trig_intarray
+ {
+ 100000000, 100000000, 115915494, 130918953, 135768883, 176337251,
+ 143620344, 159645740, 145644874, 176673440, 158896797, 163422653,
+ 150901138, 102766253, 108595607, 128427267, 157958036, 189291184,
+ 161145786, 152877967, 141073169, 198392292, 139966937, 140907757,
+ 130777463, 196925307, 168871739, 128962173, 197661693, 136239024,
+ 117236290, 111832380, 111422269, 197557159, 140461890, 108690267,
+ 139561204, 189410936, 193784408, 155287230, 199946443, 140024867,
+ 123477394, 159610898, 132309678, 130749061, 166986462, 180469944,
+ 186521878, 181574786, 156696424, 110389958, 174139348, 160998386,
+ 180991999, 162442875, 158517117, 188584311, 117518767, 116054654,
+ 175369880, 109739460, 136475933, 137680593, 102494496, 163530532,
+ 171567755, 103220324, 177781639, 171660229, 146748119, 159816584,
+ 106060168, 103035998, 113391198, 174988327, 186654435, 127975507,
+ 100162406, 177564388, 184957131, 108801221, 199376147, 168137776,
+ 147378906, 133068046, 145797848, 117613124, 127314069, 196077502,
+ 145002977, 159857089, 105690279, 167851315, 125210016, 131774602,
+ 109248116, 106240561, 145620314, 164840892, 148459191, 143521157,
+ 154075562, 100871526, 160680221, 171591407, 157474582, 172259774,
+ 162853998, 175155329, 139081398, 117724093, 158254797, 107332871,
+ 190406999, 175907657, 170784934, 170393589, 182808717, 134256403,
+ 166895116, 162545705, 194332763, 112686500, 126122717, 197115321,
+ 112599504, 138667945, 103762556, 108363171, 116952597, 158128224,
+ 194162333, 143145106, 112353687, 185631136, 136692167, 114206974,
+ 169601292, 150578336, 105311960, 185945098, 139556718, 170995474,
+ 165104316, 123815517, 158083944, 129799709, 199505254, 138756612,
+ 194458833, 106846050, 178529151, 151410404, 189298850, 163881607,
+ 176196993, 107341038, 199957869, 118905980, 193737772, 106187543,
+ 122271893, 101366255, 126123878, 103875388, 181106814, 106765434,
+ 108282785, 126933426, 179955607, 107903860, 160352738, 199624512,
+ 159957492, 176297023, 159409558, 143011648, 129641185, 157771240,
+ 157544494, 157021789, 176979240, 194903272, 194770216, 164960356,
+ 153181535, 144003840, 168987471, 176915887, 163190966, 150696440,
+ 147769706, 187683656, 177810477, 197954503, 153395758, 130188183,
+ 186879377, 166124814, 195305996, 155802190, 183598751, 103512712,
+ 190432315, 180498719, 168687775, 194656634, 162210342, 104440855,
+ 149785037, 192738694, 129353661, 193778292, 187359378, 143470323,
+ 102371458, 137923557, 111863634, 119294601, 183182291, 196416500,
+ 187830793, 131353497, 179099745, 186492902, 167450609, 189368909,
+ 145883050, 133703053, 180547312, 132158094, 131976760, 132283131,
+ 141898097, 149822438, 133517435, 169898475, 101039500, 168388003,
+ 197867235, 199608024, 100273901, 108749548, 154787923, 156826113,
+ 199489032, 168997427, 108349611, 149208289, 103776784, 174303550,
+ 145684560, 183671479, 130845672, 133270354, 185392556, 120208683,
+ 193240995, 162211753, 131839402, 109707935, 170774965, 149880868,
+ 160663609, 168661967, 103747454, 121028312, 119251846, 122483499,
+ 111611495, 166556037, 196967613, 199312829, 196077608, 127799010,
+ 107830360, 102338272, 198790854, 102387615, 157445430, 192601191,
+ 100543379, 198389046, 154921248, 129516070, 172853005, 122721023,
+ 160175233, 113173179, 175931105, 103281551, 109373913, 163964530,
+ 157926071, 180083617, 195487672, 146459804, 173977292, 144810920,
+ 109371257, 186918332, 189588628, 139904358, 168666639, 175673445,
+ 114095036, 137327191, 174311388, 106638307, 125923027, 159734506,
+ 105482127, 178037065, 133778303, 121709877, 134966568, 149080032,
+ 169885067, 141791464, 168350828, 116168533, 114336160, 173099514,
+ 198531198, 119733758, 144420984, 116559541, 152250643, 139431286,
+ 144403838, 183561508, 179771645, 101706470, 167518774, 156059160,
+ 187168578, 157939226, 123475633, 117111329, 198655941, 159689071,
+ 198506887, 144230057, 151919770, 156900382, 118392562, 120338742,
+ 135362568, 108354156, 151729710, 188117217, 195936832, 156488518,
+ 174997487, 108553116, 159830610, 113921445, 144601614, 188452770,
+ 125114110, 170248521, 173974510, 138667364, 103872860, 109967489,
+ 131735618, 112071174, 104788993, 168886556, 192307848, 150230570,
+ 157144063, 163863202, 136852010, 174100574, 185922811, 115721968,
+ 100397824, 175953001, 166958522, 112303464, 118773650, 143546764,
+ 164565659, 171901123, 108476709, 193097085, 191283646, 166919177,
+ 169387914, 133315566, 150669813, 121641521, 100895711, 172862384,
+ 126070678, 145176011, 113450800, 169947684, 122356989, 162488051,
+ 157759809, 153397080, 185475059, 175362656, 149034394, 145420581,
+ 178864356, 183042000, 131509559, 147434392, 152544850, 167491429,
+ 108647514, 142303321, 133245695, 111634945, 167753939, 142403609,
+ 105438335, 152829243, 142203494, 184366151, 146632286, 102477666,
+ 166049531, 140657343, 157553014, 109082798, 180914786, 169343492,
+ 127376026, 134997829, 195701816, 119643212, 133140475, 176289748,
+ 140828911, 174097478, 126378991, 181699939, 148749771, 151989818,
+ 172666294, 160183053, 195832752, 109236350, 168538892, 128468247,
+ 125997252, 183007668, 156937583, 165972291, 198244297, 147406163,
+ 181831139, 158306744, 134851692, 185973832, 137392662, 140243450,
+ 119978099, 140402189, 161348342, 173613676, 144991382, 171541660,
+ 163424829, 136374185, 106122610, 186132119, 198633462, 184709941,
+ 183994274, 129559156, 128333990, 148038211, 175011612, 111667205,
+ 119125793, 103552929, 124113440, 131161341, 112495318, 138592695,
+ 184904438, 146807849, 109739828, 108855297, 104515305, 139914009,
+ 188698840, 188365483, 166522246, 168624087, 125401404, 100911787,
+ 142122045, 123075334, 173972538, 114940388, 141905868, 142311594,
+ 163227443, 139066125, 116239310, 162831953, 123883392, 113153455,
+ 163815117, 152035108, 174595582, 101123754, 135976815, 153401874,
+ 107394340, 136339780, 138817210, 104531691, 182951948, 179591767,
+ 139541778, 179243527, 161740724, 160593916, 102732282, 187946819,
+ 136491289, 149714953, 143255272, 135916592, 198072479, 198580612,
+ 169007332, 118844526, 179433504, 155801952, 149256630, 162048766,
+ 116134365, 133992028, 175452085, 155344144, 109905129, 182727454,
+ 165911813, 122232840, 151166615, 165070983, 175574337, 129548631,
+ 120411217, 116380915, 160616116, 157320000, 183306114, 160618128,
+ 103262586, 195951602, 146321661, 138576614, 180471993, 127077713,
+ 116441201, 159496011, 106328305, 120759583, 148503050, 179095584,
+ 198298218, 167402898, 138551383, 123957020, 180763975, 150429225,
+ 198476470, 171016426, 197438450, 143091658, 164528360, 132493360,
+ 143546572, 137557916, 113663241, 120457809, 196971566, 134022158,
+ 180545794, 131328278, 100552461, 132088901, 187421210, 192448910,
+ 141005215, 149680971, 113720754, 100571096, 134066431, 135745439,
+ 191597694, 135788920, 179342561, 177830222, 137011486, 142492523,
+ 192487287, 113132021, 176673607, 156645598, 127260957, 141566023,
+ 143787436, 129132109, 174858971, 150713073, 191040726, 143541417,
+ 197057222, 165479803, 181512759, 157912400, 125344680, 148220261,
+ 173422990, 101020483, 106246303, 137964746, 178190501, 181183037,
+ 151538028, 179523433, 141955021, 135689770, 191290561, 143178787,
+ 192086205, 174499925, 178975690, 118492103, 124206471, 138519113,
+ 188147564, 102097605, 154895793, 178514140, 141453051, 151583964,
+ 128232654, 106020603, 131189158, 165702720, 186250269, 191639375,
+ 115278873, 160608114, 155694842, 110322407, 177272742, 116513642,
+ 134366992, 171634030, 194053074, 180652685, 109301658, 192136921,
+ 141431293, 171341061, 157153714, 106203978, 147618426, 150297807,
+ 186062669, 169960809, 118422347, 163350477, 146719017, 145045144,
+ 161663828, 146208240, 186735951, 102371302, 190444377, 194085350,
+ 134454426, 133413062, 163074595, 113830310, 122931469, 134466832,
+ 185176632, 182415152, 110179422, 164439571, 181217170, 121756492,
+ 119644493, 196532222, 118765848, 182445119, 109401340, 150443213,
+ 198586286, 121083179, 139396084, 143898019, 114787389, 177233102,
+ 186310131, 148695521, 126205182, 178063494, 157118662, 177825659,
+ 188310053, 151552316, 165984394, 109022180, 163144545, 121212978,
+ 197344714, 188741258, 126822386, 102360271, 109981191, 152056882,
+ 134723983, 158013366, 106837863, 128867928, 161973236, 172536066,
+ 185216856, 132011948, 197807339, 158419190, 166595838, 167852941,
+ 124187182, 117279875, 106103946, 106481958, 157456200, 160892122,
+ 184163943, 173846549, 158993202, 184812364, 133466119, 170732430,
+ 195458590, 173361878, 162906318, 150165106, 126757685, 112163575,
+ 188696307, 145199922, 100107766, 176830946, 198149756, 122682434,
+ 179367131, 108412102, 119520899, 148191244, 140487511, 171059184,
+ 141399078, 189455775, 118462161, 190415309, 134543802, 180893862,
+ 180732375, 178615267, 179711433, 123241969, 185780563, 176301808,
+ 184386640, 160717536, 183213626, 129671224, 126094285, 140110963,
+ 121826276, 151201170, 122552929, 128965559, 146082049, 138409069,
+ 107606920, 103954646, 119164002, 115673360, 117909631, 187289199,
+ 186343410, 186903200, 157966371, 103128612, 135698881, 176403642,
+ 152540837, 109810814, 183519031, 121318624, 172281810, 150845123,
+ 169019064, 166322359, 138872454, 163073727, 128087898, 130041018,
+ 194859136, 173742589, 141812405, 167291912, 138003306, 134499821,
+ 196315803, 186381054, 124578934, 150084553, 128031351, 118843410,
+ 107373060, 159565443, 173624887, 171292628, 198074235, 139074061,
+ 178690578, 144431052, 174262641, 176783005, 182214864, 162289361,
+ 192966929, 192033046, 169332843, 181580535, 164864073, 118444059,
+ 195496893, 153773183, 167266131, 130108623, 158802128, 180432893,
+ 144562140, 147978945, 142337360, 158506327, 104399819, 132635916,
+ 168734194, 136567839, 101281912, 120281622, 195003330, 112236091,
+ 185875592, 101959081, 122415367, 194990954, 148881099, 175891989,
+ 108115811, 163538891, 163394029, 123722049, 184837522, 142362091,
+ 100834097, 156679171, 100841679, 157022331, 178971071, 102928884,
+ 189701309, 195339954, 124415335, 106062584, 139214524, 133864640,
+ 134324406, 157317477, 155340540, 144810061, 177612569, 108474646,
+ 114329765, 143900008, 138265211, 145210162, 136643111, 197987319,
+ 102751191, 144121361, 169620456, 193602633, 161023559, 162140467,
+ 102901215, 167964187, 135746835, 187317233, 110047459, 163339773,
+ 124770449, 118885134, 141536376, 100915375, 164267438, 145016622,
+ 113937193, 106748706, 128815954, 164819775, 119220771, 102367432,
+ 189062690, 170911791, 194127762, 112245117, 123546771, 115640433,
+ 135772061, 166615646, 174474627, 130562291, 133320309, 153340551,
+ 138417181, 194605321, 150142632, 180008795, 151813296, 175497284,
+ 167018836, 157425342, 150169942, 131069156, 134310662, 160434122,
+ 105213831, 158797111, 150754540, 163290657, 102484886, 148697402,
+ 187203725, 198692811, 149360627, 140384233, 128749423, 132178578,
+ 177507355, 171857043, 178737969, 134023369, 102911446, 196144864,
+ 197697194, 134527467, 144296030, 189437192, 154052665, 188907106,
+ 162062575, 150993037, 199766583, 167936112, 181374511, 104971506,
+ 115378374, 135795558, 167972129, 135876446, 130937572, 103221320,
+ 124605656, 161129971, 131027586, 191128460, 143251843, 143269155,
+ 129284585, 173495971, 150425653, 199302112, 118494723, 121323805,
+ 116549802, 190991967, 168151180, 122483192, 151273721, 199792134,
+ 133106764, 121874844, 126215985, 112167639, 167793529, 182985195,
+ 185453921, 106957880, 158685312, 132775454, 133229161, 198905318,
+ 190537253, 191582222, 192325972, 178133427, 181825606, 148823337,
+ 160719681, 101448145, 131983362, 137910767, 112550175, 128826351,
+ 183649210, 135725874, 110356573, 189469487, 154446940, 118175923,
+ 106093708, 128146501, 185742532, 149692127, 164624247, 183221076,
+ 154737505, 168198834, 156410354, 158027261, 125228550, 131543250,
+ 139591848, 191898263, 104987591, 115406321, 103542638, 190012837,
+ 142615518, 178773183, 175862355, 117537850, 169565995, 170028011,
+ 158412588, 170150030, 117025916, 174630208, 142412449, 112839238,
+ 105257725, 114737141, 123102301, 172563968, 130555358, 132628403,
+ 183638157, 168682846, 143304568, 105994018, 170010719, 152092970,
+ 117799058, 132164175, 179868116, 158654714, 177489647, 116547948,
+ 183121404, 131836079, 184431405, 157311793, 149677763, 173989893,
+ 102277656, 107058530, 140837477, 152640947, 143507039, 152145247,
+ 101683884, 107090870, 161471944, 137225650, 128231458, 172995869,
+ 173831689, 171268519, 139042297, 111072135, 107569780, 137262545,
+ 181410950, 138270388, 198736451, 162848201, 180468288, 120582913,
+ 153390138, 135649144, 130040157, 106509887, 192671541, 174507066,
+ 186888783, 143805558, 135011967, 145862340, 180595327, 124727843,
+ 182925939, 157715840, 136885940, 198993925, 152416883, 178793572,
+ 179679516, 154076673, 192703125, 164187609, 162190243, 104699348,
+ 159891990, 160012977, 174692145, 132970421, 167781726, 115178506,
+ 153008552, 155999794, 102099694, 155431545, 127458567, 104403686,
+ 168042864, 184045128, 181182309, 179349696, 127218364, 192935516,
+ 120298724, 169583299, 148193297, 183358034, 159023227, 105261254,
+ 121144370, 184359584, 194433836, 138388317, 175184116, 108817112,
+ 151279233, 137457721, 193398208, 119005406, 132929377, 175306906,
+ 160741530, 149976826, 147124407, 176881724, 186734216, 185881509,
+ 191334220, 175930947, 117385515, 193408089, 157124410, 163472089,
+ 131949128, 180783576, 131158294, 100549708, 191802336, 165960770,
+ 170927599, 101052702, 181508688, 197828549, 143403726, 142729262,
+ 110348701, 139928688, 153550062, 106151434, 130786653, 196085995,
+ 100587149, 139141652, 106530207, 100852656, 124074703, 166073660,
+ 153338052, 163766757, 120188394, 197277047, 122215363, 138511354,
+ 183463624, 161985542, 159938719, 133367482, 104220974, 149956672,
+ 170250544, 164232439, 157506869, 159133019, 137469191, 142980999,
+ 134242305, 150172665, 121209241, 145596259, 160554427, 159095199,
+ 168243130, 184279693, 171132070, 121049823, 123819574, 171759855,
+ 119501864, 163094029, 175943631, 194450091, 191506160, 149228764,
+ 132319212, 197034460, 193584259, 126727638, 168143633, 109856853,
+ 127860243, 132141052, 133076065, 188414958, 158718197, 107124299,
+ 159592267, 181172796, 144388537, 196763139, 127431422, 179531145,
+ 100064922, 112650013, 132686230, 121550837,
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_trig_large:ww,
+% \@@_trig_large_auxi:w,
+% \@@_trig_large_auxii:w,
+% \@@_trig_large_auxiii:w,
+% }
+% The exponent~|#1| is between $1$ and~$\ExplSyntaxOn \int_use:N
+% \c__fp_max_exponent_int$. We wish to look up decimals
+% $10^{\text{\texttt{\#1}}-16}/(2\pi)$ starting from the digit
+% $|#1|+1$. Since they are stored in batches of~$8$, compute
+% $\lfloor|#1|/8\rfloor$ and fetch blocks of $8$ digits starting
+% there. The numbering of items in \cs{c_@@_trig_intarray} starts
+% at~$1$, so the block $\lfloor|#1|/8\rfloor+1$ contains the digit we
+% want, at one of the eight positions. Each call to \cs{int_value:w}
+% \cs{__kernel_intarray_item:Nn} expands the next, until being stopped
+% by \cs{@@_trig_large_auxiii:w} using \cs{exp_stop_f:}. Once all
+% these blocks are unpacked, the \cs{exp_stop_f:} and $0$ to $7$
+% digits are removed by \cs[no-index]{use_none:n\ldots{}n}.
+% Finally, \cs{@@_trig_large_auxii:w} packs $64$ digits (there are
+% between $65$ and $72$ at this point) into groups of~$4$ and the
+% \texttt{auxv} auxiliary is called.
+% \begin{macrocode}
+\cs_new:Npn \@@_trig_large:ww #1, #2#3#4#5#6;
+ {
+ \exp_after:wN \@@_trig_large_auxi:w
+ \int_value:w \@@_int_eval:w (#1 - 4) / 8 \exp_after:wN ,
+ \int_value:w #1 , ;
+ {#2}{#3}{#4}{#5} ;
+ }
+\cs_new:Npn \@@_trig_large_auxi:w #1, #2,
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_trig_large_auxii:w
+ \cs:w
+ use_none:n \prg_replicate:nn { #2 - #1 * 8 } { n }
+ \exp_after:wN
+ \cs_end:
+ \int_value:w
+ \__kernel_intarray_item:Nn \c_@@_trig_intarray
+ { \@@_int_eval:w #1 + 1 \scan_stop: }
+ \exp_after:wN \@@_trig_large_auxiii:w \int_value:w
+ \__kernel_intarray_item:Nn \c_@@_trig_intarray
+ { \@@_int_eval:w #1 + 2 \scan_stop: }
+ \exp_after:wN \@@_trig_large_auxiii:w \int_value:w
+ \__kernel_intarray_item:Nn \c_@@_trig_intarray
+ { \@@_int_eval:w #1 + 3 \scan_stop: }
+ \exp_after:wN \@@_trig_large_auxiii:w \int_value:w
+ \__kernel_intarray_item:Nn \c_@@_trig_intarray
+ { \@@_int_eval:w #1 + 4 \scan_stop: }
+ \exp_after:wN \@@_trig_large_auxiii:w \int_value:w
+ \__kernel_intarray_item:Nn \c_@@_trig_intarray
+ { \@@_int_eval:w #1 + 5 \scan_stop: }
+ \exp_after:wN \@@_trig_large_auxiii:w \int_value:w
+ \__kernel_intarray_item:Nn \c_@@_trig_intarray
+ { \@@_int_eval:w #1 + 6 \scan_stop: }
+ \exp_after:wN \@@_trig_large_auxiii:w \int_value:w
+ \__kernel_intarray_item:Nn \c_@@_trig_intarray
+ { \@@_int_eval:w #1 + 7 \scan_stop: }
+ \exp_after:wN \@@_trig_large_auxiii:w \int_value:w
+ \__kernel_intarray_item:Nn \c_@@_trig_intarray
+ { \@@_int_eval:w #1 + 8 \scan_stop: }
+ \exp_after:wN \@@_trig_large_auxiii:w \int_value:w
+ \__kernel_intarray_item:Nn \c_@@_trig_intarray
+ { \@@_int_eval:w #1 + 9 \scan_stop: }
+ \exp_stop_f:
+ }
+\cs_new:Npn \@@_trig_large_auxii:w
+ {
+ \@@_pack_twice_four:wNNNNNNNN \@@_pack_twice_four:wNNNNNNNN
+ \@@_pack_twice_four:wNNNNNNNN \@@_pack_twice_four:wNNNNNNNN
+ \@@_pack_twice_four:wNNNNNNNN \@@_pack_twice_four:wNNNNNNNN
+ \@@_pack_twice_four:wNNNNNNNN \@@_pack_twice_four:wNNNNNNNN
+ \@@_trig_large_auxv:www ;
+ }
+\cs_new:Npn \@@_trig_large_auxiii:w 1 { \exp_stop_f: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_trig_large_auxv:www,
+% \@@_trig_large_auxvi:wnnnnnnnn,
+% \@@_trig_large_pack:NNNNNw
+% }
+% First come the first $64$~digits of the fractional part of
+% $10^{\text{\texttt{\#1}}-16}/(2\pi)$, arranged in $16$~blocks
+% of~$4$, and ending with a semicolon. Then a few more digits of the
+% same fractional part, ending with a semicolon, then $4$~blocks of
+% $4$~digits holding the significand of the original argument.
+% Multiply the $16$-digit significand with the $64$-digit fractional
+% part: the \texttt{auxvi} auxiliary receives the significand
+% as~|#2#3#4#5| and $16$~digits of the fractional part as~|#6#7#8#9|,
+% and computes one step of the usual ladder of \texttt{pack} functions
+% we use for multiplication (see \emph{e.g.,} \cs{@@_fixed_mul:wwn}),
+% then discards one block of the fractional part to set things up for
+% the next step of the ladder. We perform $13$~such steps, replacing
+% the last \texttt{middle} shift by the appropriate \texttt{trailing}
+% shift, then discard the significand and remaining $3$~blocks from
+% the fractional part, as there are not enough digits to compute any
+% more step in the ladder. The last semicolon closes the ladder, and
+% we return control to the \texttt{auxvii} auxiliary.
+% \begin{macrocode}
+\cs_new:Npn \@@_trig_large_auxv:www #1; #2; #3;
+ {
+ \exp_after:wN \@@_use_i_until_s:nw
+ \exp_after:wN \@@_trig_large_auxvii:w
+ \int_value:w \@@_int_eval:w \c_@@_leading_shift_int
+ \prg_replicate:nn { 13 }
+ { \@@_trig_large_auxvi:wnnnnnnnn }
+ + \c_@@_trailing_shift_int - \c_@@_middle_shift_int
+ \@@_use_i_until_s:nw
+ ; #3 #1 ; ;
+ }
+\cs_new:Npn \@@_trig_large_auxvi:wnnnnnnnn #1; #2#3#4#5#6#7#8#9
+ {
+ \exp_after:wN \@@_trig_large_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_middle_shift_int
+ + #2*#9 + #3*#8 + #4*#7 + #5*#6
+ #1; {#2}{#3}{#4}{#5} {#7}{#8}{#9}
+ }
+\cs_new:Npn \@@_trig_large_pack:NNNNNw #1#2#3#4#5#6;
+ { + #1#2#3#4#5 ; #6 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_trig_large_auxvii:w,
+% \@@_trig_large_auxviii:w,
+% }
+% \begin{macro}[EXP]
+% {
+% \@@_trig_large_auxix:Nw,
+% \@@_trig_large_auxx:wNNNNN,
+% \@@_trig_large_auxxi:w
+% }
+% The \texttt{auxvii} auxiliary is followed by $52$~digits and a
+% semicolon. We find the octant as the integer part of $8$~times what
+% follows, or equivalently as the integer part of $|#1#2#3|/125$, and
+% add it to the surrounding integer expression for the octant. We
+% then compute $8$~times the $52$-digit number, with a minus sign if
+% the octant is odd. Again, the last \texttt{middle} shift is
+% converted to a \texttt{trailing} shift. Any integer part (including
+% negative values which come up when the octant is odd) is discarded
+% by \cs{@@_use_i_until_s:nw}. The resulting fractional part should
+% then be converted to radians by multiplying by~$2\pi/8$, but first,
+% build an extended precision number by abusing
+% \cs{@@_ep_to_ep_loop:N} with the appropriate trailing markers.
+% Finally, \cs{@@_trig_small:ww} sets up the argument for the
+% functions which compute the Taylor series.
+% \begin{macrocode}
+\cs_new:Npn \@@_trig_large_auxvii:w #1#2#3
+ {
+ \exp_after:wN \@@_trig_large_auxviii:ww
+ \int_value:w \@@_int_eval:w (#1#2#3 - 62) / 125 ;
+ #1#2#3
+ }
+\cs_new:Npn \@@_trig_large_auxviii:ww #1;
+ {
+ + #1
+ \if_int_odd:w #1 \exp_stop_f:
+ \exp_after:wN \@@_trig_large_auxix:Nw
+ \exp_after:wN -
+ \else:
+ \exp_after:wN \@@_trig_large_auxix:Nw
+ \exp_after:wN +
+ \fi:
+ }
+\cs_new:Npn \@@_trig_large_auxix:Nw
+ {
+ \exp_after:wN \@@_use_i_until_s:nw
+ \exp_after:wN \@@_trig_large_auxxi:w
+ \int_value:w \@@_int_eval:w \c_@@_leading_shift_int
+ \prg_replicate:nn { 13 }
+ { \@@_trig_large_auxx:wNNNNN }
+ + \c_@@_trailing_shift_int - \c_@@_middle_shift_int
+ ;
+ }
+\cs_new:Npn \@@_trig_large_auxx:wNNNNN #1; #2 #3#4#5#6
+ {
+ \exp_after:wN \@@_trig_large_pack:NNNNNw
+ \int_value:w \@@_int_eval:w \c_@@_middle_shift_int
+ #2 8 * #3#4#5#6
+ #1; #2
+ }
+\cs_new:Npn \@@_trig_large_auxxi:w #1;
+ {
+ \exp_after:wN \@@_ep_mul_raw:wwwwN
+ \int_value:w \@@_int_eval:w 0 \@@_ep_to_ep_loop:N #1 ; ; !
+ 0,{7853}{9816}{3397}{4483}{0961}{5661};
+ \@@_trig_small:ww
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Computing the power series}
+%
+% \begin{macro}[EXP]
+% {\@@_sin_series_o:NNwwww, \@@_sin_series_aux_o:NNnwww}
+% Here we receive a conversion function \cs{@@_ep_to_float_o:wwN} or
+% \cs{@@_ep_inv_to_float_o:wwN}, a \meta{sign} ($0$ or~$2$), a
+% (non-negative) \meta{octant} delimited by a dot, a \meta{fixed
+% point} number delimited by a semicolon, and an extended-precision
+% number. The auxiliary receives:
+% \begin{itemize}
+% \item the conversion function~|#1|;
+% \item the final sign, which depends on the octant~|#3| and the
+% sign~|#2|;
+% \item the octant~|#3|, which controls the series we use;
+% \item the square |#4 * #4| of the argument as a fixed point number,
+% computed with \cs{@@_fixed_mul:wwn};
+% \item the number itself as an extended-precision number.
+% \end{itemize}
+% If the octant is in $\{1,2,5,6,\ldots{}\}$, we are near an extremum
+% of the function and we use the series
+% \[
+% \cos(x) = 1 - x^2 \bigg( \frac{1}{2!} - x^2 \bigg( \frac{1}{4!}
+% - x^2 \bigg( \cdots \bigg) \bigg) \bigg) .
+% \]
+% Otherwise, the series
+% \[
+% \sin(x) = x \bigg( 1 - x^2 \bigg( \frac{1}{3!} - x^2 \bigg(
+% \frac{1}{5!} - x^2 \bigg( \cdots \bigg) \bigg) \bigg) \bigg)
+% \]
+% is used. Finally, the extended-precision number is converted to a
+% floating point number with the given sign, and \cs{@@_sanitize:Nw}
+% checks for overflow and underflow.
+% \begin{macrocode}
+\cs_new:Npn \@@_sin_series_o:NNwwww #1#2#3. #4;
+ {
+ \@@_fixed_mul:wwn #4; #4;
+ {
+ \exp_after:wN \@@_sin_series_aux_o:NNnwww
+ \exp_after:wN #1
+ \int_value:w
+ \if_int_odd:w \@@_int_eval:w (#3 + 2) / 4 \@@_int_eval_end:
+ #2
+ \else:
+ \if_meaning:w #2 0 2 \else: 0 \fi:
+ \fi:
+ {#3}
+ }
+ }
+\cs_new:Npn \@@_sin_series_aux_o:NNnwww #1#2#3 #4; #5,#6;
+ {
+ \if_int_odd:w \@@_int_eval:w #3 / 2 \@@_int_eval_end:
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ { % 1/18!
+ \@@_fixed_mul_sub_back:wwwn {0000}{0000}{0000}{0001}{5619}{2070};
+ #4;{0000}{0000}{0000}{0477}{9477}{3324};
+ \@@_fixed_mul_sub_back:wwwn #4;{0000}{0000}{0011}{4707}{4559}{7730};
+ \@@_fixed_mul_sub_back:wwwn #4;{0000}{0000}{2087}{6756}{9878}{6810};
+ \@@_fixed_mul_sub_back:wwwn #4;{0000}{0027}{5573}{1922}{3985}{8907};
+ \@@_fixed_mul_sub_back:wwwn #4;{0000}{2480}{1587}{3015}{8730}{1587};
+ \@@_fixed_mul_sub_back:wwwn #4;{0013}{8888}{8888}{8888}{8888}{8889};
+ \@@_fixed_mul_sub_back:wwwn #4;{0416}{6666}{6666}{6666}{6666}{6667};
+ \@@_fixed_mul_sub_back:wwwn #4;{5000}{0000}{0000}{0000}{0000}{0000};
+ \@@_fixed_mul_sub_back:wwwn#4;{10000}{0000}{0000}{0000}{0000}{0000};
+ { \@@_fixed_continue:wn 0, }
+ }
+ { % 1/17!
+ \@@_fixed_mul_sub_back:wwwn {0000}{0000}{0000}{0028}{1145}{7254};
+ #4;{0000}{0000}{0000}{7647}{1637}{3182};
+ \@@_fixed_mul_sub_back:wwwn #4;{0000}{0000}{0160}{5904}{3836}{8216};
+ \@@_fixed_mul_sub_back:wwwn #4;{0000}{0002}{5052}{1083}{8544}{1719};
+ \@@_fixed_mul_sub_back:wwwn #4;{0000}{0275}{5731}{9223}{9858}{9065};
+ \@@_fixed_mul_sub_back:wwwn #4;{0001}{9841}{2698}{4126}{9841}{2698};
+ \@@_fixed_mul_sub_back:wwwn #4;{0083}{3333}{3333}{3333}{3333}{3333};
+ \@@_fixed_mul_sub_back:wwwn #4;{1666}{6666}{6666}{6666}{6666}{6667};
+ \@@_fixed_mul_sub_back:wwwn#4;{10000}{0000}{0000}{0000}{0000}{0000};
+ { \@@_ep_mul:wwwwn 0, } #5,#6;
+ }
+ {
+ \exp_after:wN \@@_sanitize:Nw
+ \exp_after:wN #2
+ \int_value:w \@@_int_eval:w #1
+ }
+ #2
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_tan_series_o:NNwwww, \@@_tan_series_aux_o:Nnwww}
+% Contrarily to \cs{@@_sin_series_o:NNwwww} which received a
+% conversion auxiliary as~|#1|, here, |#1| is $0$ for tangent
+% and $2$ for
+% cotangent. Consider first the case of the tangent. The octant |#3|
+% starts at $1$, which means that it is $1$ or $2$ for $\lvert
+% x\rvert\in[0,\pi/2]$, it is $3$ or $4$ for $\lvert
+% x\rvert\in[\pi/2,\pi]$, and so on: the intervals on which
+% $\tan\lvert x\rvert\geq 0$ coincide with those for which $\lfloor
+% (|#3| + 1) / 2\rfloor$ is odd. We also have to take into account
+% the original sign of $x$ to get the sign of the final result; it is
+% straightforward to check that the first \cs{int_value:w} expansion
+% produces $0$ for a positive final result, and $2$ otherwise. A
+% similar story holds for $\cot(x)$.
+%
+% The auxiliary receives the sign, the octant, the square of the
+% (reduced) input, and the (reduced) input (an extended-precision
+% number) as arguments. It then
+% computes the numerator and denominator of
+% \[
+% \tan(x) \simeq
+% \frac{x (1 - x^2 (a_1 - x^2 (a_2 - x^2 (a_3 - x^2 (a_4 - x^2 a_5)))))}
+% {1 - x^2 (b_1 - x^2 (b_2 - x^2 (b_3 - x^2 (b_4 - x^2 b_5))))} .
+% \]
+% The ratio is computed by \cs{@@_ep_div:wwwwn}, then converted to a
+% floating point number. For octants~|#3| (really, quadrants) next to
+% a pole of the
+% functions, the fixed point numerator and denominator are exchanged
+% before computing the ratio. Note that this \cs{if_int_odd:w} test
+% relies on the fact that the octant is at least~$1$.
+% \begin{macrocode}
+\cs_new:Npn \@@_tan_series_o:NNwwww #1#2#3. #4;
+ {
+ \@@_fixed_mul:wwn #4; #4;
+ {
+ \exp_after:wN \@@_tan_series_aux_o:Nnwww
+ \int_value:w
+ \if_int_odd:w \@@_int_eval:w #3 / 2 \@@_int_eval_end:
+ \exp_after:wN \reverse_if:N
+ \fi:
+ \if_meaning:w #1#2 2 \else: 0 \fi:
+ {#3}
+ }
+ }
+\cs_new:Npn \@@_tan_series_aux_o:Nnwww #1 #2 #3; #4,#5;
+ {
+ \@@_fixed_mul_sub_back:wwwn {0000}{0000}{1527}{3493}{0856}{7059};
+ #3; {0000}{0159}{6080}{0274}{5257}{6472};
+ \@@_fixed_mul_sub_back:wwwn #3; {0002}{4571}{2320}{0157}{2558}{8481};
+ \@@_fixed_mul_sub_back:wwwn #3; {0115}{5830}{7533}{5397}{3168}{2147};
+ \@@_fixed_mul_sub_back:wwwn #3; {1929}{8245}{6140}{3508}{7719}{2982};
+ \@@_fixed_mul_sub_back:wwwn #3;{10000}{0000}{0000}{0000}{0000}{0000};
+ { \@@_ep_mul:wwwwn 0, } #4,#5;
+ {
+ \@@_fixed_mul_sub_back:wwwn {0000}{0007}{0258}{0681}{9408}{4706};
+ #3;{0000}{2343}{7175}{1399}{6151}{7670};
+ \@@_fixed_mul_sub_back:wwwn #3;{0019}{2638}{4588}{9232}{8861}{3691};
+ \@@_fixed_mul_sub_back:wwwn #3;{0536}{6357}{0691}{4344}{6852}{4252};
+ \@@_fixed_mul_sub_back:wwwn #3;{5263}{1578}{9473}{6842}{1052}{6315};
+ \@@_fixed_mul_sub_back:wwwn#3;{10000}{0000}{0000}{0000}{0000}{0000};
+ {
+ \reverse_if:N \if_int_odd:w
+ \@@_int_eval:w (#2 - 1) / 2 \@@_int_eval_end:
+ \exp_after:wN \@@_reverse_args:Nww
+ \fi:
+ \@@_ep_div:wwwwn 0,
+ }
+ }
+ {
+ \exp_after:wN \@@_sanitize:Nw
+ \exp_after:wN #1
+ \int_value:w \@@_int_eval:w \@@_ep_to_float_o:wwN
+ }
+ #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Inverse trigonometric functions}
+%
+% All inverse trigonometric functions (arcsine, arccosine, arctangent,
+% arccotangent, arccosecant, and arcsecant) are based on a function
+% often denoted \texttt{atan2}. This function is accessed directly by
+% feeding two arguments to arctangent, and is defined by \(\operatorname{atan}(y, x) =
+% \operatorname{atan}(y/x)\) for generic \(y\) and~\(x\). Its advantages over the
+% conventional arctangent is that it takes values in $[-\pi,\pi]$ rather
+% than $[-\pi/2,\pi/2]$, and that it is better behaved in boundary
+% cases. Other inverse trigonometric functions are expressed in terms
+% of \(\operatorname{atan}\) as
+% \begin{align}
+% \operatorname{acos} x & = \operatorname{atan}(\sqrt{1-x^2}, x) \\
+% \operatorname{asin} x & = \operatorname{atan}(x, \sqrt{1-x^2}) \\
+% \operatorname{asec} x & = \operatorname{atan}(\sqrt{x^2-1}, 1) \\
+% \operatorname{acsc} x & = \operatorname{atan}(1, \sqrt{x^2-1}) \\
+% \operatorname{atan} x & = \operatorname{atan}(x, 1) \\
+% \operatorname{acot} x & = \operatorname{atan}(1, x) .
+% \end{align}
+% Rather than introducing a new function, \texttt{atan2}, the arctangent
+% function \texttt{atan} is overloaded: it can take one or two
+% arguments. In the comments below, following many texts, we call the
+% first argument~$y$ and the second~$x$, because $\operatorname{atan}(y, x) = \operatorname{atan}(y
+% / x)$ is the angular coordinate of the point $(x, y)$.
+%
+% As for direct trigonometric functions, the first step in computing
+% $\operatorname{atan}(y, x)$ is argument reduction. The sign of~$y$ gives that
+% of the result. We distinguish eight regions where the point $(x,
+% \lvert y\rvert)$ can lie, of angular size roughly $\pi/8$,
+% characterized by their \enquote{octant}, between $0$ and~$7$ included. In
+% each region, we compute an arctangent as a Taylor series, then shift
+% this arctangent by the appropriate multiple of $\pi/4$ and sign to get
+% the result. Here is a list of octants, and how we compute the
+% arctangent (we assume $y>0$: otherwise replace $y$ by~$-y$ below):
+% \begin{itemize}
+% \item[0] $0 < \lvert y\rvert < 0.41421 x$, then
+% $\operatorname{atan}\frac{\lvert y\rvert}{x}$
+% is given by a nicely convergent Taylor series;
+% \item[1] $0 < 0.41421 x < \lvert y\rvert < x$, then
+% $\operatorname{atan}\frac{\lvert y\rvert}{x}
+% = \frac{\pi}{4}-\operatorname{atan}\frac{x-\lvert y\rvert}{x+\lvert y\rvert}$;
+% \item[2] $0 < 0.41421 \lvert y\rvert < x < \lvert y\rvert$, then
+% $\operatorname{atan}\frac{\lvert y\rvert}{x}
+% = \frac{\pi}{4}+\operatorname{atan}\frac{-x+\lvert y\rvert}{x+\lvert y\rvert}$;
+% \item[3] $0 < x < 0.41421 \lvert y\rvert$, then
+% $\operatorname{atan}\frac{\lvert y\rvert}{x}
+% = \frac{\pi}{2}-\operatorname{atan}\frac{x}{\lvert y\rvert}$;
+% \item[4] $0 < -x < 0.41421 \lvert y\rvert$, then
+% $\operatorname{atan}\frac{\lvert y\rvert}{x}
+% = \frac{\pi}{2}+\operatorname{atan}\frac{-x}{\lvert y\rvert}$;
+% \item[5] $0 < 0.41421 \lvert y\rvert < -x < \lvert y\rvert$, then
+% $\operatorname{atan}\frac{\lvert y\rvert}{x}
+% =\frac{3\pi}{4}-\operatorname{atan}\frac{x+\lvert y\rvert}{-x+\lvert y\rvert}$;
+% \item[6] $0 < -0.41421 x < \lvert y\rvert < -x$, then
+% $\operatorname{atan}\frac{\lvert y\rvert}{x}
+% =\frac{3\pi}{4}+\operatorname{atan}\frac{-x-\lvert y\rvert}{-x+\lvert y\rvert}$;
+% \item[7] $0 < \lvert y\rvert < -0.41421 x$, then
+% $\operatorname{atan}\frac{\lvert y\rvert}{x}
+% = \pi-\operatorname{atan}\frac{\lvert y\rvert}{-x}$.
+% \end{itemize}
+% In the following, we denote by~$z$ the ratio among
+% $\lvert\frac{y}{x}\rvert$, $\lvert\frac{x}{y}\rvert$,
+% $\lvert\frac{x+y}{x-y}\rvert$, $\lvert\frac{x-y}{x+y}\rvert$ which
+% appears in the right-hand side above.
+%
+% \subsubsection{Arctangent and arccotangent}
+%
+% \begin{macro}[EXP]{\@@_atan_o:Nw, \@@_acot_o:Nw, \@@_atan_default:w}
+% The parsing step manipulates \texttt{atan} and \texttt{acot} like
+% \texttt{min} and \texttt{max}, reading in an array of operands, but
+% also leaves \cs{use_i:nn} or \cs{use_ii:nn} depending on whether the
+% result should be given in radians or in degrees. The helper
+% \cs{@@_parse_function_one_two:nnw} checks that the operand is one or
+% two floating point numbers (not tuples) and leaves its second
+% argument or its tail accordingly (its first argument is used for
+% error messages). More precisely if we are given a single floating
+% point number \cs{@@_atan_default:w} places \cs{c_one_fp} (expanded)
+% after it; otherwise \cs{@@_atan_default:w} is omitted by
+% \cs{@@_parse_function_one_two:nnw}.
+% \begin{macrocode}
+\cs_new:Npn \@@_atan_o:Nw #1
+ {
+ \@@_parse_function_one_two:nnw
+ { #1 { atan } { atand } }
+ { \@@_atan_default:w \@@_atanii_o:Nww #1 }
+ }
+\cs_new:Npn \@@_acot_o:Nw #1
+ {
+ \@@_parse_function_one_two:nnw
+ { #1 { acot } { acotd } }
+ { \@@_atan_default:w \@@_acotii_o:Nww #1 }
+ }
+\cs_new:Npe \@@_atan_default:w #1#2#3 @ { #1 #2 #3 \c_one_fp @ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_atanii_o:Nww, \@@_acotii_o:Nww}
+% If either operand is \texttt{nan}, we return it. If both are
+% normal, we call \cs{@@_atan_normal_o:NNnwNnw}. If both are zero or
+% both infinity, we call \cs{@@_atan_inf_o:NNNw} with argument~$2$,
+% leading to a result among $\{\pm\pi/4, \pm 3\pi/4\}$ (in degrees,
+% $\{\pm 45, \pm 135\}$). Otherwise, one is much bigger than the
+% other, and we call \cs{@@_atan_inf_o:NNNw} with either an argument
+% of~$4$, leading to the values $\pm\pi/2$ (in degrees,~$\pm 90$),
+% or~$0$, leading to $\{\pm 0, \pm\pi\}$ (in degrees, $\{\pm 0,\pm
+% 180\}$). Since $\operatorname{acot}(x, y) = \operatorname{atan}(y, x)$,
+% \cs{@@_acotii_o:ww} simply reverses its two arguments.
+% \begin{macrocode}
+\cs_new:Npn \@@_atanii_o:Nww
+ #1 \s_@@ \@@_chk:w #2#3#4; \s_@@ \@@_chk:w #5 #6 @
+ {
+ \if_meaning:w 3 #2 \@@_case_return_i_o:ww \fi:
+ \if_meaning:w 3 #5 \@@_case_return_ii_o:ww \fi:
+ \if_case:w
+ \if_meaning:w #2 #5
+ \if_meaning:w 1 #2 10 \else: 0 \fi:
+ \else:
+ \if_int_compare:w #2 > #5 \exp_stop_f: 1 \else: 2 \fi:
+ \fi:
+ \exp_stop_f:
+ \@@_case_return:nw { \@@_atan_inf_o:NNNw #1 #3 2 }
+ \or: \@@_case_return:nw { \@@_atan_inf_o:NNNw #1 #3 4 }
+ \or: \@@_case_return:nw { \@@_atan_inf_o:NNNw #1 #3 0 }
+ \fi:
+ \@@_atan_normal_o:NNnwNnw #1
+ \s_@@ \@@_chk:w #2#3#4;
+ \s_@@ \@@_chk:w #5 #6
+ }
+\cs_new:Npn \@@_acotii_o:Nww #1#2; #3;
+ { \@@_atanii_o:Nww #1#3; #2; }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_atan_inf_o:NNNw}
+% This auxiliary is called whenever one number is $\pm 0$ or
+% $\pm\infty$ (and neither is \nan{}). Then the result only depends
+% on the signs, and its value is a multiple of $\pi/4$. We use the
+% same auxiliary as for normal numbers,
+% \cs{@@_atan_combine_o:NwwwwwN}, with arguments the final sign~|#2|;
+% the octant~|#3|; $\operatorname{atan} z/z=1$ as a fixed point number; $z=0$~as a
+% fixed point number; and $z=0$~as an extended-precision number.
+% Given the values we provide, $\operatorname{atan} z$ is computed to be~$0$,
+% and the result is $[|#3|/2]\cdot\pi/4$ if the sign~|#5| of~$x$
+% is positive, and $[(7-|#3|)/2]\cdot\pi/4$ for negative~$x$, where
+% the divisions are rounded up.
+% \begin{macrocode}
+\cs_new:Npn \@@_atan_inf_o:NNNw #1#2#3 \s_@@ \@@_chk:w #4#5#6;
+ {
+ \exp_after:wN \@@_atan_combine_o:NwwwwwN
+ \exp_after:wN #2
+ \int_value:w \@@_int_eval:w
+ \if_meaning:w 2 #5 7 - \fi: #3 \exp_after:wN ;
+ \c_@@_one_fixed_tl
+ {0000}{0000}{0000}{0000}{0000}{0000};
+ 0,{0000}{0000}{0000}{0000}{0000}{0000}; #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_atan_normal_o:NNnwNnw}
+% Here we simply reorder the floating point data into a pair of signed
+% extended-precision numbers, that is, a sign, an exponent ending with
+% a comma, and a six-block mantissa ending with a semi-colon. This
+% extended precision is required by other inverse trigonometric
+% functions, to compute things like $\operatorname{atan}(x,\sqrt{1-x^2})$ without
+% intermediate rounding errors.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_atan_normal_o:NNnwNnw
+ #1 \s_@@ \@@_chk:w 1#2#3#4; \s_@@ \@@_chk:w 1#5#6#7;
+ {
+ \@@_atan_test_o:NwwNwwN
+ #2 #3, #4{0000}{0000};
+ #5 #6, #7{0000}{0000}; #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_atan_test_o:NwwNwwN}
+% This receives: the sign~|#1| of~$y$, its exponent~|#2|, its $24$
+% digits~|#3| in groups of~$4$, and similarly for~$x$. We prepare to
+% call \cs{@@_atan_combine_o:NwwwwwN} which expects the sign~|#1|, the
+% octant, the ratio $(\operatorname{atan} z)/z = 1 - \cdots$, and the value of~$z$,
+% both as a fixed point number and as an extended-precision floating
+% point number with a mantissa in $[0.01,1)$. For now, we place |#1|
+% as a first argument, and start an integer expression for the octant.
+% The sign of $x$ does not affect~$z$, so we simply leave
+% a contribution to the octant: $\meta{octant} \to 7 - \meta{octant}$
+% for negative~$x$. Then we order $\lvert y\rvert$ and $\lvert
+% x\rvert$ in a non-decreasing order: if $\lvert y\rvert > \lvert
+% x\rvert$, insert $3-$ in the expression for the octant, and swap the
+% two numbers. The finer test with $0.41421$ is done by
+% \cs{@@_atan_div:wnwwnw} after the operands have been ordered.
+% \begin{macrocode}
+\cs_new:Npn \@@_atan_test_o:NwwNwwN #1#2,#3; #4#5,#6;
+ {
+ \exp_after:wN \@@_atan_combine_o:NwwwwwN
+ \exp_after:wN #1
+ \int_value:w \@@_int_eval:w
+ \if_meaning:w 2 #4
+ 7 - \@@_int_eval:w
+ \fi:
+ \if_int_compare:w
+ \@@_ep_compare:wwww #2,#3; #5,#6; > \c_zero_int
+ 3 -
+ \exp_after:wN \@@_reverse_args:Nww
+ \fi:
+ \@@_atan_div:wnwwnw #2,#3; #5,#6;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_atan_div:wnwwnw, \@@_atan_near:wwwn}
+% \begin{macro}[EXP]{\@@_atan_near_aux:wwn}
+% This receives two positive numbers $a$ and~$b$ (equal to $\lvert
+% x\rvert$ and~$\lvert y\rvert$ in some order), each as an exponent
+% and $6$~blocks of $4$~digits, such that $0<a<b$. If $0.41421b<a$,
+% the two numbers are \enquote{near}, hence the point $(y,x)$ that we
+% started with is closer to the diagonals $\{\lvert y\rvert = \lvert
+% x\rvert\}$ than to the axes $\{xy = 0\}$. In that case, the octant
+% is~$1$ (possibly combined with the $7-$ and $3-$ inserted earlier)
+% and we wish to compute $\operatorname{atan}\frac{b-a}{a+b}$. Otherwise, the
+% octant is~$0$ (again, combined with earlier terms) and we wish to
+% compute $\operatorname{atan}\frac{a}{b}$. In any case, call \cs{@@_atan_auxi:ww}
+% followed by~$z$, as a comma-delimited exponent and a fixed point
+% number.
+% \begin{macrocode}
+\cs_new:Npn \@@_atan_div:wnwwnw #1,#2#3; #4,#5#6;
+ {
+ \if_int_compare:w
+ \@@_int_eval:w 41421 * #5 < #2 000
+ \if_case:w \@@_int_eval:w #4 - #1 \@@_int_eval_end:
+ 00 \or: 0 \fi:
+ \exp_stop_f:
+ \exp_after:wN \@@_atan_near:wwwn
+ \fi:
+ 0
+ \@@_ep_div:wwwwn #1,{#2}#3; #4,{#5}#6;
+ \@@_atan_auxi:ww
+ }
+\cs_new:Npn \@@_atan_near:wwwn
+ 0 \@@_ep_div:wwwwn #1,#2; #3,
+ {
+ 1
+ \@@_ep_to_fixed:wwn #1 - #3, #2;
+ \@@_atan_near_aux:wwn
+ }
+\cs_new:Npn \@@_atan_near_aux:wwn #1; #2;
+ {
+ \@@_fixed_add:wwn #1; #2;
+ { \@@_fixed_sub:wwn #2; #1; { \@@_ep_div:wwwwn 0, } 0, }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_atan_auxi:ww, \@@_atan_auxii:w}
+% Convert~$z$ from a representation as an exponent and a fixed point
+% number in $[0.01,1)$ to a fixed point number only, then set up the
+% call to \cs{@@_atan_Taylor_loop:www}, followed by the fixed point
+% representation of~$z$ and the old representation.
+% \begin{macrocode}
+\cs_new:Npn \@@_atan_auxi:ww #1,#2;
+ { \@@_ep_to_fixed:wwn #1,#2; \@@_atan_auxii:w #1,#2; }
+\cs_new:Npn \@@_atan_auxii:w #1;
+ {
+ \@@_fixed_mul:wwn #1; #1;
+ {
+ \@@_atan_Taylor_loop:www 39 ;
+ {0000}{0000}{0000}{0000}{0000}{0000} ;
+ }
+ ! #1;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_atan_Taylor_loop:www, \@@_atan_Taylor_break:w}
+% We compute the series of $(\operatorname{atan} z)/z$. A typical intermediate
+% stage has $|#1|=2k-1$, $|#2| =
+% \frac{1}{2k+1}-z^2(\frac{1}{2k+3}-z^2(\cdots-z^2\frac{1}{39}))$, and
+% $|#3|=z^2$. To go to the next step $k\to k-1$, we compute
+% $\frac{1}{2k-1}$, then subtract from it $z^2$ times |#2|. The loop
+% stops when $k=0$: then |#2| is $(\operatorname{atan} z)/z$, and there is a need to
+% clean up all the unnecessary data, end the integer expression
+% computing the octant with a semicolon, and leave the result~|#2|
+% afterwards.
+% \begin{macrocode}
+\cs_new:Npn \@@_atan_Taylor_loop:www #1; #2; #3;
+ {
+ \if_int_compare:w #1 = - \c_one_int
+ \@@_atan_Taylor_break:w
+ \fi:
+ \exp_after:wN \@@_fixed_div_int:wwN \c_@@_one_fixed_tl #1;
+ \@@_rrot:www \@@_fixed_mul_sub_back:wwwn #2; #3;
+ {
+ \exp_after:wN \@@_atan_Taylor_loop:www
+ \int_value:w \@@_int_eval:w #1 - 2 ;
+ }
+ #3;
+ }
+\cs_new:Npn \@@_atan_Taylor_break:w
+ \fi: #1 \@@_fixed_mul_sub_back:wwwn #2; #3 !
+ { \fi: ; #2 ; }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_atan_combine_o:NwwwwwN, \@@_atan_combine_aux:ww}
+% This receives a \meta{sign}, an \meta{octant}, a fixed point value
+% of $(\operatorname{atan} z)/z$, a fixed point number~$z$, and another
+% representation of~$z$, as an \meta{exponent} and the fixed point
+% number $10^{-\meta{exponent}} z$, followed by either \cs{use_i:nn}
+% (when working in radians) or \cs{use_ii:nn} (when working in
+% degrees). The function computes the floating point result
+% \begin{equation}
+% \meta{sign} \left(
+% \left\lceil\frac{\meta{octant}}{2}\right\rceil
+% \frac{\pi}{4}
+% + (-1)^{\meta{octant}} \frac{\operatorname{atan} z}{z} \cdot z\right) \,,
+% \end{equation}
+% multiplied by $180/\pi$ if working in degrees, and using in any case
+% the most appropriate representation of~$z$. The floating point
+% result is passed to \cs{@@_sanitize:Nw}, which checks for overflow
+% or underflow. If the octant is~$0$, leave the exponent~|#5| for
+% \cs{@@_sanitize:Nw}, and multiply $|#3|=\frac{\operatorname{atan} z}{z}$
+% with~|#6|, the adjusted~$z$. Otherwise, multiply $|#3|=\frac{\operatorname{atan}
+% z}{z}$ with $|#4|=z$, then compute the appropriate multiple of
+% $\frac{\pi}{4}$ and add or subtract the product $|#3|\cdot|#4|$. In
+% both cases, convert to a floating point with
+% \cs{@@_fixed_to_float_o:wN}.
+% \begin{macrocode}
+\cs_new:Npn \@@_atan_combine_o:NwwwwwN #1 #2; #3; #4; #5,#6; #7
+ {
+ \exp_after:wN \@@_sanitize:Nw
+ \exp_after:wN #1
+ \int_value:w \@@_int_eval:w
+ \if_meaning:w 0 #2
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ { #5 \@@_fixed_mul:wwn #3; #6; }
+ {
+ \@@_fixed_mul:wwn #3; #4;
+ {
+ \exp_after:wN \@@_atan_combine_aux:ww
+ \int_value:w \@@_int_eval:w #2 / 2 ; #2;
+ }
+ }
+ { #7 \@@_fixed_to_float_o:wN \@@_fixed_to_float_rad_o:wN }
+ #1
+ }
+\cs_new:Npn \@@_atan_combine_aux:ww #1; #2;
+ {
+ \@@_fixed_mul_short:wwn
+ {7853}{9816}{3397}{4483}{0961}{5661};
+ {#1}{0000}{0000};
+ {
+ \if_int_odd:w #2 \exp_stop_f:
+ \exp_after:wN \@@_fixed_sub:wwn
+ \else:
+ \exp_after:wN \@@_fixed_add:wwn
+ \fi:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Arcsine and arccosine}
+%
+% \begin{macro}[EXP]{\@@_asin_o:w}
+% Again, the first argument provided by \pkg{l3fp-parse} is
+% \cs{use_i:nn} if we are to work in radians and \cs{use_ii:nn} for
+% degrees. Then comes a floating point number. The arcsine of $\pm
+% 0$ or \nan{} is the same floating point number. The arcsine of
+% $\pm\infty$ raises an invalid operation exception. Otherwise, call
+% an auxiliary common with \cs{@@_acos_o:w}, feeding it information
+% about what function is being performed (for \enquote{invalid operation}
+% exceptions).
+% \begin{macrocode}
+\cs_new:Npn \@@_asin_o:w #1 \s_@@ \@@_chk:w #2#3; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \@@_case_return_same_o:w
+ \or:
+ \@@_case_use:nw
+ { \@@_asin_normal_o:NfwNnnnnw #1 { #1 { asin } { asind } } }
+ \or:
+ \@@_case_use:nw
+ { \@@_invalid_operation_o:fw { #1 { asin } { asind } } }
+ \else:
+ \@@_case_return_same_o:w
+ \fi:
+ \s_@@ \@@_chk:w #2 #3;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_acos_o:w}
+% The arccosine of $\pm 0$ is $\pi / 2$ (in degrees,~$90$). The
+% arccosine of $\pm\infty$ raises an invalid operation exception. The
+% arccosine of \nan{} is itself. Otherwise, call an auxiliary common
+% with \cs{@@_sin_o:w}, informing it that it was called by
+% \texttt{acos} or \texttt{acosd}, and preparing to swap some
+% arguments down the line.
+% \begin{macrocode}
+\cs_new:Npn \@@_acos_o:w #1 \s_@@ \@@_chk:w #2#3; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \@@_case_use:nw { \@@_atan_inf_o:NNNw #1 0 4 }
+ \or:
+ \@@_case_use:nw
+ {
+ \@@_asin_normal_o:NfwNnnnnw #1 { #1 { acos } { acosd } }
+ \@@_reverse_args:Nww
+ }
+ \or:
+ \@@_case_use:nw
+ { \@@_invalid_operation_o:fw { #1 { acos } { acosd } } }
+ \else:
+ \@@_case_return_same_o:w
+ \fi:
+ \s_@@ \@@_chk:w #2 #3;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_asin_normal_o:NfwNnnnnw}
+% If the exponent~|#5| is at most $0$, the operand lies
+% within $(-1,1)$ and the operation is permitted: call
+% \cs{@@_asin_auxi_o:NnNww} with the appropriate arguments. If the
+% number is exactly~$\pm 1$ (the test works because we know that
+% $|#5|\geq 1$, $|#6#7|\geq 10000000$, $|#8#9|\geq 0$, with equality
+% only for $\pm 1$), we also call \cs{@@_asin_auxi_o:NnNww}.
+% Otherwise, \cs{@@_use_i:ww} gets rid of the \texttt{asin} auxiliary,
+% and raises instead an invalid operation, because the operand is
+% outside the domain of arcsine or arccosine.
+% \begin{macrocode}
+\cs_new:Npn \@@_asin_normal_o:NfwNnnnnw
+ #1#2#3 \s_@@ \@@_chk:w 1#4#5#6#7#8#9;
+ {
+ \if_int_compare:w #5 < \c_one_int
+ \exp_after:wN \@@_use_none_until_s:w
+ \fi:
+ \if_int_compare:w \@@_int_eval:w #5 + #6#7 + #8#9 = 1000 0001 ~
+ \exp_after:wN \@@_use_none_until_s:w
+ \fi:
+ \@@_use_i:ww
+ \@@_invalid_operation_o:fw {#2}
+ \s_@@ \@@_chk:w 1#4{#5}{#6}{#7}{#8}{#9};
+ \@@_asin_auxi_o:NnNww
+ #1 {#3} #4 #5,{#6}{#7}{#8}{#9}{0000}{0000};
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_asin_auxi_o:NnNww, \@@_asin_isqrt:wn}
+% We compute $x/\sqrt{1-x^2}$. This function is used by \texttt{asin}
+% and \texttt{acos}, but also by \texttt{acsc} and \texttt{asec} after
+% inverting the operand, thus it must manipulate extended-precision
+% numbers. First evaluate $1-x^2$ as $(1+x)(1-x)$: this behaves
+% better near~$x=1$. We do the addition/subtraction with fixed point
+% numbers (they are not implemented for extended-precision floats),
+% but go back to extended-precision floats to multiply and compute the
+% inverse square root $1/\sqrt{1-x^2}$. Finally, multiply by the
+% (positive) extended-precision float $\lvert x\rvert$, and feed the
+% (signed) result, and the number~$+1$, as arguments to the arctangent
+% function. When computing the arccosine, the arguments
+% $x/\sqrt{1-x^2}$ and~$+1$ are swapped by~|#2|
+% (\cs{@@_reverse_args:Nww} in that case) before
+% \cs{@@_atan_test_o:NwwNwwN} is evaluated. Note that the arctangent
+% function requires normalized arguments, hence the need for
+% \texttt{ep_to_ep} and \texttt{continue} after \texttt{ep_mul}.
+% \begin{macrocode}
+\cs_new:Npn \@@_asin_auxi_o:NnNww #1#2#3#4,#5;
+ {
+ \@@_ep_to_fixed:wwn #4,#5;
+ \@@_asin_isqrt:wn
+ \@@_ep_mul:wwwwn #4,#5;
+ \@@_ep_to_ep:wwN
+ \@@_fixed_continue:wn
+ { #2 \@@_atan_test_o:NwwNwwN #3 }
+ 0 1,{1000}{0000}{0000}{0000}{0000}{0000}; #1
+ }
+\cs_new:Npn \@@_asin_isqrt:wn #1;
+ {
+ \exp_after:wN \@@_fixed_sub:wwn \c_@@_one_fixed_tl #1;
+ {
+ \@@_fixed_add_one:wN #1;
+ \@@_fixed_continue:wn { \@@_ep_mul:wwwwn 0, } 0,
+ }
+ \@@_ep_isqrt:wwn
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Arccosecant and arcsecant}
+%
+% \begin{macro}[EXP]{\@@_acsc_o:w}
+% Cases are mostly labelled by~|#2|, except when |#2| is~$2$: then we
+% use |#3#2|, which is $02=2$ when the number is $+\infty$ and
+% $22$~when the number is $-\infty$. The arccosecant of $\pm 0$
+% raises an invalid operation exception. The arccosecant of
+% $\pm\infty$ is $\pm 0$ with the same sign. The arcosecant of \nan{}
+% is itself. Otherwise, \cs{@@_acsc_normal_o:NfwNnw} does some more
+% tests, keeping the function name (\texttt{acsc} or \texttt{acscd})
+% as an argument for invalid operation exceptions.
+% \begin{macrocode}
+\cs_new:Npn \@@_acsc_o:w #1 \s_@@ \@@_chk:w #2#3#4; @
+ {
+ \if_case:w \if_meaning:w 2 #2 #3 \fi: #2 \exp_stop_f:
+ \@@_case_use:nw
+ { \@@_invalid_operation_o:fw { #1 { acsc } { acscd } } }
+ \or: \@@_case_use:nw
+ { \@@_acsc_normal_o:NfwNnw #1 { #1 { acsc } { acscd } } }
+ \or: \@@_case_return_o:Nw \c_zero_fp
+ \or: \@@_case_return_same_o:w
+ \else: \@@_case_return_o:Nw \c_minus_zero_fp
+ \fi:
+ \s_@@ \@@_chk:w #2 #3 #4;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_asec_o:w}
+% The arcsecant of $\pm 0$ raises an invalid operation exception. The
+% arcsecant of $\pm\infty$ is $\pi / 2$ (in degrees,~$90$). The
+% arcosecant of \nan{} is itself. Otherwise, do some more tests,
+% keeping the function name \texttt{asec} (or \texttt{asecd}) as an
+% argument for invalid operation exceptions, and a
+% \cs{@@_reverse_args:Nww} following precisely that appearing in
+% \cs{@@_acos_o:w}.
+% \begin{macrocode}
+\cs_new:Npn \@@_asec_o:w #1 \s_@@ \@@_chk:w #2#3; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \@@_case_use:nw
+ { \@@_invalid_operation_o:fw { #1 { asec } { asecd } } }
+ \or:
+ \@@_case_use:nw
+ {
+ \@@_acsc_normal_o:NfwNnw #1 { #1 { asec } { asecd } }
+ \@@_reverse_args:Nww
+ }
+ \or: \@@_case_use:nw { \@@_atan_inf_o:NNNw #1 0 4 }
+ \else: \@@_case_return_same_o:w
+ \fi:
+ \s_@@ \@@_chk:w #2 #3;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_acsc_normal_o:NfwNnw}
+% If the exponent is non-positive, the operand is less than~$1$ in
+% absolute value, which is always an invalid operation: complain.
+% Otherwise, compute the inverse of the operand, and feed it to
+% \cs{@@_asin_auxi_o:NnNww} (with all the appropriate arguments). This
+% computes what we want thanks to
+% $\operatorname{acsc}(x)=\operatorname{asin}(1/x)$ and
+% $\operatorname{asec}(x)=\operatorname{acos}(1/x)$.
+% \begin{macrocode}
+\cs_new:Npn \@@_acsc_normal_o:NfwNnw #1#2#3 \s_@@ \@@_chk:w 1#4#5#6;
+ {
+ \int_compare:nNnTF {#5} < 1
+ {
+ \@@_invalid_operation_o:fw {#2}
+ \s_@@ \@@_chk:w 1#4{#5}#6;
+ }
+ {
+ \@@_ep_div:wwwwn
+ 1,{1000}{0000}{0000}{0000}{0000}{0000};
+ #5,#6{0000}{0000};
+ { \@@_asin_auxi_o:NnNww #1 {#3} #4 }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-types.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-types.dtx
new file mode 100644
index 00000000000..2c23c613930
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp-types.dtx
@@ -0,0 +1,184 @@
+% \iffalse
+%
+%% File l3fp-types.dtx (C) Copyright 2012-2015,2017,2018,2020,2021,2023 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% http://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full]{l3doc}
+\usepackage{amsmath}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3fp-types} module\\ Floating point types^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-types} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% \subsection{Support for types}
+%
+% Despite lack of documentation, the \pkg{l3fp} internals support types.
+% Each additional type must define
+% \begin{itemize}
+% \item |\s__fp_|\meta{type} and |\__fp_|\meta{type}|_chk:w|;
+% \item |\__fp_exp_after_|\meta{type}|_f:nw|;
+% \item |\__fp_|\meta{type}|_to_|\meta{out}|:w| for \meta{out} among
+% |decimal|, |scientific|, |tl|;
+% \end{itemize}
+% and may define
+% \begin{itemize}
+% \item |\__fp_|\meta{type}|_to_int:w| and |\__fp_|\meta{type}|_to_dim:w|;
+% \item |\__fp_|\meta{op}|_|\meta{type}|_o:w| for any of the \meta{op}
+% that the type implements, among |acos|, |acsc|, |asec|, |asin|,
+% |cos|, |cot|, |csc|, |exp|, |ln|, |not|, |sec|, |set_sign|, |sin|,
+% |tan|;
+% \item |\__fp_|\meta{type_1}|_|\meta{op}|_|\meta{type_2}|_o:ww| for
+% \meta{op} among |^*/-+&|\verb"|" and for every pair of types;
+% \item |\__fp_|\meta{type_1}|_bcmp_|\meta{type_2}|:ww| for every
+% pair of types.
+% \end{itemize}
+% The latter is set up in \pkg{l3fp-logic}.
+%
+% \subsection{Dispatch according to the type}
+%
+% \begin{macro}[EXP]
+% {\@@_types_cs_to_op:N, \@@_types_cs_to_op_auxi:wwwn}
+% From |\__fp_|\meta{op}|_o:w| produce \meta{op}, otherwise~|?|.
+% \begin{macrocode}
+\cs_new:Npe \@@_types_cs_to_op:N #1
+ {
+ \exp_not:N \exp_after:wN \exp_not:N \@@_types_cs_to_op_auxi:wwwn
+ \exp_not:N \token_to_str:N #1 \s_@@_mark
+ \exp_not:N \@@_use_i_delimit_by_s_stop:nw
+ \tl_to_str:n { @@_ _o:w } \s_@@_mark
+ { \exp_not:N \@@_use_i_delimit_by_s_stop:nw ? }
+ \s_@@_stop
+ }
+\use:e
+ {
+ \cs_new:Npn \exp_not:N \@@_types_cs_to_op_auxi:wwwn
+ #1 \tl_to_str:n { @@_ } #2
+ \tl_to_str:n { _o:w } #3 \s_@@_mark #4 { #4 {#2} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_types_unary:NNw}
+% \begin{macro}[EXP]
+% {\@@_types_unary_auxi:nNw, \@@_types_unary_auxii:NnNw}
+% \begin{syntax}
+% \cs{@@_types_unary:NNw} |\__fp_|\meta{function}|_o:w|
+% ~~\meta{token} \meta{operand} |@|
+% \end{syntax}
+% \begin{macrocode}
+\cs_new:Npn \@@_types_unary:NNw #1
+ {
+ \exp_args:Nf \@@_types_unary_auxi:nNw
+ { \@@_types_cs_to_op:N #1 }
+ }
+\cs_new:Npn \@@_types_unary_auxi:nNw #1#2#3
+ {
+ \exp_after:wN \@@_types_unary_auxii:NnNw
+ \cs:w @@_#1 \@@_type_from_scan:N #3 _o:w \cs_end:
+ {#1}
+ #2#3
+ }
+\cs_new:Npn \@@_types_unary_auxii:NnNw #1#2#3
+ {
+ \token_if_eq_meaning:NNTF \scan_stop: #1
+ { \@@_invalid_operation_o:nw {#2} }
+ { #1 #3 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_types_binary:Nww}
+% \begin{macro}[EXP]
+% {\@@_types_binary_auxi:Nww, \@@_types_binary_auxii:NNww}
+% \begin{syntax}
+% \cs{@@_types_binary:Nww} |\__fp_|\meta{binop}|_o:ww|
+% ~~\meta{operand_1} \meta{operand_2} |@|
+% \end{syntax}
+% \begin{macrocode}
+\cs_new:Npn \@@_types_binary:Nww #1
+ {
+ \exp_last_unbraced:Nf \@@_types_binary_auxi:Nww
+ { \@@_types_cs_to_op:N #1 }
+ }
+\cs_new:Npn \@@_types_binary_auxi:Nww #1#2#3; #4#5; @
+ {
+ \exp_after:wN \@@_types_binary_auxii:NNww
+ \cs:w
+ @@
+ \@@_type_from_scan:N #2
+ _#1
+ \@@_type_from_scan:N #4
+ _o:ww
+ \cs_end:
+ #1 #2#3; #4#5;
+ }
+\cs_new:Npn \@@_types_binary_auxii:NNww #1#2
+ {
+ \token_if_eq_meaning:NNTF \scan_stop: #1
+ { \@@_invalid_operation_o:Nww #2 }
+ {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fp.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp.dtx
new file mode 100644
index 00000000000..7cef5739702
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fp.dtx
@@ -0,0 +1,1663 @@
+% \iffalse meta-comment
+%
+%% File: l3fp.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\usepackage{amsmath}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% ^^A need to provide this inside the file:
+%
+% \providecommand\nan{\texttt{NaN}}
+%
+%
+% \title{^^A
+% The \pkg{l3fp} module\\ Floating points^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% A decimal floating point number is one which is stored as a significand and a
+% separate exponent. The module implements expandably a wide set of
+% arithmetic, trigonometric, and other operations on decimal floating point
+% numbers, to be used within floating point expressions. \emph{Floating point
+% expressions} (\enquote{\meta{fp expr}}) support the following operations with their usual
+% precedence.
+% \begin{itemize}
+% \item Basic arithmetic: addition $x+y$, subtraction $x-y$,
+% multiplication $x*y$, division $x/y$, square root~$\sqrt{x}$,
+% and parentheses.
+% \item Comparison operators: $x\mathop{\mathtt{<}}y$,
+% $x\mathop{\mathtt{<=}}y$, $x\mathop{\mathtt{>?}}y$,
+% $x\mathop{\mathtt{!=}}y$ \emph{etc.}
+% \item Boolean logic: sign $\operatorname{sign} x$,
+% negation $\mathop{!}x$, conjunction
+% $x\mathop{\&\&}y$, disjunction $x\mathop{\vert\vert}y$, ternary
+% operator $x\mathop{?}y\mathop{:}z$.
+% \item Exponentials: $\exp x$, $\ln x$, $x^y$, $\operatorname{logb} x$.
+% \item Integer factorial: $\operatorname{fact} x$.
+% \item Trigonometry: $\sin x$, $\cos x$, $\tan x$, $\cot x$, $\sec
+% x$, $\csc x$ expecting their arguments in radians, and
+% $\operatorname{sind} x$, $\operatorname{cosd} x$,
+% $\operatorname{tand} x$, $\operatorname{cotd} x$,
+% $\operatorname{secd} x$, $\operatorname{cscd} x$ expecting their
+% arguments in degrees.
+% \item Inverse trigonometric functions: $\operatorname{asin} x$,
+% $\operatorname{acos} x$, $\operatorname{atan} x$,
+% $\operatorname{acot} x$, $\operatorname{asec} x$,
+% $\operatorname{acsc} x$ giving a result in radians, and
+% $\operatorname{asind} x$, $\operatorname{acosd} x$,
+% $\operatorname{atand} x$, $\operatorname{acotd} x$,
+% $\operatorname{asecd} x$, $\operatorname{acscd} x$ giving a result
+% in degrees.
+% \item [\emph{(not yet)}] Hyperbolic functions and their inverse
+% functions: $\sinh x$, $\cosh x$, $\tanh x$, $\coth x$,
+% $\operatorname{sech} x$, $\operatorname{csch}$, and
+% $\operatorname{asinh} x$, $\operatorname{acosh} x$,
+% $\operatorname{atanh} x$, $\operatorname{acoth} x$,
+% $\operatorname{asech} x$, $\operatorname{acsch} x$.
+% \item Extrema: $\max(x_{1},x_{2},\ldots)$, $\min(x_{1},x_{2},\ldots)$,
+% $\operatorname{abs}(x)$.
+% \item Rounding functions, controlled by two optional
+% values, $n$ (number of places, $0$ by default) and
+% $t$ (behavior on a tie, $\nan$ by default):
+% \begin{itemize}
+% \item $\operatorname{trunc}(x,n)$ rounds towards zero,
+% \item $\operatorname{floor}(x,n)$ rounds towards~$-\infty$,
+% \item $\operatorname{ceil}(x,n)$ rounds towards~$+\infty$,
+% \item $\operatorname{round}(x,n,t)$ rounds to the closest value, with
+% ties rounded to an even value by default, towards zero if $t=0$,
+% towards $+\infty$ if $t>0$ and towards $-\infty$ if $t<0$.
+% \end{itemize}
+% And \emph{(not yet)} modulo, and \enquote{quantize}.
+% \item Random numbers: $\mathop{rand}()$, $\mathop{randint}(m,n)$.
+% \item Constants: \texttt{pi}, \texttt{deg} (one degree in radians).
+% \item Dimensions, automatically expressed in points, \emph{e.g.},
+% \texttt{pc} is~$12$.
+% \item Automatic conversion (no need for \cs[no-index]{\meta{type}_use:N}) of
+% integer, dimension, and skip variables to floating point numbers,
+% expressing dimensions in points and ignoring the stretch and
+% shrink components of skips.
+% \item Tuples: $(x_1,\ldots{},x_n)$ that can be stored in variables,
+% added together, multiplied or divided by a floating point number,
+% and nested.
+% \end{itemize}
+% Floating point numbers can be given either explicitly (in a form such
+% as |1.234e-34|, or |-.0001|), or as a stored floating point variable,
+% which is automatically replaced by its current value.
+% A \enquote{floating point} is a floating point number or a tuple thereof. See
+% section~\ref{sec:l3fp:fp-floats} for a description of what a floating point is,
+% section~\ref{sec:l3fp:fp-precedence} for details about how an expression is
+% parsed, and section~\ref{sec:l3fp:fp-operations} to know what the various
+% operations do. Some operations may raise exceptions (error messages),
+% described in section~\ref{sec:l3fp:fp-exceptions}.
+%
+% An example of use could be the following.
+% \begin{verbatim}
+% \LaTeX{} can now compute: $ \frac{\sin (3.5)}{2} + 2\cdot 10^{-3}
+% = \ExplSyntaxOn \fp_to_decimal:n {sin(3.5)/2 + 2e-3} $.
+% \end{verbatim}
+% The operation \texttt{round} can be used to limit the result's
+% precision. Adding $+0$ avoids the possibly undesirable output |-0|,
+% replacing it by |+0|. However, the \pkg{l3fp} module is mostly meant
+% as an underlying tool for higher-level commands. For example, one
+% could provide a function to typeset nicely the result of floating
+% point computations.
+% \begin{verbatim}
+% \documentclass{article}
+% \usepackage{siunitx}
+% \ExplSyntaxOn
+% \NewDocumentCommand { \calcnum } { m }
+% { \num { \fp_to_scientific:n {#1} } }
+% \ExplSyntaxOff
+% \begin{document}
+% \calcnum { 2 pi * sin ( 2.3 ^ 5 ) }
+% \end{document}
+% \end{verbatim}
+% See the documentation of \pkg{siunitx} for various options of
+% \cs{num}.
+%
+% \section{Creating and initialising floating point variables}
+%
+% \begin{function}[updated = 2012-05-08, tested = m3fp001]
+% {\fp_new:N, \fp_new:c}
+% \begin{syntax}
+% \cs{fp_new:N} \meta{fp~var}
+% \end{syntax}
+% Creates a new \meta{fp~var} or raises an error if the name is
+% already taken. The declaration is global. The \meta{fp~var} is
+% initially~$+0$.
+% \end{function}
+%
+% \begin{function}[updated = 2012-05-08, tested = m3fp001]
+% {\fp_const:Nn, \fp_const:cn}
+% \begin{syntax}
+% \cs{fp_const:Nn} \meta{fp~var} \Arg{fp expr}
+% \end{syntax}
+% Creates a new constant \meta{fp~var} or raises an error if the name
+% is already taken. The \meta{fp~var} is set globally equal to
+% the result of evaluating the \meta{fp expr}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-05-08, tested = m3fp001]
+% {\fp_zero:N, \fp_zero:c, \fp_gzero:N, \fp_gzero:c}
+% \begin{syntax}
+% \cs{fp_zero:N} \meta{fp~var}
+% \end{syntax}
+% Sets the \meta{fp~var} to~$+0$.
+% \end{function}
+%
+% \begin{function}[updated = 2012-05-08, tested = m3fp001]
+% {\fp_zero_new:N, \fp_zero_new:c, \fp_gzero_new:N, \fp_gzero_new:c}
+% \begin{syntax}
+% \cs{fp_zero_new:N} \meta{fp~var}
+% \end{syntax}
+% Ensures that the \meta{fp~var} exists globally
+% by applying \cs{fp_new:N} if necessary, then applies
+% \cs[index=fp_zero:N]{fp_(g)zero:N} to leave the \meta{fp~var} set to~$+0$.
+% \end{function}
+%
+% \section{Setting floating point variables}
+%
+% \begin{function}[updated = 2012-05-08, tested = m3fp002]
+% {\fp_set:Nn, \fp_set:cn, \fp_gset:Nn, \fp_gset:cn}
+% \begin{syntax}
+% \cs{fp_set:Nn} \meta{fp~var} \Arg{fp expr}
+% \end{syntax}
+% Sets \meta{fp~var} equal to the result of computing the
+% \meta{fp expr}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-05-08, tested = m3fp002]
+% {
+% \fp_set_eq:NN , \fp_set_eq:cN , \fp_set_eq:Nc , \fp_set_eq:cc ,
+% \fp_gset_eq:NN, \fp_gset_eq:cN, \fp_gset_eq:Nc, \fp_gset_eq:cc
+% }
+% \begin{syntax}
+% \cs{fp_set_eq:NN} \meta{fp~var_1} \meta{fp~var_2}
+% \end{syntax}
+% Sets the floating point variable \meta{fp~var_1} equal to the current
+% value of \meta{fp~var_2}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-05-08, tested = m3fp002]
+% {\fp_add:Nn, \fp_add:cn, \fp_gadd:Nn, \fp_gadd:cn}
+% \begin{syntax}
+% \cs{fp_add:Nn} \meta{fp~var} \Arg{fp expr}
+% \end{syntax}
+% Adds the result of computing the \meta{fp expr} to
+% the \meta{fp~var}.
+% This also applies if \meta{fp~var} and \meta{floating point
+% expression} evaluate to tuples of the same size.
+% \end{function}
+%
+% \begin{function}[updated = 2012-05-08, tested = m3fp002]
+% {\fp_sub:Nn, \fp_sub:cn, \fp_gsub:Nn, \fp_gsub:cn}
+% \begin{syntax}
+% \cs{fp_sub:Nn} \meta{fp~var} \Arg{fp expr}
+% \end{syntax}
+% Subtracts the result of computing the \meta{floating point
+% expression} from the \meta{fp~var}.
+% This also applies if \meta{fp~var} and \meta{floating point
+% expression} evaluate to tuples of the same size.
+% \end{function}
+%
+% \section{Using floating points}
+%
+% \begin{function}[EXP, added = 2012-05-08, updated = 2012-07-08,
+% tested = m3fp-convert003]{\fp_eval:n}
+% \begin{syntax}
+% \cs{fp_eval:n} \Arg{fp expr}
+% \end{syntax}
+% Evaluates the \meta{fp expr} and expresses the
+% result as a decimal number with no
+% exponent. Leading or trailing zeros may be inserted to compensate
+% for the exponent. Non-significant trailing zeros are trimmed, and
+% integers are expressed without a decimal separator. The values
+% $\pm\infty$ and \nan{} trigger an \enquote{invalid operation}
+% exception.
+% For a tuple, each item is converted using \cs{fp_eval:n} and they are combined as
+% |(|\meta{fp_1}\verb*|, |\meta{fp_2}\verb*|, |\ldots{}\meta{fp_n}|)|
+% if $n>1$ and |(|\meta{fp_1}|,)| or |()| for fewer items.
+% This function is identical to \cs{fp_to_decimal:n}.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-11-03]{\fp_sign:n}
+% \begin{syntax}
+% \cs{fp_sign:n} \Arg{fp expr}
+% \end{syntax}
+% Evaluates the \meta{fp expr} and leaves its sign in the input stream
+% using \cs{fp_eval:n} |{sign(|\meta{result}|)}|: $+1$ for positive
+% numbers and for $+\infty$, $-1$ for negative numbers and for
+% $-\infty$, $\pm 0$ for $\pm 0$. If the operand is a tuple or is
+% \nan{}, then \enquote{invalid operation} occurs and the result
+% is~$0$.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2012-05-08, updated = 2012-07-08]
+% {\fp_to_decimal:N, \fp_to_decimal:c, \fp_to_decimal:n}
+% \begin{syntax}
+% \cs{fp_to_decimal:N} \meta{fp~var}
+% \cs{fp_to_decimal:n} \Arg{fp expr}
+% \end{syntax}
+% Evaluates the \meta{fp expr} and expresses the
+% result as a decimal number with no
+% exponent. Leading or trailing zeros may be inserted to compensate
+% for the exponent. Non-significant trailing zeros are trimmed, and
+% integers are expressed without a decimal separator. The values
+% $\pm\infty$ and~\nan{} trigger an \enquote{invalid operation}
+% exception.
+% For a tuple, each item is converted using \cs{fp_to_decimal:n} and they are combined as
+% |(|\meta{fp_1}\verb*|, |\meta{fp_2}\verb*|, |\ldots{}\meta{fp_n}|)|
+% if $n>1$ and |(|\meta{fp_1}|,)| or |()| for fewer items.
+% \end{function}
+%
+% \begin{function}[EXP, updated = 2016-03-22]
+% {\fp_to_dim:N, \fp_to_dim:c, \fp_to_dim:n}
+% \begin{syntax}
+% \cs{fp_to_dim:N} \meta{fp~var}
+% \cs{fp_to_dim:n} \Arg{fp expr}
+% \end{syntax}
+% Evaluates the \meta{fp expr} and expresses the
+% result as a dimension (in~\texttt{pt}) suitable for use in dimension
+% expressions. The output is identical to \cs{fp_to_decimal:n}, with
+% an additional trailing~\texttt{pt} (both letter tokens).
+% In particular, the result may
+% be outside the range $[- 2^{14} + 2^{-17}, 2^{14} - 2^{-17}]$ of
+% valid \TeX{} dimensions, leading to overflow errors if used as a
+% dimension. Tuples, as well as the values $\pm\infty$ and~\nan{},
+% trigger an \enquote{invalid operation} exception.
+% \end{function}
+%
+% \begin{function}[EXP, updated = 2012-07-08]
+% {\fp_to_int:N, \fp_to_int:c, \fp_to_int:n}
+% \begin{syntax}
+% \cs{fp_to_int:N} \meta{fp~var}
+% \cs{fp_to_int:n} \Arg{fp expr}
+% \end{syntax}
+% Evaluates the \meta{fp expr}, and rounds the
+% result to the closest integer, rounding exact ties to an even
+% integer.
+% The result may be outside the range $[- 2^{31} + 1, 2^{31} - 1]$ of
+% valid \TeX{}~integers, leading to overflow errors if used in an
+% integer expression. Tuples, as well as the values $\pm\infty$
+% and~\nan{}, trigger an \enquote{invalid operation} exception.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2012-05-08, updated = 2016-03-22]
+% {\fp_to_scientific:N, \fp_to_scientific:c, \fp_to_scientific:n}
+% \begin{syntax}
+% \cs{fp_to_scientific:N} \meta{fp~var}
+% \cs{fp_to_scientific:n} \Arg{fp expr}
+% \end{syntax}
+% Evaluates the \meta{fp expr} and expresses the
+% result in scientific notation:
+% \begin{quote}
+% \meta{optional \texttt{-}}\meta{digit}\texttt{.}\meta{15 digits}\texttt{e}\meta{optional sign}\meta{exponent}
+% \end{quote}
+% The leading \meta{digit} is non-zero except in the case of $\pm 0$.
+% The values $\pm\infty$ and~\nan{} trigger an \enquote{invalid
+% operation} exception. Normal category codes apply: thus the |e| is
+% category code~$11$ (a letter).
+% For a tuple, each item is converted using \cs{fp_to_scientific:n} and they are combined as
+% |(|\meta{fp_1}\verb*|, |\meta{fp_2}\verb*|, |\ldots{}\meta{fp_n}|)|
+% if $n>1$ and |(|\meta{fp_1}|,)| or |()| for fewer items.
+% \end{function}
+%
+% \begin{function}[EXP, updated = 2016-03-22]
+% {\fp_to_tl:N, \fp_to_tl:c, \fp_to_tl:n}
+% \begin{syntax}
+% \cs{fp_to_tl:N} \meta{fp~var}
+% \cs{fp_to_tl:n} \Arg{fp expr}
+% \end{syntax}
+% Evaluates the \meta{fp expr} and expresses the
+% result in (almost) the shortest possible form. Numbers in the
+% ranges $(0,10^{-3})$ and $[10^{16},\infty)$ are expressed in
+% scientific notation with trailing zeros trimmed and no decimal
+% separator when there is a single significant digit (this differs from
+% \cs{fp_to_scientific:n}). Numbers in the range $[10^{-3},10^{16})$
+% are expressed in a decimal notation without exponent, with trailing
+% zeros trimmed, and no decimal separator for integer values (see
+% \cs{fp_to_decimal:n}. Negative numbers start with~|-|. The
+% special values $\pm 0$, $\pm\infty$ and~\nan{} are rendered as
+% |0|, |-0|, \texttt{inf}, \texttt{-inf}, and~\texttt{nan}
+% respectively. Normal category codes apply and thus \texttt{inf} or
+% \texttt{nan}, if produced, are made up of letters.
+% For a tuple, each item is converted using \cs{fp_to_tl:n} and they are combined as
+% |(|\meta{fp_1}\verb*|, |\meta{fp_2}\verb*|, |\ldots{}\meta{fp_n}|)|
+% if $n>1$ and |(|\meta{fp_1}|,)| or |()| for fewer items.
+% \end{function}
+%
+% \begin{function}[EXP, updated = 2012-07-08]
+% {\fp_use:N, \fp_use:c}
+% \begin{syntax}
+% \cs{fp_use:N} \meta{fp~var}
+% \end{syntax}
+% Inserts the value of the \meta{fp~var} into the input stream as a
+% decimal number with no exponent.
+% Leading or trailing zeros may be inserted to compensate for the
+% exponent. Non-significant trailing zeros are trimmed. Integers are
+% expressed without a decimal separator. The values $\pm\infty$
+% and~\nan{} trigger an \enquote{invalid operation} exception.
+% For a tuple, each item is converted using \cs{fp_to_decimal:n} and they are combined as
+% |(|\meta{fp_1}\verb*|, |\meta{fp_2}\verb*|, |\ldots{}\meta{fp_n}|)|
+% if $n>1$ and |(|\meta{fp_1}|,)| or |()| for fewer items.
+% This function is identical to \cs{fp_to_decimal:N}.
+% \end{function}
+%
+% \section{Floating point conditionals}
+%
+% \begin{function}[EXP, pTF, updated = 2012-05-08, tested = m3fp002]
+% {\fp_if_exist:N, \fp_if_exist:c}
+% \begin{syntax}
+% \cs{fp_if_exist_p:N} \meta{fp~var}
+% \cs{fp_if_exist:NTF} \meta{fp~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{fp~var} is currently defined. This does not
+% check that the \meta{fp~var} really is a floating point variable.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, updated = 2012-05-08,
+% tested = m3fp-logic001]{\fp_compare:nNn}
+% \begin{syntax}
+% \cs{fp_compare_p:nNn} \Arg{fp expr_1} \meta{relation} \Arg{fp expr_2}
+% \cs{fp_compare:nNnTF} \Arg{fp expr_1} \meta{relation} \Arg{fp expr_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Compares the \meta{fp expr_1} and the \meta{fp expr_2}, and returns
+% \texttt{true} if the \meta{relation} is obeyed. Two floating points
+% $x$ and~$y$ may obey four mutually exclusive relations:
+% $x<y$, $x=y$, $x>y$, or $x?y$ (\enquote{not ordered}). The last
+% case occurs exactly if one or both operands is~\nan{} or is a tuple,
+% unless they are equal tuples. Note that a~\nan{} is distinct from
+% any value, even another~\nan{}, hence $x=x$ is not true for
+% a~\nan{}. To test if a value is~\nan{}, compare it to an arbitrary
+% number with the \enquote{not ordered} relation.
+% \begin{verbatim}
+% \fp_compare:nNnTF { <value> } ? { 0 }
+% { } % <value> is nan
+% { } % <value> is not nan
+% \end{verbatim}
+% Tuples are equal if they have the same number of items and items
+% compare equal (in particular there must be no~\nan{}).
+% At present any other comparison with tuples yields |?| (not ordered).
+% This is experimental.
+%
+% This function is less flexible than \cs{fp_compare:nTF} but slightly
+% faster. It is provided for consistency with \cs{int_compare:nNnTF}
+% and \cs{dim_compare:nNnTF}.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, updated = 2013-12-14,
+% tested = m3fp-logic001]{\fp_compare:n}
+% \begin{syntax}
+% \cs{fp_compare_p:n} \\
+% ~~\{ \\
+% ~~~~\meta{fp expr_1} \meta{relation_1} \\
+% ~~~~\ldots{} \\
+% ~~~~\meta{fp expr_N} \meta{relation_N} \\
+% ~~~~\meta{fp expr_{N+1}} \\
+% ~~\} \\
+% \cs{fp_compare:nTF}
+% ~~\{ \\
+% ~~~~\meta{fp expr_1} \meta{relation_1} \\
+% ~~~~\ldots{} \\
+% ~~~~\meta{fp expr_N} \meta{relation_N} \\
+% ~~~~\meta{fp expr_{N+1}} \\
+% ~~\} \\
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Evaluates the \meta{fp exprs} as described for
+% \cs{fp_eval:n} and compares consecutive result using the
+% corresponding \meta{relation}, namely it compares \meta{fp expr_1}
+% and \meta{fp expr_2} using the \meta{relation_1}, then
+% \meta{fp expr_2} and \meta{fp expr_3} using the \meta{relation_2},
+% until finally comparing \meta{fp expr_N} and \meta{fp expr_{N+1}}
+% using the \meta{relation_N}. The test yields \texttt{true} if all
+% comparisons are \texttt{true}. Each \meta{floating point
+% expression} is evaluated only once. Contrarily to
+% \cs{int_compare:nTF}, all \meta{fp exprs} are
+% computed, even if one comparison is \texttt{false}. Two floating
+% points $x$ and~$y$ may obey four mutually exclusive
+% relations: $x<y$, $x=y$, $x>y$, or $x?y$ (\enquote{not ordered}).
+% The last case occurs exactly if one or both operands is~\nan{} or is
+% a tuple, unless they are equal tuples. Each \meta{relation}
+% can be any (non-empty) combination of |<|, |=|, |>|, and~|?|, plus
+% an optional leading~|!| (which negates the \meta{relation}), with
+% the restriction that the \meta{relation} may not start with~|?|, as
+% this symbol has a different meaning (in combination with~|:|) within
+% floating point expressions. The comparison $x$~\meta{relation}~$y$
+% is then \texttt{true} if the \meta{relation} does not start with~|!|
+% and the actual relation (|<|, |=|, |>|, or~|?|) between $x$ and~$y$
+% appears within the \meta{relation}, or on the contrary if the
+% \meta{relation} starts with~|!| and the relation between $x$ and~$y$
+% does not appear within the \meta{relation}. Common choices of
+% \meta{relation} include |>=|~(greater or equal), |!=|~(not equal),
+% |!?|~or~|<=>| (comparable).
+%
+% This function is more flexible than \cs{fp_compare:nNnTF} and only
+% slightly slower.
+% \end{function}
+%
+% \begin{function}[pTF, added = 2019-08-25]{\fp_if_nan:n}
+% \begin{syntax}
+% \cs{fp_if_nan_p:n} \Arg{fp expr}
+% \cs{fp_if_nan:nTF} \Arg{fp expr} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Evaluates the \meta{fp expr} and tests whether the result is exactly
+% \nan{}. The test returns \texttt{false} for any other result, even
+% a tuple containing \nan{}.
+% \end{function}
+%
+% \section{Floating point expression loops}
+%
+% \begin{function}[rEXP, added = 2012-08-16, tested = m3fp-logic003]
+% {\fp_do_until:nNnn}
+% \begin{syntax}
+% \cs{fp_do_until:nNnn} \Arg{fp expr_1} \meta{relation} \Arg{fp expr_2} \Arg{code}
+% \end{syntax}
+% Places the \meta{code} in the input stream for \TeX{} to process,
+% and then evaluates the relationship between the two \meta{floating
+% point expressions} as described for \cs{fp_compare:nNnTF}. If the
+% test is \texttt{false} then the \meta{code} is inserted into
+% the input stream again and a loop occurs until the
+% \meta{relation} is \texttt{true}.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2012-08-16, tested = m3fp-logic003]
+% {\fp_do_while:nNnn}
+% \begin{syntax}
+% \cs{fp_do_while:nNnn} \Arg{fp expr_1} \meta{relation} \Arg{fp expr_2} \Arg{code}
+% \end{syntax}
+% Places the \meta{code} in the input stream for \TeX{} to process,
+% and then evaluates the relationship between the two \meta{floating
+% point expressions} as described for \cs{fp_compare:nNnTF}. If the
+% test is \texttt{true} then the \meta{code} is inserted into the
+% input stream again and a loop occurs until the \meta{relation}
+% is \texttt{false}.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2012-08-16, tested = m3fp-logic003]
+% {\fp_until_do:nNnn}
+% \begin{syntax}
+% \cs{fp_until_do:nNnn} \Arg{fp expr_1} \meta{relation} \Arg{fp expr_2} \Arg{code}
+% \end{syntax}
+% Evaluates the relationship between the two \meta{floating point
+% expressions} as described for \cs{fp_compare:nNnTF}, and then
+% places the \meta{code} in the input stream if the \meta{relation} is
+% \texttt{false}. After the \meta{code} has been processed by \TeX{}
+% the test is repeated, and a loop occurs until the test is
+% \texttt{true}.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2012-08-16, tested = m3fp-logic003]
+% {\fp_while_do:nNnn}
+% \begin{syntax}
+% \cs{fp_while_do:nNnn} \Arg{fp expr_1} \meta{relation} \Arg{fp expr_2} \Arg{code}
+% \end{syntax}
+% Evaluates the relationship between the two \meta{floating point
+% expressions} as described for \cs{fp_compare:nNnTF}, and then
+% places the \meta{code} in the input stream if the \meta{relation} is
+% \texttt{true}. After the \meta{code} has been processed by \TeX{}
+% the test is repeated, and a loop occurs until the test is
+% \texttt{false}.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2012-08-16, updated = 2013-12-14, tested = m3fp-logic003]
+% {\fp_do_until:nn}
+% \begin{syntax}
+% \cs{fp_do_until:nn} \{ \meta{fp expr_1} \meta{relation} \meta{fp expr_2} \} \Arg{code}
+% \end{syntax}
+% Places the \meta{code} in the input stream for \TeX{} to process,
+% and then evaluates the relationship between the two \meta{floating
+% point expressions} as described for \cs{fp_compare:nTF}. If the
+% test is \texttt{false} then the \meta{code} is inserted into
+% the input stream again and a loop occurs until the
+% \meta{relation} is \texttt{true}.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2012-08-16, updated = 2013-12-14, tested = m3fp-logic003]
+% {\fp_do_while:nn}
+% \begin{syntax}
+% \cs{fp_do_while:nn} \{ \meta{fp expr_1} \meta{relation} \meta{fp expr_2} \} \Arg{code}
+% \end{syntax}
+% Places the \meta{code} in the input stream for \TeX{} to process,
+% and then evaluates the relationship between the two \meta{floating
+% point expressions} as described for \cs{fp_compare:nTF}. If the
+% test is \texttt{true} then the \meta{code} is inserted into the
+% input stream again and a loop occurs until the \meta{relation}
+% is \texttt{false}.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2012-08-16, updated = 2013-12-14, tested = m3fp-logic003]
+% {\fp_until_do:nn}
+% \begin{syntax}
+% \cs{fp_until_do:nn} \{ \meta{fp expr_1} \meta{relation} \meta{fp expr_2} \} \Arg{code}
+% \end{syntax}
+% Evaluates the relationship between the two \meta{floating point
+% expressions} as described for \cs{fp_compare:nTF}, and then places
+% the \meta{code} in the input stream if the \meta{relation} is
+% \texttt{false}. After the \meta{code} has been processed by \TeX{}
+% the test is repeated, and a loop occurs until the test is
+% \texttt{true}.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2012-08-16, updated = 2013-12-14, tested = m3fp-logic003]
+% {\fp_while_do:nn}
+% \begin{syntax}
+% \cs{fp_while_do:nn} \{ \meta{fp expr_1} \meta{relation} \meta{fp expr_2} \} \Arg{code}
+% \end{syntax}
+% Evaluates the relationship between the two \meta{floating point
+% expressions} as described for \cs{fp_compare:nTF}, and then places
+% the \meta{code} in the input stream if the \meta{relation} is
+% \texttt{true}. After the \meta{code} has been processed by \TeX{}
+% the test is repeated, and a loop occurs until the test is
+% \texttt{false}.
+% \end{function}
+%
+% \begin{function}[added = 2016-11-21, updated = 2016-12-06, rEXP]
+% {\fp_step_function:nnnN, \fp_step_function:nnnc}
+% \begin{syntax}
+% \cs{fp_step_function:nnnN} \Arg{initial value} \Arg{step} \Arg{final value} \meta{function}
+% \end{syntax}
+% This function first evaluates the \meta{initial value}, \meta{step}
+% and \meta{final value}, each of which should be a floating point
+% expression evaluating to a floating point number, not a tuple.
+% The \meta{function} is then placed in front of each \meta{value}
+% from the \meta{initial value} to the \meta{final value} in turn
+% (using \meta{step} between each \meta{value}). The \meta{step} must
+% be non-zero. If the \meta{step} is positive, the loop stops when
+% the \meta{value} becomes larger than the \meta{final value}. If the
+% \meta{step} is negative, the loop stops when the \meta{value}
+% becomes smaller than the \meta{final value}. The \meta{function}
+% should absorb one numerical argument. For example
+% \begin{verbatim}
+% \cs_set:Npn \my_func:n #1 { [I~saw~#1] \quad }
+% \fp_step_function:nnnN { 1.0 } { 0.1 } { 1.5 } \my_func:n
+% \end{verbatim}
+% would print
+% \begin{quote}
+% [I saw 1.0] \quad
+% [I saw 1.1] \quad
+% [I saw 1.2] \quad
+% [I saw 1.3] \quad
+% [I saw 1.4] \quad
+% [I saw 1.5] \quad
+% \end{quote}
+% \begin{texnote}
+% Due to rounding, it may happen that adding the \meta{step} to the
+% \meta{value} does not change the \meta{value}; such cases give an
+% error, as they would otherwise lead to an infinite loop.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2016-11-21, updated = 2016-12-06]
+% {\fp_step_inline:nnnn}
+% \begin{syntax}
+% \cs{fp_step_inline:nnnn} \Arg{initial value} \Arg{step} \Arg{final value} \Arg{code}
+% \end{syntax}
+% This function first evaluates the \meta{initial value}, \meta{step}
+% and \meta{final value}, all of which should be floating point
+% expressions evaluating to a floating point number, not a tuple.
+% Then for each \meta{value} from the \meta{initial value} to the
+% \meta{final value} in turn (using \meta{step} between each
+% \meta{value}), the \meta{code} is inserted into the input stream
+% with |#1| replaced by the current \meta{value}. Thus the
+% \meta{code} should define a function of one argument~(|#1|).
+% \end{function}
+%
+% \begin{function}[added = 2017-04-12]{\fp_step_variable:nnnNn}
+% \begin{syntax}
+% \cs{fp_step_variable:nnnNn} \\
+% ~~\Arg{initial value} \Arg{step} \Arg{final value} \meta{tl~var} \Arg{code}
+% \end{syntax}
+% This function first evaluates the \meta{initial value}, \meta{step}
+% and \meta{final value}, all of which should be floating point
+% expressions evaluating to a floating point number, not a tuple.
+% Then for each \meta{value} from the \meta{initial value} to the
+% \meta{final value} in turn (using \meta{step} between each
+% \meta{value}), the \meta{code} is inserted into the input stream,
+% with the \meta{tl~var} defined as the current \meta{value}. Thus
+% the \meta{code} should make use of the \meta{tl~var}.
+% \end{function}
+%
+% \section{Symbolic expressions}
+%
+% Floating point expressions support variables: these can only be set locally,
+% so act like standard \cs[no-index]{l_\dots} variables.
+% \begin{quote}\let\obeyedline=\newline\obeylines^^A
+% \cs{fp_new_variable:n} |{ A }|
+% \cs{fp_set:Nn} \cs{l_tmpb_fp} |{ 1 * sin(A) + 3**2 }|
+% \cs{fp_show:n} |{| \cs{l_tmpb_fp} |}|
+% \cs{fp_show:N} \cs{l_tmpb_fp}
+% \cs{fp_set_variable:nn} |{ A }| |{ pi/2 }|
+% \cs{fp_show:n} |{| \cs{l_tmpb_fp} |}|
+% \cs{fp_show:N} \cs{l_tmpb_fp}
+% \cs{fp_set_variable:nn} |{ A }| |{ 0 }|
+% \cs{fp_show:n} |{| \cs{l_tmpb_fp} |}|
+% \cs{fp_show:N} \cs{l_tmpb_fp}
+% \end{quote}
+% defines~|A| to be a variable, then defines \cs{l_tmpb_fp} to stand for
+% |1*sin(A)+9| (note that |3**2| is evaluated, but the |1*|~product is
+% not simplified away). Until \cs{l_tmpb_fp} is changed, \cs{fp_show:N}
+% \cs{l_tmpb_fp} will show |((1*sin(A))+9)| regardless of the value
+% of~|A|. The next step defines~|A| to be equal to~|pi/2|: then
+% \cs{fp_show:n} |{| \cs{l_tmpb_fp} |}| will evaluate \cs{l_tmpb_fp} and
+% show~|10|. We then redefine~|A| to be~|0|: since \cs{l_tmpb_fp} still
+% stands for |1*sin(A)+9|, the value shown is then~|9|. Variables can
+% be set with \cs{fp_set_variable:nn} to arbitrary floating point
+% expressions including other variables.
+%
+% \begin{function}[added = 2023-10-19]{\fp_new_variable:n}
+% \begin{syntax}
+% \cs{fp_new_variable:n} \Arg{identifier}
+% \end{syntax}
+% Declares the \meta{identifier} as a variable, which allows it to be
+% used in floating point expressions. For instance,
+% \begin{quote}
+% \cs{fp_new_variable:n} |{ A }| \\
+% \cs{fp_show:n} |{ A**2 - A + 1 }|
+% \end{quote}
+% shows |(((A^2)-A)+1)|. If the declaration was missing, the parser
+% would complain about an \enquote{\texttt{Unknown fp word 'A'}}. The
+% \meta{identifier} must consist entirely of Latin letters among
+% |[a-zA-Z]|.
+% \end{function}
+%
+% \begin{function}[added = 2023-10-19]{\fp_set_variable:nn}
+% \begin{syntax}
+% \cs{fp_set_variable:nn} \Arg{identifier} \Arg{fp expr}
+% \end{syntax}
+% Defines the \meta{identifier} to stand in any further expression for
+% the result of evaluating the \meta{floating point expression} as
+% much as possible. The result may contain other variables, which are
+% then replaced by their values if they have any. For instance,
+% \begin{quote}\let\obeyedline=\newline\obeylines^^A
+% \cs{fp_new_variable:n} |{ A }|
+% \cs{fp_new_variable:n} |{ B }|
+% \cs{fp_new_variable:n} |{ C }|
+% \cs{fp_set_variable:nn} |{ A } { 3 }|
+% \cs{fp_set_variable:nn} |{ C } { A ** 2 + B * 1 }|
+% \cs{fp_show:n} |{ C + 4 }|
+% \cs{fp_set_variable:nn} |{ A } { 4 }|
+% \cs{fp_show:n} |{ C + 4 }|
+% \end{quote}
+% shows |((9+(B*1))+4)| twice: changing the value of~|A| to~|4| does
+% not alter~|C| because |A|~was replaced by its value~|3| when
+% evaluating |A**2+B*1|.
+% \end{function}
+%
+% \begin{function}[added = 2023-10-19]{\fp_clear_variable:n}
+% \begin{syntax}
+% \cs{fp_clear_variable:n} \Arg{identifier}
+% \end{syntax}
+% Removes any value given by \cs{fp_set_variable:nn} to the variable
+% with this \meta{identifier}. For instance,
+% \begin{quote}\let\obeyedline=\newline\obeylines^^A
+% \cs{fp_new_variable:n} |{ A }|
+% \cs{fp_set_variable:nn} |{ A } { 3 }|
+% \cs{fp_show:n} |{ A ^ 2 }|
+% \cs{fp_clear_variable:n} |{ A }|
+% \cs{fp_show:n} |{ A ^ 2 }|
+% \end{quote}
+% shows~|9|, then~|(A^2)|.
+% \end{function}
+%
+% \section{User-defined functions}
+%
+% It is possible to define new user functions which can be used inside
+% the argument to \cs{fp_eval:n}, etc. These functions may take one or
+% more named arguments, and should be implemented using expansion methods
+% only.
+%
+% \begin{function}[added = 2023-10-19]{\fp_new_function:n}
+% \begin{syntax}
+% \cs{fp_new_function:n} \Arg{identifier}
+% \end{syntax}
+% Declares the \meta{identifier} as a function, which allows it to be
+% used in floating point expressions. For instance,
+% \begin{quote}
+% \cs{fp_new_function:n} |{ foo }| \\
+% \cs{fp_show:n} |{ foo ( 1 + 2 , foo(3), A ) ** 2 } }|
+% \end{quote}
+% shows |(foo(3, foo(3), A))^(2)|. If the declaration was missing,
+% the parser would complain about an \enquote{\texttt{Unknown fp word 'foo'}}.
+% The \meta{identifier} must consist entirely of Latin letters |[a-zA-Z]|.
+% \end{function}
+%
+% \begin{function}[added = 2023-10-19]{\fp_set_function:nnn}
+% \begin{syntax}
+% \cs{fp_set_function:nnn} \Arg{identifier} \Arg{vars} \Arg{fp expr}
+% \end{syntax}
+% Defines the \meta{identifier} to stand in any further expression for
+% the result of evaluating the \meta{floating point expression}, with
+% the \meta{identifier} accepting the \meta{vars} (a non-empty
+% comma-separated list).
+% The result may contain other functions, which are
+% then replaced by their results if they have any. For instance,
+% \begin{quote}
+% \cs{fp_new_function:n} |{ foo }| \\
+% \cs{fp_set_function:nnn} |{ npow } { a,b } { a**b }| \\
+% \cs{fp_show:n} |{ npow(16,0.25) } }|
+% \end{quote}
+% shows |2|. The names of the \meta{vars} must
+% consist entirely of Latin letters |[a-zA-Z]|, but are otherwise not
+% restricted: in particular, they are independent of any variables
+% declared by \cs{fp_new_variable:n}.
+% \end{function}
+%
+% \begin{function}[added = 2023-10-19]{\fp_clear_function:n}
+% \begin{syntax}
+% \cs{fp_clear_function:n} \Arg{identifier}
+% \end{syntax}
+% Removes any definition given by \cs{fp_set_function:nnn} to the function
+% with this \meta{identifier}.
+% \end{function}
+%
+% \section{Some useful constants, and scratch variables}
+%
+% \begin{variable}[added = 2012-05-08, module = fp]{\c_zero_fp, \c_minus_zero_fp}
+% Zero, with either sign.
+% \end{variable}
+%
+% \begin{variable}[added = 2012-05-08, module = fp]{\c_one_fp}
+% One as an \texttt{fp}: useful for comparisons in some places.
+% \end{variable}
+%
+% \begin{variable}[added = 2012-05-08, module = fp]{\c_inf_fp, \c_minus_inf_fp}
+% Infinity, with either sign. These can be input directly in a
+% floating point expression as \texttt{inf} and \texttt{-inf}.
+% \end{variable}
+%
+% \begin{variable}[added = 2012-05-08, module = fp]{\c_nan_fp}
+% Not a number. This can be input directly in a floating point expression
+% as \texttt{nan}.
+% \end{variable}
+%
+% \begin{variable}[updated = 2012-05-08, module = fp]{\c_e_fp}
+% The value of the base of the natural logarithm, $\mathrm{e} = \exp(1)$.
+% \end{variable}
+%
+% \begin{variable}[updated = 2013-11-17, module = fp]{\c_pi_fp}
+% The value of~$\pi$. This can be input directly in a floating point
+% expression as~\texttt{pi}.
+% \end{variable}
+%
+% \begin{variable}[added = 2012-05-08, updated = 2013-11-17, module = fp]
+% {\c_one_degree_fp}
+% The value of $1^{\circ}$ in radians. Multiply an angle given in
+% degrees by this value to obtain a result in radians. Note that
+% trigonometric functions expecting an argument in radians or in
+% degrees are both available. Within floating point expressions, this
+% can be accessed as \texttt{deg}.
+% \end{variable}
+%
+% \section{Scratch variables}
+%
+% \begin{variable}[module = fp]{\l_tmpa_fp, \l_tmpb_fp}
+% Scratch floating points for local assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \begin{variable}[module = fp]{\g_tmpa_fp, \g_tmpb_fp}
+% Scratch floating points for global assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \section{Floating point exceptions}
+% \label{sec:l3fp:fp-exceptions}
+%
+% \emph{The functions defined in this section are experimental, and
+% their functionality may be altered or removed altogether.}
+%
+% \enquote{Exceptions} may occur when performing some floating point
+% operations, such as \texttt{0 / 0}, or \texttt{10 ** 1e9999}. The
+% relevant \textsc{IEEE} standard defines $5$ types of exceptions,
+% of which we implement~$4$.
+% \begin{itemize}
+% \item \emph{Overflow} occurs whenever the result of an operation is
+% too large to be represented as a normal floating point number. This
+% results in $\pm \infty$.
+% \item \emph{Underflow} occurs whenever the result of an operation is
+% too close to $0$ to be represented as a normal floating point
+% number. This results in $\pm 0$.
+% \item \emph{Invalid operation} occurs for operations with no defined
+% outcome, for instance $0/0$ or $\sin(\infty)$, and results in a \nan{}.
+% It also occurs for conversion functions whose target type does not
+% have the appropriate infinite or \nan{} value (\emph{e.g.},
+% \cs{fp_to_dim:n}).
+% \item \emph{Division by zero} occurs when dividing a non-zero number
+% by $0$, or when evaluating functions at poles, \emph{e.g.},
+% $\ln(0)$ or $\cot(0)$. This results in $\pm\infty$.
+% \item [\emph{(not yet)}] \emph{Inexact} occurs whenever the result of
+% a computation is not exact, in other words, almost always. At the
+% moment, this exception is entirely ignored in \LaTeX3.
+% \end{itemize}
+% To each exception we associate a \enquote{flag}: \cs{l_fp_overflow_flag},
+% \cs{l_fp_underflow_flag}, \cs{l_fp_invalid_operation_flag} and
+% \cs{l_fp_division_by_zero_flag}. The state of these flags can be tested
+% and modified with commands from \pkg{l3flag}
+%
+% By default, the \enquote{invalid operation} exception triggers an
+% (expandable) error, and raises the corresponding flag. Other
+% exceptions raise the corresponding flag but do not trigger an error.
+% The behaviour when an exception occurs can be modified (using
+% \cs{fp_trap:nn}) to either produce an error and raise the flag, or
+% only raise the flag, or do nothing at all.
+%
+% \begin{function}[added = 2012-07-19, updated = 2017-02-13,
+% tested = m3fp-traps001]{\fp_trap:nn}
+% \begin{syntax}
+% \cs{fp_trap:nn} \Arg{exception} \Arg{trap type}
+% \end{syntax}
+% All occurrences of the \meta{exception} (\texttt{overflow},
+% \texttt{underflow}, \texttt{invalid_operation} or
+% \texttt{division_by_zero}) within the current
+% group are treated as \meta{trap type}, which can be
+% \begin{itemize}
+% \item \texttt{none}: the \meta{exception} will be entirely
+% ignored, and leave no trace;
+% \item \texttt{flag}: the \meta{exception} will turn the
+% corresponding flag on when it occurs;
+% \item \texttt{error}: additionally, the \meta{exception} will halt
+% the \TeX{} run and display some information about the current
+% operation in the terminal.
+% \end{itemize}
+% \emph{This function is experimental, and may be altered or removed.}
+% \end{function}
+%
+% \begin{variable}
+% {
+% \l_fp_overflow_flag,
+% \l_fp_underflow_flag,
+% \l_fp_invalid_operation_flag,
+% \l_fp_division_by_zero_flag
+% }
+% Flags denoting the occurrence of various floating-point exceptions.
+% \end{variable}
+%
+% \section{Viewing floating points}
+%
+% \begin{function}[added = 2012-05-08, updated = 2021-04-29,
+% tested = m3fp002]{\fp_show:N, \fp_show:c, \fp_show:n}
+% \begin{syntax}
+% \cs{fp_show:N} \meta{fp~var}
+% \cs{fp_show:n} \Arg{fp expr}
+% \end{syntax}
+% Evaluates the \meta{fp expr} and displays the
+% result in the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2021-04-29]
+% {\fp_log:N, \fp_log:c, \fp_log:n}
+% \begin{syntax}
+% \cs{fp_log:N} \meta{fp~var}
+% \cs{fp_log:n} \Arg{fp expr}
+% \end{syntax}
+% Evaluates the \meta{fp expr} and writes the
+% result in the log file.
+% \end{function}
+%
+% \section{Floating point expressions}
+%
+% \subsection{Input of floating point numbers} \label{sec:l3fp:fp-floats}
+%
+% We support four types of floating point numbers:
+% \begin{itemize}
+% \item $\pm m \cdot 10^{n}$, a floating
+% point number, with integer $1\leq m\leq 10^{16}$, and
+% $-{\ExplSyntaxOn\int_use:N\c__fp_minus_min_exponent_int}\leq
+% n\leq {\ExplSyntaxOn\int_use:N\c__fp_max_exponent_int}$;
+% \item $\pm 0$, zero, with a given sign;
+% \item $\pm \infty$, infinity, with a given sign;
+% \item \nan{}, is \enquote{not a number}, and can be either quiet
+% or signalling (\emph{not yet}: this distinction is currently
+% unsupported);
+% \end{itemize}
+% Normal floating point numbers are stored in base $10$, with up to $16$
+% significant figures.
+%
+% On input, a normal floating point number consists of:
+% \begin{itemize}
+% \item \meta{sign}: a possibly empty string of |+| and |-| characters;
+% \item \meta{significand}: a non-empty string of digits together with zero
+% or one dot;
+% \item \meta{exponent} optionally: the character |e| or |E|, followed by a
+% possibly empty string of |+|~and~|-| tokens, and a non-empty string
+% of digits.
+% \end{itemize}
+% The sign of the resulting number is |+| if \meta{sign} contains an
+% even number of |-|, and |-| otherwise, hence, an empty \meta{sign}
+% denotes a non-negative input. The stored significand is obtained from
+% \meta{significand} by omitting the decimal separator and leading zeros,
+% and rounding to $16$ significant digits, filling with trailing zeros
+% if necessary. In particular, the value stored is exact if the input
+% \meta{significand} has at most $16$ digits. The stored \meta{exponent}
+% is obtained by combining the input \meta{exponent} ($0$ if absent)
+% with a shift depending on the position of the significand and the number
+% of leading zeros.
+%
+% A special case arises if the resulting \meta{exponent} is either too
+% large or too small for the floating point number to be
+% represented. This results either in an overflow (the number is then
+% replaced by $\pm\infty$), or an underflow (resulting in $\pm 0$).
+%
+% The result is thus $\pm 0$ if and only if \meta{significand} contains no
+% non-zero digit (\emph{i.e.}, consists only in characters~|0|, and an
+% optional period), or if there is an underflow. Note that a
+% single dot is currently a valid floating point number, equal to~$+0$,
+% but that is not guaranteed to remain true.
+%
+% The \meta{significand} must be non-empty, so |e1| and |e-1| are not
+% valid floating point numbers. Note that the latter could be mistaken
+% with the difference of \enquote{\texttt{e}} and $1$. To avoid
+% confusions, the base of natural logarithms cannot be input as |e| and
+% should be input as \texttt{exp(1)} or \cs[module = fp]{c_e_fp} (which is faster).
+%
+% Special numbers are input as follows:
+% \begin{itemize}
+% \item \texttt{inf} represents $+\infty$, and can be preceded by any
+% \meta{sign}, yielding $\pm\infty$ as appropriate.
+% \item \texttt{nan} represents a (quiet) non-number. It can be
+% preceded by any sign, but that sign is ignored.
+% \item Any unrecognizable string triggers an error, and produces a
+% \nan{}.
+% \item Note that commands such as \tn{infty}, \tn{pi}, or \tn{sin}
+% \emph{do not} work in floating point expressions. They may
+% silently be interpreted as completely unexpected numbers, because
+% integer constants (allowed in expressions) are commonly stored as
+% mathematical characters.
+% \end{itemize}
+%
+% \subsection{Precedence of operators}
+% \label{sec:l3fp:fp-precedence}
+%
+% We list here all the operations supported in floating point
+% expressions, in order of decreasing precedence: operations listed
+% earlier bind more tightly than operations listed below them.
+% \begin{itemize}
+% \item Function calls (\texttt{sin}, \texttt{ln}, \emph{etc}).
+% \item Binary |**| and |^| (right associative).
+% \item Unary |+|, |-|, |!|.
+% \item Implicit multiplication by juxtaposition (\texttt{2pi})
+% when neither factor is in parentheses.
+% \item Binary |*| and |/|, implicit multiplication by juxtaposition with parentheses (for instance \texttt{3(4+5)}).
+% \item Binary |+| and |-|.
+% \item Comparisons |>=|, |!=|, |<?|, \emph{etc}.
+% \item Logical \texttt{and}, denoted by |&&|.
+% \item Logical \texttt{or}, denoted by \verb+||+.
+% \item Ternary operator |?:| (right associative).
+% \item Comma (to build tuples).
+% \end{itemize}
+% The precedence of operations can be overridden using parentheses.
+% In particular, the precedence of juxtaposition implies that
+% \begin{align*}
+% \mathtt{1/2pi} & = 1/(2\pi), \\
+% \mathtt{1/2pi(pi+pi)} & = (2\pi)^{-1}(\pi+\pi) \simeq 1, \\
+% \mathtt{sin 2pi} & = \sin(2)\pi \neq 0, \\
+% \mathtt{2\char`\^2max(3,5)} & = 2^2 \max(3,5) = 20, \\
+% \mathtt{1in/1cm} & = (1\mathrm{in})/(1\mathrm{cm}) = 2.54 .
+% \end{align*}
+% Functions are called on the value of their argument, contrarily to
+% \TeX{} macros.
+%
+% \subsection{Operations} \label{sec:l3fp:fp-operations}
+%
+% We now present the various operations allowed in floating point
+% expressions, from the lowest precedence to the highest. When used as
+% a truth value, a floating point expression is \texttt{false} if it is
+% $\pm 0$, and \texttt{true} otherwise, including when it is \nan{} or a
+% tuple such as $(0,0)$. Tuples are only supported to some extent by
+% operations that work with truth values (|?:|, \verb"||", |&&|, |!|),
+% by comparisons (|!<=>?|), and by |+|, |-|, |*|, |/|. Unless otherwise
+% specified, providing a tuple as an argument of any other operation
+% yields the \enquote{invalid operation} exception and a \nan{} result.
+%
+% \begin{function}[tested = m3fp-logic002, module = {}]{?:}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ \meta{operand_1} |?| \meta{operand_2} |:| \meta{operand_3} \}
+% \end{syntax}
+% The ternary operator |?:| results in \meta{operand_2} if
+% \meta{operand_1} is true (not $\pm 0$), and \meta{operand_3} if \meta{operand_1}
+% is false ($\pm 0$). All three \meta{operands} are evaluated in all
+% cases; they may be tuples. The operator is right associative, hence
+% \begin{verbatim}
+% \fp_eval:n
+% {
+% 1 + 3 > 4 ? 1 :
+% 2 + 4 > 5 ? 2 :
+% 3 + 5 > 6 ? 3 : 4
+% }
+% \end{verbatim}
+% first tests whether $1 + 3 > 4$; since this isn't true, the branch
+% following |:| is taken, and $2 + 4 > 5$ is compared; since this is
+% true, the branch before |:| is taken, and everything else is
+% (evaluated then) ignored. That allows testing for various cases in
+% a concise manner, with the drawback that all computations are made
+% in all cases.
+% \end{function}
+%
+% \begin{function}[tested = m3fp-logic002]{||}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ \meta{operand_1} \verb"||" \meta{operand_2} \}
+% \end{syntax}
+% If \meta{operand_1} is true (not $\pm 0$), use that value, otherwise the
+% value of \meta{operand_2}. Both \meta{operands} are evaluated in all
+% cases; they may be tuples. In \meta{operand_1} \verb"||"
+% \meta{operand_2} \verb"||" \ldots{} \verb"||" \meta{operands_n}, the
+% first true (nonzero) \meta{operand} is used and if all are zero the
+% last one ($\pm 0$) is used.
+% \end{function}
+%
+% \begin{function}[tested = m3fp-logic002]{&&}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ \meta{operand_1} |&&| \meta{operand_2} \}
+% \end{syntax}
+% If \meta{operand_1} is false (equal to~$\pm 0$), use that value,
+% otherwise the value of \meta{operand_2}. Both \meta{operands} are
+% evaluated in all cases; they may be tuples. In \meta{operand_1}
+% |&&| \meta{operand_2} |&&| \ldots{} |&&| \meta{operands_n}, the
+% first false ($\pm 0$) \meta{operand} is used and if none is zero the
+% last one is used.
+% \end{function}
+%
+% \begin{function}[tested = m3fp-logic001, updated = 2013-12-14]
+% {<, =, >, ?}
+% \begin{syntax}
+% \cs{fp_eval:n} \\
+% ~~\{ \\
+% ~~~~\meta{operand_1} \meta{relation_1} \\
+% ~~~~\ldots{} \\
+% ~~~~\meta{operand_N} \meta{relation_N} \\
+% ~~~~\meta{operand_{N+1}} \\
+% ~~\}
+% \end{syntax}
+% Each \meta{relation} consists of a non-empty string of |<|, |=|,
+% |>|, and~|?|, optionally preceded by~|!|, and may not start
+% with~|?|. This evaluates to $+1$ if all comparisons
+% \meta{operand_i} \meta{relation_i} \meta{operand_{i+1}} are true, and
+% $+0$ otherwise. All \meta{operands} are evaluated (once) in all cases.
+% See \cs{fp_compare:nTF} for details.
+% \end{function}
+%
+% \begin{function}[tested = m3fp-basics001]{+, -}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ \meta{operand_1} |+| \meta{operand_2} \}
+% \cs{fp_eval:n} \{ \meta{operand_1} |-| \meta{operand_2} \}
+% \end{syntax}
+% Computes the sum or the difference of its two \meta{operands}. The
+% \enquote{invalid operation} exception occurs for $\infty-\infty$.
+% \enquote{Underflow} and \enquote{overflow} occur when appropriate.
+% These operations supports the itemwise addition or subtraction of
+% two tuples, but if they have a different number of items the
+% \enquote{invalid operation} exception occurs and the result is \nan{}.
+% \end{function}
+%
+% \begin{function}[tested = {m3fp-basics002, m3fp-basics003}]{*, /}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ \meta{operand_1} |*| \meta{operand_2} \}
+% \cs{fp_eval:n} \{ \meta{operand_1} |/| \meta{operand_2} \}
+% \end{syntax}
+% Computes the product or the ratio of its two \meta{operands}. The
+% \enquote{invalid operation} exception occurs for $\infty/\infty$,
+% $0/0$, or $0*\infty$. \enquote{Division by zero} occurs when
+% dividing a finite non-zero number by $\pm 0$. \enquote{Underflow}
+% and \enquote{overflow} occur when appropriate.
+% When \meta{operand_1} is a tuple and \meta{operand_2} is a floating
+% point number, each item of \meta{operand_1} is multiplied or divided
+% by \meta{operand_2}. Multiplication also supports the case where
+% \meta{operand_1} is a floating point number and \meta{operand_2} a
+% tuple. Other combinations yield an \enquote{invalid operation}
+% exception and a \nan{} result.
+% \end{function}
+%
+% \begin{function}[tested = m3fp-basics004, label = !]{+, -, !}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |+| \meta{operand} \}
+% \cs{fp_eval:n} \{ |-| \meta{operand} \}
+% \cs{fp_eval:n} \{ |!| \meta{operand} \}
+% \end{syntax}
+% The unary |+| does nothing, the unary |-| changes the sign of the
+% \meta{operand} (for a tuple, of all its components), and
+% |!| \meta{operand} evaluates to $1$ if \meta{operand} is false
+% (is $\pm 0$) and $0$ otherwise (this is the \texttt{not}
+% boolean function). Those operations never raise exceptions.
+% \end{function}
+%
+% \begin{function}[tested = m3fp-expo001]{**, ^}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ \meta{operand_1} |**| \meta{operand_2} \}
+% \cs{fp_eval:n} \{ \meta{operand_1} |^| \meta{operand_2} \}
+% \end{syntax}
+% Raises \meta{operand_1} to the power \meta{operand_2}. This
+% operation is right associative, hence \texttt{2 ** 2 ** 3} equals
+% $2^{2^{3}} = 256$. If \meta{operand_1} is negative or $-0$ then:
+% the result's sign is $+$ if the \meta{operand_2} is infinite and
+% $(-1)^p$ if the \meta{operand_2} is $p/5^q$ with $p$, $q$ integers;
+% the result is $+0$ if
+% |abs(|\meta{operand_1}|)^|\meta{operand_2} evaluates to zero; in
+% other cases the \enquote{invalid operation} exception occurs because
+% the sign cannot be determined. \enquote{Division by zero} occurs
+% when raising $\pm 0$ to a finite strictly negative power.
+% \enquote{Underflow} and \enquote{overflow} occur when appropriate.
+% If either operand is a tuple, \enquote{invalid operation} occurs.
+% \end{function}
+%
+% \begin{function}[tested = m3fp-basics004]{abs}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |abs(| \meta{fp expr} |)| \}
+% \end{syntax}
+% Computes the absolute value of the \meta{fp expr}. If the operand is
+% a tuple, \enquote{invalid operation} occurs. This operation does
+% not raise exceptions in other cases. See also \cs{fp_abs:n}.
+% \end{function}
+%
+% \begin{function}[tested = m3fp-expo001]{exp}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |exp(| \meta{fp expr} |)| \}
+% \end{syntax}
+% Computes the exponential of the \meta{fp expr}. \enquote{Underflow}
+% and \enquote{overflow} occur when appropriate.
+% If the operand is a tuple, \enquote{invalid operation} occurs.
+% \end{function}
+%
+% \begin{function}[tested = m3fp-expo001]{fact}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |fact(| \meta{fp expr} |)| \}
+% \end{syntax}
+% Computes the factorial of the \meta{fp expr}. If the \meta{fp expr}
+% is an integer between $-0$ and $3248$ included, the result is finite
+% and correctly rounded. Larger positive integers give $+\infty$ with
+% \enquote{overflow}, while $|fact(|{+\infty}|)|=+\infty$ and
+% $|fact(nan)|=|nan|$ with no exception. All other inputs give \nan{}
+% with the \enquote{invalid operation} exception.
+% \end{function}
+%
+% \begin{function}[tested = m3fp-expo001]{ln}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |ln(| \meta{fp expr} |)| \}
+% \end{syntax}
+% Computes the natural logarithm of the \meta{fp expr}. Negative
+% numbers have no (real) logarithm, hence the \enquote{invalid
+% operation} is raised in that case, including for $\ln(-0)$.
+% \enquote{Division by zero} occurs when evaluating
+% $\ln(+0) = -\infty$. \enquote{Underflow} and \enquote{overflow}
+% occur when appropriate. If the operand is a tuple, \enquote{invalid
+% operation} occurs.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-11-03]{logb}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |logb(| \meta{fp expr} |)| \}
+% \end{syntax}
+% Determines the exponent of the \meta{fp expr}, namely the floor of
+% the base-$10$ logarithm of its absolute value. \enquote{Division by
+% zero} occurs when evaluating $\operatorname{logb}(\pm 0) = -\infty$.
+% Other special values are $\operatorname{logb}(\pm\infty)=+\infty$
+% and $\operatorname{logb}(\nan{})=\nan{}$. If the operand is a tuple
+% or is \nan{}, then \enquote{invalid operation} occurs and the result
+% is \nan{}.
+% \end{function}
+%
+% \begin{function}[tested = m3fp-logic002]{max, min}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |max(| \meta{fp expr_1} |,| \meta{fp expr_2} |,| \ldots{} |)| \}
+% \cs{fp_eval:n} \{ |min(| \meta{fp expr_1} |,| \meta{fp expr_2} |,| \ldots{} |)| \}
+% \end{syntax}
+% Evaluates each \meta{fp expr} and computes the largest (smallest) of
+% those. If any of the \meta{fp expr} is a \nan{} or tuple, the result
+% is \nan{}. If any operand is a tuple, \enquote{invalid operation}
+% occurs; these operations do not raise exceptions in other cases.
+% \end{function}
+%
+% \begin{function}
+% [tested = {m3fp-round001, m3fp-round002}, added = 2013-12-14, updated = 2015-08-08]
+% {round, trunc, ceil, floor}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |round| |(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |round| |(| \meta{fp expr_1} , \meta{fp expr_2} |)| \}
+% \cs{fp_eval:n} \{ |round| |(| \meta{fp expr_1} , \meta{fp expr_2} , \meta{fp expr_3} |)| \}
+% \end{syntax}
+% Only |round| accepts a third argument.
+% Evaluates $\meta{fp expr_1}=x$ and $\meta{fp expr_2}=n$ and $\meta{fp expr_3}=t$ then rounds
+% $x$~to $n$~places. If $n$~is an integer, this rounds~$x$ to a
+% multiple of~$10^{-n}$; if $n=+\infty$, this always yields~$x$; if
+% $n=-\infty$, this yields one of $\pm 0$, $\pm\infty$, or~\nan{}; if
+% $n=\nan{}$, this yields \nan{}; if
+% $n$~is neither $\pm\infty$ nor an integer, then an \enquote{invalid
+% operation} exception is raised. When \meta{fp expr_2} is omitted,
+% $n=0$, \emph{i.e.}, \meta{fp expr_1} is rounded to an integer. The
+% rounding direction depends on the function.
+% \begin{itemize}
+% \item |round| yields the multiple of~$10^{-n}$ closest to~$x$,
+% with ties ($x$ half-way between two such multiples) rounded
+% as follows. If $t$ is \texttt{nan} (or not given) the even
+% multiple is chosen (\enquote{ties to even}), if $t=\pm 0$ the
+% multiple closest to $0$ is chosen (\enquote{ties to zero}),
+% if $t$ is positive/negative the multiple closest to $\infty$/$-\infty$ is chosen
+% (\enquote{ties towards positive/negative infinity}).
+% \item |floor| yields the largest
+% multiple of~$10^{-n}$ smaller or equal to~$x$ (\enquote{round
+% towards negative infinity});
+% \item |ceil| yields the smallest
+% multiple of~$10^{-n}$ greater or equal to~$x$ (\enquote{round
+% towards positive infinity});
+% \item |trunc| yields a multiple
+% of~$10^{-n}$ with the same sign as~$x$ and with the largest
+% absolute value less than that of~$x$ (\enquote{round towards
+% zero}).
+% \end{itemize}
+% \enquote{Overflow} occurs if $x$~is finite and the result is
+% infinite (this can only happen if $\meta{fp expr_2}\string<-9984$).
+% If any operand is a tuple, \enquote{invalid operation} occurs.
+% \end{function}
+%
+% \begin{function}[tested = m3fp-logic002]{sign}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |sign(| \meta{fp expr} |)| \}
+% \end{syntax}
+% Evaluates the \meta{fp expr} and determines its sign: $+1$ for
+% positive numbers and for $+\infty$, $-1$ for negative numbers and
+% for $-\infty$, $\pm 0$ for $\pm 0$, and \nan{} for \nan{}.
+% If the operand is a tuple, \enquote{invalid operation} occurs.
+% This operation does not raise exceptions in other cases.
+% \end{function}
+%
+% \begin{function}[updated = 2013-11-17, tested = m3fp-trig001]
+% {sin, cos, tan, cot, csc, sec}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |sin(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |cos(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |tan(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |cot(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |csc(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |sec(| \meta{fp expr} |)| \}
+% \end{syntax}
+% Computes the sine, cosine, tangent, cotangent, cosecant, or secant
+% of the \meta{fp expr} given in radians. For arguments given in
+% degrees, see \texttt{sind}, \texttt{cosd}, \emph{etc.} Note that
+% since $\pi$~is irrational, $\operatorname{sin}(8\mathrm{pi})$ is not quite
+% zero, while its analogue $\operatorname{sind}(8\times 180)$ is exactly
+% zero. The trigonometric functions are undefined for
+% an argument of $\pm\infty$, leading to the \enquote{invalid
+% operation} exception. Additionally, evaluating tangent,
+% cotangent, cosecant, or secant at one of their poles leads to a
+% \enquote{division by zero} exception. \enquote{Underflow} and
+% \enquote{overflow} occur when appropriate.
+% If the operand is a tuple, \enquote{invalid operation} occurs.
+% \end{function}
+%
+% \begin{function}[added = 2013-11-02, tested = m3fp-trig003]
+% {sind, cosd, tand, cotd, cscd, secd}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |sind(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |cosd(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |tand(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |cotd(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |cscd(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |secd(| \meta{fp expr} |)| \}
+% \end{syntax}
+% Computes the sine, cosine, tangent, cotangent, cosecant, or secant
+% of the \meta{fp expr} given in degrees. For arguments given in
+% radians, see \texttt{sin}, \texttt{cos}, \emph{etc.} Note that
+% since $\pi$~is irrational, $\operatorname{sin}(8\mathrm{pi})$ is not quite
+% zero, while its analogue $\operatorname{sind}(8\times 180)$ is exactly
+% zero. The trigonometric functions are undefined for
+% an argument of $\pm\infty$, leading to the \enquote{invalid
+% operation} exception. Additionally, evaluating tangent,
+% cotangent, cosecant, or secant at one of their poles leads to a
+% \enquote{division by zero} exception. \enquote{Underflow} and
+% \enquote{overflow} occur when appropriate.
+% If the operand is a tuple, \enquote{invalid operation} occurs.
+% \end{function}
+%
+% \begin{function}[added = 2013-11-02, tested = m3fp-trig002]
+% {asin, acos, acsc, asec}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |asin(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |acos(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |acsc(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |asec(| \meta{fp expr} |)| \}
+% \end{syntax}
+% Computes the arcsine, arccosine, arccosecant, or arcsecant of the
+% \meta{fp expr} and returns the result in radians, in the range
+% $[-\pi/2,\pi/2]$ for \texttt{asin} and \texttt{acsc} and $[0,\pi]$
+% for \texttt{acos} and \texttt{asec}. For a result in degrees, use
+% \texttt{asind}, \emph{etc.} If the argument of |asin| or |acos|
+% lies outside the range $[-1,1]$, or the argument of |acsc| or |asec|
+% inside the range $(-1,1)$, an \enquote{invalid operation} exception
+% is raised. \enquote{Underflow} and \enquote{overflow} occur when
+% appropriate.
+% If the operand is a tuple, \enquote{invalid operation} occurs.
+% \end{function}
+%
+% \begin{function}[added = 2013-11-02, tested = m3fp-trig004]
+% {asind, acosd, acscd, asecd}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |asind(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |acosd(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |acscd(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |asecd(| \meta{fp expr} |)| \}
+% \end{syntax}
+% Computes the arcsine, arccosine, arccosecant, or arcsecant of the
+% \meta{fp expr} and returns the result in degrees, in the range
+% $[-90,90]$ for \texttt{asind} and \texttt{acscd} and $[0,180]$ for
+% \texttt{acosd} and \texttt{asecd}. For a result in radians, use
+% \texttt{asin}, \emph{etc.} If the argument of |asind| or |acosd| lies
+% outside the range $[-1,1]$, or the argument of |acscd| or |asecd|
+% inside the range $(-1,1)$, an \enquote{invalid operation} exception
+% is raised. \enquote{Underflow} and \enquote{overflow} occur when
+% appropriate.
+% If the operand is a tuple, \enquote{invalid operation} occurs.
+% \end{function}
+%
+% \begin{function}[added = 2013-11-02, tested = m3fp-trig002]
+% {atan, acot}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |atan(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |atan(| \meta{fp expr_1} , \meta{fp expr_2} |)| \}
+% \cs{fp_eval:n} \{ |acot(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |acot(| \meta{fp expr_1} , \meta{fp expr_2} |)| \}
+% \end{syntax}
+% Those functions yield an angle in radians: \texttt{atand} and
+% \texttt{acotd} are their analogs in degrees. The one-argument
+% versions compute the arctangent or arccotangent of the
+% \meta{fp expr}: arctangent takes values in the range
+% $[-\pi/2,\pi/2]$, and arccotangent in the range $[0,\pi]$. The
+% two-argument arctangent computes the angle in polar coordinates of
+% the point with Cartesian coordinates $(\meta{fp expr_2},
+% \meta{fp expr_1})$: this is the arctangent of
+% $\meta{fp expr_1}/\meta{fp expr_2}$, possibly shifted by~$\pi$
+% depending on the signs of \meta{fp expr_1} and \meta{fp expr_2}. The
+% two-argument arccotangent computes the angle in polar coordinates of
+% the point $(\meta{fp expr_1}, \meta{fp expr_2})$, equal to the
+% arccotangent of $\meta{fp expr_1}/\meta{fp expr_2}$, possibly shifted
+% by~$\pi$. Both two-argument functions take values in the wider
+% range $[-\pi,\pi]$. The ratio $\meta{fp expr_1}/\meta{fp expr_2}$
+% need not be defined for the two-argument arctangent: when both
+% expressions yield~$\pm 0$, or when both yield~$\pm\infty$, the
+% resulting angle is one of $\{\pm\pi/4,\pm 3\pi/4\}$ depending on
+% signs. The \enquote{underflow} exception can occur.
+% If any operand is a tuple, \enquote{invalid operation} occurs.
+% \end{function}
+%
+% \begin{function}[added = 2013-11-02, tested = m3fp-trig004]
+% {atand, acotd}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |atand(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |atand(| \meta{fp expr_1} , \meta{fp expr_2} |)| \}
+% \cs{fp_eval:n} \{ |acotd(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |acotd(| \meta{fp expr_1} , \meta{fp expr_2} |)| \}
+% \end{syntax}
+% Those functions yield an angle in degrees: \texttt{atan} and
+% \texttt{acot} are their analogs in radians. The one-argument
+% versions compute the arctangent or arccotangent of the
+% \meta{fp expr}: arctangent takes values in the range $[-90,90]$, and
+% arccotangent in the range $[0,180]$. The two-argument arctangent
+% computes the angle in polar coordinates of the point with Cartesian
+% coordinates $(\meta{fp expr_2}, \meta{fp expr_1})$: this is the
+% arctangent of $\meta{fp expr_1}/\meta{fp expr_2}$, possibly shifted
+% by~$180$ depending on the signs of \meta{fp expr_1} and
+% \meta{fp expr_2}. The two-argument arccotangent computes the angle
+% in polar coordinates of the point $(\meta{fp expr_1},
+% \meta{fp expr_2})$, equal to the arccotangent of
+% $\meta{fp expr_1}/\meta{fp expr_2}$, possibly shifted by~$180$. Both
+% two-argument functions take values in the wider range $[-180,180]$.
+% The ratio $\meta{fp expr_1}/\meta{fp expr_2}$ need not be defined for
+% the two-argument arctangent: when both expressions yield~$\pm 0$, or
+% when both yield~$\pm\infty$, the resulting angle is one of $\{\pm
+% 45,\pm 135\}$ depending on signs. The \enquote{underflow}
+% exception can occur.
+% If any operand is a tuple, \enquote{invalid operation} occurs.
+% \end{function}
+%
+% \begin{function}[added = 2013-12-14, tested = m3fp-basics005]{sqrt}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |sqrt(| \meta{fp expr} |)| \}
+% \end{syntax}
+% Computes the square root of the \meta{fp expr}. The \enquote{invalid
+% operation} is raised when the \meta{fp expr} is negative or is a tuple; no other
+% exception can occur. Special values yield $\sqrt{-0} = -0$,
+% $\sqrt{+0} = +0$, $\sqrt{+\infty} = +\infty$ and
+% $\sqrt{\text{\nan{}}}=\text{\nan{}}$.
+% \end{function}
+%
+% \begin{function}[added = 2016-12-05]{rand}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |rand()| \}
+% \end{syntax}
+% Produces a pseudo-random floating-point number (multiple of
+% $10^{-16}$) between $0$~included and $1$~excluded. This is not available
+% in older versions of \XeTeX{}. The random seed can be queried using
+% \cs{sys_rand_seed:} and set using \cs{sys_gset_rand_seed:n}.
+% \begin{texnote}
+% This is based on pseudo-random numbers provided by the engine's
+% primitive \tn{pdfuniformdeviate} in \pdfTeX{}, \pTeX{}, \upTeX{}
+% and \tn{uniformdeviate} in \LuaTeX{} and \XeTeX{}. The underlying code is
+% based on Metapost, which follows an additive scheme recommended in
+% Section 3.6 of \enquote{The Art of Computer Programming,
+% Volume~2}.
+%
+% While we are more careful than \tn{uniformdeviate} to preserve
+% uniformity of the underlying stream of $28$-bit pseudo-random
+% integers, these pseudo-random numbers should of course not be
+% relied upon for serious numerical computations nor cryptography.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2016-12-05]{randint}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |randint(| \meta{fp expr} |)| \}
+% \cs{fp_eval:n} \{ |randint(| \meta{fp expr_1} , \meta{fp expr_2} |)| \}
+% \end{syntax}
+% Produces a pseudo-random integer between $1$~and \meta{fp expr} or
+% between \meta{fp expr_1} and \meta{fp expr_2} inclusive. The bounds
+% must be integers in the range $(-10^{16},10^{16})$ and the first
+% must be smaller or equal to the second. See \texttt{rand} for
+% important comments on how these pseudo-random numbers are generated.
+% \end{function}
+%
+% \begin{variable}[tested = m3fp-parse001]{inf, nan}
+% The special values $+\infty$, $-\infty$, and \nan{} are represented
+% as \texttt{inf}, \texttt{-inf} and \texttt{nan} (see \cs[module = fp]{c_inf_fp},
+% \cs[module = fp]{c_minus_inf_fp} and \cs[module = fp]{c_nan_fp}).
+% \end{variable}
+%
+% \begin{variable}[tested = m3fp-parse001]{pi}
+% The value of $\pi$ (see \cs[module = fp]{c_pi_fp}).
+% \end{variable}
+%
+% \begin{variable}[tested = m3fp-parse001]{deg}
+% The value of $1^{\circ}$ in radians (see \cs[module = fp]{c_one_degree_fp}).
+% \end{variable}
+%
+% \begin{variable}[tested = m3fp-parse001]
+% {em, ex, in, pt, pc, cm, mm, dd, cc, nd, nc, bp, sp}
+% \newcommand{\unit}[1]{\ifmmode\,\fi\text{\texttt{#1}}}
+% Those units of measurement are equal to their values in \unit{pt},
+% namely
+% \begin{align*}
+% 1 \unit{in} & = 72.27 \unit{pt} \\
+% 1 \unit{pt} & = 1 \unit{pt} \\
+% 1 \unit{pc} & = 12 \unit{pt} \\
+% 1 \unit{cm} & = \frac{1}{2.54} \unit{in} = 28.45275590551181 \unit{pt} \\
+% 1 \unit{mm} & = \frac{1}{25.4} \unit{in} = 2.845275590551181 \unit{pt} \\
+% 1 \unit{dd} & = 0.376065 \unit{mm} = 1.07000856496063 \unit{pt} \\
+% 1 \unit{cc} & = 12 \unit{dd} = 12.84010277952756 \unit{pt} \\
+% 1 \unit{nd} & = 0.375 \unit{mm} = 1.066978346456693 \unit{pt} \\
+% 1 \unit{nc} & = 12 \unit{nd} = 12.80374015748031 \unit{pt} \\
+% 1 \unit{bp} & = \frac{1}{72} \unit{in} = 1.00375 \unit{pt} \\
+% 1 \unit{sp} & = 2^{-16} \unit{pt} = 1.52587890625 \times 10^{-5} \unit{pt}.
+% \end{align*}
+% The values of the (font-dependent) units \unit{em} and \unit{ex} are
+% gathered from \TeX{} when the surrounding floating point expression
+% is evaluated.
+% \end{variable}
+%
+% \begin{variable}[tested = m3fp-parse001]{true, false}
+% Other names for $1$ and $+0$.
+% \end{variable}
+%
+% \begin{function}[EXP, added = 2012-05-14, updated = 2012-07-08,
+% tested = m3fp-convert003]{\fp_abs:n}
+% \begin{syntax}
+% \cs{fp_abs:n} \Arg{fp expr}
+% \end{syntax}
+% Evaluates the \meta{fp expr} as described for
+% \cs{fp_eval:n} and leaves the absolute value of the result in the
+% input stream. If the argument is $\pm\infty$, \nan{} or a tuple,
+% \enquote{invalid operation} occurs. Within floating point
+% expressions, |abs()| can be used; it accepts $\pm\infty$ and \nan{}
+% as arguments.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2012-09-26, tested = m3fp-convert003]
+% {\fp_max:nn, \fp_min:nn}
+% \begin{syntax}
+% \cs{fp_max:nn} \Arg{fp expr_1} \Arg{fp expr_2}
+% \end{syntax}
+% Evaluates the \meta{fp exprs} as described for
+% \cs{fp_eval:n} and leaves the resulting larger (\texttt{max}) or
+% smaller (\texttt{min}) value in the input stream. If the argument
+% is a tuple, \enquote{invalid operation} occurs, but no other case
+% raises exceptions. Within floating point expressions, |max()| and
+% |min()| can be used.
+% \end{function}
+%
+% \section{Disclaimer and roadmap}
+%
+% This module may break if the escape character is among
+% |0123456789_+|, or if it receives a \TeX{} primitive conditional affected
+% by \cs{exp_not:N}.
+%
+% The following need to be done. I'll try to time-order the items.
+% \begin{itemize}
+% \item Function to count items in a tuple (and to determine if something is a tuple).
+% \item Decide what exponent range to consider.
+% \item Support signalling \texttt{nan}.
+% \item Modulo and remainder, and rounding function |quantize| (and its friends analogous to |trunc|, |ceil|, |floor|).
+% \item \cs{fp_format:nn} \Arg{fp expr} \Arg{format}, but what should
+% \meta{format} be? More general pretty printing?
+% \item Add |and|, |or|, |xor|? Perhaps under the names \texttt{all},
+% \texttt{any}, and \texttt{xor}?
+% \item Add $\log(x,b)$ for logarithm of $x$ in base $b$.
+% \item \texttt{hypot} (Euclidean length).
+% Cartesian-to-polar transform.
+% \item Hyperbolic functions \texttt{cosh}, \texttt{sinh}, \texttt{tanh}.
+% \item Inverse hyperbolics.
+% \item Base conversion, input such as \texttt{0xAB.CDEF}.
+% \item Factorial (not with |!|), gamma function.
+% \item Improve coefficients of the \texttt{sin} and \texttt{tan}
+% series.
+% \item Treat upper and lower case letters identically in
+% identifiers, and ignore underscores.
+% \item Add an |array(1,2,3)| and |i=complex(0,1)|.
+% \item Provide an experimental |map| function? Perhaps easier to
+% implement if it is a single character, |@sin(1,2)|?
+% \item Provide an |isnan| function analogue of \cs{fp_if_nan:nTF}?
+% \item Support keyword arguments?
+% \end{itemize}
+% \pkg{Pgfmath} also provides box-measurements (depth, height, width), but
+% boxes are not possible expandably.
+%
+% Bugs, and tests to add.
+% \begin{itemize}
+% \item Check that functions are monotonic when they should.
+% \item Add exceptions to |?:|, |!<=>?|, |&&|, \verb"||", and |!|.
+% \item Logarithms of numbers very close to $1$ are inaccurate.
+% \item When rounding towards $-\infty$, |\dim_to_fp:n {0pt}| should
+% return $-0$, not $+0$.
+% \item The result of $(\pm0)+(\pm0)$, of $x+(-x)$, and of $(-x)+x$
+% should depend on the rounding mode.
+% \item \texttt{0e9999999999} gives a \TeX{} \enquote{number too
+% large} error.
+% \item Subnormals are not implemented.
+% \end{itemize}
+%
+% Possible optimizations/improvements.
+% \begin{itemize}
+% \item Document that \pkg{l3trial/l3fp-types} introduces tools for
+% adding new types.
+% \item In subsection~\ref{sec:l3fp:fp-floats}, write a grammar.
+% \item It would be nice if the \texttt{parse} auxiliaries for each
+% operation were set up in the corresponding module, rather than
+% centralizing in \pkg{l3fp-parse}.
+% \item Some functions should get an |_o| ending to indicate that they
+% expand after their result.
+% \item More care should be given to distinguish expandable/restricted
+% expandable (auxiliary and internal) functions.
+% \item The code for the \texttt{ternary} set of functions is ugly.
+% \item There are many |~| missing in the doc to avoid bad line-breaks.
+% \item The algorithm for computing the logarithm of the significand
+% could be made to use a $5$ terms Taylor series instead of $10$
+% terms by taking $c = 2000/(\lfloor 200x\rfloor +1) \in [10,95]$
+% instead of $c\in [1,10]$. Also, it would then be possible to
+% simplify the computation of $t$. However, we would then have to
+% hard-code the logarithms of $44$ small integers instead of $9$.
+% \item Improve notations in the explanations of the division
+% algorithm (\pkg{l3fp-basics}).
+% \item Understand and document \cs[no-index]{__fp_basics_pack_weird_low:NNNNw}
+% and \cs[no-index]{__fp_basics_pack_weird_high:NNNNNNNNw} better. Move the
+% other \texttt{basics_pack} auxiliaries to \pkg{l3fp-aux} under a
+% better name.
+% \item Find out if underflow can really occur for trigonometric
+% functions, and redoc as appropriate.
+% \item Add bibliography. Some of Kahan's articles, some previous
+% \TeX{} fp packages, the international standards,\ldots{}
+% \item Also take into account the \enquote{inexact} exception?
+% \item Support multi-character prefix operators (\emph{e.g.}, |@/| or
+% whatever)?
+% \end{itemize}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp} implementation}
+%
+% Nothing to see here: everything is in the subfiles!
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3fparray.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3fparray.dtx
new file mode 100644
index 00000000000..24f4ac85f80
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3fparray.dtx
@@ -0,0 +1,427 @@
+% \iffalse meta-comment
+%
+%% File: l3fparray.dtx
+%
+% Copyright (C) 2018-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+%
+% \title{^^A
+% The \pkg{l3fparray} module\\ Fast global floating point arrays^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% For applications requiring heavy use of floating points, this module
+% provides arrays which can be accessed in constant time (contrast
+% \pkg{l3seq}, where access time is linear). The interface is very
+% close to that of \pkg{l3intarray}. The size of the array is fixed and
+% must be given at point of initialisation
+%
+% \section{Creating and initialising floating point array variables}
+%
+% \begin{function}[added = 2018-05-05]{\fparray_new:Nn, \fparray_new:cn}
+% \begin{syntax}
+% \cs{fparray_new:Nn} \meta{fparray~var} \Arg{size}
+% \end{syntax}
+% Evaluates the integer expression \meta{size} and allocates an
+% \meta{floating point array variable} with that number of (zero)
+% entries. The variable name should start with |\g_| because
+% assignments are always global.
+% \end{function}
+%
+% \begin{function}[added = 2018-05-05]{\fparray_gzero:N, \fparray_gzero:c}
+% \begin{syntax}
+% \cs{fparray_gzero:N} \meta{fparray~var}
+% \end{syntax}
+% Sets all entries of the \meta{floating point array variable} to
+% $+0$. Assignments are always global.
+% \end{function}
+%
+% \section{Adding data to floating point arrays}
+%
+% \begin{function}[added = 2018-05-05]{\fparray_gset:Nnn, \fparray_gset:cnn}
+% \begin{syntax}
+% \cs{fparray_gset:Nnn} \meta{fparray~var} \Arg{position} \Arg{value}
+% \end{syntax}
+% Stores the result of evaluating the floating point expression
+% \meta{value} into the \meta{floating point array variable} at the
+% (integer expression) \meta{position}. If the \meta{position} is not
+% between $1$ and the \cs{fparray_count:N}, an error occurs.
+% Assignments are always global.
+% \end{function}
+%
+% \section{Couting entries in floating point arrays}
+%
+% \begin{function}[EXP, added = 2018-05-05]{\fparray_count:N, \fparray_count:c}
+% \begin{syntax}
+% \cs{fparray_count:N} \meta{fparray~var}
+% \end{syntax}
+% Expands to the number of entries in the \meta{floating point array
+% variable}. This is performed in constant time.
+% \end{function}
+%
+% \section{Using a single entry}
+%
+% \begin{function}[EXP, added = 2018-05-05]
+% {
+% \fparray_item:Nn, \fparray_item:cn,
+% \fparray_item_to_tl:Nn, \fparray_item_to_tl:cn
+% }
+% \begin{syntax}
+% \cs{fparray_item:Nn} \meta{fparray~var} \Arg{position}
+% \end{syntax}
+% Applies \cs{fp_use:N} or \cs{fp_to_tl:N} (respectively) to the
+% floating point entry stored at the (integer expression)
+% \meta{position} in the \meta{floating point array variable}. If the
+% \meta{position} is not between $1$ and the
+% \cs{fparray_count:N} \meta{fparray~var}, an error occurs.
+% \end{function}
+%
+% \section{Floating point array conditional}
+%
+% \begin{function}[pTF, added = 2024-03-31]
+% {\fparray_if_exist:N, \fparray_if_exist:c}
+% \begin{syntax}
+% \cs{fparray_if_exist_p:N} \meta{fparray~var}
+% \cs{fparray_if_exist:NTF} \meta{fparray~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{fparray~var} is currently defined. This
+% does not check that the \meta{fparray~var} really is a floating
+% point array variable.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fparray} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% In analogy to \pkg{l3intarray} it would make sense to have
+% |<@@=fparray>|, but we need direct access to \cs{@@_parse:n} from
+% \pkg{l3fp-parse}, and a few other (less crucial) internals of the
+% \pkg{l3fp} family.
+%
+% \subsection{Allocating arrays}
+%
+% There are somewhat more than $(2^{31}-1)^2$ floating point numbers so
+% we store each floating point number as three entries in integer
+% arrays. To avoid having to multiply indices by three or to add 1 etc,
+% a floating point array is just a token list consisting of three
+% tokens: integer arrays of the same size.
+%
+% \begin{variable}{\g_@@_array_int}
+% Used to generate unique names for the three integer arrays.
+% \begin{macrocode}
+\int_new:N \g_@@_array_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_array_loop_int}
+% Used to loop in \cs{@@_array_gzero:N}.
+% \begin{macrocode}
+\int_new:N \l_@@_array_loop_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\fparray_new:Nn, \fparray_new:cn}
+% \begin{macro}{\@@_array_new:nNNN}
+% Build a three-token token list, then define all three tokens to be
+% integer arrays of the same size. No need to initialize the data:
+% the integer arrays start with zeros, and three zeros denote
+% precisely \cs{c_zero_fp}, as we want.
+% \begin{macrocode}
+\cs_new_protected:Npn \fparray_new:Nn #1#2
+ {
+ \tl_new:N #1
+ \prg_replicate:nn { 3 }
+ {
+ \int_gincr:N \g_@@_array_int
+ \exp_args:NNc \tl_gput_right:Nn #1
+ { g_@@_array_ \@@_int_to_roman:w \g_@@_array_int _intarray }
+ }
+ \exp_last_unbraced:Nfo \@@_array_new:nNNNN
+ { \int_eval:n {#2} } #1 #1
+ }
+\cs_generate_variant:Nn \fparray_new:Nn { c }
+\cs_new_protected:Npn \@@_array_new:nNNNN #1#2#3#4#5
+ {
+ \int_compare:nNnTF {#1} < 0
+ {
+ \msg_error:nnn { kernel } { negative-array-size } {#1}
+ \cs_undefine:N #1
+ \int_gsub:Nn \g_@@_array_int { 3 }
+ }
+ {
+ \intarray_new:Nn #2 {#1}
+ \intarray_new:Nn #3 {#1}
+ \intarray_new:Nn #4 {#1}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\fparray_count:N, \fparray_count:c}
+% Size of any of the intarrays, here we pick the third.
+% \begin{macrocode}
+\cs_new:Npn \fparray_count:N #1
+ {
+ \exp_after:wN \use_i:nnn
+ \exp_after:wN \intarray_count:N #1
+ }
+\cs_generate_variant:Nn \fparray_count:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Array items}
+%
+% \begin{macro}[EXP]{\@@_array_bounds:NNnTF, \@@_array_bounds_error:NNn}
+% See the \pkg{l3intarray} analogue: only names change.
+% The functions \cs{fparray_gset:Nnn} and \cs{fparray_item:Nn} share
+% bounds checking. The |T| branch is used if |#3| is within bounds of
+% the array |#2|.
+% \begin{macrocode}
+\cs_new:Npn \@@_array_bounds:NNnTF #1#2#3#4#5
+ {
+ \if_int_compare:w 1 > #3 \exp_stop_f:
+ \@@_array_bounds_error:NNn #1 #2 {#3}
+ #5
+ \else:
+ \if_int_compare:w #3 > \fparray_count:N #2 \exp_stop_f:
+ \@@_array_bounds_error:NNn #1 #2 {#3}
+ #5
+ \else:
+ #4
+ \fi:
+ \fi:
+ }
+\cs_new:Npn \@@_array_bounds_error:NNn #1#2#3
+ {
+ #1 { kernel } { out-of-bounds }
+ { \token_to_str:N #2 } {#3} { \fparray_count:N #2 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\fparray_gset:Nnn, \fparray_gset:cnn}
+% \begin{macro}
+% {
+% \@@_array_gset:NNNNww, \@@_array_gset:w,
+% \@@_array_gset_recover:Nw, \@@_array_gset_special:nnNNN,
+% \@@_array_gset_normal:w
+% }
+% Evaluate, then store exponent in one intarray, sign and $8$ digits
+% of mantissa in the next, and $8$ trailing digits in the last.
+% \begin{macrocode}
+\cs_new_protected:Npn \fparray_gset:Nnn #1#2#3
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_array_gset:NNNNww
+ \exp_after:wN #1
+ \exp_after:wN #1
+ \int_value:w \int_eval:n {#2} \exp_after:wN ;
+ \exp:w \exp_end_continue_f:w \@@_parse:n {#3}
+ }
+\cs_generate_variant:Nn \fparray_gset:Nnn { c }
+\cs_new_protected:Npn \@@_array_gset:NNNNww #1#2#3#4#5 ; #6 ;
+ {
+ \@@_array_bounds:NNnTF \msg_error:nneee #4 {#5}
+ {
+ \exp_after:wN \@@_change_func_type:NNN
+ \@@_use_i_until_s:nw #6 ;
+ \@@_array_gset:w
+ \@@_array_gset_recover:Nw
+ #6 ; {#5} #1 #2 #3
+ }
+ { }
+ }
+\cs_new_protected:Npn \@@_array_gset_recover:Nw #1#2 ;
+ {
+ \@@_error:nffn { unknown-type } { \tl_to_str:n { #2 ; } } { } { }
+ \exp_after:wN #1 \c_nan_fp
+ }
+\cs_new_protected:Npn \@@_array_gset:w \s_@@ \@@_chk:w #1#2
+ {
+ \if_case:w #1 \exp_stop_f:
+ \@@_case_return:nw { \@@_array_gset_special:nnNNN {#2} }
+ \or: \exp_after:wN \@@_array_gset_normal:w
+ \or: \@@_case_return:nw { \@@_array_gset_special:nnNNN { #2 3 } }
+ \or: \@@_case_return:nw { \@@_array_gset_special:nnNNN { 1 } }
+ \fi:
+ \s_@@ \@@_chk:w #1 #2
+ }
+\cs_new_protected:Npn \@@_array_gset_normal:w
+ \s_@@ \@@_chk:w 1 #1 #2 #3#4#5 ; #6#7#8#9
+ {
+ \__kernel_intarray_gset:Nnn #7 {#6} {#2}
+ \__kernel_intarray_gset:Nnn #8 {#6}
+ { \if_meaning:w 2 #1 3 \else: 1 \fi: #3#4 }
+ \__kernel_intarray_gset:Nnn #9 {#6} { 1 \use:nn #5 }
+ }
+\cs_new_protected:Npn \@@_array_gset_special:nnNNN #1#2#3#4#5
+ {
+ \__kernel_intarray_gset:Nnn #3 {#2} {#1}
+ \__kernel_intarray_gset:Nnn #4 {#2} {0}
+ \__kernel_intarray_gset:Nnn #5 {#2} {0}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\fparray_gzero:N, \fparray_gzero:c}
+% \begin{macrocode}
+\cs_new_protected:Npn \fparray_gzero:N #1
+ {
+ \int_zero:N \l_@@_array_loop_int
+ \prg_replicate:nn { \fparray_count:N #1 }
+ {
+ \int_incr:N \l_@@_array_loop_int
+ \exp_after:wN \@@_array_gset_special:nnNNN
+ \exp_after:wN 0
+ \exp_after:wN \l_@@_array_loop_int
+ #1
+ }
+ }
+\cs_generate_variant:Nn \fparray_gzero:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \fparray_item:Nn,
+% \fparray_item:cn,
+% \fparray_item_to_tl:Nn,
+% \fparray_item_to_tl:cn,
+% }
+% \begin{macro}[EXP]
+% {
+% \@@_array_item:NwN,
+% \@@_array_item:NNNnN,
+% \@@_array_item:N,
+% \@@_array_item:w,
+% \@@_array_item_special:w,
+% \@@_array_item_normal:w
+% }
+% \begin{macrocode}
+\cs_new:Npn \fparray_item:Nn #1#2
+ {
+ \exp_after:wN \@@_array_item:NwN
+ \exp_after:wN #1
+ \int_value:w \int_eval:n {#2} ;
+ \@@_to_decimal:w
+ }
+\cs_generate_variant:Nn \fparray_item:Nn { c }
+\cs_new:Npn \fparray_item_to_tl:Nn #1#2
+ {
+ \exp_after:wN \@@_array_item:NwN
+ \exp_after:wN #1
+ \int_value:w \int_eval:n {#2} ;
+ \@@_to_tl:w
+ }
+\cs_generate_variant:Nn \fparray_item_to_tl:Nn { c }
+\cs_new:Npn \@@_array_item:NwN #1#2 ; #3
+ {
+ \@@_array_bounds:NNnTF \msg_expandable_error:nnfff #1 {#2}
+ { \exp_after:wN \@@_array_item:NNNnN #1 {#2} #3 }
+ { \exp_after:wN #3 \c_nan_fp }
+ }
+\cs_new:Npn \@@_array_item:NNNnN #1#2#3#4
+ {
+ \exp_after:wN \@@_array_item:N
+ \int_value:w \__kernel_intarray_item:Nn #2 {#4} \exp_after:wN ;
+ \int_value:w \__kernel_intarray_item:Nn #3 {#4} \exp_after:wN ;
+ \int_value:w \__kernel_intarray_item:Nn #1 {#4} ;
+ }
+\cs_new:Npn \@@_array_item:N #1
+ {
+ \if_meaning:w 0 #1 \exp_after:wN \@@_array_item_special:w \fi:
+ \@@_array_item:w #1
+ }
+\cs_new:Npn \@@_array_item:w #1 #2#3#4#5 #6 ; 1 #7 ;
+ {
+ \exp_after:wN \@@_array_item_normal:w
+ \int_value:w \if_meaning:w #1 1 0 \else: 2 \fi: \exp_stop_f:
+ #7 ; {#2#3#4#5} {#6} ;
+ }
+\cs_new:Npn \@@_array_item_special:w #1 ; #2 ; #3 ; #4
+ {
+ \exp_after:wN #4
+ \exp:w \exp_end_continue_f:w
+ \if_case:w #3 \exp_stop_f:
+ \exp_after:wN \c_zero_fp
+ \or: \exp_after:wN \c_nan_fp
+ \or: \exp_after:wN \c_minus_zero_fp
+ \or: \exp_after:wN \c_inf_fp
+ \else: \exp_after:wN \c_minus_inf_fp
+ \fi:
+ }
+\cs_new:Npn \@@_array_item_normal:w #1 #2#3#4#5 #6 ; #7 ; #8 ; #9
+ { #9 \s_@@ \@@_chk:w 1 #1 {#8} #7 {#2#3#4#5} {#6} ; }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\fparray_if_exist:N, \fparray_if_exist:c}
+% Copies of the \texttt{cs} functions defined in \pkg{l3basics}.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \fparray_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \fparray_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3int.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3int.dtx
new file mode 100644
index 00000000000..a1c9458436f
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3int.dtx
@@ -0,0 +1,2679 @@
+% \iffalse meta-comment
+%
+%% File: l3int.dtx
+%
+% Copyright (C) 1990-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3int} module\\ Integers^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% Calculation and comparison of integer values can be carried out
+% using literal numbers, \texttt{int} registers, constants and
+% integers stored in token list variables. The standard operators
+% \texttt{+}, \texttt{-}, \texttt{/} and \texttt{*} and
+% parentheses can be used within such expressions to carry
+% arithmetic operations. This module carries out these functions
+% on \emph{integer expressions} (\enquote{\meta{int expr}}).
+%
+% \section{Integer expressions}
+%
+% Throughout this module, (almost) all \texttt{n}-type argument allow
+% for an \meta{intexpr} argument with the following syntax. The
+% \meta{integer expression} should consist,
+% after expansion, of \texttt{+}, \texttt{-}, \texttt{*}, \texttt{/},
+% \texttt{(}, \texttt{)} and of course integer operands. The result
+% is calculated by applying standard mathematical rules with the
+% following peculiarities:
+% \begin{itemize}
+% \item \texttt{/} denotes division rounded to the closest integer with
+% ties rounded away from zero;
+% \item there is an error and the overall expression evaluates to zero
+% whenever the absolute value of any intermediate result exceeds
+% $2^{31}-1$, except in the case of scaling operations
+% $a$\texttt{*}$b$\texttt{/}$c$, for which $a$\texttt{*}$b$ may be
+% arbitrarily large (but the operands $a$, $b$, $c$ are still
+% constrained to an absolute value at most $2^{31}-1$);
+% \item parentheses may not appear after unary \texttt{+} or
+% \texttt{-}, namely placing \texttt{+(} or \texttt{-(} at the start
+% of an expression or after \texttt{+}, \texttt{-}, \texttt{*},
+% \texttt{/} or~\texttt{(} leads to an error.
+% \end{itemize}
+% Each integer operand can be either an integer variable (with no need
+% for \cs{int_use:N}) or an integer denotation. For example both
+% \begin{verbatim}
+% \int_show:n { 5 + 4 * 3 - ( 3 + 4 * 5 ) }
+% \end{verbatim}
+% and
+% \begin{verbatim}
+% \tl_new:N \l_my_tl
+% \tl_set:Nn \l_my_tl { 5 }
+% \int_new:N \l_my_int
+% \int_set:Nn \l_my_int { 4 }
+% \int_show:n { \l_my_tl + \l_my_int * 3 - ( 3 + 4 * 5 ) }
+% \end{verbatim}
+% show the same result $-6$ because \cs[no-index]{l_my_tl} expands to
+% the integer denotation~|5| while the integer variable \cs[no-index]{l_my_int}
+% takes the value~$4$. As the \meta{integer expression} is fully
+% expanded from left to right during evaluation, fully expandable and
+% restricted-expandable functions can both be used, and \cs{exp_not:n}
+% and its variants have no effect while \cs{exp_not:N} may incorrectly
+% interrupt the expression.
+% \begin{texnote}
+% Exactly two expansions are needed to evaluate \cs{int_eval:n}.
+% The result is \emph{not} an \meta{internal integer}, and therefore
+% should be terminated by a space if used in \cs{int_value:w} or in
+% a \TeX{}-style integer assignment.
+%
+% As all \TeX{} integers, integer operands can also be:
+% \tn{value}\Arg{\LaTeXe{} counter}; dimension or skip variables,
+% converted to integers in~\texttt{sp}; the character code of some
+% character given as \texttt{`}\meta{char} or
+% \texttt{`\textbackslash}\meta{char}; octal numbers given as
+% \texttt{'} followed by digits from \texttt{0} to \texttt{7}; or
+% hexadecimal numbers given as |"| followed by digits and upper case
+% letters from \texttt{A} to~\texttt{F}.
+% \end{texnote}
+%
+% \begin{function}[EXP]{\int_eval:n}
+% \begin{syntax}
+% \cs{int_eval:n} \Arg{int expr}
+% \end{syntax}
+% Evaluates the \meta{int expr} and leaves the result in the
+% input stream as an integer denotation: for positive results an
+% explicit sequence of decimal digits not starting with~\texttt{0},
+% for negative results \texttt{-}~followed by such a sequence, and
+% \texttt{0}~for zero. The \meta{int expr} should consist,
+% after expansion, of \texttt{+}, \texttt{-}, \texttt{*}, \texttt{/},
+% \texttt{(}, \texttt{)} and of course integer operands. The result
+% is calculated by applying standard mathematical rules with the
+% following peculiarities:
+% \begin{itemize}
+% \item \texttt{/} denotes division rounded to the closest integer with
+% ties rounded away from zero;
+% \item there is an error and the overall expression evaluates to zero
+% whenever the absolute value of any intermediate result exceeds
+% $2^{31}-1$, except in the case of scaling operations
+% $a$\texttt{*}$b$\texttt{/}$c$, for which $a$\texttt{*}$b$ may be
+% arbitrarily large;
+% \item parentheses may not appear after unary \texttt{+} or
+% \texttt{-}, namely placing \texttt{+(} or \texttt{-(} at the start
+% of an expression or after \texttt{+}, \texttt{-}, \texttt{*},
+% \texttt{/} or~\texttt{(} leads to an error.
+% \end{itemize}
+% Each integer operand can be either an integer variable (with no need
+% for \cs{int_use:N}) or an integer denotation. For example both
+% \begin{verbatim}
+% \int_eval:n { 5 + 4 * 3 - ( 3 + 4 * 5 ) }
+% \end{verbatim}
+% and
+% \begin{verbatim}
+% \tl_new:N \l_my_tl
+% \tl_set:Nn \l_my_tl { 5 }
+% \int_new:N \l_my_int
+% \int_set:Nn \l_my_int { 4 }
+% \int_eval:n { \l_my_tl + \l_my_int * 3 - ( 3 + 4 * 5 ) }
+% \end{verbatim}
+% evaluate to $-6$ because \cs[no-index]{l_my_tl} expands to the
+% integer denotation~|5|. As the \meta{int expr} is fully
+% expanded from left to right during evaluation, fully expandable and
+% restricted-expandable functions can both be used, and \cs{exp_not:n}
+% and its variants have no effect while \cs{exp_not:N} may incorrectly
+% interrupt the expression.
+% \begin{texnote}
+% Exactly two expansions are needed to evaluate \cs{int_eval:n}.
+% The result is \emph{not} an \meta{internal integer}, and therefore
+% requires suitable termination if used in a \TeX{}-style integer
+% assignment.
+%
+% As all \TeX{} integers, integer operands can also be dimension or
+% skip variables, converted to integers in~\texttt{sp}, or octal
+% numbers given as \texttt{'} followed by digits other than
+% \texttt{8} and \texttt{9}, or hexadecimal numbers given as
+% |"| followed by digits or upper case letters from
+% \texttt{A} to~\texttt{F}, or the character code of some character
+% or one-character control sequence, given as \texttt{`}\meta{char}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-03-30]{\int_eval:w}
+% \begin{syntax}
+% \cs{int_eval:w} \meta{int expr}
+% \end{syntax}
+% Evaluates the \meta{int expr} as described for
+% \cs{int_eval:n}. The end of the expression is the first token
+% encountered that cannot form part of such an expression. If that
+% token is \cs{scan_stop:} it is removed, otherwise not. Spaces do
+% \emph{not} terminate the expression. However, spaces terminate
+% explict integers, and this may terminate the expression: for
+% instance, \cs{int_eval:w} \verb*|1 + 1 9| (with explicit space
+% tokens inserted using |~| in a code setting) expands to \texttt{29}
+% since the digit~\texttt{9} is not part of the expression. Expansion
+% details, etc., are as given for \cs{int_eval:n}.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-11-03]{\int_sign:n}
+% \begin{syntax}
+% \cs{int_sign:n} \Arg{int expr}
+% \end{syntax}
+% Evaluates the \meta{int expr} then leaves $1$ or $0$ or
+% $-1$ in the input stream according to the sign of the result.
+% \end{function}
+%
+% \begin{function}[EXP, updated = 2012-09-26]{\int_abs:n}
+% \begin{syntax}
+% \cs{int_abs:n} \Arg{int expr}
+% \end{syntax}
+% Evaluates the \meta{int expr} as described for
+% \cs{int_eval:n} and leaves the absolute value of the result in
+% the input stream as an \meta{integer denotation} after two
+% expansions.
+% \end{function}
+%
+% \begin{function}[EXP, updated = 2012-09-26]{\int_div_round:nn}
+% \begin{syntax}
+% \cs{int_div_round:nn} \Arg{int expr_1} \Arg{int expr_2}
+% \end{syntax}
+% Evaluates the two \meta{int expr}s as described earlier,
+% then divides the first value by the second, and rounds the result
+% to the closest integer. Ties are rounded away from zero.
+% Note that this is identical to using
+% |/| directly in an \meta{int expr}. The result is left in
+% the input stream as an \meta{integer denotation} after two expansions.
+% \end{function}
+%
+% \begin{function}[EXP, updated = 2012-02-09]{\int_div_truncate:nn}
+% \begin{syntax}
+% \cs{int_div_truncate:nn} \Arg{int expr_1} \Arg{int expr_2}
+% \end{syntax}
+% Evaluates the two \meta{int expr}s as described earlier,
+% then divides the first value by the second, and rounds the result
+% towards zero. Note that division using |/|
+% rounds to the closest integer instead.
+% The result is left in the input stream as an
+% \meta{integer denotation} after two expansions.
+% \end{function}
+%
+% \begin{function}[EXP, updated = 2012-09-26]{\int_max:nn, \int_min:nn}
+% \begin{syntax}
+% \cs{int_max:nn} \Arg{int expr_1} \Arg{int expr_2}
+% \cs{int_min:nn} \Arg{int expr_1} \Arg{int expr_2}
+% \end{syntax}
+% Evaluates the \meta{int expr}s as described for
+% \cs{int_eval:n} and leaves either the larger or smaller value
+% in the input stream as an \meta{integer denotation} after two
+% expansions.
+% \end{function}
+%
+% \begin{function}[EXP, updated = 2012-09-26]{\int_mod:nn}
+% \begin{syntax}
+% \cs{int_mod:nn} \Arg{int expr_1} \Arg{int expr_2}
+% \end{syntax}
+% Evaluates the two \meta{int expr}s as described earlier,
+% then calculates the integer remainder of dividing the first
+% expression by the second. This is obtained by subtracting
+% \cs{int_div_truncate:nn} \Arg{int expr_1} \Arg{int expr_2} times
+% \meta{int expr_2} from \meta{int expr_1}. Thus, the result has the
+% same sign as \meta{int expr_1} and its absolute value is strictly
+% less than that of \meta{int expr_2}. The result is left in the input
+% stream as an \meta{integer denotation} after two expansions.
+% \end{function}
+%
+% \section{Creating and initialising integers}
+%
+% \begin{function}{\int_new:N, \int_new:c}
+% \begin{syntax}
+% \cs{int_new:N} \meta{integer}
+% \end{syntax}
+% Creates a new \meta{integer} or raises an error if the name is
+% already taken. The declaration is global. The \meta{integer} is
+% initially equal to $0$.
+% \end{function}
+%
+% \begin{function}[updated = 2011-10-22]{\int_const:Nn, \int_const:cn}
+% \begin{syntax}
+% \cs{int_const:Nn} \meta{integer} \Arg{int expr}
+% \end{syntax}
+% Creates a new constant \meta{integer} or raises an error if the name
+% is already taken. The value of the \meta{integer} is set
+% globally to the \meta{int expr}.
+% \end{function}
+%
+% \begin{function}{\int_zero:N, \int_zero:c, \int_gzero:N, \int_gzero:c}
+% \begin{syntax}
+% \cs{int_zero:N} \meta{integer}
+% \end{syntax}
+% Sets \meta{integer} to $0$.
+% \end{function}
+%
+% \begin{function}[added = 2011-12-13]
+% {\int_zero_new:N, \int_zero_new:c, \int_gzero_new:N, \int_gzero_new:c}
+% \begin{syntax}
+% \cs{int_zero_new:N} \meta{integer}
+% \end{syntax}
+% Ensures that the \meta{integer} exists globally by applying
+% \cs{int_new:N} if necessary, then applies
+% \cs[index=int_zero:N]{int_(g)zero:N} to leave
+% the \meta{integer} set to zero.
+% \end{function}
+%
+% \begin{function}
+% {
+% \int_set_eq:NN, \int_set_eq:cN, \int_set_eq:Nc, \int_set_eq:cc,
+% \int_gset_eq:NN, \int_gset_eq:cN, \int_gset_eq:Nc, \int_gset_eq:cc
+% }
+% \begin{syntax}
+% \cs{int_set_eq:NN} \meta{integer_1} \meta{integer_2}
+% \end{syntax}
+% Sets the content of \meta{integer_1} equal to that of
+% \meta{integer_2}.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added=2012-03-03]
+% {\int_if_exist:N, \int_if_exist:c}
+% \begin{syntax}
+% \cs{int_if_exist_p:N} \meta{int}
+% \cs{int_if_exist:NTF} \meta{int} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{int} is currently defined. This does not
+% check that the \meta{int} really is an integer variable.
+% \end{function}
+%
+% \section{Setting and incrementing integers}
+%
+% \begin{function}[updated = 2011-10-22]
+% {\int_add:Nn, \int_add:cn, \int_gadd:Nn, \int_gadd:cn}
+% \begin{syntax}
+% \cs{int_add:Nn} \meta{integer} \Arg{int expr}
+% \end{syntax}
+% Adds the result of the \meta{int expr} to the current
+% content of the \meta{integer}.
+% \end{function}
+%
+% \begin{function}{\int_decr:N, \int_decr:c, \int_gdecr:N, \int_gdecr:c}
+% \begin{syntax}
+% \cs{int_decr:N} \meta{integer}
+% \end{syntax}
+% Decreases the value stored in \meta{integer} by $1$.
+% \end{function}
+%
+% \begin{function}{\int_incr:N, \int_incr:c, \int_gincr:N, \int_gincr:c}
+% \begin{syntax}
+% \cs{int_incr:N} \meta{integer}
+% \end{syntax}
+% Increases the value stored in \meta{integer} by $1$.
+% \end{function}
+%
+% \begin{function}[updated = 2011-10-22]
+% {\int_set:Nn, \int_set:cn, \int_gset:Nn, \int_gset:cn}
+% \begin{syntax}
+% \cs{int_set:Nn} \meta{integer} \Arg{int expr}
+% \end{syntax}
+% Sets \meta{integer} to the value of \meta{int expr},
+% which must evaluate to an integer (as described for
+% \cs{int_eval:n}).
+% \end{function}
+%
+% \begin{function}[updated = 2011-10-22]
+% {\int_sub:Nn, \int_sub:cn, \int_gsub:Nn, \int_gsub:cn}
+% \begin{syntax}
+% \cs{int_sub:Nn} \meta{integer} \Arg{int expr}
+% \end{syntax}
+% Subtracts the result of the \meta{int expr} from the
+% current content of the \meta{integer}.
+% \end{function}
+%
+% \section{Using integers}
+%
+% \begin{function}[updated = 2011-10-22, EXP]{\int_use:N, \int_use:c}
+% \begin{syntax}
+% \cs{int_use:N} \meta{integer}
+% \end{syntax}
+% Recovers the content of an \meta{integer} and places it directly
+% in the input stream. An error is raised if the variable does
+% not exist or if it is invalid. Can be omitted in places where an
+% \meta{integer} is required (such as in the first and third arguments
+% of \cs{int_compare:nNnTF}).
+% \begin{texnote}
+% \cs{int_use:N} is the \TeX{} primitive \tn{the}: this is one of
+% several \LaTeX3 names for this primitive.
+% \end{texnote}
+% \end{function}
+%
+% \section{Integer expression conditionals}
+%
+% \begin{function}[EXP,pTF]{\int_compare:nNn}
+% \begin{syntax}
+% \cs{int_compare_p:nNn} \Arg{int expr_1} \meta{relation} \Arg{int expr_2} \\
+% \cs{int_compare:nNnTF}
+% ~~\Arg{int expr_1} \meta{relation} \Arg{int expr_2}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% This function first evaluates each of the \meta{int expr}s
+% as described for \cs{int_eval:n}. The two results are then
+% compared using the \meta{relation}:
+% \begin{center}
+% \begin{tabular}{ll}
+% Equal & |=| \\
+% Greater than & |>| \\
+% Less than & |<| \\
+% \end{tabular}
+% \end{center}
+% This function is less flexible than \cs{int_compare:nTF} but around
+% $5$~times faster.
+% \end{function}
+%
+% \begin{function}[updated = 2013-01-13, EXP, pTF]{\int_compare:n}
+% \begin{syntax}
+% \cs{int_compare_p:n} \\
+% ~~\{ \\
+% ~~~~\meta{int expr_1} \meta{relation_1} \\
+% ~~~~\ldots{} \\
+% ~~~~\meta{int expr_N} \meta{relation_N} \\
+% ~~~~\meta{int expr_{N+1}} \\
+% ~~\} \\
+% \cs{int_compare:nTF}
+% ~~\{ \\
+% ~~~~\meta{int expr_1} \meta{relation_1} \\
+% ~~~~\ldots{} \\
+% ~~~~\meta{int expr_N} \meta{relation_N} \\
+% ~~~~\meta{int expr_{N+1}} \\
+% ~~\} \\
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% This function evaluates the \meta{int expr}s as described
+% for \cs{int_eval:n} and compares consecutive result using the
+% corresponding \meta{relation}, namely it compares \meta{int expr_1}
+% and \meta{int expr_2} using the \meta{relation_1}, then
+% \meta{int expr_2} and \meta{int expr_3} using the \meta{relation_2},
+% until finally comparing \meta{int expr_N} and \meta{int expr_{N+1}}
+% using the \meta{relation_N}. The test yields \texttt{true} if all
+% comparisons are \texttt{true}. Each \meta{int expr} is
+% evaluated only once, and the evaluation is lazy, in the sense that
+% if one comparison is \texttt{false}, then no other \meta{integer
+% expression} is evaluated and no other comparison is performed.
+% The \meta{relations} can be any of the following:
+% \begin{center}
+% \begin{tabular}{ll}
+% Equal & |=| or |==| \\
+% Greater than or equal to & |>=| \\
+% Greater than & |>| \\
+% Less than or equal to & |<=| \\
+% Less than & |<| \\
+% Not equal & |!=| \\
+% \end{tabular}
+% \end{center}
+% This function is more flexible than \cs{int_compare:nNnTF} but
+% around $5$~times slower.
+% \end{function}
+%
+% \begin{function}[added = 2013-07-24, EXP, noTF]{\int_case:nn}
+% \begin{syntax}
+% \cs{int_case:nnTF} \Arg{test int expr} \\
+% ~~|{| \\
+% ~~~~\Arg{int expr case_1} \Arg{code case_1} \\
+% ~~~~\Arg{int expr case_2} \Arg{code case_2} \\
+% ~~~~\ldots \\
+% ~~~~\Arg{int expr case_n} \Arg{code case_n} \\
+% ~~|}| \\
+% ~~\Arg{true code}
+% ~~\Arg{false code}
+% \end{syntax}
+% This function evaluates the \meta{test int expr} and
+% compares this in turn to each of the
+% \meta{int expr cases}. If the two are equal 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 function
+% \cs{int_case:nn}, which does nothing if there is no match, is also
+% available. For example
+% \begin{verbatim}
+% \int_case:nnF
+% { 2 * 5 }
+% {
+% { 5 } { Small }
+% { 4 + 6 } { Medium }
+% { -2 * 10 } { Negative }
+% }
+% { No idea! }
+% \end{verbatim}
+% leaves \enquote{\texttt{Medium}} in the input stream.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\int_if_even:n, \int_if_odd:n}
+% \begin{syntax}
+% \cs{int_if_odd_p:n} \Arg{int expr}
+% \cs{int_if_odd:nTF} \Arg{int expr}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% This function first evaluates the \meta{int expr}
+% as described for \cs{int_eval:n}. It then evaluates if this
+% is odd or even, as appropriate.
+% \end{function}
+%
+% \begin{function}[EXP,pTF, added = 2023-05-17]{\int_if_zero:n}
+% \begin{syntax}
+% \cs{int_if_zero_p:n} \Arg{int expr}
+% \cs{int_if_zero:nTF} \Arg{int expr}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% This function first evaluates the \meta{int expr}
+% as described for \cs{int_eval:n}. It then evaluates if this
+% is zero or not.
+% \end{function}
+%
+% \section{Integer expression loops}
+%
+% \begin{function}[rEXP]{\int_do_until:nNnn}
+% \begin{syntax}
+% \cs{int_do_until:nNnn} \Arg{int expr_1} \meta{relation} \Arg{int expr_2} \Arg{code}
+% \end{syntax}
+% Places the \meta{code} in the input stream for \TeX{} to process, and
+% then evaluates the relationship between the two
+% \meta{int expr}s as described for \cs{int_compare:nNnTF}.
+% If the test is \texttt{false} then the \meta{code} is inserted
+% into the input stream again and a loop occurs until the
+% \meta{relation} is \texttt{true}.
+% \end{function}
+%
+% \begin{function}[rEXP]{\int_do_while:nNnn}
+% \begin{syntax}
+% \cs{int_do_while:nNnn} \Arg{int expr_1} \meta{relation} \Arg{int expr_2} \Arg{code}
+% \end{syntax}
+% Places the \meta{code} in the input stream for \TeX{} to process, and
+% then evaluates the relationship between the two
+% \meta{int expr}s as described for \cs{int_compare:nNnTF}.
+% If the test is \texttt{true} then the \meta{code} is inserted
+% into the input stream again and a loop occurs until the
+% \meta{relation} is \texttt{false}.
+% \end{function}
+%
+% \begin{function}[rEXP]{\int_until_do:nNnn}
+% \begin{syntax}
+% \cs{int_until_do:nNnn} \Arg{int expr_1} \meta{relation} \Arg{int expr_2} \Arg{code}
+% \end{syntax}
+% Evaluates the relationship between the two \meta{int expr}s
+% as described for \cs{int_compare:nNnTF}, and then places the
+% \meta{code} in the input stream if the \meta{relation} is
+% \texttt{false}. After the \meta{code} has been processed by \TeX{} the
+% test is repeated, and a loop occurs until the test is
+% \texttt{true}.
+% \end{function}
+%
+% \begin{function}[rEXP]{\int_while_do:nNnn}
+% \begin{syntax}
+% \cs{int_while_do:nNnn} \Arg{int expr_1} \meta{relation} \Arg{int expr_2} \Arg{code}
+% \end{syntax}
+% Evaluates the relationship between the two \meta{int expr}s
+% as described for \cs{int_compare:nNnTF}, and then places the
+% \meta{code} in the input stream if the \meta{relation} is
+% \texttt{true}. After the \meta{code} has been processed by \TeX{} the
+% test is repeated, and a loop occurs until the test is
+% \texttt{false}.
+% \end{function}
+%
+% \begin{function}[updated = 2013-01-13, rEXP]{\int_do_until:nn}
+% \begin{syntax}
+% \cs{int_do_until:nn} \Arg{integer relation} \Arg{code}
+% \end{syntax}
+% Places the \meta{code} in the input stream for \TeX{} to process, and
+% then evaluates the \meta{integer relation}
+% as described for \cs{int_compare:nTF}.
+% If the test is \texttt{false} then the \meta{code} is inserted
+% into the input stream again and a loop occurs until the
+% \meta{relation} is \texttt{true}.
+% \end{function}
+%
+% \begin{function}[updated = 2013-01-13, rEXP]{\int_do_while:nn}
+% \begin{syntax}
+% \cs{int_do_while:nn} \Arg{integer relation} \Arg{code}
+% \end{syntax}
+% Places the \meta{code} in the input stream for \TeX{} to process, and
+% then evaluates the \meta{integer relation}
+% as described for \cs{int_compare:nTF}.
+% If the test is \texttt{true} then the \meta{code} is inserted
+% into the input stream again and a loop occurs until the
+% \meta{relation} is \texttt{false}.
+% \end{function}
+%
+% \begin{function}[updated = 2013-01-13, rEXP]{\int_until_do:nn}
+% \begin{syntax}
+% \cs{int_until_do:nn} \Arg{integer relation} \Arg{code}
+% \end{syntax}
+% Evaluates the \meta{integer relation}
+% as described for \cs{int_compare:nTF}, and then places the
+% \meta{code} in the input stream if the \meta{relation} is
+% \texttt{false}. After the \meta{code} has been processed by \TeX{} the
+% test is repeated, and a loop occurs until the test is
+% \texttt{true}.
+% \end{function}
+%
+% \begin{function}[updated = 2013-01-13, rEXP]{\int_while_do:nn}
+% \begin{syntax}
+% \cs{int_while_do:nn} \Arg{integer relation} \Arg{code}
+% \end{syntax}
+% Evaluates the \meta{integer relation}
+% as described for \cs{int_compare:nTF}, and then places the
+% \meta{code} in the input stream if the \meta{relation} is
+% \texttt{true}. After the \meta{code} has been processed by \TeX{} the
+% test is repeated, and a loop occurs until the test is
+% \texttt{false}.
+% \end{function}
+%
+% \section{Integer step functions}
+%
+% \begin{function}[added = 2012-06-04, updated = 2018-04-22, rEXP]
+% {\int_step_function:nN, \int_step_function:nnN, \int_step_function:nnnN}
+% \begin{syntax}
+% \cs{int_step_function:nN} \Arg{final value} \meta{function}
+% \cs{int_step_function:nnN} \Arg{initial value} \Arg{final value} \meta{function}
+% \cs{int_step_function:nnnN} \Arg{initial value} \Arg{step} \Arg{final value} \meta{function}
+% \end{syntax}
+% This function first evaluates the \meta{initial value}, \meta{step}
+% and \meta{final value}, all of which should be integer expressions.
+% The \meta{function} is then placed in front of each \meta{value}
+% from the \meta{initial value} to the \meta{final value} in turn
+% (using \meta{step} between each \meta{value}). The \meta{step} must
+% be non-zero. If the \meta{step} is positive, the loop stops when
+% the \meta{value} becomes larger than the \meta{final value}. If the
+% \meta{step} is negative, the loop stops when the \meta{value}
+% becomes smaller than the \meta{final value}. The \meta{function}
+% should absorb one numerical argument. For example
+% \begin{verbatim}
+% \cs_set:Npn \my_func:n #1 { [I~saw~#1] \quad }
+% \int_step_function:nnnN { 1 } { 1 } { 5 } \my_func:n
+% \end{verbatim}
+% would print
+% \begin{quote}
+% [I saw 1] \quad
+% [I saw 2] \quad
+% [I saw 3] \quad
+% [I saw 4] \quad
+% [I saw 5] \quad
+% \end{quote}
+%
+% The functions \cs{int_step_function:nN} and \cs{int_step_function:nnN}
+% both use a fixed \meta{step} of $1$, and in the case of
+% \cs{int_step_function:nN} the \meta{initial value} is also fixed as
+% $1$. These functions are provided as simple short-cuts for code clarity.
+% \end{function}
+%
+% \begin{function}[added = 2012-06-04, updated = 2018-04-22]
+% {\int_step_inline:nn, \int_step_inline:nnn, \int_step_inline:nnnn}
+% \begin{syntax}
+% \cs{int_step_inline:nn} \Arg{final value} \Arg{code}
+% \cs{int_step_inline:nnn} \Arg{initial value} \Arg{final value} \Arg{code}
+% \cs{int_step_inline:nnnn} \Arg{initial value} \Arg{step} \Arg{final value} \Arg{code}
+% \end{syntax}
+% This function first evaluates the \meta{initial value}, \meta{step}
+% and \meta{final value}, all of which should be integer expressions.
+% Then for each \meta{value} from the \meta{initial value} to the
+% \meta{final value} in turn (using \meta{step} between each
+% \meta{value}), the \meta{code} is inserted into the input stream
+% with |#1| replaced by the current \meta{value}. Thus the
+% \meta{code} should define a function of one argument~(|#1|).
+%
+% The functions \cs{int_step_inline:nn} and \cs{int_step_inline:nnn}
+% both use a fixed \meta{step} of $1$, and in the case of
+% \cs{int_step_inline:nn} the \meta{initial value} is also fixed as
+% $1$. These functions are provided as simple short-cuts for code clarity.
+% \end{function}
+%
+% \begin{function}[added = 2012-06-04, updated = 2018-04-22]
+% {\int_step_variable:nNn, \int_step_variable:nnNn, \int_step_variable:nnnNn}
+% \begin{syntax}
+% \cs{int_step_variable:nNn} \Arg{final value} \meta{tl~var} \Arg{code}
+% \cs{int_step_variable:nnNn} \Arg{initial value} \Arg{final value} \meta{tl~var} \Arg{code}
+% \cs{int_step_variable:nnnNn} \Arg{initial value} \Arg{step} \Arg{final value} \meta{tl~var} \Arg{code}
+% \end{syntax}
+% This function first evaluates the \meta{initial value}, \meta{step}
+% and \meta{final value}, all of which should be integer expressions.
+% Then for each \meta{value} from the \meta{initial value} to the
+% \meta{final value} in turn (using \meta{step} between each
+% \meta{value}), the \meta{code} is inserted into the input stream,
+% with the \meta{tl~var} defined as the current \meta{value}. Thus
+% the \meta{code} should make use of the \meta{tl~var}.
+%
+% The functions \cs{int_step_variable:nNn} and \cs{int_step_variable:nnNn}
+% both use a fixed \meta{step} of $1$, and in the case of
+% \cs{int_step_variable:nNn} the \meta{initial value} is also fixed as
+% $1$. These functions are provided as simple short-cuts for code clarity.
+% \end{function}
+%
+% \section{Formatting integers}
+%
+% Integers can be placed into the output stream with formatting. These
+% conversions apply to any integer expressions.
+%
+% \begin{function}[updated = 2011-10-22, EXP]{\int_to_arabic:n, \int_to_arabic:v}
+% \begin{syntax}
+% \cs{int_to_arabic:n} \Arg{int expr}
+% \end{syntax}
+% Places the value of the \meta{int expr} in the input
+% stream as digits, with category code $12$ (other).
+% \end{function}
+%
+% \begin{function}[updated = 2011-09-17, EXP]{\int_to_alph:n, \int_to_Alph:n}
+% \begin{syntax}
+% \cs{int_to_alph:n} \Arg{int expr}
+% \end{syntax}
+% Evaluates the \meta{int expr} and converts the result
+% into a series of letters, which are then left in the input stream.
+% The conversion rule uses the $26$ letters of the English
+% alphabet, in order, adding letters when necessary to increase the total
+% possible range of representable numbers. Thus
+% \begin{verbatim}
+% \int_to_alph:n { 1 }
+% \end{verbatim}
+% places |a| in the input stream,
+% \begin{verbatim}
+% \int_to_alph:n { 26 }
+% \end{verbatim}
+% is represented as |z| and
+% \begin{verbatim}
+% \int_to_alph:n { 27 }
+% \end{verbatim}
+% is converted to |aa|. For conversions using other alphabets, use
+% \cs{int_to_symbols:nnn} to define an alphabet-specific
+% function. The basic \cs{int_to_alph:n} and \cs{int_to_Alph:n}
+% functions should not be modified.
+% The resulting tokens are digits with category code $12$ (other) and
+% letters with category code $11$ (letter).
+% \end{function}
+%
+% \begin{function}[updated = 2011-09-17, EXP]{\int_to_symbols:nnn}
+% \begin{syntax}
+% \cs{int_to_symbols:nnn}
+% ~~\Arg{int expr} \Arg{total symbols}
+% ~~\Arg{value to symbol mapping}
+% \end{syntax}
+% This is the low-level function for conversion of an
+% \meta{int expr} into a symbolic form (often
+% letters). The \meta{total symbols} available should be given
+% as an integer expression. Values are actually converted to symbols
+% according to the \meta{value to symbol mapping}. This should be given
+% as \meta{total symbols} pairs of entries, a number and the
+% appropriate symbol. Thus the \cs{int_to_alph:n} function is defined
+% as
+% \begin{verbatim}
+% \cs_new:Npn \int_to_alph:n #1
+% {
+% \int_to_symbols:nnn {#1} { 26 }
+% {
+% { 1 } { a }
+% { 2 } { b }
+% ...
+% { 26 } { z }
+% }
+% }
+% \end{verbatim}
+% \end{function}
+%
+% \begin{function}[added = 2014-02-11, EXP]{\int_to_bin:n}
+% \begin{syntax}
+% \cs{int_to_bin:n} \Arg{int expr}
+% \end{syntax}
+% Calculates the value of the \meta{int expr} and places
+% the binary representation of the result in the input stream.
+% \end{function}
+%
+% \begin{function}[added = 2014-02-11, EXP]{\int_to_hex:n, \int_to_Hex:n}
+% \begin{syntax}
+% \cs{int_to_hex:n} \Arg{int expr}
+% \end{syntax}
+% Calculates the value of the \meta{int expr} and places
+% the hexadecimal (base~$16$) representation of the result in the
+% input stream. Letters are used for digits beyond~$9$: lower
+% case letters for \cs{int_to_hex:n} and upper case ones for
+% \cs{int_to_Hex:n}.
+% The resulting tokens are digits with category code $12$ (other) and
+% letters with category code $11$ (letter).
+% \end{function}
+%
+% \begin{function}[added = 2014-02-11, EXP]{\int_to_oct:n}
+% \begin{syntax}
+% \cs{int_to_oct:n} \Arg{int expr}
+% \end{syntax}
+% Calculates the value of the \meta{int expr} and places
+% the octal (base~$8$) representation of the result in the input
+% stream.
+% The resulting tokens are digits with category code $12$ (other) and
+% letters with category code $11$ (letter).
+% \end{function}
+%
+% \begin{function}[updated = 2014-02-11, EXP]
+% {\int_to_base:nn, \int_to_Base:nn}
+% \begin{syntax}
+% \cs{int_to_base:nn} \Arg{int expr} \Arg{base}
+% \end{syntax}
+% Calculates the value of the \meta{int expr} and
+% converts it into the appropriate representation in the \meta{base};
+% the later may be given as an integer expression. For bases greater
+% than $10$ the higher \enquote{digits} are represented by
+% letters from the English alphabet: lower
+% case letters for \cs{int_to_base:n} and upper case ones for
+% \cs{int_to_Base:n}.
+% The maximum \meta{base} value is $36$.
+% The resulting tokens are digits with category code $12$ (other) and
+% letters with category code $11$ (letter).
+% \begin{texnote}
+% This is a generic version of \cs{int_to_bin:n}, \emph{etc.}
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[updated = 2011-10-22, rEXP]{\int_to_roman:n, \int_to_Roman:n}
+% \begin{syntax}
+% \cs{int_to_roman:n} \Arg{int expr}
+% \end{syntax}
+% Places the value of the \meta{int expr} in the input
+% stream as Roman numerals, either lower case (\cs{int_to_roman:n}) or
+% upper case (\cs{int_to_Roman:n}). If the value is negative or zero,
+% the output is empty. The Roman numerals are letters with category
+% code $11$ (letter). The letters used are |mdclxvi|, repeated as
+% needed: the notation with bars (such as $\bar{\mbox{v}}$ for $5000$)
+% is \emph{not} used. For instance \cs{int_to_roman:n} |{| 8249 |}|
+% expands to |mmmmmmmmccxlix|.
+% \end{function}
+%
+% \section{Converting from other formats to integers}
+%
+% \begin{function}[updated = 2014-08-25, EXP]{\int_from_alph:n}
+% \begin{syntax}
+% \cs{int_from_alph:n} \Arg{letters}
+% \end{syntax}
+% Converts the \meta{letters} into the integer (base~$10$)
+% representation and leaves this in the input stream. The
+% \meta{letters} are first converted to a string, with no expansion.
+% Lower and upper case letters from the English alphabet may be used,
+% with \enquote{a} equal to $1$ through to \enquote{z} equal to $26$.
+% The function also accepts a leading sign, made of |+| and~|-|. This
+% is the inverse function of \cs{int_to_alph:n} and
+% \cs{int_to_Alph:n}.
+% \end{function}
+%
+% \begin{function}[added = 2014-02-11, updated = 2014-08-25, EXP]
+% {\int_from_bin:n}
+% \begin{syntax}
+% \cs{int_from_bin:n} \Arg{binary number}
+% \end{syntax}
+% Converts the \meta{binary number} into the integer (base~$10$)
+% representation and leaves this in the input stream.
+% The \meta{binary number} is first converted to a string, with no
+% expansion. The function accepts a leading sign, made of |+|
+% and~|-|, followed by binary digits. This is the inverse function
+% of \cs{int_to_bin:n}.
+% \end{function}
+%
+% \begin{function}[added = 2014-02-11, updated = 2014-08-25, EXP]
+% {\int_from_hex:n}
+% \begin{syntax}
+% \cs{int_from_hex:n} \Arg{hexadecimal number}
+% \end{syntax}
+% Converts the \meta{hexadecimal number} into the integer (base~$10$)
+% representation and leaves this in the input stream. Digits greater
+% than $9$ may be represented in the \meta{hexadecimal number} by
+% upper or lower case letters. The \meta{hexadecimal number} is first
+% converted to a string, with no expansion. The function also accepts
+% a leading sign, made of |+| and~|-|. This is the inverse function
+% of \cs{int_to_hex:n} and \cs{int_to_Hex:n}.
+% \end{function}
+%
+% \begin{function}[added = 2014-02-11, updated = 2014-08-25, EXP]
+% {\int_from_oct:n}
+% \begin{syntax}
+% \cs{int_from_oct:n} \Arg{octal number}
+% \end{syntax}
+% Converts the \meta{octal number} into the integer (base~$10$)
+% representation and leaves this in the input stream.
+% The \meta{octal number} is first converted to a string, with no
+% expansion. The function accepts a leading sign, made of |+|
+% and~|-|, followed by octal digits. This is the inverse function
+% of \cs{int_to_oct:n}.
+% \end{function}
+%
+% \begin{function}[updated = 2014-08-25, updated = 2014-08-25, EXP]
+% {\int_from_roman:n}
+% \begin{syntax}
+% \cs{int_from_roman:n} \Arg{roman numeral}
+% \end{syntax}
+% Converts the \meta{roman numeral} into the integer (base~$10$)
+% representation and leaves this in the input stream. The \meta{roman
+% numeral} is first converted to a string, with no expansion. The
+% \meta{roman numeral} may be in upper or lower case; if the numeral
+% contains characters besides |mdclxvi| or |MDCLXVI| then the
+% resulting value is $-1$. This is the inverse function of
+% \cs{int_to_roman:n} and \cs{int_to_Roman:n}.
+% \end{function}
+%
+% \begin{function}[updated = 2014-08-25, EXP]{\int_from_base:nn}
+% \begin{syntax}
+% \cs{int_from_base:nn} \Arg{number} \Arg{base}
+% \end{syntax}
+% Converts the \meta{number} expressed in \meta{base} into the
+% appropriate value in base $10$. The \meta{number} is first
+% converted to a string, with no expansion. The \meta{number} should
+% consist of digits and letters (either lower or upper case), plus
+% optionally a leading sign. The maximum \meta{base} value is $36$.
+% This is the inverse function of \cs{int_to_base:nn} and
+% \cs{int_to_Base:nn}.
+% \end{function}
+%
+% \section{Random integers}
+%
+% \begin{function}[EXP, added = 2016-12-06, updated = 2018-04-27]{\int_rand:nn}
+% \begin{syntax}
+% \cs{int_rand:nn} \Arg{int expr_1} \Arg{int expr_2}
+% \end{syntax}
+% Evaluates the two \meta{int expr}s and produces a
+% pseudo-random number between the two (with bounds included).
+% This is not available in older versions of \XeTeX{}.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-05-05]{\int_rand:n}
+% \begin{syntax}
+% \cs{int_rand:n} \Arg{int expr}
+% \end{syntax}
+% Evaluates the \meta{int expr} then produces a
+% pseudo-random number between $1$ and the \meta{int expr} (included).
+% This is not available in older versions of \XeTeX{}.
+% \end{function}
+%
+% \section{Viewing integers}
+%
+% \begin{function}{\int_show:N, \int_show:c}
+% \begin{syntax}
+% \cs{int_show:N} \meta{integer}
+% \end{syntax}
+% Displays the value of the \meta{integer} on the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2011-11-22, updated = 2015-08-07]{\int_show:n}
+% \begin{syntax}
+% \cs{int_show:n} \Arg{int expr}
+% \end{syntax}
+% Displays the result of evaluating the \meta{int expr}
+% on the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2015-08-03]{\int_log:N, \int_log:c}
+% \begin{syntax}
+% \cs{int_log:N} \meta{integer}
+% \end{syntax}
+% Writes the value of the \meta{integer} in the log file.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2015-08-07]{\int_log:n}
+% \begin{syntax}
+% \cs{int_log:n} \Arg{int expr}
+% \end{syntax}
+% Writes the result of evaluating the \meta{int expr}
+% in the log file.
+% \end{function}
+%
+% \section{Constant integers}
+%
+% \begin{variable}[added = 2018-05-07]{\c_zero_int, \c_one_int}
+% Integer values used with primitive tests and assignments: their
+% self-terminating nature makes these more convenient and faster than
+% literal numbers.
+% \end{variable}
+%
+% \begin{variable}[module = int]{\c_max_int}
+% The maximum value that can be stored as an integer.
+% \end{variable}
+%
+% \begin{variable}[module = int]{\c_max_register_int}
+% Maximum number of registers.
+% \end{variable}
+%
+% \begin{variable}[module = int]{\c_max_char_int}
+% Maximum character code completely supported by the engine.
+% \end{variable}
+%
+% \section{Scratch integers}
+%
+% \begin{variable}{\l_tmpa_int, \l_tmpb_int}
+% Scratch integer for local assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \begin{variable}{\g_tmpa_int, \g_tmpb_int}
+% Scratch integer for global assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \section{Direct number expansion}
+%
+% \begin{function}[EXP, added = 2018-03-27]{\int_value:w}
+% \begin{syntax}
+% \cs{int_value:w} \meta{integer}
+% \cs{int_value:w} \meta{integer denotation} \meta{optional space}
+% \end{syntax}
+% Expands the following tokens until an \meta{integer} is formed, and
+% leaves a normalized form (no leading sign except for negative
+% numbers, no leading digit~|0| except for zero) in the input stream
+% as category code $12$ (other) characters. The \meta{integer} can
+% consist of any number of signs (with intervening spaces) followed
+% by
+% \begin{itemize}
+% \item an integer variable (in fact, any \TeX{} register except
+% \tn{toks}) or
+% \item explicit digits (or by |'|\meta{octal digits} or |"|\meta{hexadecimal digits} or |`|\meta{character}).
+% \end{itemize}
+% In this last case expansion stops once a non-digit is found; if that is a
+% space it is removed as in \texttt{f}-expansion, and so \cs{exp_stop_f:}
+% may be employed as an end marker. Note that protected functions
+% \emph{are} expanded by this process.
+%
+% This function requires exactly one expansion to produce a value, and so
+% is suitable for use in cases where a number is required \enquote{directly}.
+% In general, \cs{int_eval:n} is the preferred approach to generating
+% numbers.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{number}.
+% \end{texnote}
+% \end{function}
+%
+% \section{Primitive conditionals}
+%
+% \begin{function}[EXP]{\if_int_compare:w}
+% \begin{syntax}
+% \cs{if_int_compare:w} \meta{integer_1} \meta{relation} \meta{integer_2}
+% ~~\meta{true code}
+% \cs{else:}
+% ~~\meta{false code}
+% \cs{fi:}
+% \end{syntax}
+% Compare two integers using \meta{relation}, which must be one of
+% |=|, |<| or |>| with category code $12$.
+% The \cs{else:} branch is optional.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{ifnum}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\if_case:w, \or:}
+% \begin{syntax}
+% \cs{if_case:w} \meta{integer} \meta{case_0}
+% ~~\cs{or:} \meta{case_1}
+% ~~\cs{or:} |...|
+% ~~\cs{else:} \meta{default}
+% \cs{fi:}
+% \end{syntax}
+% Selects a case to execute based on the value of the \meta{integer}. The
+% first case (\meta{case_0}) is executed if \meta{integer} is $0$, the second
+% (\meta{case_1}) if the \meta{integer} is $1$, \emph{etc.} The
+% \meta{integer} may be a literal, a constant or an integer
+% expression (\emph{e.g.}~using \cs{int_eval:n}).
+% \begin{texnote}
+% These are the \TeX{} primitives \tn{ifcase} and \tn{or}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\if_int_odd:w}
+% \begin{syntax}
+% \cs{if_int_odd:w} \meta{tokens} \meta{optional space}
+% ~~\meta{true code}
+% \cs{else:}
+% ~~\meta{true code}
+% \cs{fi:}
+% \end{syntax}
+% Expands \meta{tokens} until a non-numeric token or a space is found, and
+% tests whether the resulting \meta{integer} is odd. If so, \meta{true code}
+% is executed. The \cs{else:} branch is optional.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{ifodd}.
+% \end{texnote}
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3int} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=int>
+% \end{macrocode}
+%
+% \TestFiles{m3int001,m3int002,m3int03}
+%
+% \begin{variable}{\c_max_register_int}
+% Done in \pkg{l3basics}.
+% \end{variable}
+%
+% \begin{macro}{\@@_to_roman:w}
+% \begin{macro}{\if_int_compare:w}
+% Done in \pkg{l3basics}.
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\or:}
+% Done in \pkg{l3basics}.
+% \end{macro}
+%
+% \begin{macro}{\int_value:w}
+% \begin{macro}{\@@_eval:w}
+% \begin{macro}{\@@_eval_end:}
+% \begin{macro}{\if_int_odd:w}
+% \begin{macro}{\if_case:w}
+% Here are the remaining primitives for number comparisons and
+% expressions.
+% \begin{macrocode}
+\cs_new_eq:NN \int_value:w \tex_number:D
+\cs_new_eq:NN \@@_eval:w \tex_numexpr:D
+\cs_new_eq:NN \@@_eval_end: \tex_relax:D
+\cs_new_eq:NN \if_int_odd:w \tex_ifodd:D
+\cs_new_eq:NN \if_case:w \tex_ifcase:D
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\s_@@_mark,\s_@@_stop}
+% Scan marks used throughout the module.
+% \begin{macrocode}
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_use_none_delimit_by_s_stop:w}
+% Function to gobble until a scan mark.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_none_delimit_by_s_stop:w #1 \s_@@_stop { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\q_@@_recursion_tail,\q_@@_recursion_stop}
+% Quarks for recursion.
+% \begin{macrocode}
+\quark_new:N \q_@@_recursion_tail
+\quark_new:N \q_@@_recursion_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_if_recursion_tail_stop_do:Nn,\@@_if_recursion_tail_stop:N}
+% Functions to query quarks.
+% \begin{macrocode}
+\__kernel_quark_new_test:N \@@_if_recursion_tail_stop_do:Nn
+\__kernel_quark_new_test:N \@@_if_recursion_tail_stop:N
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Integer expressions}
+%
+% \begin{macro}{\int_eval:n}
+% \begin{macro}{\int_eval:w}
+% Wrapper for \cs{@@_eval:w}: can be used in an integer expression or
+% directly in the input stream. It is very slightly faster to use
+% \tn{the} rather than \tn{number} to turn the expression to a number.
+% When debugging, we introduce parentheses to catch early termination (see \pkg{l3debug}).
+% \begin{macrocode}
+\cs_new:Npn \int_eval:n #1
+ { \tex_the:D \@@_eval:w #1 \@@_eval_end: }
+\cs_new:Npn \int_eval:w { \tex_the:D \@@_eval:w }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\int_sign:n, \@@_sign:Nw}
+% See \cs{int_abs:n}. Evaluate the expression once (and when
+% debugging is enabled, check that the expression is well-formed),
+% then test the first character to determine the sign. This is
+% wrapped in \cs{int_value:w} \ldots{} \cs{exp_stop_f:} to ensure a
+% fixed number of expansions and to avoid dealing with closing the
+% conditionals.
+% \begin{macrocode}
+\cs_new:Npn \int_sign:n #1
+ {
+ \int_value:w \exp_after:wN \@@_sign:Nw
+ \int_value:w \@@_eval:w #1 \@@_eval_end: ;
+ \exp_stop_f:
+ }
+\cs_new:Npn \@@_sign:Nw #1#2 ;
+ {
+ \if_meaning:w 0 #1
+ 0
+ \else:
+ \if_meaning:w - #1 - \fi: 1
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\int_abs:n}
+% \begin{macro}[EXP]{\@@_abs:N}
+% \UnitTested
+% \begin{macro}[EXP]{\int_max:nn}
+% \begin{macro}[EXP]{\int_min:nn}
+% \begin{macro}[EXP]{\@@_maxmin:wwN}
+% \UnitTested
+% \UnitTested
+% Functions for $\min$, $\max$, and absolute value with only one
+% evaluation. The absolute value is obtained by removing a leading
+% sign if any. All three functions expand in two steps.
+% \begin{macrocode}
+\cs_new:Npn \int_abs:n #1
+ {
+ \int_value:w \exp_after:wN \@@_abs:N
+ \int_value:w \@@_eval:w #1 \@@_eval_end:
+ \exp_stop_f:
+ }
+\cs_new:Npn \@@_abs:N #1
+ { \if_meaning:w - #1 \else: \exp_after:wN #1 \fi: }
+\cs_new:Npn \int_max:nn #1#2
+ {
+ \int_value:w \exp_after:wN \@@_maxmin:wwN
+ \int_value:w \@@_eval:w #1 \exp_after:wN ;
+ \int_value:w \@@_eval:w #2 ;
+ >
+ \exp_stop_f:
+ }
+\cs_new:Npn \int_min:nn #1#2
+ {
+ \int_value:w \exp_after:wN \@@_maxmin:wwN
+ \int_value:w \@@_eval:w #1 \exp_after:wN ;
+ \int_value:w \@@_eval:w #2 ;
+ <
+ \exp_stop_f:
+ }
+\cs_new:Npn \@@_maxmin:wwN #1 ; #2 ; #3
+ {
+ \if_int_compare:w #1 #3 #2 ~
+ #1
+ \else:
+ #2
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\int_div_truncate:nn}
+% \UnitTested
+% \begin{macro}[EXP]{\int_div_round:nn}
+% \UnitTested
+% \begin{macro}[EXP]{\int_mod:nn}
+% \UnitTested
+% \begin{macro}[EXP]{\@@_div_truncate:NwNw}
+% \begin{macro}[EXP]{\@@_mod:ww}
+% As \cs{@@_eval:w} rounds the result of a division we also provide a
+% version that truncates the result. We use an auxiliary to make sure
+% numerator and denominator are only evaluated once: this comes in
+% handy when those are more expressions are expensive to evaluate
+% (\emph{e.g.}, \cs{tl_count:n}). If the numerator |#1#2| is $0$,
+% then we divide $0$ by the denominator (this ensures that $0/0$ is
+% correctly reported as an error). Otherwise, shift the numerator
+% |#1#2| towards $0$ by $(\left\vert\texttt{\#3\#4}\right\vert-1)/2$, which we
+% round away from zero. It turns out that this quantity exactly
+% compensates the difference between \eTeX{}'s rounding and the
+% truncating behaviour that we want. The details are thanks to Heiko
+% Oberdiek: getting things right in all cases is not so easy.
+% \begin{macrocode}
+\cs_new:Npn \int_div_truncate:nn #1#2
+ {
+ \int_value:w \@@_eval:w
+ \exp_after:wN \@@_div_truncate:NwNw
+ \int_value:w \@@_eval:w #1 \exp_after:wN ;
+ \int_value:w \@@_eval:w #2 ;
+ \@@_eval_end:
+ }
+\cs_new:Npn \@@_div_truncate:NwNw #1#2; #3#4;
+ {
+ \if_meaning:w 0 #1
+ 0
+ \else:
+ (
+ #1#2
+ \if_meaning:w - #1 + \else: - \fi:
+ ( \if_meaning:w - #3 - \fi: #3#4 - 1 ) / 2
+ )
+ \fi:
+ / #3#4
+ }
+% \end{macrocode}
+% For the sake of completeness:
+% \begin{macrocode}
+\cs_new:Npn \int_div_round:nn #1#2
+ { \int_value:w \@@_eval:w ( #1 ) / ( #2 ) \@@_eval_end: }
+% \end{macrocode}
+% Finally there's the modulus operation.
+% \begin{macrocode}
+\cs_new:Npn \int_mod:nn #1#2
+ {
+ \int_value:w \@@_eval:w \exp_after:wN \@@_mod:ww
+ \int_value:w \@@_eval:w #1 \exp_after:wN ;
+ \int_value:w \@@_eval:w #2 ;
+ \@@_eval_end:
+ }
+\cs_new:Npn \@@_mod:ww #1; #2;
+ { #1 - ( \@@_div_truncate:NwNw #1 ; #2 ; ) * #2 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\__kernel_int_add:nnn}
+% Equivalent to \cs{int_eval:n} |{#1+#2+#3}| except that overflow only
+% occurs if the final result overflows $[-2^{31}+1,2^{31}-1]$. The
+% idea is to choose the order in which the three numbers are added
+% together. If |#1| and |#2| have opposite signs (one is in
+% $[-2^{31}+1,-1]$ and the other in $[0,2^{31}-1]$) then |#1+#2|
+% cannot overflow so we compute the result as |#1+#2+#3|. If they
+% have the same sign, then either |#3| has the same sign and the order
+% does not matter, or |#3| has the opposite sign and any order in
+% which |#3| is not last will work. We use |#1+#3+#2|.
+% \begin{macrocode}
+\cs_new:Npn \__kernel_int_add:nnn #1#2#3
+ {
+ \int_value:w \@@_eval:w #1
+ \if_int_compare:w #2 < \c_zero_int \exp_after:wN \reverse_if:N \fi:
+ \if_int_compare:w #1 < \c_zero_int + #2 + #3 \else: + #3 + #2 \fi:
+ \@@_eval_end:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Creating and initialising integers}
+%
+% \begin{macro}{\int_new:N, \int_new:c}
+% \UnitTested
+% Two ways to do this: one for the format and one for the \LaTeXe{}
+% package. In plain \TeX{}, \cs{newcount} (and other allocators) are
+% \cs{outer}: to allow the code here to work in \enquote{generic} mode
+% this is therefore accessed by name. (The same applies to \cs{newbox},
+% \cs{newdimen} and so on.)
+% \begin{macrocode}
+\cs_new_protected:Npn \int_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs:w newcount \cs_end: #1
+ }
+\cs_generate_variant:Nn \int_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\int_const:Nn, \int_const:cn}
+% \begin{macro}{\@@_const:nN, \@@_const:eN}
+% \begin{macro}{\@@_constdef:Nw}
+% \begin{variable}{\c_@@_max_constdef_int}
+% \UnitTested
+% As stated, most constants can be defined as \tn{chardef} or
+% \tn{mathchardef} but that's engine dependent. As a result, there is some
+% set up code to determine what can be done. No full engine testing just yet
+% so everything is a little awkward.
+% We cannot use \cs{int_gset:Nn} because (when |check-declarations| is
+% enabled) this runs some checks that constants would fail.
+% \begin{macrocode}
+\cs_new_protected:Npn \int_const:Nn #1#2
+ { \@@_const:eN { \int_eval:n {#2} } #1 }
+\cs_generate_variant:Nn \int_const:Nn { c }
+\cs_new_protected:Npn \@@_const:nN #1#2
+ {
+ \int_compare:nNnTF {#1} < \c_zero_int
+ {
+ \int_new:N #2
+ \tex_global:D
+ }
+ {
+ \int_compare:nNnTF {#1} > \c_@@_max_constdef_int
+ {
+ \int_new:N #2
+ \tex_global:D
+ }
+ {
+ \__kernel_chk_if_free_cs:N #2
+ \tex_global:D \@@_constdef:Nw
+ }
+ }
+ #2 = \@@_eval:w #1 \@@_eval_end:
+ }
+\cs_generate_variant:Nn \@@_const:nN { e }
+\if_int_odd:w 0
+ \cs_if_exist:NT \tex_luatexversion:D { 1 }
+ \cs_if_exist:NT \tex_omathchardef:D { 1 }
+ \cs_if_exist:NT \tex_XeTeXversion:D { 1 } ~
+ \cs_if_exist:NTF \tex_omathchardef:D
+ { \cs_new_eq:NN \@@_constdef:Nw \tex_omathchardef:D }
+ { \cs_new_eq:NN \@@_constdef:Nw \tex_chardef:D }
+ \tex_global:D \@@_constdef:Nw \c_@@_max_constdef_int 1114111 ~
+\else:
+ \cs_new_eq:NN \@@_constdef:Nw \tex_mathchardef:D
+ \tex_global:D \@@_constdef:Nw \c_@@_max_constdef_int 32767 ~
+\fi:
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\int_zero:N, \int_zero:c}
+% \UnitTested
+% \begin{macro}{\int_gzero:N, \int_gzero:c}
+% \UnitTested
+% Functions that reset an \meta{integer} register to zero.
+% \begin{macrocode}
+\cs_new_protected:Npn \int_zero:N #1 { #1 = \c_zero_int }
+\cs_new_protected:Npn \int_gzero:N #1 { \tex_global:D #1 = \c_zero_int }
+\cs_generate_variant:Nn \int_zero:N { c }
+\cs_generate_variant:Nn \int_gzero:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\int_zero_new:N, \int_zero_new:c, \int_gzero_new:N, \int_gzero_new:c}
+% Create a register if needed, otherwise clear it.
+% \begin{macrocode}
+\cs_new_protected:Npn \int_zero_new:N #1
+ { \int_if_exist:NTF #1 { \int_zero:N #1 } { \int_new:N #1 } }
+\cs_new_protected:Npn \int_gzero_new:N #1
+ { \int_if_exist:NTF #1 { \int_gzero:N #1 } { \int_new:N #1 } }
+\cs_generate_variant:Nn \int_zero_new:N { c }
+\cs_generate_variant:Nn \int_gzero_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\int_set_eq:NN, \int_set_eq:cN, \int_set_eq:Nc, \int_set_eq:cc}
+% \UnitTested
+% \begin{macro}
+% {\int_gset_eq:NN, \int_gset_eq:cN, \int_gset_eq:Nc, \int_gset_eq:cc}
+% \UnitTested
+% Setting equal means using one integer inside the set function of
+% another. Check that assigned integer is local/global. No need to
+% check that the other one is defined as \TeX{} does it for us.
+% \begin{macrocode}
+\cs_new_protected:Npn \int_set_eq:NN #1#2 { #1 = #2 }
+\cs_generate_variant:Nn \int_set_eq:NN { c , Nc , cc }
+\cs_new_protected:Npn \int_gset_eq:NN #1#2 { \tex_global:D #1 = #2 }
+\cs_generate_variant:Nn \int_gset_eq:NN { c , Nc , cc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\int_if_exist:N, \int_if_exist:c}
+% Copies of the \texttt{cs} functions defined in \pkg{l3basics}.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \int_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \int_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Setting and incrementing integers}
+%
+% \begin{macro}{\int_add:Nn, \int_add:cn}
+% \UnitTested
+% \begin{macro}{\int_gadd:Nn, \int_gadd:cn}
+% \UnitTested
+% \begin{macro}{\int_sub:Nn, \int_sub:cn}
+% \UnitTested
+% \begin{macro}{\int_gsub:Nn, \int_gsub:cn}
+% \UnitTested
+% Adding and subtracting to and from a counter.
+% Including here the optional |by| would slow down these operations
+% by a few percent.
+% \begin{macrocode}
+\cs_new_protected:Npn \int_add:Nn #1#2
+ { \tex_advance:D #1 \@@_eval:w #2 \@@_eval_end: }
+\cs_new_protected:Npn \int_sub:Nn #1#2
+ { \tex_advance:D #1 - \@@_eval:w #2 \@@_eval_end: }
+\cs_new_protected:Npn \int_gadd:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 \@@_eval:w #2 \@@_eval_end: }
+\cs_new_protected:Npn \int_gsub:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 - \@@_eval:w #2 \@@_eval_end: }
+\cs_generate_variant:Nn \int_add:Nn { c }
+\cs_generate_variant:Nn \int_gadd:Nn { c }
+\cs_generate_variant:Nn \int_sub:Nn { c }
+\cs_generate_variant:Nn \int_gsub:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\int_incr:N, \int_incr:c}
+% \UnitTested
+% \begin{macro}{\int_gincr:N, \int_gincr:c}
+% \UnitTested
+% \begin{macro}{\int_decr:N, \int_decr:c}
+% \UnitTested
+% \begin{macro}{\int_gdecr:N, \int_gdecr:c}
+% \UnitTested
+% Incrementing and decrementing of integer registers is done with
+% the following functions.
+% \begin{macrocode}
+\cs_new_protected:Npn \int_incr:N #1
+ { \tex_advance:D #1 \c_one_int }
+\cs_new_protected:Npn \int_decr:N #1
+ { \tex_advance:D #1 - \c_one_int }
+\cs_new_protected:Npn \int_gincr:N #1
+ { \tex_global:D \tex_advance:D #1 \c_one_int }
+\cs_new_protected:Npn \int_gdecr:N #1
+ { \tex_global:D \tex_advance:D #1 - \c_one_int }
+\cs_generate_variant:Nn \int_incr:N { c }
+\cs_generate_variant:Nn \int_decr:N { c }
+\cs_generate_variant:Nn \int_gincr:N { c }
+\cs_generate_variant:Nn \int_gdecr:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\int_set:Nn, \int_set:cn}
+% \UnitTested
+% \begin{macro}{\int_gset:Nn, \int_gset:cn}
+% \UnitTested
+% As integers are register-based \TeX{} issues an error
+% if they are not defined. While the |=| sign is optional, this
+% version with |=| is slightly quicker than without, while adding the
+% optional space after |=| slows things down minutely.
+% \begin{macrocode}
+\cs_new_protected:Npn \int_set:Nn #1#2
+ { #1 = \@@_eval:w #2 \@@_eval_end: }
+\cs_new_protected:Npn \int_gset:Nn #1#2
+ { \tex_global:D #1 = \@@_eval:w #2 \@@_eval_end: }
+\cs_generate_variant:Nn \int_set:Nn { c }
+\cs_generate_variant:Nn \int_gset:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Using integers}
+%
+% \begin{macro}{\int_use:N, \int_use:c}
+% \UnitTested
+% Here is how counters are accessed.
+% We hand-code the |c| variant for some speed gain.
+% \begin{macrocode}
+\cs_new_eq:NN \int_use:N \tex_the:D
+\cs_new:Npn \int_use:c #1 { \tex_the:D \cs:w #1 \cs_end: }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Integer expression conditionals}
+%
+% \begin{macro}[EXP]{\@@_compare_error:, \@@_compare_error:Nw}
+% Those functions are used for comparison tests which use a simple
+% syntax where only one set of braces is required and additional
+% operators such as |!=| and |>=| are supported. The tests first
+% evaluate their left-hand side, with a trailing
+% \cs{@@_compare_error:}. This marker is normally not expanded,
+% but if the relation symbol is missing from the test's argument, then
+% the marker inserts |=| (and itself) after triggering the relevant
+% \TeX{} error. If the first token which appears after evaluating and
+% removing the left-hand side is not a known relation symbol, then a
+% judiciously placed \cs{@@_compare_error:Nw} gets expanded,
+% cleaning up the end of the test and telling the user what the
+% problem was.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compare_error:
+ {
+ \if_int_compare:w \c_zero_int \c_zero_int \fi:
+ =
+ \@@_compare_error:
+ }
+\cs_new:Npn \@@_compare_error:Nw
+ #1#2 \s_@@_stop
+ {
+ { }
+ \c_zero_int \fi:
+ \msg_expandable_error:nnn
+ { kernel } { unknown-comparison } {#1}
+ \prg_return_false:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF, EXP]{\int_compare:n}
+% \begin{macro}[EXP]
+% {\@@_compare:w, \@@_compare:Nw, \@@_compare:NNw, \@@_compare:nnN}
+% \begin{macro}[EXP]
+% {
+% \@@_compare_end_=:NNw,
+% \@@_compare_=:NNw,
+% \@@_compare_<:NNw,
+% \@@_compare_>:NNw,
+% \@@_compare_==:NNw,
+% \@@_compare_!=:NNw,
+% \@@_compare_<=:NNw,
+% \@@_compare_>=:NNw
+% }
+% Comparison tests using a simple syntax where only one set of braces
+% is required, additional operators such as |!=| and |>=| are
+% supported, and multiple comparisons can be performed at once, for
+% instance |0 < 5 <= 1|. The idea is to loop through the argument,
+% finding one operand at a time, and comparing it to the previous one.
+% The looping auxiliary \cs{@@_compare:Nw} reads one \meta{operand}
+% and one \meta{comparison} symbol, and leaves roughly
+% \begin{quote}
+% \meta{operand} \cs{prg_return_false:} \cs{fi:} \\
+% \cs{reverse_if:N} \cs{if_int_compare:w} \meta{operand} \meta{comparison} \\
+% \cs{@@_compare:Nw}
+% \end{quote}
+% in the input stream. Each call to this auxiliary provides the
+% second operand of the last call's \cs{if_int_compare:w}. If one of
+% the \meta{comparisons} is \texttt{false}, the \texttt{true} branch
+% of the \TeX{} conditional is taken (because of \cs{reverse_if:N}),
+% immediately returning \texttt{false} as the result of the test.
+% There is no \TeX{} conditional waiting the first operand, so we add
+% an \cs{if_false:} and expand by hand with \cs{int_value:w}, thus
+% skipping \cs{prg_return_false:} on the first iteration.
+%
+% Before starting the loop, the first step is to make sure that there
+% is at least one relation symbol. We first let \TeX{} evaluate this
+% left hand side of the (in)equality using \cs{@@_eval:w}. Since the
+% relation symbols |<|, |>|, |=| and |!| are not allowed in integer
+% expressions, they would terminate the expression. If the argument contains no
+% relation symbol, \cs{@@_compare_error:} is expanded,
+% inserting~|=| and itself after an error. In all cases,
+% \cs{@@_compare:w} receives as its argument an integer, a relation
+% symbol, and some more tokens. We then setup the loop, which is
+% ended by the two odd-looking items |e| and |{=nd_}|, with a trailing
+% \cs{s_@@_stop} used to grab the entire argument when necessary.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \int_compare:n #1 { p , T , F , TF }
+ {
+ \exp_after:wN \@@_compare:w
+ \int_value:w \@@_eval:w #1 \@@_compare_error:
+ }
+\cs_new:Npn \@@_compare:w #1 \@@_compare_error:
+ {
+ \exp_after:wN \if_false: \int_value:w
+ \@@_compare:Nw #1 e { = nd_ } \s_@@_stop
+ }
+% \end{macrocode}
+% The goal here is to find an \meta{operand} and a \meta{comparison}.
+% The \meta{operand} is already evaluated, but we cannot yet grab it
+% as an argument. To access the following relation symbol, we remove
+% the number by applying \cs{@@_to_roman:w}, after making sure that
+% the argument becomes non-positive: its roman numeral representation
+% is then empty. Then probe the first two tokens with
+% \cs{@@_compare:NNw} to determine the relation symbol, building a
+% control sequence from it (\cs{token_to_str:N} gives better errors if
+% |#1| is not a character). All the extended forms have an extra |=|
+% hence the test for that as a second token. If the relation symbol
+% is unknown, then the control sequence is turned by \TeX{} into
+% \cs{scan_stop:}, ignored thanks to \tn{unexpanded}, and
+% \cs{@@_compare_error:Nw} raises an error.
+% \begin{macrocode}
+\cs_new:Npn \@@_compare:Nw #1#2 \s_@@_stop
+ {
+ \exp_after:wN \@@_compare:NNw
+ \@@_to_roman:w - 0 #2 \s_@@_mark
+ #1#2 \s_@@_stop
+ }
+\cs_new:Npn \@@_compare:NNw #1#2#3 \s_@@_mark
+ {
+ \__kernel_exp_not:w
+ \use:c
+ {
+ @@_compare_ \token_to_str:N #1
+ \if_meaning:w = #2 = \fi:
+ :NNw
+ }
+ \@@_compare_error:Nw #1
+ }
+% \end{macrocode}
+% When the last \meta{operand} is seen, \cs{@@_compare:NNw} receives
+% |e| and |=nd_| as arguments, hence calling
+% \cs{@@_compare_end_=:NNw} to end the loop: return the result of the
+% last comparison (involving the operand that we just found). When a
+% normal relation is found, the appropriate auxiliary calls
+% \cs{@@_compare:nnN} where |#1| is \cs{if_int_compare:w} or
+% \cs{reverse_if:N} \cs{if_int_compare:w}, |#2| is the \meta{operand},
+% and |#3| is one of |<|, |=|, or~|>|. As announced earlier, we leave
+% the \meta{operand} for the previous conditional. If this
+% conditional is true the result of the test is known, so we remove
+% all tokens and return \texttt{false}. Otherwise, we apply the
+% conditional |#1| to the \meta{operand} |#2| and the comparison |#3|,
+% and call \cs{@@_compare:Nw} to look for additional operands, after
+% evaluating the following expression.
+% \begin{macrocode}
+\cs_new:cpn { @@_compare_end_=:NNw } #1#2#3 e #4 \s_@@_stop
+ {
+ {#3} \exp_stop_f:
+ \prg_return_false: \else: \prg_return_true: \fi:
+ }
+\cs_new:Npn \@@_compare:nnN #1#2#3
+ {
+ {#2} \exp_stop_f:
+ \prg_return_false: \exp_after:wN \@@_use_none_delimit_by_s_stop:w
+ \fi:
+ #1 #2 #3 \exp_after:wN \@@_compare:Nw \int_value:w \@@_eval:w
+ }
+% \end{macrocode}
+% The actual comparisons are then simple function calls, using the
+% relation as delimiter for a delimited argument and discarding
+% \cs{@@_compare_error:Nw} \meta{token} responsible for error
+% detection.
+% \begin{macrocode}
+\cs_new:cpn { @@_compare_=:NNw } #1#2#3 =
+ { \@@_compare:nnN { \reverse_if:N \if_int_compare:w } {#3} = }
+\cs_new:cpn { @@_compare_<:NNw } #1#2#3 <
+ { \@@_compare:nnN { \reverse_if:N \if_int_compare:w } {#3} < }
+\cs_new:cpn { @@_compare_>:NNw } #1#2#3 >
+ { \@@_compare:nnN { \reverse_if:N \if_int_compare:w } {#3} > }
+\cs_new:cpn { @@_compare_==:NNw } #1#2#3 ==
+ { \@@_compare:nnN { \reverse_if:N \if_int_compare:w } {#3} = }
+\cs_new:cpn { @@_compare_!=:NNw } #1#2#3 !=
+ { \@@_compare:nnN { \if_int_compare:w } {#3} = }
+\cs_new:cpn { @@_compare_<=:NNw } #1#2#3 <=
+ { \@@_compare:nnN { \if_int_compare:w } {#3} > }
+\cs_new:cpn { @@_compare_>=:NNw } #1#2#3 >=
+ { \@@_compare:nnN { \if_int_compare:w } {#3} < }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\int_compare:nNn}
+% \UnitTested
+% More efficient but less natural in typing.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \int_compare:nNn #1#2#3 { p , T , F , TF }
+ {
+ \if_int_compare:w \@@_eval:w #1 #2 \@@_eval:w #3 \@@_eval_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\int_if_zero:n}
+% \begin{macrocode}
+\prg_new_conditional:Npnn \int_if_zero:n #1 { p , T , F , TF }
+ {
+ \if_int_compare:w \@@_eval:w #1 = \c_zero_int
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, noTF]{\int_case:nn}
+% \begin{macro}{\@@_case:nnTF}
+% \begin{macro}{\@@_case:nw, \@@_case_end:nw}
+% For integer cases, the first task to fully expand the check
+% condition. The over all idea is then much the same as for
+% \cs{str_case:nnTF} as described in \pkg{l3str}.
+% \begin{macrocode}
+\cs_new:Npn \int_case:nnTF #1
+ {
+ \exp:w
+ \exp_args:Nf \@@_case:nnTF { \int_eval:n {#1} }
+ }
+\cs_new:Npn \int_case:nnT #1#2#3
+ {
+ \exp:w
+ \exp_args:Nf \@@_case:nnTF { \int_eval:n {#1} } {#2} {#3} { }
+ }
+\cs_new:Npn \int_case:nnF #1#2
+ {
+ \exp:w
+ \exp_args:Nf \@@_case:nnTF { \int_eval:n {#1} } {#2} { }
+ }
+\cs_new:Npn \int_case:nn #1#2
+ {
+ \exp:w
+ \exp_args:Nf \@@_case:nnTF { \int_eval:n {#1} } {#2} { } { }
+ }
+\cs_new:Npn \@@_case:nnTF #1#2#3#4
+ { \@@_case:nw {#1} #2 {#1} { } \s_@@_mark {#3} \s_@@_mark {#4} \s_@@_stop }
+\cs_new:Npn \@@_case:nw #1#2#3
+ {
+ \int_compare:nNnTF {#1} = {#2}
+ { \@@_case_end:nw {#3} }
+ { \@@_case:nw {#1} }
+ }
+\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}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\int_if_odd:n}
+% \UnitTested
+% \begin{macro}[pTF]{\int_if_even:n}
+% \UnitTested
+% A predicate function.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \int_if_odd:n #1 { p , T , F , TF}
+ {
+ \if_int_odd:w \@@_eval:w #1 \@@_eval_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_new_conditional:Npnn \int_if_even:n #1 { p , T , F , TF}
+ {
+ \reverse_if:N \if_int_odd:w \@@_eval:w #1 \@@_eval_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Integer expression loops}
+%
+% \begin{macro}{\int_while_do:nn}
+% \UnitTested
+% \TestMissing{Boundary cases}
+% \begin{macro}{\int_until_do:nn}
+% \UnitTested
+% \TestMissing{Boundary cases}
+% \begin{macro}{\int_do_while:nn}
+% \UnitTested
+% \TestMissing{Boundary cases}
+% \begin{macro}{\int_do_until:nn}
+% \UnitTested
+% \TestMissing{Boundary cases}
+% These are quite easy given the above functions. The |while| versions
+% test first and then execute the body. The |do_while| does it the
+% other way round.
+% \begin{macrocode}
+\cs_new:Npn \int_while_do:nn #1#2
+ {
+ \int_compare:nT {#1}
+ {
+ #2
+ \int_while_do:nn {#1} {#2}
+ }
+ }
+\cs_new:Npn \int_until_do:nn #1#2
+ {
+ \int_compare:nF {#1}
+ {
+ #2
+ \int_until_do:nn {#1} {#2}
+ }
+ }
+\cs_new:Npn \int_do_while:nn #1#2
+ {
+ #2
+ \int_compare:nT {#1}
+ { \int_do_while:nn {#1} {#2} }
+ }
+\cs_new:Npn \int_do_until:nn #1#2
+ {
+ #2
+ \int_compare:nF {#1}
+ { \int_do_until:nn {#1} {#2} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\int_while_do:nNnn}
+% \begin{macro}{\int_until_do:nNnn}
+% \begin{macro}{\int_do_while:nNnn}
+% \begin{macro}{\int_do_until:nNnn}
+% As above but not using the more natural syntax.
+% \begin{macrocode}
+\cs_new:Npn \int_while_do:nNnn #1#2#3#4
+ {
+ \int_compare:nNnT {#1} #2 {#3}
+ {
+ #4
+ \int_while_do:nNnn {#1} #2 {#3} {#4}
+ }
+ }
+\cs_new:Npn \int_until_do:nNnn #1#2#3#4
+ {
+ \int_compare:nNnF {#1} #2 {#3}
+ {
+ #4
+ \int_until_do:nNnn {#1} #2 {#3} {#4}
+ }
+ }
+\cs_new:Npn \int_do_while:nNnn #1#2#3#4
+ {
+ #4
+ \int_compare:nNnT {#1} #2 {#3}
+ { \int_do_while:nNnn {#1} #2 {#3} {#4} }
+ }
+\cs_new:Npn \int_do_until:nNnn #1#2#3#4
+ {
+ #4
+ \int_compare:nNnF {#1} #2 {#3}
+ { \int_do_until:nNnn {#1} #2 {#3} {#4} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Integer step functions}
+%
+% \begin{macro}{\int_step_function:nnnN}
+% \begin{macro}{\@@_step:wwwN, \@@_step:NwnnN}
+% \begin{macro}{\int_step_function:nN}
+% \begin{macro}{\int_step_function:nnN}
+% Before all else, evaluate the initial value, step, and final value.
+% Repeating a function by steps first needs a check on the direction
+% of the steps. After that, do the function for the start value then
+% step and loop around. It would be more symmetrical to test for a
+% step size of zero before checking the sign, but we optimize for the
+% most frequent case (positive step).
+% \begin{macrocode}
+\cs_new:Npn \int_step_function:nnnN #1#2#3
+ {
+ \exp_after:wN \@@_step:wwwN
+ \int_value:w \@@_eval:w #1 \exp_after:wN ;
+ \int_value:w \@@_eval:w #2 \exp_after:wN ;
+ \int_value:w \@@_eval:w #3 ;
+ }
+\cs_new:Npn \@@_step:wwwN #1; #2; #3; #4
+ {
+ \int_compare:nNnTF {#2} > \c_zero_int
+ { \@@_step:NwnnN > }
+ {
+ \int_compare:nNnTF {#2} = \c_zero_int
+ {
+ \msg_expandable_error:nnn
+ { kernel } { zero-step } {#4}
+ \prg_break:
+ }
+ { \@@_step:NwnnN < }
+ }
+ #1 ; {#2} {#3} #4
+ \prg_break_point:
+ }
+\cs_new:Npn \@@_step:NwnnN #1#2 ; #3#4#5
+ {
+ \if_int_compare:w #2 #1 #4 \exp_stop_f:
+ \prg_break:n
+ \fi:
+ #5 {#2}
+ \exp_after:wN \@@_step:NwnnN
+ \exp_after:wN #1
+ \int_value:w \@@_eval:w #2 + #3 ; {#3} {#4} #5
+ }
+\cs_new:Npn \int_step_function:nN
+ { \int_step_function:nnnN { 1 } { 1 } }
+\cs_new:Npn \int_step_function:nnN #1
+ { \int_step_function:nnnN {#1} { 1 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\int_step_inline:nn, \int_step_inline:nnn, \int_step_inline:nnnn}
+% \begin{macro}{\int_step_variable:nNn, \int_step_variable:nnNn, \int_step_variable:nnnNn}
+% \UnitTested
+% \begin{macro}{\@@_step:NNnnnn}
+% The approach here is to build a function, with a global integer
+% required to make the nesting safe (as seen in other in line
+% functions), and map that function using \cs{int_step_function:nnnN}.
+% We put a \cs{prg_break_point:Nn} so that \texttt{map_break}
+% functions from other modules correctly decrement \cs{g__kernel_prg_map_int}
+% before looking for their own break point. The first argument is
+% \cs{scan_stop:}, so that no breaking function recognizes this break
+% point as its own.
+% \begin{macrocode}
+\cs_new_protected:Npn \int_step_inline:nn
+ { \int_step_inline:nnnn { 1 } { 1 } }
+\cs_new_protected:Npn \int_step_inline:nnn #1
+ { \int_step_inline:nnnn {#1} { 1 } }
+\cs_new_protected:Npn \int_step_inline:nnnn
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:NNc \@@_step:NNnnnn
+ \cs_gset_protected:Npn
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w }
+ }
+\cs_new_protected:Npn \int_step_variable:nNn
+ { \int_step_variable:nnnNn { 1 } { 1 } }
+\cs_new_protected:Npn \int_step_variable:nnNn #1
+ { \int_step_variable:nnnNn {#1} { 1 } }
+\cs_new_protected:Npn \int_step_variable:nnnNn #1#2#3#4#5
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:NNc \@@_step:NNnnnn
+ \cs_gset_protected:Npe
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w }
+ {#1}{#2}{#3}
+ {
+ \tl_set:Nn \exp_not:N #4 {##1}
+ \exp_not:n {#5}
+ }
+ }
+\cs_new_protected:Npn \@@_step:NNnnnn #1#2#3#4#5#6
+ {
+ #1 #2 ##1 {#6}
+ \int_step_function:nnnN {#3} {#4} {#5} #2
+ \prg_break_point:Nn \scan_stop: { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Formatting integers}
+%
+% \begin{macro}{\int_to_arabic:n, \int_to_arabic:v}
+% \UnitTested
+% Nothing exciting here.
+% \begin{macrocode}
+\cs_new_eq:NN \int_to_arabic:n \int_eval:n
+\cs_generate_variant:Nn \int_to_arabic:n { v }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\int_to_symbols:nnn}
+% \UnitTested
+% \begin{macro}{\@@_to_symbols:nnnn, \@@_to_symbols:ennn}
+% For conversion of integers to arbitrary symbols the method is in
+% general as follows. The input number (|#1|) is compared to the total
+% number of symbols available at each place (|#2|). If the input is larger
+% than the total number of symbols available then the modulus is needed,
+% with one added so that the positions don't have to number from
+% zero. Using an \texttt{f}-type expansion, this is done so that the system
+% is recursive. The actual conversion function therefore gets a `nice'
+% number at each stage. Of course, if the initial input was small enough
+% then there is no problem and everything is easy.
+% \begin{macrocode}
+\cs_new:Npn \int_to_symbols:nnn #1#2#3
+ {
+ \int_compare:nNnTF {#1} > {#2}
+ {
+ \@@_to_symbols:ennn
+ {
+ \int_case:nn
+ { 1 + \int_mod:nn { #1 - 1 } {#2} }
+ {#3}
+ }
+ {#1} {#2} {#3}
+ }
+ { \int_case:nn {#1} {#3} }
+ }
+\cs_new:Npn \@@_to_symbols:nnnn #1#2#3#4
+ {
+ \exp_args:Nf \int_to_symbols:nnn
+ { \int_div_truncate:nn { #2 - 1 } {#3} } {#3} {#4}
+ #1
+ }
+\cs_generate_variant:Nn \@@_to_symbols:nnnn { e }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\int_to_alph:n, \int_to_Alph:n}
+% \UnitTested
+% These both use the above function with input functions that make sense
+% for the alphabet in English.
+% \begin{macrocode}
+\cs_new:Npn \int_to_alph:n #1
+ {
+ \int_to_symbols:nnn {#1} { 26 }
+ {
+ { 1 } { a }
+ { 2 } { b }
+ { 3 } { c }
+ { 4 } { d }
+ { 5 } { e }
+ { 6 } { f }
+ { 7 } { g }
+ { 8 } { h }
+ { 9 } { i }
+ { 10 } { j }
+ { 11 } { k }
+ { 12 } { l }
+ { 13 } { m }
+ { 14 } { n }
+ { 15 } { o }
+ { 16 } { p }
+ { 17 } { q }
+ { 18 } { r }
+ { 19 } { s }
+ { 20 } { t }
+ { 21 } { u }
+ { 22 } { v }
+ { 23 } { w }
+ { 24 } { x }
+ { 25 } { y }
+ { 26 } { z }
+ }
+ }
+\cs_new:Npn \int_to_Alph:n #1
+ {
+ \int_to_symbols:nnn {#1} { 26 }
+ {
+ { 1 } { A }
+ { 2 } { B }
+ { 3 } { C }
+ { 4 } { D }
+ { 5 } { E }
+ { 6 } { F }
+ { 7 } { G }
+ { 8 } { H }
+ { 9 } { I }
+ { 10 } { J }
+ { 11 } { K }
+ { 12 } { L }
+ { 13 } { M }
+ { 14 } { N }
+ { 15 } { O }
+ { 16 } { P }
+ { 17 } { Q }
+ { 18 } { R }
+ { 19 } { S }
+ { 20 } { T }
+ { 21 } { U }
+ { 22 } { V }
+ { 23 } { W }
+ { 24 } { X }
+ { 25 } { Y }
+ { 26 } { Z }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\int_to_base:nn, \int_to_Base:nn}
+% \UnitTested
+% \begin{macro}{\@@_to_base:nn, \@@_to_Base:nn,
+% \@@_to_base:nnN, \@@_to_Base:nnN, \@@_to_base:nnnN, \@@_to_Base:nnnN}
+% \begin{macro}{\@@_to_letter:n, \@@_to_Letter:n}
+% \UnitTested
+% Converting from base ten (|#1|) to a second base (|#2|) starts with
+% computing |#1|: if it is a complicated calculation, we shouldn't
+% perform it twice. Then check the sign, store it, either \texttt{-}
+% or \cs{c_empty_tl}, and feed the absolute value to the next auxiliary
+% function.
+% \begin{macrocode}
+\cs_new:Npn \int_to_base:nn #1
+ { \exp_args:Nf \@@_to_base:nn { \int_eval:n {#1} } }
+\cs_new:Npn \int_to_Base:nn #1
+ { \exp_args:Nf \@@_to_Base:nn { \int_eval:n {#1} } }
+\cs_new:Npn \@@_to_base:nn #1#2
+ {
+ \int_compare:nNnTF {#1} < 0
+ { \exp_args:No \@@_to_base:nnN { \use_none:n #1 } {#2} - }
+ { \@@_to_base:nnN {#1} {#2} \c_empty_tl }
+ }
+\cs_new:Npn \@@_to_Base:nn #1#2
+ {
+ \int_compare:nNnTF {#1} < 0
+ { \exp_args:No \@@_to_Base:nnN { \use_none:n #1 } {#2} - }
+ { \@@_to_Base:nnN {#1} {#2} \c_empty_tl }
+ }
+% \end{macrocode}
+% Here, the idea is to provide a recursive system to deal with the
+% input. The output is built up after the end of the function.
+% At each pass, the value in |#1| is checked to see if it is less
+% than the new base (|#2|). If it is, then it is converted directly,
+% putting the sign back in front.
+% On the other hand, if the value to convert is greater than or equal
+% to the new base then the modulus and remainder values are found. The
+% modulus is converted to a symbol and put on the right,
+% and the remainder is carried forward to the next round.
+% \begin{macrocode}
+\cs_new:Npn \@@_to_base:nnN #1#2#3
+ {
+ \int_compare:nNnTF {#1} < {#2}
+ { \exp_last_unbraced:Nf #3 { \@@_to_letter:n {#1} } }
+ {
+ \exp_args:Nf \@@_to_base:nnnN
+ { \@@_to_letter:n { \int_mod:nn {#1} {#2} } }
+ {#1}
+ {#2}
+ #3
+ }
+ }
+\cs_new:Npn \@@_to_base:nnnN #1#2#3#4
+ {
+ \exp_args:Nf \@@_to_base:nnN
+ { \int_div_truncate:nn {#2} {#3} }
+ {#3}
+ #4
+ #1
+ }
+\cs_new:Npn \@@_to_Base:nnN #1#2#3
+ {
+ \int_compare:nNnTF {#1} < {#2}
+ { \exp_last_unbraced:Nf #3 { \@@_to_Letter:n {#1} } }
+ {
+ \exp_args:Nf \@@_to_Base:nnnN
+ { \@@_to_Letter:n { \int_mod:nn {#1} {#2} } }
+ {#1}
+ {#2}
+ #3
+ }
+ }
+\cs_new:Npn \@@_to_Base:nnnN #1#2#3#4
+ {
+ \exp_args:Nf \@@_to_Base:nnN
+ { \int_div_truncate:nn {#2} {#3} }
+ {#3}
+ #4
+ #1
+ }
+% \end{macrocode}
+% Convert to a letter only if necessary, otherwise simply return the
+% value unchanged. It would be cleaner to use \cs{int_case:nn},
+% but in our case, the cases are contiguous, so it is forty times faster
+% to use the \cs{if_case:w} primitive. The first \cs{exp_after:wN}
+% expands the conditional, jumping to the correct case, the second one
+% expands after the resulting character to close the conditional.
+% Since |#1| might be an expression, and not directly a single digit,
+% we need to evaluate it properly, and expand the trailing \cs{fi:}.
+% \begin{macrocode}
+\cs_new:Npn \@@_to_letter:n #1
+ {
+ \exp_after:wN \exp_after:wN
+ \if_case:w \@@_eval:w #1 - 10 \@@_eval_end:
+ a
+ \or: b
+ \or: c
+ \or: d
+ \or: e
+ \or: f
+ \or: g
+ \or: h
+ \or: i
+ \or: j
+ \or: k
+ \or: l
+ \or: m
+ \or: n
+ \or: o
+ \or: p
+ \or: q
+ \or: r
+ \or: s
+ \or: t
+ \or: u
+ \or: v
+ \or: w
+ \or: x
+ \or: y
+ \or: z
+ \else: \int_value:w \@@_eval:w #1 \exp_after:wN \@@_eval_end:
+ \fi:
+ }
+\cs_new:Npn \@@_to_Letter:n #1
+ {
+ \exp_after:wN \exp_after:wN
+ \if_case:w \@@_eval:w #1 - 10 \@@_eval_end:
+ A
+ \or: B
+ \or: C
+ \or: D
+ \or: E
+ \or: F
+ \or: G
+ \or: H
+ \or: I
+ \or: J
+ \or: K
+ \or: L
+ \or: M
+ \or: N
+ \or: O
+ \or: P
+ \or: Q
+ \or: R
+ \or: S
+ \or: T
+ \or: U
+ \or: V
+ \or: W
+ \or: X
+ \or: Y
+ \or: Z
+ \else: \int_value:w \@@_eval:w #1 \exp_after:wN \@@_eval_end:
+ \fi:
+ }
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%\end{macro}
+%
+% \begin{macro}{\int_to_bin:n, \int_to_hex:n, \int_to_Hex:n, \int_to_oct:n}
+% \UnitTested
+% Wrappers around the generic function.
+% \begin{macrocode}
+\cs_new:Npn \int_to_bin:n #1
+ { \int_to_base:nn {#1} { 2 } }
+\cs_new:Npn \int_to_hex:n #1
+ { \int_to_base:nn {#1} { 16 } }
+\cs_new:Npn \int_to_Hex:n #1
+ { \int_to_Base:nn {#1} { 16 } }
+\cs_new:Npn \int_to_oct:n #1
+ { \int_to_base:nn {#1} { 8 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\int_to_roman:n, \int_to_Roman:n}
+% \UnitTested
+% \begin{macro}{\@@_to_roman:N, \@@_to_roman:N}
+% \begin{macro}
+% {
+% \@@_to_roman_i:w, \@@_to_roman_v:w, \@@_to_roman_x:w,
+% \@@_to_roman_l:w, \@@_to_roman_c:w, \@@_to_roman_d:w,
+% \@@_to_roman_m:w, \@@_to_roman_Q:w,
+% \@@_to_Roman_i:w, \@@_to_Roman_v:w, \@@_to_Roman_x:w,
+% \@@_to_Roman_l:w, \@@_to_Roman_c:w, \@@_to_Roman_d:w,
+% \@@_to_Roman_m:w, \@@_to_Roman_Q:w,
+% }
+% The \cs{@@_to_roman:w} primitive creates tokens of category
+% code $12$ (other). Usually, what is actually wanted is letters.
+% The approach here is to convert the output of the primitive into
+% letters using appropriate control sequence names. That keeps
+% everything expandable. The loop is terminated by the conversion
+% of the |Q|.
+% \begin{macrocode}
+\cs_new:Npn \int_to_roman:n #1
+ {
+ \exp_after:wN \@@_to_roman:N
+ \@@_to_roman:w \int_eval:n {#1} Q
+ }
+\cs_new:Npn \@@_to_roman:N #1
+ {
+ \use:c { @@_to_roman_ #1 :w }
+ \@@_to_roman:N
+ }
+\cs_new:Npn \int_to_Roman:n #1
+ {
+ \exp_after:wN \@@_to_Roman_aux:N
+ \@@_to_roman:w \int_eval:n {#1} Q
+ }
+\cs_new:Npn \@@_to_Roman_aux:N #1
+ {
+ \use:c { @@_to_Roman_ #1 :w }
+ \@@_to_Roman_aux:N
+ }
+\cs_new:Npn \@@_to_roman_i:w { i }
+\cs_new:Npn \@@_to_roman_v:w { v }
+\cs_new:Npn \@@_to_roman_x:w { x }
+\cs_new:Npn \@@_to_roman_l:w { l }
+\cs_new:Npn \@@_to_roman_c:w { c }
+\cs_new:Npn \@@_to_roman_d:w { d }
+\cs_new:Npn \@@_to_roman_m:w { m }
+\cs_new:Npn \@@_to_roman_Q:w #1 { }
+\cs_new:Npn \@@_to_Roman_i:w { I }
+\cs_new:Npn \@@_to_Roman_v:w { V }
+\cs_new:Npn \@@_to_Roman_x:w { X }
+\cs_new:Npn \@@_to_Roman_l:w { L }
+\cs_new:Npn \@@_to_Roman_c:w { C }
+\cs_new:Npn \@@_to_Roman_d:w { D }
+\cs_new:Npn \@@_to_Roman_m:w { M }
+\cs_new:Npn \@@_to_Roman_Q:w #1 { }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Converting from other formats to integers}
+%
+% \begin{macro}[rEXP]{\@@_pass_signs:wn, \@@_pass_signs_end:wn}
+% Called as \cs{@@_pass_signs:wn} \meta{signs and digits} \cs{s_@@_stop}
+% \Arg{code}, this function leaves in the input stream any sign it
+% finds, then inserts the \meta{code} before the first non-sign token
+% (and removes \cs{s_@@_stop}). More precisely, it deletes any~|+| and
+% passes any~|-| to the input stream, hence should be called in an
+% integer expression.
+% \begin{macrocode}
+\cs_new:Npn \@@_pass_signs:wn #1
+ {
+ \if:w + \if:w - \exp_not:N #1 + \fi: \exp_not:N #1
+ \exp_after:wN \@@_pass_signs:wn
+ \else:
+ \exp_after:wN \@@_pass_signs_end:wn
+ \exp_after:wN #1
+ \fi:
+ }
+\cs_new:Npn \@@_pass_signs_end:wn #1 \s_@@_stop #2 { #2 #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\int_from_alph:n}
+% \UnitTested
+% \begin{macro}{\@@_from_alph:nN, \@@_from_alph:N}
+% First take care of signs then loop through the input using the
+% \texttt{recursion} quarks. The \cs{@@_from_alph:nN} auxiliary
+% collects in its first argument the value obtained so far, and the
+% auxiliary \cs{@@_from_alph:N} converts one letter to an expression
+% which evaluates to the correct number.
+% \begin{macrocode}
+\cs_new:Npn \int_from_alph:n #1
+ {
+ \int_eval:n
+ {
+ \exp_after:wN \@@_pass_signs:wn \tl_to_str:n {#1}
+ \s_@@_stop { \@@_from_alph:nN { 0 } }
+ \q_@@_recursion_tail \q_@@_recursion_stop
+ }
+ }
+\cs_new:Npn \@@_from_alph:nN #1#2
+ {
+ \@@_if_recursion_tail_stop_do:Nn #2 {#1}
+ \exp_args:Nf \@@_from_alph:nN
+ { \int_eval:n { #1 * 26 + \@@_from_alph:N #2 } }
+ }
+\cs_new:Npn \@@_from_alph:N #1
+ { `#1 - \int_compare:nNnTF { `#1 } < { 91 } { 64 } { 96 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\int_from_base:nn}
+% \UnitTested
+% \begin{macro}{\@@_from_base:nnN, \@@_from_base:N}
+% Leave the signs into the integer expression, then loop through
+% characters, collecting the value found so far in the first argument
+% of \cs{@@_from_base:nnN}. To convert a single character,
+% \cs{@@_from_base:N} checks first for digits, then distinguishes
+% lower from upper case letters, turning them into the appropriate
+% number. Note that this auxiliary does not use \cs{int_eval:n},
+% hence is not safe for general use.
+% \begin{macrocode}
+\cs_new:Npn \int_from_base:nn #1#2
+ {
+ \int_eval:n
+ {
+ \exp_after:wN \@@_pass_signs:wn \tl_to_str:n {#1}
+ \s_@@_stop { \@@_from_base:nnN { 0 } {#2} }
+ \q_@@_recursion_tail \q_@@_recursion_stop
+ }
+ }
+\cs_new:Npn \@@_from_base:nnN #1#2#3
+ {
+ \@@_if_recursion_tail_stop_do:Nn #3 {#1}
+ \exp_args:Nf \@@_from_base:nnN
+ { \int_eval:n { #1 * #2 + \@@_from_base:N #3 } }
+ {#2}
+ }
+\cs_new:Npn \@@_from_base:N #1
+ {
+ \int_compare:nNnTF { `#1 } < { 58 }
+ {#1}
+ { `#1 - \int_compare:nNnTF { `#1 } < { 91 } { 55 } { 87 } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\int_from_bin:n, \int_from_hex:n, \int_from_oct:n}
+% \UnitTested
+% Wrappers around the generic function.
+% \begin{macrocode}
+\cs_new:Npn \int_from_bin:n #1
+ { \int_from_base:nn {#1} { 2 } }
+\cs_new:Npn \int_from_hex:n #1
+ { \int_from_base:nn {#1} { 16 } }
+\cs_new:Npn \int_from_oct:n #1
+ { \int_from_base:nn {#1} { 8 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}
+% {
+% \c_@@_from_roman_i_int, \c_@@_from_roman_v_int,
+% \c_@@_from_roman_x_int, \c_@@_from_roman_l_int,
+% \c_@@_from_roman_c_int, \c_@@_from_roman_d_int,
+% \c_@@_from_roman_m_int,
+% \c_@@_from_roman_I_int, \c_@@_from_roman_V_int,
+% \c_@@_from_roman_X_int, \c_@@_from_roman_L_int,
+% \c_@@_from_roman_C_int, \c_@@_from_roman_D_int,
+% \c_@@_from_roman_M_int
+% }
+% Constants used to convert from Roman numerals to integers.
+% \begin{macrocode}
+\int_const:cn { c_@@_from_roman_i_int } { 1 }
+\int_const:cn { c_@@_from_roman_v_int } { 5 }
+\int_const:cn { c_@@_from_roman_x_int } { 10 }
+\int_const:cn { c_@@_from_roman_l_int } { 50 }
+\int_const:cn { c_@@_from_roman_c_int } { 100 }
+\int_const:cn { c_@@_from_roman_d_int } { 500 }
+\int_const:cn { c_@@_from_roman_m_int } { 1000 }
+\int_const:cn { c_@@_from_roman_I_int } { 1 }
+\int_const:cn { c_@@_from_roman_V_int } { 5 }
+\int_const:cn { c_@@_from_roman_X_int } { 10 }
+\int_const:cn { c_@@_from_roman_L_int } { 50 }
+\int_const:cn { c_@@_from_roman_C_int } { 100 }
+\int_const:cn { c_@@_from_roman_D_int } { 500 }
+\int_const:cn { c_@@_from_roman_M_int } { 1000 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\int_from_roman:n}
+% \UnitTested
+% \TestMissing{boundary cases / wrong input?}
+% \begin{macro}{\@@_from_roman:NN}
+% \begin{macro}{\@@_from_roman_error:w}
+% The method here is to iterate through the input, finding the
+% appropriate value for each letter and building up a sum. This is
+% then evaluated by \TeX{}. If any unknown letter is found, skip to
+% the closing parenthesis and insert |*0-1| afterwards, to replace the
+% value by $-1$.
+% \begin{macrocode}
+\cs_new:Npn \int_from_roman:n #1
+ {
+ \int_eval:n
+ {
+ (
+ 0
+ \exp_after:wN \@@_from_roman:NN \tl_to_str:n {#1}
+ \q_@@_recursion_tail \q_@@_recursion_tail \q_@@_recursion_stop
+ )
+ }
+ }
+\cs_new:Npn \@@_from_roman:NN #1#2
+ {
+ \@@_if_recursion_tail_stop:N #1
+ \int_if_exist:cF { c_@@_from_roman_ #1 _int }
+ { \@@_from_roman_error:w }
+ \@@_if_recursion_tail_stop_do:Nn #2
+ { + \use:c { c_@@_from_roman_ #1 _int } }
+ \int_if_exist:cF { c_@@_from_roman_ #2 _int }
+ { \@@_from_roman_error:w }
+ \int_compare:nNnTF
+ { \use:c { c_@@_from_roman_ #1 _int } }
+ <
+ { \use:c { c_@@_from_roman_ #2 _int } }
+ {
+ + \use:c { c_@@_from_roman_ #2 _int }
+ - \use:c { c_@@_from_roman_ #1 _int }
+ \@@_from_roman:NN
+ }
+ {
+ + \use:c { c_@@_from_roman_ #1 _int }
+ \@@_from_roman:NN #2
+ }
+ }
+\cs_new:Npn \@@_from_roman_error:w #1 \q_@@_recursion_stop #2
+ { #2 * 0 - 1 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Viewing integer}
+%
+% \begin{macro}{\int_show:N, \int_show:c}
+% \UnitTested
+% \begin{macro}{\@@_show:nN}
+% Diagnostics.
+% \begin{macrocode}
+\cs_new_eq:NN \int_show:N \__kernel_register_show:N
+\cs_generate_variant:Nn \int_show:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\int_show:n}
+% \UnitTested
+% We don't use the \TeX{} primitive \tn{showthe} to show integer
+% expressions: this gives a more unified output.
+% \begin{macrocode}
+\cs_new_protected:Npn \int_show:n
+ { \__kernel_msg_show_eval:Nn \int_eval:n }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\int_log:N, \int_log:c}
+% Diagnostics.
+% \begin{macrocode}
+\cs_new_eq:NN \int_log:N \__kernel_register_log:N
+\cs_generate_variant:Nn \int_log:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\int_log:n}
+% Similar to \cs{int_show:n}.
+% \begin{macrocode}
+\cs_new_protected:Npn \int_log:n
+ { \__kernel_msg_log_eval:Nn \int_eval:n }
+% \end{macrocode}
+% \end{macro}
+%
+%\subsection{Random integers}
+%
+% \begin{macro}{\int_rand:nn}
+% Defined in \pkg{l3fp-random}.
+% \end{macro}
+%
+% \subsection{Constant integers}
+%
+% \begin{variable}{\c_zero_int, \c_one_int}
+% The zero is defined in \pkg{l3basics}.
+% \begin{macrocode}
+\int_const:Nn \c_one_int { 1 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_max_int}
+% The largest number allowed is $2^{31}-1$
+% \begin{macrocode}
+\int_const:Nn \c_max_int { 2 147 483 647 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_max_char_int}
+% The largest character code is $1114111$ (hexadecimal |10FFFF|) in
+% \XeTeX{} and \LuaTeX{} and $255$ in other engines. In many places
+% \pTeX{} and \upTeX{} support larger character codes but for instance
+% the values of \tn{lccode} are restricted to $[0,255]$.
+% \begin{macrocode}
+\int_const:Nn \c_max_char_int
+ {
+ \if_int_odd:w 0
+ \cs_if_exist:NT \tex_luatexversion:D { 1 }
+ \cs_if_exist:NT \tex_XeTeXversion:D { 1 } ~
+ "10FFFF
+ \else:
+ "FF
+ \fi:
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Scratch integers}
+%
+% \begin{variable}{\l_tmpa_int, \l_tmpb_int}
+% \begin{variable}{\g_tmpa_int, \g_tmpb_int}
+% We provide two local and two global scratch counters, maybe we
+% need more or less.
+% \begin{macrocode}
+\int_new:N \l_tmpa_int
+\int_new:N \l_tmpb_int
+\int_new:N \g_tmpa_int
+\int_new:N \g_tmpb_int
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \subsection{Integers for earlier modules}
+%
+%<@@=seq>
+%
+% \begin{variable}{\l_@@_internal_a_int, \l_@@_internal_b_int}
+% \begin{macrocode}
+\int_new:N \l_@@_internal_a_int
+\int_new:N \l_@@_internal_b_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3intarray.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3intarray.dtx
new file mode 100644
index 00000000000..bbc41c0cdb3
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3intarray.dtx
@@ -0,0 +1,974 @@
+% \iffalse meta-comment
+%
+%% File: l3intarray.dtx
+%
+% Copyright (C) 2017-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+%
+% \title{^^A
+% The \pkg{l3intarray} module\\ Fast global integer arrays^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% For applications requiring heavy use of integers, this module provides
+% arrays which can be accessed in constant time (contrast \pkg{l3seq},
+% where access time is linear). These arrays have several important
+% features
+% \begin{itemize}
+% \item The size of the array is fixed and must be given at
+% point of initialisation
+% \item The absolute value of each entry has maximum $2^{30}-1$
+% (\emph{i.e.}~one power lower than the usual \cs{c_max_int}
+% ceiling of $2^{31}-1$)
+% \end{itemize}
+% The use of \texttt{intarray} data is therefore recommended for cases where
+% the need for fast access is of paramount importance.
+%
+% \section{Creating and initialising integer array variables}
+%
+% \begin{function}[added = 2018-03-29]{\intarray_new:Nn, \intarray_new:cn}
+% \begin{syntax}
+% \cs{intarray_new:Nn} \meta{intarray~var} \Arg{size}
+% \end{syntax}
+% Evaluates the integer expression \meta{size} and allocates an
+% \meta{integer array variable} with that number of (zero) entries.
+% The variable name should start with |\g_| because assignments are
+% always global.
+% \end{function}
+%
+% \begin{function}[added = 2018-05-04]
+% {\intarray_const_from_clist:Nn, \intarray_const_from_clist:cn}
+% \begin{syntax}
+% \cs{intarray_const_from_clist:Nn} \meta{intarray~var} \meta{int expr clist}
+% \end{syntax}
+% Creates a new constant \meta{integer array variable} or raises an
+% error if the name is already taken. The \meta{integer array
+% variable} is set (globally) to contain as its items the results of
+% evaluating each \meta{integer expression} in the \meta{comma list}.
+% \end{function}
+%
+% \begin{function}[added = 2018-05-04]{\intarray_gzero:N, \intarray_gzero:c}
+% \begin{syntax}
+% \cs{intarray_gzero:N} \meta{intarray~var}
+% \end{syntax}
+% Sets all entries of the \meta{integer array variable} to zero.
+% Assignments are always global.
+% \end{function}
+%
+% \section{Adding data to integer arrays}
+%
+% \begin{function}[added = 2018-03-29]{\intarray_gset:Nnn, \intarray_gset:cnn}
+% \begin{syntax}
+% \cs{intarray_gset:Nnn} \meta{intarray~var} \Arg{position} \Arg{value}
+% \end{syntax}
+% Stores the result of evaluating the integer expression \meta{value}
+% into the \meta{integer array variable} at the (integer expression)
+% \meta{position}. If the \meta{position} is not between $1$ and the
+% \cs{intarray_count:N}, or the \meta{value}'s absolute value is
+% bigger than $2^{30}-1$, an error occurs. Assignments are always
+% global.
+% \end{function}
+%
+% \section{Couting entries in integer arrays}
+%
+% \begin{function}[EXP, added = 2018-03-29]{\intarray_count:N, \intarray_count:c}
+% \begin{syntax}
+% \cs{intarray_count:N} \meta{intarray~var}
+% \end{syntax}
+% Expands to the number of entries in the \meta{integer array variable}.
+% Contrarily to \cs{seq_count:N} this is performed in constant time.
+% \end{function}
+%
+% \section{Using a single entry}
+%
+% \begin{function}[EXP, added = 2018-03-29]{\intarray_item:Nn, \intarray_item:cn}
+% \begin{syntax}
+% \cs{intarray_item:Nn} \meta{intarray~var} \Arg{position}
+% \end{syntax}
+% Expands to the integer entry stored at the (integer expression)
+% \meta{position} in the \meta{integer array variable}. If the
+% \meta{position} is not between $1$ and the \cs{intarray_count:N}, an
+% error occurs.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-05-05]
+% {\intarray_rand_item:N, \intarray_rand_item:c}
+% \begin{syntax}
+% \cs{intarray_rand_item:N} \meta{intarray~var}
+% \end{syntax}
+% Selects a pseudo-random item of the \meta{integer array}. If the
+% \meta{integer array} is empty, produce an error.
+% \end{function}
+%
+% \section{Integer array conditional}
+%
+% \begin{function}[pTF, added = 2024-03-31]
+% {\intarray_if_exist:N, \intarray_if_exist:c}
+% \begin{syntax}
+% \cs{intarray_if_exist_p:N} \meta{intarray~var}
+% \cs{intarray_if_exist:NTF} \meta{intarray~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{intarray~var} is currently defined. This
+% does not check that the \meta{intarray~var} really is an integer
+% array variable.
+% \end{function}
+%
+% \section{Viewing integer arrays}
+%
+% \begin{function}[added = 2018-05-04]
+% {\intarray_show:N, \intarray_show:c, \intarray_log:N, \intarray_log:c}
+% \begin{syntax}
+% \cs{intarray_show:N} \meta{intarray~var}
+% \cs{intarray_log:N} \meta{intarray~var}
+% \end{syntax}
+% Displays the items in the \meta{integer array variable} in the
+% terminal or writes them in the log file.
+% \end{function}
+%
+% \section{Implementation notes}
+%
+% It is a wrapper around the \tn{fontdimen} primitive, used to store
+% arrays of integers (with a restricted range: absolute value at most
+% $2^{30}-1$). In contrast to \pkg{l3seq} sequences the access to
+% individual entries is done in constant time rather than linear time,
+% but only integers can be stored. More precisely, the primitive
+% \tn{fontdimen} stores dimensions but the \pkg{l3intarray} module
+% transparently converts these from/to integers. Assignments are always
+% global.
+%
+% While \LuaTeX{}'s memory is extensible, other engines can
+% \enquote{only} deal with a bit less than $4\times 10^6$ entries in all
+% \tn{fontdimen} arrays combined (with default \TeX{} Live settings).
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3intarray} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=intarray>
+% \end{macrocode}
+%
+% There are two implementations for this module: One \cs{fontdimen} based one
+% for more traditional \TeX\ engines and a Lua based one for engines with Lua support.
+%
+% Both versions do not allow negative array sizes.
+% \begin{macrocode}
+%<*tex>
+\msg_new:nnn { kernel } { negative-array-size }
+ { Size~of~array~may~not~be~negative:~#1 }
+% \end{macrocode}
+%
+% \begin{variable}{\l_@@_loop_int}
+% A loop index.
+% \begin{macrocode}
+\int_new:N \l_@@_loop_int
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Lua implementation}
+% First, let's look at the Lua variant:
+%
+% We select the Lua version if the Lua helpers were defined. This can be detected by
+% the presence of \cs{@@_gset_count:Nw}.
+%
+% \begin{macrocode}
+\cs_if_exist:NTF \@@_gset_count:Nw
+ {
+% \end{macrocode}
+%
+% \subsubsection{Allocating arrays}
+%
+% \begin{variable}{\g_@@_table_int, \l_@@_bad_index_int}
+% Used to differentiate intarrays in Lua and to record an invalid index.
+% \begin{macrocode}
+ \int_new:N \g_@@_table_int
+ \int_new:N \l_@@_bad_index_int
+%</tex>
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@:w}
+% Used as marker for intarrays in Lua. Followed by an unbraced number
+% identifying the array and a single space. This format is used to make it
+% easy to scan from Lua.
+% \begin{macrocode}
+%<*lua>
+luacmd('@@:w', function()
+ scan_int()
+ tex.error'LaTeX Error: Isolated intarray ignored'
+end, 'protected', 'global')
+%</lua>
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\intarray_new:Nn, \intarray_new:cn}
+% \begin{macro}{\@@_new:N}
+% Declare |#1| as a tokenlist with the scanmark and a unique number.
+% Pass the array's size to the Lua helper.
+% Every \texttt{intarray} must be global; it's enough to run this
+% check in \cs{intarray_new:Nn}.
+% \begin{macrocode}
+%<*tex>
+ \cs_new_protected:Npn \@@_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \int_gincr:N \g_@@_table_int
+ \cs_gset_nopar:Npe #1 { \@@:w \int_use:N \g_@@_table_int \c_space_tl }
+ }
+ \cs_new_protected:Npn \intarray_new:Nn #1#2
+ {
+ \@@_new:N #1
+ \@@_gset_count:Nw #1 \int_eval:n {#2} \scan_stop:
+ \int_compare:nNnT { \intarray_count:N #1 } < 0
+ {
+ \msg_error:nne { kernel } { negative-array-size }
+ { \intarray_count:N #1 }
+ }
+ }
+ \cs_generate_variant:Nn \intarray_new:Nn { c }
+%</tex>
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% Before we get to the first command implmented in Lua, we first need some
+% definitions. Since \texttt{token.create} only works correctly if \TeX{}
+% has seen the tokens before, we first run a short \TeX{} sequence to ensure
+% that all relevant control sequences are known.
+% \begin{macrocode}
+%<*lua>
+
+local scan_token = token.scan_token
+local put_next = token.put_next
+local intarray_marker = token_create_safe'@@:w'
+local use_none = token_create_safe'use_none:n'
+local use_i = token_create_safe'use:n'
+local expand_after_scan_stop = {token_create_safe'exp_after:wN',
+ token_create_safe'scan_stop:'}
+local comma = token_create(string.byte',')
+% \end{macrocode}
+%
+% \begin{macro}{@@_table}
+% Internal helper to scan an intarray token, extract the associated
+% Lua table and return an error if the input is invalid.
+%
+% \begin{macrocode}
+local @@_table do
+ local tables = get_luadata and get_luadata'@@' or {[0] = {}}
+ function @@_table()
+ local t = scan_token()
+ if t ~= intarray_marker then
+ put_next(t)
+ tex.error'LaTeX Error: intarray expected'
+ return tables[0]
+ end
+ local i = scan_int()
+ local current_table = tables[i]
+ if current_table then return current_table end
+ current_table = {}
+ tables[i] = current_table
+ return current_table
+ end
+% \end{macrocode}
+% Since in \LaTeX{} this is loaded in the format, we want to preserve any intarrays
+% which are created while format building for the actual run.
+%
+% To do this, we use the \texttt{register_luadata} mechanism from \pkg{l3luatex}:
+% Directly before the format get dumped, the following function gets invoked and serializes
+% all existing tables into a string. This string gets compiled and dumped into the format and
+% is made available at the beginning of regular runs as \texttt{get_luadata'@@'}.
+% \begin{macrocode}
+ if register_luadata then
+ register_luadata('@@', function()
+ local t = "{[0]={},"
+ for i=1, #tables do
+ t = string.format("%s{%s},", t, table.concat(tables[i], ','))
+ end
+ return t .. "}"
+ end)
+ end
+end
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\intarray_count:N, \intarray_count:c}
+% \begin{macro}[EXP]{\@@_gset_count:Nw}
+% Set and get the size of an array. ``Setting the size'' means in this context that
+% we add zeros until we reach the desired size.
+% \begin{macrocode}
+
+local sprint = tex.sprint
+
+luacmd('@@_gset_count:Nw', function()
+ local t = @@_table()
+ local n = scan_int()
+ for i=#t+1, n do t[i] = 0 end
+end, 'protected', 'global')
+
+luacmd('intarray_count:N', function()
+ sprint(-2, #@@_table())
+end, 'global')
+%</lua>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*tex>
+ \cs_generate_variant:Nn \intarray_count:N { c }
+%</tex>
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Array items}
+%
+% \begin{macro}{\@@_gset:wF, \@@_gset:w}
+% The setter provided by Lua. The argument order somewhat emulates the |\fontdimen|:
+% First the array index, followed by the intarray and then the new value.
+% This has been chosen over a more conventional order to provide a delimiter for the numbers.
+% \begin{macrocode}
+%<*lua>
+luacmd('@@_gset:wF', function()
+ local i = scan_int()
+ local t = @@_table()
+ if t[i] then
+ t[i] = scan_int()
+ put_next(use_none)
+ else
+ tex.count.l_@@_bad_index_int = i
+ scan_int()
+ put_next(use_i)
+ end
+end, 'protected', 'global')
+
+luacmd('@@_gset:w', function()
+ local i = scan_int()
+ local t = @@_table()
+ t[i] = scan_int()
+end, 'protected', 'global')
+%</lua>
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\intarray_gset:Nnn, \intarray_gset:cnn, \__kernel_intarray_gset:Nnn}
+% The \cs{__kernel_intarray_gset:Nnn} function does not use
+% \cs{int_eval:n}, namely its arguments must be suitable for
+% \cs{int_value:w}. The user version checks the position and value
+% are within bounds.
+% \begin{macrocode}
+%<*tex>
+ \cs_new_protected:Npn \__kernel_intarray_gset:Nnn #1#2#3
+ { \@@_gset:w #2 #1 #3 \scan_stop: }
+ \cs_new_protected:Npn \intarray_gset:Nnn #1#2#3
+ {
+ \@@_gset:wF \int_eval:n {#2} #1 \int_eval:n{#3}
+ {
+ \msg_error:nneee { kernel } { out-of-bounds }
+ { \token_to_str:N #1 } { \int_use:N \l_@@_bad_index_int } { \intarray_count:N #1 }
+ }
+ }
+ \cs_generate_variant:Nn \intarray_gset:Nnn { c }
+%</tex>
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\intarray_gzero:N, \intarray_gzero:c}
+% Set the appropriate array entry to zero. No bound checking
+% needed.
+% \begin{macrocode}
+%<*lua>
+luacmd('intarray_gzero:N', function()
+ local t = @@_table()
+ for i=1, #t do
+ t[i] = 0
+ end
+end, 'global', 'protected')
+%</lua>
+%<*tex>
+ \cs_generate_variant:Nn \intarray_gzero:N { c }
+%</tex>
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\intarray_item:Nn, \intarray_item:cn, \__kernel_intarray_item:Nn}
+% \begin{macro}{\@@_item:wF,\@@_item:w}
+% Get the appropriate entry and perform bound checks. The
+% \cs{__kernel_intarray_item:Nn} function omits bound checks and omits
+% \cs{int_eval:n}, namely its argument must be a \TeX{} integer
+% suitable for \cs{int_value:w}.
+% \begin{macrocode}
+%<*lua>
+luacmd('@@_item:wF', function()
+ local i = scan_int()
+ local t = @@_table()
+ local item = t[i]
+ if item then
+ put_next(use_none)
+ else
+ tex.l_@@_bad_index_int = i
+ put_next(use_i)
+ end
+ put_next(expand_after_scan_stop)
+ scan_token()
+ if item then
+ sprint(-2, item)
+ end
+end, 'global')
+
+luacmd('@@_item:w', function()
+ local i = scan_int()
+ local t = @@_table()
+ sprint(-2, t[i])
+end, 'global')
+%</lua>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*tex>
+ \cs_new:Npn \__kernel_intarray_item:Nn #1#2
+ { \@@_item:w #2 #1 }
+ \cs_new:Npn \intarray_item:Nn #1#2
+ {
+ \@@_item:wF \int_eval:n {#2} #1
+ {
+ \msg_expandable_error:nnfff { kernel } { out-of-bounds }
+ { \token_to_str:N #1 } { \int_use:N \l_@@_bad_index_int } { \intarray_count:N #1 }
+ 0
+ }
+ }
+ \cs_generate_variant:Nn \intarray_item:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\intarray_rand_item:N, \intarray_rand_item:c}
+% Importantly, \cs{intarray_item:Nn} only evaluates its argument once.
+% \begin{macrocode}
+ \cs_new:Npn \intarray_rand_item:N #1
+ { \intarray_item:Nn #1 { \int_rand:n { \intarray_count:N #1 } } }
+ \cs_generate_variant:Nn \intarray_rand_item:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Working with contents of integer arrays}
+%
+% \begin{macro}{\intarray_const_from_clist:Nn, \intarray_const_from_clist:cn}
+% We use the \cs{__kernel_intarray_gset:Nnn} which does not do bounds checking
+% and instead automatically resizes the array.
+% This is not implemented in Lua to ensure that the clist parsing is consistent
+% with the clist module.
+% \begin{macrocode}
+ \cs_new_protected:Npn \intarray_const_from_clist:Nn #1#2
+ {
+ \@@_new:N #1
+ \int_zero:N \l_@@_loop_int
+ \clist_map_inline:nn {#2}
+ {
+ \int_incr:N \l_@@_loop_int
+ \__kernel_intarray_gset:Nnn #1 \l_@@_loop_int { \int_eval:n {##1} } }
+ }
+ \cs_generate_variant:Nn \intarray_const_from_clist:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_to_clist:Nn, \@@_to_clist:w}
+% The \cs{@@_to_clist:Nn} auxiliary allows to choose the delimiter and
+% is also used in \cs{intarray_show:N}. Here we just pass the information
+% to Lua and let \texttt{table.concat} do the actual work.
+% We discard the category codes of the passed delimiter but this is not
+% an issue since the delimiter is always just a comma or a comma and a space.
+% In both cases \texttt{sprint(2, ...)} provides the right catcodes.
+% \begin{macrocode}
+%</tex>
+%<*lua>
+local concat = table.concat
+luacmd('@@_to_clist:Nn', function()
+ local t = @@_table()
+ local sep = token.scan_string()
+ sprint(-2, concat(t, sep))
+end, 'global')
+%</lua>
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\__kernel_intarray_range_to_clist:Nnn, \@@_range_to_clist:w}
+% Loop through part of the array.
+% \begin{macrocode}
+%<*tex>
+ \cs_new:Npn \__kernel_intarray_range_to_clist:Nnn #1#2#3
+ {
+ \@@_range_to_clist:w #1
+ \int_eval:n {#2} ~ \int_eval:n {#3} ~
+ }
+%</tex>
+%<*lua>
+luacmd('@@_range_to_clist:w', function()
+ local t = @@_table()
+ local from = scan_int()
+ local to = scan_int()
+ sprint(-2, concat(t, ',', from, to))
+end, 'global')
+%</lua>
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__kernel_intarray_gset_range_from_clist:Nnn, \@@_gset_range:nNw}
+% Loop through part of the array. We allow additional commas at the end.
+% \begin{macrocode}
+%<*tex>
+ \cs_new_protected:Npn \__kernel_intarray_gset_range_from_clist:Nnn #1#2#3
+ {
+ \@@_gset_range:w \int_eval:w #2 #1 #3 , , \scan_stop:
+ }
+%</tex>
+%<*lua>
+luacmd('@@_gset_range:w', function()
+ local from = scan_int()
+ local t = @@_table()
+ while true do
+ local tok = scan_token()
+ if tok == comma then
+ repeat
+ tok = scan_token()
+ until tok ~= comma
+ break
+ else
+ put_next(tok)
+ end
+ t[from] = scan_int()
+ scan_token()
+ from = from + 1
+ end
+ end, 'global', 'protected')
+%</lua>
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_gset_overflow_test:nw}
+% In order to allow some code sharing later we provide the
+% \cs{@@_gset_overflow_test:nw} name here. It doesn't actually test anything
+% since the Lua implementation accepts all integers which could be tested with
+% \cs{tex_ifabsnum:D}.
+% \begin{macrocode}
+%<*tex>
+ \cs_new_protected:Npn \@@_gset_overflow_test:nw #1
+ {
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Font dimension based implementation}
+%
+% Go to the false branch of the conditional above.
+% \begin{macrocode}
+ }
+ {
+% \end{macrocode}
+%
+% \subsubsection{Allocating arrays}
+%
+% \begin{macro}{\@@_entry:w, \@@_count:w}
+% We use these primitives quite a lot in this module.
+% \begin{macrocode}
+ \cs_new_eq:NN \@@_entry:w \tex_fontdimen:D
+ \cs_new_eq:NN \@@_count:w \tex_hyphenchar:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\c_@@_sp_dim}
+% Used to convert integers to dimensions fast.
+% \begin{macrocode}
+ \dim_const:Nn \c_@@_sp_dim { 1 sp }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_font_int}
+% Used to assign one font per array.
+% \begin{macrocode}
+ \int_new:N \g_@@_font_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\intarray_new:Nn, \intarray_new:cn}
+% \begin{macro}{\@@_new:N}
+% Declare |#1| to be a font (arbitrarily |cmr10| at a never-used
+% size). Store the array's size as the \tn{hyphenchar} of that font
+% and make sure enough \tn{fontdimen} are allocated, by setting the
+% last one. Then clear any \tn{fontdimen} that |cmr10| starts with.
+% It seems \LuaTeX{}'s |cmr10| has an extra \tn{fontdimen} parameter
+% number $8$ compared to other engines (for a math font we would
+% replace $8$ by $22$ or some such).
+% Every \texttt{intarray} must be global; it's enough to run this
+% check in \cs{intarray_new:Nn}.
+% \begin{macrocode}
+ \cs_new_protected:Npn \@@_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \int_gincr:N \g_@@_font_int
+ \tex_global:D \tex_font:D #1
+ = cmr10~at~ \g_@@_font_int \c_@@_sp_dim \scan_stop:
+ \int_step_inline:nn { 8 }
+ { \__kernel_intarray_gset:Nnn #1 {##1} \c_zero_int }
+ }
+ \cs_new_protected:Npn \intarray_new:Nn #1#2
+ {
+ \@@_new:N #1
+ \@@_count:w #1 = \int_eval:n {#2} \scan_stop:
+ \int_compare:nNnT { \intarray_count:N #1 } < 0
+ {
+ \msg_error:nne { kernel } { negative-array-size }
+ { \intarray_count:N #1 }
+ }
+ \int_compare:nNnT { \intarray_count:N #1 } > 0
+ { \__kernel_intarray_gset:Nnn #1 { \intarray_count:N #1 } { 0 } }
+ }
+ \cs_generate_variant:Nn \intarray_new:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\intarray_count:N, \intarray_count:c}
+% Size of an array.
+% \begin{macrocode}
+ \cs_new:Npn \intarray_count:N #1 { \int_value:w \@@_count:w #1 }
+ \cs_generate_variant:Nn \intarray_count:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Array items}
+%
+% \begin{macro}[EXP]{\@@_signed_max_dim:n}
+% Used when an item to be stored is larger than \cs{c_max_dim} in
+% absolute value; it is replaced by $\pm\cs{c_max_dim}$.
+% \begin{macrocode}
+ \cs_new:Npn \@@_signed_max_dim:n #1
+ { \int_value:w \int_compare:nNnT {#1} < 0 { - } \c_max_dim }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_bounds:NNnTF, \@@_bounds_error:NNnw}
+% The functions \cs{intarray_gset:Nnn} and \cs{intarray_item:Nn} share
+% bounds checking. The |T| branch is used if |#3| is within bounds of
+% the array |#2|.
+% \begin{macrocode}
+ \cs_new:Npn \@@_bounds:NNnTF #1#2#3
+ {
+ \if_int_compare:w 1 > #3 \exp_stop_f:
+ \@@_bounds_error:NNnw #1 #2 {#3}
+ \else:
+ \if_int_compare:w #3 > \intarray_count:N #2 \exp_stop_f:
+ \@@_bounds_error:NNnw #1 #2 {#3}
+ \fi:
+ \fi:
+ \use_i:nn
+ }
+ \cs_new:Npn \@@_bounds_error:NNnw #1#2#3#4 \use_i:nn #5#6
+ {
+ #4
+ #1 { kernel } { out-of-bounds }
+ { \token_to_str:N #2 } {#3} { \intarray_count:N #2 }
+ #6
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\intarray_gset:Nnn, \intarray_gset:cnn, \__kernel_intarray_gset:Nnn}
+% \begin{macro}{\@@_gset:Nnn, \@@_gset_overflow:Nnn}
+% Set the appropriate \tn{fontdimen}. The
+% \cs{__kernel_intarray_gset:Nnn} function does not use
+% \cs{int_eval:n}, namely its arguments must be suitable for
+% \cs{int_value:w}. The user version checks the position and value
+% are within bounds.
+% \begin{macrocode}
+ \cs_new_protected:Npn \__kernel_intarray_gset:Nnn #1#2#3
+ { \@@_entry:w #2 #1 #3 \c_@@_sp_dim }
+ \cs_new_protected:Npn \intarray_gset:Nnn #1#2#3
+ {
+ \exp_after:wN \@@_gset:Nww
+ \exp_after:wN #1
+ \int_value:w \int_eval:n {#2} \exp_after:wN ;
+ \int_value:w \int_eval:n {#3} ;
+ }
+ \cs_generate_variant:Nn \intarray_gset:Nnn { c }
+ \cs_new_protected:Npn \@@_gset:Nww #1#2 ; #3 ;
+ {
+ \@@_bounds:NNnTF \msg_error:nneee #1 {#2}
+ {
+ \@@_gset_overflow_test:nw {#3}
+ \__kernel_intarray_gset:Nnn #1 {#2} {#3}
+ }
+ { }
+ }
+ \cs_if_exist:NTF \tex_ifabsnum:D
+ {
+ \cs_new_protected:Npn \@@_gset_overflow_test:nw #1
+ {
+ \tex_ifabsnum:D #1 > \c_max_dim
+ \exp_after:wN \@@_gset_overflow:NNnn
+ \fi:
+ }
+ }
+ {
+ \cs_new_protected:Npn \@@_gset_overflow_test:nw #1
+ {
+ \if_int_compare:w \int_abs:n {#1} > \c_max_dim
+ \exp_after:wN \@@_gset_overflow:NNnn
+ \fi:
+ }
+ }
+ \cs_new_protected:Npn \@@_gset_overflow:NNnn #1#2#3#4
+ {
+ \msg_error:nneeee { kernel } { overflow }
+ { \token_to_str:N #2 } {#3} {#4} { \@@_signed_max_dim:n {#4} }
+ #1 #2 {#3} { \@@_signed_max_dim:n {#4} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\intarray_gzero:N, \intarray_gzero:c}
+% Set the appropriate \tn{fontdimen} to zero. No bound checking
+% needed. The \cs{prg_replicate:nn} possibly uses quite a lot of
+% memory, but this is somewhat comparable to the size of the array,
+% and it is much faster than an \cs{int_step_inline:nn} loop.
+% \begin{macrocode}
+ \cs_new_protected:Npn \intarray_gzero:N #1
+ {
+ \int_zero:N \l_@@_loop_int
+ \prg_replicate:nn { \intarray_count:N #1 }
+ {
+ \int_incr:N \l_@@_loop_int
+ \@@_entry:w \l_@@_loop_int #1 \c_zero_dim
+ }
+ }
+ \cs_generate_variant:Nn \intarray_gzero:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\intarray_item:Nn, \intarray_item:cn, \__kernel_intarray_item:Nn}
+% \begin{macro}{\@@_item:Nw}
+% Get the appropriate \tn{fontdimen} and perform bound checks. The
+% \cs{__kernel_intarray_item:Nn} function omits bound checks and omits
+% \cs{int_eval:n}, namely its argument must be a \TeX{} integer
+% suitable for \cs{int_value:w}.
+% \begin{macrocode}
+ \cs_new:Npn \__kernel_intarray_item:Nn #1#2
+ { \int_value:w \@@_entry:w #2 #1 }
+ \cs_new:Npn \intarray_item:Nn #1#2
+ {
+ \exp_after:wN \@@_item:Nw
+ \exp_after:wN #1
+ \int_value:w \int_eval:n {#2} ;
+ }
+ \cs_generate_variant:Nn \intarray_item:Nn { c }
+ \cs_new:Npn \@@_item:Nw #1#2 ;
+ {
+ \@@_bounds:NNnTF \msg_expandable_error:nnfff #1 {#2}
+ { \__kernel_intarray_item:Nn #1 {#2} }
+ { 0 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\intarray_rand_item:N, \intarray_rand_item:c}
+% Importantly, \cs{intarray_item:Nn} only evaluates its argument once.
+% \begin{macrocode}
+ \cs_new:Npn \intarray_rand_item:N #1
+ { \intarray_item:Nn #1 { \int_rand:n { \intarray_count:N #1 } } }
+ \cs_generate_variant:Nn \intarray_rand_item:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Working with contents of integer arrays}
+%
+% \begin{macro}{\intarray_const_from_clist:Nn, \intarray_const_from_clist:cn}
+% \begin{macro}{\@@_const_from_clist:nN}
+% Similar to \cs{intarray_new:Nn} (which we don't use because when
+% debugging is enabled that function checks the variable name starts
+% with |g_|). We make use of the fact that \TeX{} allows allocation
+% of successive \tn{fontdimen} as long as no other font has been
+% declared: no need to count the comma list items first. We need the
+% code in \cs{intarray_gset:Nnn} that checks the item value is not too
+% big, namely \cs{@@_gset_overflow_test:nw}, but not the code that
+% checks bounds. At the end, set the size of the intarray.
+% \begin{macrocode}
+ \cs_new_protected:Npn \intarray_const_from_clist:Nn #1#2
+ {
+ \@@_new:N #1
+ \int_zero:N \l_@@_loop_int
+ \clist_map_inline:nn {#2}
+ { \exp_args:Nf \@@_const_from_clist:nN { \int_eval:n {##1} } #1 }
+ \@@_count:w #1 \l_@@_loop_int
+ }
+ \cs_generate_variant:Nn \intarray_const_from_clist:Nn { c }
+ \cs_new_protected:Npn \@@_const_from_clist:nN #1#2
+ {
+ \int_incr:N \l_@@_loop_int
+ \@@_gset_overflow_test:nw {#1}
+ \__kernel_intarray_gset:Nnn #2 \l_@@_loop_int {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_to_clist:Nn, \@@_to_clist:w}
+% Loop through the array, putting a comma before each item. Remove
+% the leading comma with |f|-expansion. We also use the auxiliary in
+% \cs{intarray_show:N} with argument comma, space.
+% \begin{macrocode}
+ \cs_new:Npn \@@_to_clist:Nn #1#2
+ {
+ \int_compare:nNnF { \intarray_count:N #1 } = \c_zero_int
+ {
+ \exp_last_unbraced:Nf \use_none:n
+ { \@@_to_clist:w 1 ; #1 {#2} \prg_break_point: }
+ }
+ }
+ \cs_new:Npn \@@_to_clist:w #1 ; #2#3
+ {
+ \if_int_compare:w #1 > \@@_count:w #2
+ \prg_break:n
+ \fi:
+ #3 \__kernel_intarray_item:Nn #2 {#1}
+ \exp_after:wN \@@_to_clist:w
+ \int_value:w \int_eval:w #1 + \c_one_int ; #2 {#3}
+ }
+% \end{macrocode}
+% \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{macrocode}
+ }
+% \end{macrocode}
+%
+% \subsection{Common parts}
+%
+% \begin{macro}[pTF]{\intarray_if_exist:N, \intarray_if_exist:c}
+% Copies of the \texttt{cs} functions defined in \pkg{l3basics}.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \intarray_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \intarray_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+% \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}
+\cs_new_protected:Npn \intarray_show:N { \@@_show:NN \msg_show:nneeee }
+\cs_generate_variant:Nn \intarray_show:N { c }
+\cs_new_protected:Npn \intarray_log:N { \@@_show:NN \msg_log:nneeee }
+\cs_generate_variant:Nn \intarray_log:N { c }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ #1 { intarray } { show }
+ { \token_to_str:N #2 }
+ { \intarray_count:N #2 }
+ { >~ \@@_to_clist:Nn #2 { , ~ } }
+ { }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</tex>
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3kernel-functions.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3kernel-functions.dtx
new file mode 100644
index 00000000000..1706bb594a7
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3kernel-functions.dtx
@@ -0,0 +1,739 @@
+% \iffalse meta-comment
+%
+%% File: l3kernel-functions.dtx
+%
+% Copyright (C) 2018-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3kernel-functions} module\\ Kernel-reserved functions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3kernel-functions}: kernel-reserved functions}
+%
+% \subsection{Internal \pkg{l3debug} kernel functions}
+% \label{sec:l3kernel-functions:l3debug-internals}
+%
+% These function are only created if debugging is enabled, hence they are
+% actually defined in \pkg{l3debug}.
+%
+% \begin{function}{\__kernel_chk_var_local:N, \__kernel_chk_var_global:N}
+% \begin{syntax}
+% \cs{__kernel_chk_var_local:N} \meta{var}
+% \cs{__kernel_chk_var_global:N} \meta{var}
+% \end{syntax}
+% Applies \cs{__kernel_chk_var_exist:N} \meta{var} as well as
+% \cs{__kernel_chk_var_scope:NN} \meta{scope} \meta{var}, where
+% \meta{scope} is |l| or~|g|.
+% \end{function}
+%
+% \begin{function}{\__kernel_chk_var_scope:NN}
+% \begin{syntax}
+% \cs{__kernel_chk_var_scope:NN} \meta{scope} \meta{var}
+% \end{syntax}
+% Checks the \meta{var} has the correct \meta{scope}, and if not
+% raises a kernel-level error.
+% The \meta{scope} is a single letter |l|, |g|,
+% |c| denoting local variables, global variables, or constants. More
+% precisely, if the variable name starts with a letter and an
+% underscore (normal \pkg{expl3} convention) the function checks that
+% this single letter matches the \meta{scope}. Otherwise the function
+% cannot know the scope \meta{var} the first time: instead, it defines
+% |\__debug_chk_/|\meta{var name} to store that information for the
+% next call. Thus, if a given \meta{var} is subject to assignments of
+% different scopes a kernel error will result.
+% \end{function}
+%
+% \begin{function}
+% {
+% \__kernel_chk_cs_exist:N,
+% \__kernel_chk_cs_exist:c,
+% \__kernel_chk_var_exist:N
+% }
+% \begin{syntax}
+% \cs{__kernel_chk_cs_exist:N} \meta{cs}
+% \cs{__kernel_chk_var_exist:N} \meta{var}
+% \end{syntax}
+% Checks that their argument is defined according to the criteria for
+% \cs{cs_if_exist_p:N},
+% and if not raises a kernel-level error. Error messages are
+% different.
+% \end{function}
+%
+% \begin{function}[EXP]{\__kernel_chk_flag_exist:NN}
+% \begin{syntax}
+% \cs{__kernel_chk_flag_exist:NN}
+% \meta{function} \meta{flag}
+% \end{syntax}
+% Checks that the \meta{flag} is defined according to the criterion for
+% \cs{flag_if_exist_p:N}, and if not raises a kernel-level error and
+% calls the function with the argument \cs{l_tmpa_flag} to proceed
+% somehow without producing too many errors.
+% \end{function}
+%
+% \begin{function}{\__kernel_debug_log:e}
+% \begin{syntax}
+% \cs{__kernel_debug_log:e} \Arg{message text}
+% \end{syntax}
+% If the \texttt{log-functions} option is active, this function writes
+% the \meta{message text} to the log file using \cs{iow_log:e}.
+% Otherwise, the \meta{message text} is ignored using \cs{use_none:n}.
+% \end{function}
+%
+% \subsection{Internal kernel functions}
+%
+% \begin{function}{\__kernel_chk_defined:NT}
+% \begin{syntax}
+% \cs{__kernel_chk_defined:NT} \meta{variable} \Arg{true code}
+% \end{syntax}
+% If \meta{variable} is not defined (according to
+% \cs{cs_if_exist:NTF}), this triggers an error, otherwise the
+% \meta{true code} is run.
+% \end{function}
+%
+% \begin{function}{\__kernel_chk_expr:nNnN}
+% \begin{syntax}
+% \cs{__kernel_chk_expr:nNnN} \Arg{expr} \meta{eval} \Arg{convert} \meta{caller}
+% \end{syntax}
+% This function is only created if debugging is enabled. By default
+% it is equivalent to \cs{use_i:nnnn}. When expression checking is
+% enabled, it leaves in the input stream the result of \cs{tex_the:D}
+% \meta{eval} \meta{expr} \cs{tex_relax:D} after checking that no
+% token was left over. If any token was not taken as part of the
+% expression, there is an error message displaying the result of the
+% evaluation as well as the \meta{caller}. For instance \meta{eval}
+% can be \cs{__int_eval:w} and \meta{caller} can be \cs{int_eval:n} or
+% \cs{int_set:Nn}. The argument \meta{convert} is empty except for mu
+% expressions where it is \cs{tex_mutoglue:D}, used for internal
+% purposes.
+% \end{function}
+%
+% \begin{function}{\__kernel_chk_tl_type:NnnT}
+% \begin{syntax}\parskip=0pt\obeylines
+% \cs{__kernel_chk_tl_type:NnnT} \meta{control sequence} \Arg{specific type} \\
+% | |\Arg{reconstruction} \Arg{true code}
+% \end{syntax}
+% Helper to test that the \meta{control sequence} is a variable of the
+% given \meta{specific type} of token list. Produces suitable error
+% messages if the \meta{control sequence} does not exist, or if it is
+% not a token list variable at all, or if the \meta{control sequence}
+% differs from the result of |e|-expanding \meta{reconstruction}. If
+% all of these tests succeed then the \meta{true code} is run.
+% \end{function}
+%
+% \begin{function}[EXP]{\__kernel_codepoint_to_bytes:n}
+% \begin{syntax}
+% \cs{__kernel_codepoint_to_bytes:n} \Arg{codepoint}
+% \end{syntax}
+% Converts the \meta{codepoint} to UTF-8 bytes. The expansion
+% of this function comprises four brace groups, each of which will contain
+% a hexadecimal value: the appropriate byte. As UTF-8 is a variable-length,
+% one or more of the groups may be empty: the bytes read in the logical order,
+% such that a two-byte codepoint will have groups |#1| and |#2| filled
+% and |#3| and |#4| empty.
+% \end{function}
+%
+% \begin{function}{\__kernel_cs_parm_from_arg_count:nnF}
+% \begin{syntax}
+% \cs{__kernel_cs_parm_from_arg_count:nnF} \Arg{follow-on} \Arg{args} \Arg{false code}
+% \end{syntax}
+% Evaluates the number of \meta{args} and leaves the \meta{follow-on} code
+% followed by a brace group containing the required number of primitive
+% parameter markers (|#1|, \emph{etc}.). If the number of \meta{args} is outside
+% the range $[0,9]$, the \meta{false code} is inserted \emph{instead} of
+% the \meta{follow-on}.
+% \end{function}
+%
+% \begin{function}{
+% \__kernel_dependency_version_check:Nn,
+% \__kernel_dependency_version_check:nn,
+% }
+% \begin{syntax}
+% \cs{__kernel_dependency_version_check:Nn} \Arg{\cs{date}} \Arg{file}
+% \cs{__kernel_dependency_version_check:nn} \Arg{date} \Arg{file}
+% \end{syntax}
+% Checks if the loaded version of the \pkg{expl3} kernel is at least \meta{date},
+% required by \meta{file}. If the kernel date is older than \meta{date}, the
+% loading of \meta{file} is aborted and an error is raised.
+% \end{function}
+%
+% \begin{function}{\__kernel_deprecation_code:nn}
+% \begin{syntax}
+% \cs{__kernel_deprecation_code:nn} \Arg{error code} \Arg{working code}
+% \end{syntax}
+% Stores both an \meta{error} and \meta{working} definition for given material
+% such that they can be exchanged by \cs{debug_on:n} and \cs{debug_off:n}.
+% \end{function}
+%
+% \begin{function}[EXP]{\__kernel_exp_not:w}
+% \begin{syntax}
+% \cs{__kernel_exp_not:w} \meta{expandable tokens} \Arg{content}
+% \end{syntax}
+% Carries out expansion on the \meta{expandable tokens} before preventing
+% further expansion of the \meta{content} as for \cs{exp_not:n}. Typically,
+% the \meta{expandable tokens} will alter the nature of the \meta{content},
+% \emph{i.e.}~allow it to be generated in some way.
+% \end{function}
+%
+% \begin{variable}{\l__kernel_expl_bool}
+% A boolean which records the current code syntax status: \texttt{true} if
+% currently inside a code environment. This variable should only be
+% set by \cs{ExplSyntaxOn}/\cs{ExplSyntaxOff}.
+% \end{variable}
+%
+% \begin{variable}{\c__kernel_expl_date_tl}
+% A token list containing the release date of the \pkg{l3kernel} preloaded
+% in \LaTeXe{} used to check if dependencies match.
+% \end{variable}
+%
+% \begin{function}{\__kernel_file_missing:n}
+% \begin{syntax}
+% \cs{__kernel_file_missing:n} \Arg{name}
+% \end{syntax}
+% Expands the \meta{name} as per \cs{__kernel_file_name_sanitize:n} then
+% produces an error message indicating that this file was not found.
+% \end{function}
+%
+% \begin{function}[EXP,updated = 2021-04-17]{\__kernel_file_name_sanitize:n}
+% \begin{syntax}
+% \cs{__kernel_file_name_sanitize:n} \Arg{name}
+% \end{syntax}
+% Expands the file name using a \tn{csname}-based approach, and
+% relies on active characters (for example from UTF-8 characters)
+% being properly set up to expand to a expansion-safe version using
+% \cs{ifcsname}. This is less conservative than the token-by-token
+% approach used before, but it is much faster.
+% \end{function}
+%
+% \begin{function}{\__kernel_file_input_push:n, \__kernel_file_input_pop:}
+% \begin{syntax}
+% \cs{__kernel_file_input_push:n} \Arg{name}
+% \cs{__kernel_file_input_pop:}
+% \end{syntax}
+% Used to push and pop data from the internal file stack: needed only
+% in package mode, where interfacing with the \LaTeXe{} kernel is necessary.
+% \end{function}
+%
+% \begin{function}[EXP]{\__kernel_int_add:nnn}
+% \begin{syntax}
+% \cs{__kernel_int_add:nnn} \Arg{integer_1} \Arg{integer_2} \Arg{integer_3}
+% \end{syntax}
+% Expands to the result of adding the three \meta{integers} (which
+% must be suitable input for \cs{int_eval:w}), avoiding intermediate
+% overflow. Overflow occurs only if the overall result is outside
+% $[-2^{31}+1,2^{31}-1]$. The \meta{integers} may be of the form
+% \cs{int_eval:w} \dots{} \cs{scan_stop:} but may be evaluated more
+% than once.
+% \end{function}
+%
+% \begin{function}[added = 2018-03-31]{\__kernel_intarray_gset:Nnn}
+% \begin{syntax}
+% \cs{__kernel_intarray_gset:Nnn} \meta{intarray~var} \Arg{index} \Arg{value}
+% \end{syntax}
+% Faster version of \cs{intarray_gset:Nnn}. Stores the \meta{value}
+% into the \meta{integer array variable} at the \meta{position}. The
+% \meta{index} and \meta{value} must be suitable for a direct
+% assignment to a \TeX{} count register, for instance expanding to an
+% integer denotation or obtained through the primitive \tn{numexpr}
+% (which may be un-terminated). No bound checking is performed: the
+% caller is responsible for ensuring that the \meta{position} is
+% between $1$ and the \cs{intarray_count:N}, and the \meta{value}'s
+% absolute value is at most $2^{30}-1$. Assignments are always
+% global.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-03-31]{\__kernel_intarray_item:Nn}
+% \begin{syntax}
+% \cs{__kernel_intarray_item:Nn} \meta{intarray~var} \Arg{index}
+% \end{syntax}
+% Faster version of \cs{intarray_item:Nn}. Expands to the integer
+% entry stored at the \meta{index} in the \meta{integer array
+% variable}. The \meta{index} must be suitable for a direct
+% assignment to a \TeX{} count register and must be between $1$ and
+% 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}
+% \end{syntax}
+% This function has identical syntax to the public version. However,
+% is does not take precautions against active characters in the
+% \meta{file name}, and it does not attempt to add a \meta{path} to
+% the \meta{file name}: it is therefore intended to be used by
+% higher-level
+% functions which have already fully expanded the \meta{file name} and which
+% need to perform multiple open or close operations. See for example the
+% implementation of \cs{ior_shell_open:Nn}.
+% \end{function}
+%
+% \begin{function}{\__kernel_iow_open:Nn, \__kernel_iow_open:No}
+% \begin{syntax}
+% \cs{__kernel_iow_open:Nn} \meta{stream} \Arg{file name}
+% \end{syntax}
+% This function has identical syntax to the public version. However,
+% is does not take precautions against active characters in the
+% \meta{file name}, and it does not attempt to add a \meta{path} to
+% the \meta{file name}: it is therefore intended to be used by
+% higher-level
+% functions which have already fully expanded the \meta{file name} and which
+% need to perform multiple open or close operations. See for example the
+% implementation of \cs{iow_shell_open:Nn}.
+% \end{function}
+%
+% \begin{function}{\__kernel_iow_with:Nnn}
+% \begin{syntax}
+% \cs{__kernel_iow_with:Nnn} \meta{integer} \Arg{value} \Arg{code}
+% \end{syntax}
+% If the \meta{integer} is equal to the \meta{value} then this
+% function simply runs the \meta{code}. Otherwise it saves the
+% current value of the \meta{integer}, sets it to the \meta{value},
+% runs the \meta{code}, and restores the \meta{integer} to its former
+% value. This is used to ensure that the \tn{newlinechar} is~$10$
+% when writing to a stream, which lets \cs{iow_newline:} work, and
+% that \tn{errorcontextlines} is $-1$ when displaying a message.
+% \end{function}
+%
+% \begin{variable}{\__kernel_kern:n}
+% \begin{syntax}
+% \cs{__kernel_kern:n} \Arg{length}
+% \end{syntax}
+% Inserts a kern of the specified \meta{length}, a dimension expression.
+% \end{variable}
+%
+% \begin{function}{\__kernel_msg_show_eval:Nn, \__kernel_msg_log_eval:Nn}
+% \begin{syntax}
+% \cs{__kernel_msg_show_eval:Nn} \meta{function} \Arg{expression}
+% \end{syntax}
+% Shows or logs the \meta{expression} (turned into a string), an equal
+% sign, and the result of applying the \meta{function} to the
+% \Arg{expression} (with \texttt{f}-expansion). For instance, if the
+% \meta{function} is \cs{int_eval:n} and the \meta{expression} is
+% |1+2| then this logs |> 1+2=3.|
+% \end{function}
+%
+% \begin{function}[EXP]{\__kernel_pdf_object_id:n, \__kernel_pdf_object_id_indexed:nn}
+% \begin{syntax}
+% \cs{__kernel_pdf_object_id:n} \Arg{object}
+% \cs{__kernel_pdf_object_id_indexed:nn} \Arg{class} \Arg{number}
+% \end{syntax}
+% Expands to the ID of \meta{object} (or object of \meta{number} within
+% the \meta{class}), in for example page resource allocation. Depending on
+% the backend, the result may be the same as
+% \cs{pdf_object_id:n}/\cs{pdf_object_id_indexed:nn}.
+% \end{function}
+%
+% \begin{variable}{\g__kernel_prg_map_int}
+% This integer is used by non-expandable mapping functions to track
+% the level of nesting in force. The functions
+% \cs[no-index]{\meta{type}_map_1:w},
+% \cs[no-index]{\meta{type}_map_2:w}, \emph{etc.}, labelled by
+% \cs{g__kernel_prg_map_int}
+% hold functions to be mapped over various list datatypes in inline
+% and variable mappings.
+% \end{variable}
+%
+% \begin{function}{\__kernel_quark_new_test:N}
+% \begin{syntax}
+% \cs{__kernel_quark_new_test:N} \cs[no-index]{\meta{name}:\meta{arg spec}}
+% \end{syntax}
+% Defines a quark-test function \cs[no-index]{\meta{name}:\meta{arg spec}}
+% which tests if its argument is \cs[no-index]{q__\meta{namespace}_recursion_tail},
+% then acts accordingly, as described below for each possible
+% \meta{arg spec}.
+%
+% The \meta{namespace} is determined as the first (nonempty)
+% |_|-delimited word in \meta{name} and is used internally
+% in the definition of auxiliaries. The function
+% \cs{__kernel_quark_new_test:N} does \emph{not} define
+% the \cs[no-index]{q__\meta{namespace}_recursion_tail} and
+% \cs[no-index]{q__\meta{namespace}_recursion_stop} quarks.
+% They should be manually defined with \cs{quark_new:N}.
+%
+% There are $6$ different types of quark-test functions. Which one is
+% defined depends on the \meta{arg spec}, which \emph{must} be one of
+% the options listed now. Four of them are modeled after
+% \cs[no-index]{quark_if_recursion_tail:(N|n)} and
+% \cs[no-index]{quark_if_recursion_tail_do:(N|n)n}.
+% \begin{description}
+% \def\makelabel#1{\texttt{#1}}
+% \item[n] defines \cs[no-index]{\meta{name}:n} such that it
+% checks if |#1| contains only \cs[no-index]{q__\meta{namespace}_recursion_tail}, and
+% if so consumes all tokens up to \cs[no-index]{q__\meta{namespace}_recursion_stop}
+% (\emph{c.f.}~\cs{quark_if_recursion_tail_stop:n}).
+% \item[nn] defines \cs[no-index]{\meta{name}:nn} such that it
+% checks if |#1| contains only \cs[no-index]{q__\meta{namespace}_recursion_tail}, and
+% if so consumes all tokens up to \cs[no-index]{q__\meta{namespace}_recursion_stop},
+% then executes the code |#2| after that
+% (\emph{c.f.}~\cs{quark_if_recursion_tail_stop_do:nn}).
+% \item[N] defines \cs[no-index]{\meta{name}:N} such that it
+% checks if |#1| is \cs[no-index]{q__\meta{namespace}_recursion_tail}, and if so consumes
+% all tokens up to \cs[no-index]{q__\meta{namespace}_recursion_stop}
+% (\emph{c.f.}~\cs{quark_if_recursion_tail_stop:N}).
+% \item[Nn] defines \cs[no-index]{\meta{name}:Nn} such that it
+% checks if |#1| is \cs[no-index]{q__\meta{namespace}_recursion_tail}, and if so consumes
+% all tokens up to \cs[no-index]{q__\meta{namespace}_recursion_stop}, then executes
+% the code |#2| after that
+% (\emph{c.f.}~\cs{quark_if_recursion_tail_stop_do:Nn}).
+% \end{description}
+% The last two are modeled after
+% \cs[no-index]{quark_if_recursion_tail_break:(n|N)N}, and in those
+% cases the quark \cs[no-index]{q__\meta{namespace}_recursion_stop} is
+% not used (and thus needs not be defined).
+% \begin{description}
+% \def\makelabel#1{\texttt{#1}}
+% \item[nN] defines \cs[no-index]{\meta{name}:nN} such that it
+% checks if |#1| contains only \cs[no-index]{q__\meta{namespace}_recursion_tail}, and
+% if so uses the \cs[no-index]{\meta{type}_map_break:} function |#2|.
+% \item[NN] defines \cs[no-index]{\meta{name}:NN} such that it
+% checks if |#1| is \cs[no-index]{q__\meta{namespace}_recursion_tail}, and if so uses
+% the \cs[no-index]{\meta{type}_map_break:} function |#2|.
+% \end{description}
+% Any other signature, as well as a function without signature
+% are errors, and in such case the definition is aborted.
+% \end{function}
+%
+% \begin{function}{\__kernel_quark_new_conditional:Nn}
+% \begin{syntax}
+% \cs{__kernel_quark_new_conditional:Nn} \cs[no-index]{__\meta{namespace}_quark_if_\meta{name}:\meta{arg spec}} \Arg{conditions}
+% \end{syntax}
+% Defines a collection of quark conditionals that test if their
+% argument is the quark \cs[no-index]{q__\meta{namespace}_\meta{name}}
+% and perform suitable actions. The \meta{conditions} are a
+% comma-separated list of one or more of |p|, |T|, |F|, and |TF|, and
+% one conditional is defined for each \meta{condition} in the list, as
+% described for \cs{prg_new_conditional:Npnn}. The conditionals are
+% defined using \cs{prg_new_conditional:Npnn}, so that their name is
+% obtained by adding |p|, |T|, |F|, or |TF| to the base name
+% \cs[no-index]{__\meta{namespace}_quark_if_\meta{name}:\meta{arg spec}}.
+%
+% The first argument of \cs{__kernel_quark_new_conditional:Nn} must
+% contain |_quark_if_| and |:|, as these markers are used to determine
+% the \meta{name} of the quark
+% \cs[no-index]{q__\meta{namespace}_\meta{name}} to be tested. This
+% quark should be manually defined with \cs{quark_new:N}, as
+% \cs{__kernel_quark_new_conditional:Nn} does \emph{not} define it.
+%
+% The function \cs{__kernel_quark_new_conditional:Nn} can define $2$
+% different types of quark conditionals. Which one is defined depends
+% on the \meta{arg spec}, which \emph{must} be one of the following
+% options, modeled after \cs[index=quark_if_nil:NTF]{quark_if_nil:(N|n)(TF)}.
+% \begin{description}
+% \def\makelabel#1{\texttt{#1}}
+% \item[n] defines \cs[no-index]{__\meta{namespace}_quark_if_\meta{name}:n(TF)} such that it
+% checks if |#1| contains only \cs[no-index]{q__\meta{namespace}_\meta{name}}, and
+% executes the proper conditional branch.
+% \item[N] defines \cs[no-index]{__\meta{namespace}_quark_if_\meta{name}:N(TF)} such that it
+% checks if |#1| is \cs[no-index]{q__\meta{namespace}_\meta{name}}, and
+% executes the proper conditional branch.
+% \end{description}
+% Any other signature, as well as a function without signature
+% are errors, and in such case the definition is aborted.
+% \end{function}
+%
+% \begin{function}{\__kernel_sys_everyjob:}
+% \begin{syntax}
+% \cs{__kernel_sys_everyjob:}
+% \end{syntax}
+% Inserts the internal token list required at the start of every run (job).
+% \end{function}
+%
+% \begin{variable}{\c__kernel_randint_max_int}
+% Maximal allowed argument to \cs{__kernel_randint:n}. Equal to
+% $2^{17}-1$.
+% \end{variable}
+%
+% \begin{function}{\__kernel_randint:n}
+% \begin{syntax}
+% \cs{__kernel_randint:n} \Arg{max}
+% \end{syntax}
+% Used in an integer expression this gives a pseudo-random number
+% between $1$ and $\meta{max}$ included. One must have
+% $\meta{max}\leq 2^{17}-1$. The \meta{max} must be suitable for
+% \cs{int_value:w} (and any \cs{int_eval:w} must be terminated by
+% \cs{scan_stop:} or equivalent).
+% \end{function}
+%
+% \begin{function}{\__kernel_randint:nn}
+% \begin{syntax}
+% \cs{__kernel_randint:nn} \Arg{min} \Arg{max}
+% \end{syntax}
+% Used in an integer expression this gives a pseudo-random number
+% between $\meta{min}$ and $\meta{max}$ included. The \meta{min} and
+% \meta{max} must be suitable for \cs{int_value:w} (and any
+% \cs{int_eval:w} must be terminated by \cs{scan_stop:} or
+% equivalent). For small ranges
+% $R=\meta{max}-\meta{min}+1\leq 2^{17}-1$,
+% $\meta{min} - 1 + \cs{__kernel_randint:n} |{|R|}|$ is faster.
+% \end{function}
+%
+% \begin{function}{\__kernel_register_show:N, \__kernel_register_show:c}
+% \begin{syntax}
+% \cs{__kernel_register_show:N} \meta{register}
+% \end{syntax}
+% Used to show the contents of a \TeX{} register at the terminal, formatted
+% such that internal parts of the mechanism are not visible.
+% \end{function}
+%
+% \begin{function}
+% {\__kernel_register_log:N, \__kernel_register_log:c}
+% \begin{syntax}
+% \cs{__kernel_register_log:N} \meta{register}
+% \end{syntax}
+% Used to write the contents of a \TeX{} register to the log file in a
+% form similar to \cs{__kernel_register_show:N}.
+% \end{function}
+%
+% \begin{function}[EXP]{\__kernel_str_to_other:n}
+% \begin{syntax}
+% \cs{__kernel_str_to_other:n} \Arg{token list}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{other string}, where
+% spaces have category code \enquote{other}. This function can be
+% \texttt{f}-expanded without fear of losing a leading space, since
+% spaces do not have category code $10$ in its result. It takes a
+% time quadratic in the character count of the string.
+% \end{function}
+%
+% \begin{function}[rEXP]{\__kernel_str_to_other_fast:n}
+% \begin{syntax}
+% \cs{__kernel_str_to_other_fast:n} \Arg{token list}
+% \end{syntax}
+% Same behaviour \cs{__kernel_str_to_other:n} but only restricted-expandable.
+% It takes a time linear in the character count of the string.
+% \end{function}
+%
+% \begin{function}[EXP]{\__kernel_tl_to_str:w}
+% \begin{syntax}
+% \cs{__kernel_tl_to_str:w} \meta{expandable tokens} \Arg{tokens}
+% \end{syntax}
+% Carries out expansion on the \meta{expandable tokens} before conversion of
+% the \meta{tokens} to a string as describe for \cs{tl_to_str:n}. Typically,
+% the \meta{expandable tokens} will alter the nature of the \meta{tokens},
+% \emph{i.e.}~allow it to be generated in some way. This function requires
+% only a single expansion.
+% \end{function}
+%
+% \begin{function}{\__kernel_tl_set:Nx, \__kernel_tl_gset:Nx}
+% \begin{syntax}
+% \cs{__kernel_tl_set:Nx} \meta{tl~var} \Arg{tokens}
+% \end{syntax}
+% Fully expands \meta{tokens} and assigns the result to \meta{tl~var}.
+% \meta{tokens} must be given in braces and there must be no token between
+% \meta{tl~var} and \meta{tokens}.
+% \end{function}
+%
+% \begin{function}[EXP]{\__kernel_codepoint_data:nn}
+% \begin{syntax}
+% \cs{__kernel_codepoint_data:nn} \Arg{type} \Arg{codepoint}
+% \end{syntax}
+% Expands to the appropriate value for the \meta{type} of data
+% requested for a \meta{codepoint}. The current list of \meta{types} and
+% results are
+% \begin{description}
+% \item[\texttt{lowercase}] The \emph{single} codepoint specified by
+% \texttt{UnicodeData.txt} for lowercase mapping of the codepoint:
+% will be equal to the input \meta{codepoint} if there is no mapping
+% specified in \texttt{UnicodeData.txt}
+% \item[\texttt{uppercase}] The \emph{single} codepoint specified by
+% \texttt{UnicodeData.txt} for uppercase mapping of the codepoint:
+% will be equal to the input \meta{codepoint} if there is no mapping
+% specified in \texttt{UnicodeData.txt}
+% \end{description}
+% \end{function}
+%
+% \begin{function}[EXP]{\__kernel_codepoint_case:nn}
+% \begin{syntax}
+% \cs{__kernel_codepoint_case:nn} \Arg{mapping} \Arg{codepoint}
+% \end{syntax}
+% Expands to a list of three balanced text, of which at least the first
+% will contain a codepoint. This list of up to three codepoints specifies
+% the full case mapping for the input \meta{codepoint}. The \meta{mapping}
+% should be one of
+% \begin{itemize}
+% \item \texttt{casefold}
+% \item \texttt{lowercase}
+% \item \texttt{titlecase}
+% \item \texttt{uppercase}
+% \end{itemize}
+% \end{function}
+%
+% \subsection{Kernel backend functions}
+%
+% These functions are required to pass information to the backend. The nature
+% of these means that they are defined only when the relevant backend is in
+% use.
+%
+% \begin{function}
+% {
+% \__kernel_backend_literal:n,
+% \__kernel_backend_literal:e,
+% \__kernel_backend_literal:e
+% }
+% \begin{syntax}
+% \cs{__kernel_backend_literal:n} \Arg{content}
+% \end{syntax}
+% Adds the \meta{content} literally to the current vertical list as a
+% whatsit. The nature of the \meta{content} will depend on the backend in
+% use.
+% \end{function}
+%
+% \begin{function}
+% {
+% \__kernel_backend_literal_postscript:n,
+% \__kernel_backend_literal_postscript:e
+% }
+% \begin{syntax}
+% \cs{__kernel_backend_literal_postscript:n} \Arg{PostScript}
+% \end{syntax}
+% Adds the \meta{PostScript} literally to the current vertical list as a
+% whatsit. No positioning is applied.
+% \end{function}
+%
+% \begin{function}
+% {
+% \__kernel_backend_literal_pdf:n,
+% \__kernel_backend_literal_pdf:e
+% }
+% \begin{syntax}
+% \cs{__kernel_backend_literal_pdf:n} \Arg{PDF instructions}
+% \end{syntax}
+% Adds the \meta{PDF instructions} literally to the current vertical list as
+% a whatsit. No positioning is applied.
+% \end{function}
+%
+% \begin{function}
+% {
+% \__kernel_backend_literal_svg:n,
+% \__kernel_backend_literal_svg:e
+% }
+% \begin{syntax}
+% \cs{__kernel_backend_literal_svg:n} \Arg{SVG instructions}
+% \end{syntax}
+% Adds the \meta{SVG instructions} literally to the current vertical list as
+% a whatsit. No positioning is applied.
+% \end{function}
+%
+% \begin{function}
+% {
+% \__kernel_backend_postscript:n,
+% \__kernel_backend_postscript:e
+% }
+% \begin{syntax}
+% \cs{__kernel_backend_postscript:n} \Arg{PostScript}
+% \end{syntax}
+% Adds the \meta{PostScript} to the current vertical list as a
+% whatsit. The PostScript reference point is adjusted to match the
+% current position. The PostScript is inserted inside a |SDict begin|/|end|
+% pair.
+% \end{function}
+%
+% \begin{function}{\__kernel_backend_align_begin:, \__kernel_backend_align_end:}
+% \begin{syntax}
+% \cs{__kernel_backend_align_begin:}
+% \meta{PostScript literals}
+% \cs{__kernel_backend_align_end:}
+% \end{syntax}
+% Arranges to align the PostScript and DVI current positions and scales.
+% \end{function}
+%
+% \begin{function}{\__kernel_backend_scope_begin:, \__kernel_backend_scope_end:}
+% \begin{syntax}
+% \cs{__kernel_backend_scope_begin:}
+% \meta{content}
+% \cs{__kernel_backend_scope_end:}
+% \end{syntax}
+% Creates a scope for instructions at the backend level.
+% \end{function}
+%
+% \begin{function}
+% {
+% \__kernel_backend_matrix:n,
+% \__kernel_backend_matrix:e
+% }
+% \begin{syntax}
+% \cs{__kernel_backend_matrix:n} \Arg{matrix}
+% \end{syntax}
+% Applies the \meta{matrix} to the current transformation matrix.
+% \end{function}
+%
+% \begin{function}{\g__kernel_backend_header_bool}
+% Specifies whether to write headers for the backend.
+% \end{function}
+%
+% \begin{function}{\l__kernel_color_stack_int}
+% The color stack used in \pdfTeX{} and \LuaTeX{} for the main color.
+% \end{function}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3keys.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3keys.dtx
new file mode 100644
index 00000000000..a1d2c011691
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3keys.dtx
@@ -0,0 +1,3649 @@
+% \iffalse meta-comment
+%
+%% File: l3keys.dtx
+%
+% Copyright (C) 2006-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3keys} module\\ Key--value interfaces^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% The key--value method is a popular system for creating large numbers
+% of settings for controlling function or package behaviour. The
+% system normally results in input of the form
+% \begin{verbatim}
+% \MyModuleSetup{
+% key-one = value one,
+% key-two = value two
+% }
+% \end{verbatim}
+% or
+% \begin{verbatim}
+% \MyModuleMacro[
+% key-one = value one,
+% key-two = value two
+% ]{argument}
+% \end{verbatim}
+% for the user.
+%
+% The high level functions here are intended as a method to create
+% key--value controls. Keys are themselves created using a key--value
+% interface, minimising the number of functions and arguments
+% required. Each key is created by setting one or more \emph{properties}
+% of the key:
+% \begin{verbatim}
+% \keys_define:nn { mymodule }
+% {
+% key-one .code:n = code including parameter #1,
+% key-two .tl_set:N = \l_mymodule_store_tl
+% }
+% \end{verbatim}
+% These values can then be set as with other key--value approaches:
+% \begin{verbatim}
+% \keys_set:nn { mymodule }
+% {
+% key-one = value one,
+% key-two = value two
+% }
+% \end{verbatim}
+% As illustrated, keys are created inside a \meta{module}: a set of related
+% keys, typically those for a single module/\LaTeXe{} package. See
+% Section~\ref{sec:l3keys:subdivision} for suggestions on how to divide
+% large numbers of keys for a single module.
+%
+% At a document level, \cs{keys_set:nn} is used within a
+% document function, for example
+% \begin{verbatim}
+% \DeclareDocumentCommand \MyModuleSetup { m }
+% { \keys_set:nn { mymodule } { #1 } }
+% \DeclareDocumentCommand \MyModuleMacro { o m }
+% {
+% \group_begin:
+% \keys_set:nn { mymodule } { #1 }
+% % Main code for \MyModuleMacro
+% \group_end:
+% }
+% \end{verbatim}
+%
+% Key names may contain any tokens, as they are handled internally
+% using \cs{tl_to_str:n}. As discussed in
+% section~\ref{sec:l3keys:subdivision}, it is suggested that the character
+% |/| is reserved for sub-division of keys into different subsets.
+% Functions and variables are \emph{not} expanded when creating
+% key names, and so
+% \begin{verbatim}
+% \tl_set:Nn \l_mymodule_tmp_tl { key }
+% \keys_define:nn { mymodule }
+% {
+% \l_mymodule_tmp_tl .code:n = code
+% }
+% \end{verbatim}
+% creates a key called |\l_mymodule_tmp_tl|, and not one called
+% \texttt{key}.
+%
+% \section{Creating keys}
+%
+% \begin{function}[updated = 2017-11-14]
+% {\keys_define:nn, \keys_define:ne}
+% \begin{syntax}
+% \cs{keys_define:nn} \Arg{module} \Arg{keyval list}
+% \end{syntax}
+% Parses the \meta{keyval list} and defines the keys listed there for
+% \meta{module}. The \meta{module} name is treated as a string.
+% In practice the
+% \meta{module} should be chosen to be unique to the module in question
+% (unless deliberately adding keys to an existing module).
+%
+% The \meta{keyval list} should consist of one or more key names along
+% with an associated key \emph{property}. The properties of a key
+% determine how it acts. The individual properties are described
+% in the following text; a typical use of \cs{keys_define:nn} might
+% read
+% \begin{verbatim}
+% \keys_define:nn { mymodule }
+% {
+% keyname .code:n = Some~code~using~#1,
+% keyname .value_required:n = true
+% }
+% \end{verbatim}
+% where the properties of the key begin from the |.| after the key
+% name.
+% \end{function}
+%
+% The various properties available take either no arguments at
+% all, or require one or more arguments. This is indicated in the
+% name of the property using an argument specification. In the following
+% discussion, each property is illustrated attached to an
+% arbitrary \meta{key}, which when used may be supplied with a
+% \meta{value}. All key \emph{definitions} are local.
+%
+% Key properties are applied in the reading order and so the ordering
+% is significant. Key properties which define \enquote{actions}, such
+% as |.code:n|, |.tl_set:N|, \emph{etc.}, override one another.
+% Some other properties are mutually exclusive, notably |.value_required:n|
+% and |.value_forbidden:n|, and so they replace one another. However,
+% properties covering non-exclusive behaviours may be given in any order. Thus
+% for example the following definitions are equivalent.
+% \begin{verbatim}
+% \keys_define:nn { mymodule }
+% {
+% keyname .code:n = Some~code~using~#1,
+% keyname .value_required:n = true
+% }
+% \keys_define:nn { mymodule }
+% {
+% keyname .value_required:n = true,
+% keyname .code:n = Some~code~using~#1
+% }
+% \end{verbatim}
+% Note that all key properties define the key within the current \TeX{} group,
+% with an exception that the special |.undefine:| property \emph{undefines} the
+% key within the current \TeX{} group.
+%
+% \begin{function}[updated = 2013-07-08]
+% {.bool_set:N, .bool_set:c, .bool_gset:N, .bool_gset:c}
+% \begin{syntax}
+% \meta{key} .bool_set:N = \meta{boolean variable}
+% \end{syntax}
+% Defines \meta{key} to set \meta{boolean variable} to \meta{value} (which
+% must be either \enquote{\texttt{true}} or \enquote{\texttt{false}}). If the variable
+% does not exist, it will be created globally at the point that
+% the key is set up.
+% \end{function}
+%
+% \begin{function}[added = 2011-08-28, updated = 2013-07-08]
+% {
+% .bool_set_inverse:N, .bool_set_inverse:c,
+% .bool_gset_inverse:N, .bool_gset_inverse:c
+% }
+% \begin{syntax}
+% \meta{key} .bool_set_inverse:N = \meta{boolean variable}
+% \end{syntax}
+% Defines \meta{key} to set \meta{boolean variable} to the logical
+% inverse of \meta{value} (which must be either \enquote{\texttt{true}} or
+% \enquote{\texttt{false}}).
+% If the \meta{boolean variable} does not exist, it will be created globally
+% at the point that the key is set up.
+% \end{function}
+%
+% \begin{function}{.choice:}
+% \begin{syntax}
+% \meta{key} .choice:
+% \end{syntax}
+% Sets \meta{key} to act as a choice key. Each valid choice
+% for \meta{key} must then be created, as discussed in
+% section~\ref{sec:l3keys:choice}.
+% \end{function}
+%
+% \begin{function}[added = 2011-08-21, updated = 2013-07-10]
+% {.choices:nn, .choices:Vn, .choices:en, .choices:on}
+% \begin{syntax}
+% \meta{key} .choices:nn = \Arg{choices} \Arg{code}
+% \end{syntax}
+% Sets \meta{key} to act as a choice key, and defines a series \meta{choices}
+% which are implemented using the \meta{code}. Inside \meta{code},
+% \cs{l_keys_choice_tl} will be the name of the choice made, and
+% \cs{l_keys_choice_int} will be the position of the choice in the list
+% of \meta{choices} (indexed from~$1$).
+% Choices are discussed in detail in section~\ref{sec:l3keys:choice}.
+% \end{function}
+%
+% \begin{function}[added = 2011-09-11]
+% {.clist_set:N, .clist_set:c, .clist_gset:N, .clist_gset:c}
+% \begin{syntax}
+% \meta{key} .clist_set:N = \meta{comma list variable}
+% \end{syntax}
+% Defines \meta{key} to set \meta{comma list variable} to \meta{value}.
+% Spaces around commas and empty items will be stripped.
+% If the variable does not exist, it
+% is created globally at the point that the key is set up.
+% \end{function}
+%
+% \begin{function}[updated = 2013-07-10]{.code:n}
+% \begin{syntax}
+% \meta{key} .code:n = \Arg{code}
+% \end{syntax}
+% Stores the \meta{code} for execution when \meta{key} is used.
+% The \meta{code} can include one parameter (|#1|), which will be the
+% \meta{value} given for the \meta{key}.
+% \end{function}
+%
+% \begin{function}[added = 2020-01-11]
+% {
+% .cs_set:Np, .cs_set:cp,
+% .cs_set_protected:Np, .cs_set_protected:cp,
+% .cs_gset:Np, .cs_gset:cp,
+% .cs_gset_protected:Np, .cs_gset_protected:cp,
+% }
+% \begin{syntax}
+% \meta{key} .cs_set:Np = \meta{control sequence} \meta{arg.~spec.}
+% \end{syntax}
+% Defines \meta{key} to set \meta{control sequence} to have \meta{arg.~spec.}
+% and replacement text \meta{value}.
+% \end{function}
+%
+% \begin{function}[updated = 2013-07-09]
+% {.default:n, .default:V, .default:e, .default:o}
+% \begin{syntax}
+% \meta{key} .default:n = \Arg{default}
+% \end{syntax}
+% Creates a \meta{default} value for \meta{key}, which is used if no
+% value is given. This will be used if only the key name is given,
+% but not if a blank \meta{value} is given:
+% \begin{verbatim}
+% \keys_define:nn { mymodule }
+% {
+% key .code:n = Hello~#1,
+% key .default:n = World
+% }
+% \keys_set:nn { mymodule }
+% {
+% key = Fred, % Prints 'Hello Fred'
+% key, % Prints 'Hello World'
+% key = , % Prints 'Hello '
+% }
+% \end{verbatim}
+% The default does not affect keys where values are required or
+% forbidden. Thus a required value cannot be supplied by a default
+% value, and giving a default value for a key which cannot take a value
+% does not trigger an error.
+%
+% When no value is given for a key as part of \cs{keys_set:nn}, the
+% \texttt{.default:n} value provides the value before key properties are
+% considered. The only exception is when the \texttt{.value_required:n}
+% property is active: a required value cannot be supplied by the default,
+% and must be explicitly given as part of \cs{keys_set:nn}.
+% \end{function}
+%
+% \begin{function}[updated = 2020-01-17]
+% {.dim_set:N, .dim_set:c, .dim_gset:N, .dim_gset:c}
+% \begin{syntax}
+% \meta{key} .dim_set:N = \meta{dimension}
+% \end{syntax}
+% Defines \meta{key} to set \meta{dimension} to \meta{value} (which
+% must a dimension expression). If the variable does not exist, it
+% is created globally at the point that the key is set up. The key will
+% require a value at point-of-use unless a default is set.
+% \end{function}
+%
+% \begin{function}[updated = 2020-01-17]
+% {.fp_set:N, .fp_set:c, .fp_gset:N, .fp_gset:c}
+% \begin{syntax}
+% \meta{key} .fp_set:N = \meta{floating point}
+% \end{syntax}
+% Defines \meta{key} to set \meta{floating point} to \meta{value}
+% (which must a floating point expression). If the variable does not exist,
+% it is created globally at the point that the key is set up. The key will
+% require a value at point-of-use unless a default is set.
+% \end{function}
+%
+% \begin{function}[added = 2013-07-14]
+% {.groups:n}
+% \begin{syntax}
+% \meta{key} .groups:n = \Arg{groups}
+% \end{syntax}
+% Defines \meta{key} as belonging to the \meta{groups} (a
+% comma-separated list). Groups
+% provide a \enquote{secondary axis} for selectively setting keys, and are
+% described in Section~\ref{sec:l3keys:selective}.
+% \begin{texnote}
+% The \meta{groups} argument is turned into a string then
+% interpreted as a comma-separated list, so group names cannot
+% contain commas nor start or end with a space character.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2016-11-22]{.inherit:n}
+% \begin{syntax}
+% \meta{key} .inherit:n = \Arg{parents}
+% \end{syntax}
+% Specifies that the \meta{key} path should inherit the keys listed
+% as any of the \meta{parents} (a comma list), which can be a module
+% or a sub-division thereof. For example, after setting
+% \begin{verbatim}
+% \keys_define:nn { foo } { test .code:n = \tl_show:n {#1} }
+% \keys_define:nn { } { bar .inherit:n = foo }
+% \end{verbatim}
+% setting
+% \begin{verbatim}
+% \keys_set:nn { bar } { test = a }
+% \end{verbatim}
+% will be equivalent to
+% \begin{verbatim}
+% \keys_set:nn { foo } { test = a }
+% \end{verbatim}
+% Inheritance applies at point of use, not at definition, thus keys may
+% be added to the \meta{parent} after the use of \texttt{.inherit:n}
+% and will be active.
+% If more than one \meta{parent} is specified, the presence of the
+% \meta{key} will be tested for each in turn, with the first successful
+% hit taking priority.
+% \end{function}
+%
+% \begin{function}[updated = 2013-07-09]
+% {.initial:n, .initial:V, .initial:e, .initial:o}
+% \begin{syntax}
+% \meta{key} .initial:n = \Arg{value}
+% \end{syntax}
+% Initialises the \meta{key} with the \meta{value}, equivalent to
+% \begin{quote}
+% \cs{keys_set:nn} \Arg{module} \{ \meta{key} = \meta{value} \}
+% \end{quote}
+% \end{function}
+%
+% \begin{function}[updated = 2020-01-17]
+% {.int_set:N, .int_set:c, .int_gset:N, .int_gset:c}
+% \begin{syntax}
+% \meta{key} .int_set:N = \meta{integer}
+% \end{syntax}
+% Defines \meta{key} to set \meta{integer} to \meta{value} (which
+% must be an integer expression). If the variable does not exist, it
+% is created globally at the point that the key is set up. The key will
+% require a value at point-of-use unless a default is set.
+% \end{function}
+%
+% \begin{function}[updated = 2022-01-15]
+% {
+% .legacy_if_set:n, .legacy_if_gset:n,
+% .legacy_if_set_inverse:n, .legacy_if_gset_inverse:n
+% }
+% \begin{syntax}
+% \meta{key} .legacy_if_set:n = \meta{switch}
+% \end{syntax}
+% Defines \meta{key} to set legacy \cs[no-index]{if\meta{switch}} to \meta{value}
+% (which must be either \enquote{\texttt{true}} or \enquote{\texttt{false}}).
+% The \meta{switch} is the name of the switch \emph{without the leading
+% \texttt{if}}.
+%
+% The \texttt{inverse} versions will set the \meta{switch} to the logical
+% opposite of the \meta{value}.
+% \end{function}
+%
+% \begin{function}[updated = 2013-07-10]{.meta:n}
+% \begin{syntax}
+% \meta{key} .meta:n = \Arg{keyval list}
+% \end{syntax}
+% Makes \meta{key} a meta-key, which will set \meta{keyval list} in
+% one go. The \meta{keyval list} can refer as |#1| to the value given
+% at the time the \meta{key} is used (or, if no value is given, the
+% \meta{key}'s default value).
+% \end{function}
+%
+% \begin{function}[added = 2013-07-10]{.meta:nn}
+% \begin{syntax}
+% \meta{key} .meta:nn = \Arg{path} \Arg{keyval list}
+% \end{syntax}
+% Makes \meta{key} a meta-key, which will set \meta{keyval list} in
+% one go using the \meta{path} in place of the current one. The
+% \meta{keyval list} can refer as |#1| to the value given at the time
+% the \meta{key} is used (or, if no value is given, the \meta{key}'s
+% default value).
+% \end{function}
+%
+% \begin{function}[added = 2011-08-21]{.multichoice:}
+% \begin{syntax}
+% \meta{key} .multichoice:
+% \end{syntax}
+% Sets \meta{key} to act as a multiple choice key. Each valid choice
+% for \meta{key} must then be created, as discussed in
+% section~\ref{sec:l3keys:choice}.
+% \end{function}
+%
+% \begin{function}[added = 2011-08-21, updated = 2013-07-10]
+% {.multichoices:nn, .multichoices:Vn, .multichoices:en, .multichoices:on}
+% \begin{syntax}
+% \meta{key} .multichoices:nn \Arg{choices} \Arg{code}
+% \end{syntax}
+% Sets \meta{key} to act as a multiple choice key, and defines a series
+% \meta{choices}
+% which are implemented using the \meta{code}. Inside \meta{code},
+% \cs{l_keys_choice_tl} will be the name of the choice made, and
+% \cs{l_keys_choice_int} will be the position of the choice in the list
+% of \meta{choices} (indexed from~$1$).
+% Choices are discussed in detail in section~\ref{sec:l3keys:choice}.
+% \end{function}
+%
+% \begin{function}[added = 2019-05-05, updated = 2020-01-17]
+% {.muskip_set:N, .muskip_set:c, .muskip_gset:N, .muskip_gset:c}
+% \begin{syntax}
+% \meta{key} .muskip_set:N = \meta{muskip}
+% \end{syntax}
+% Defines \meta{key} to set \meta{muskip} to \meta{value} (which
+% must be a muskip expression). If the variable does not exist, it
+% is created globally at the point that the key is set up. The key will
+% require a value at point-of-use unless a default is set.
+% \end{function}
+%
+% \begin{function}[added = 2019-01-31]
+% {.prop_put:N, .prop_put:c, .prop_gput:N, .prop_gput:c}
+% \begin{syntax}
+% \meta{key} .prop_put:N = \meta{property list}
+% \end{syntax}
+% Defines \meta{key} to put the \meta{value} onto the \meta{property list}
+% stored under the \meta{key}.
+% If the variable does not exist, it
+% is created globally at the point that the key is set up.
+% \end{function}
+%
+% \begin{function}[updated = 2020-01-17]
+% {.skip_set:N, .skip_set:c, .skip_gset:N, .skip_gset:c}
+% \begin{syntax}
+% \meta{key} .skip_set:N = \meta{skip}
+% \end{syntax}
+% Defines \meta{key} to set \meta{skip} to \meta{value} (which
+% must be a skip expression). If the variable does not exist, it
+% is created globally at the point that the key is set up. The key will
+% require a value at point-of-use unless a default is set.
+% \end{function}
+%
+% \begin{function}[added = 2021-10-30]
+% {.str_set:N, .str_set:c, .str_gset:N, .str_gset:c}
+% \begin{syntax}
+% \meta{key} .str_set:N = \meta{string variable}
+% \end{syntax}
+% Defines \meta{key} to set \meta{string variable} to \meta{value}.
+% If the variable does not exist, it is created globally
+% at the point that the key is set up.
+% \end{function}
+%
+% \begin{function}[added = 2023-09-18]
+% {.str_set_e:N, .str_set_e:c, .str_gset_e:N, .str_gset_e:c}
+% \begin{syntax}
+% \meta{key} .str_set_e:N = \meta{string variable}
+% \end{syntax}
+% Defines \meta{key} to set \meta{string variable} to \meta{value},
+% which will be subjected to an \texttt{e}-type expansion
+% (\emph{i.e.}~using \cs{str_set:Ne}). If the variable does not exist,
+% it is created globally at the point that the key is set up.
+% \end{function}
+%
+% \begin{function}{.tl_set:N, .tl_set:c, .tl_gset:N, .tl_gset:c}
+% \begin{syntax}
+% \meta{key} .tl_set:N = \meta{token list variable}
+% \end{syntax}
+% Defines \meta{key} to set \meta{token list variable} to \meta{value}.
+% If the variable does not exist, it is created globally
+% at the point that the key is set up.
+% \end{function}
+%
+% \begin{function}[added = 2023-09-18]
+% {.tl_set_e:N, .tl_set_e:c, .tl_gset_e:N, .tl_gset_e:c}
+% \begin{syntax}
+% \meta{key} .tl_set_e:N = \meta{token list variable}
+% \end{syntax}
+% Defines \meta{key} to set \meta{token list variable} to \meta{value},
+% which will be subjected to an \texttt{e}-type expansion
+% (\emph{i.e.}~using \cs{tl_set:Ne}). If the variable does not exist,
+% it is created globally at the point that the key is set up.
+% \end{function}
+%
+% \begin{function}[added = 2015-07-14]{.undefine:}
+% \begin{syntax}
+% \meta{key} .undefine:
+% \end{syntax}
+% Removes the definition of the \meta{key} within the current \TeX{} group.
+% \end{function}
+%
+% \begin{function}[added = 2015-07-14]{.value_forbidden:n}
+% \begin{syntax}
+% \meta{key} .value_forbidden:n = \texttt{true\string|false}
+% \end{syntax}
+% Specifies that \meta{key} cannot receive a \meta{value} when used.
+% If a \meta{value} is given then an error will be issued. Setting
+% the property \enquote{\texttt{false}} cancels the restriction.
+% \end{function}
+%
+% \begin{function}[added = 2015-07-14]{.value_required:n}
+% \begin{syntax}
+% \meta{key} .value_required:n = \texttt{true\string|false}
+% \end{syntax}
+% Specifies that \meta{key} must receive a \meta{value} when used.
+% If a \meta{value} is not given then an error will be issued. Setting
+% the property \enquote{\texttt{false}} cancels the restriction.
+% \end{function}
+%
+% \section{Sub-dividing keys}
+% \label{sec:l3keys:subdivision}
+%
+% When creating large numbers of keys, it may be desirable to divide
+% them into several subsets for a given module. This can be achieved
+% either by adding a sub-division to the module name:
+% \begin{verbatim}
+% \keys_define:nn { mymodule / subset }
+% { key .code:n = code }
+% \end{verbatim}
+% or to the key name:
+% \begin{verbatim}
+% \keys_define:nn { mymodule }
+% { subset / key .code:n = code }
+% \end{verbatim}
+% As illustrated, the best choice of token for sub-dividing keys in
+% this way is |/|. This is because of the method that is
+% used to represent keys internally. Both of the above code fragments
+% set the same key, which has full name \texttt{mymodule/subset/key}.
+%
+% As illustrated in the next section, this subdivision is
+% particularly relevant to making multiple choices.
+%
+% \section{Choice and multiple choice keys}
+% \label{sec:l3keys:choice}
+%
+% The \pkg{l3keys} system supports two types of choice key, in which a series
+% of pre-defined input values are linked to varying implementations. Choice
+% keys are usually created so that the various values are mutually-exclusive:
+% only one can apply at any one time. \enquote{Multiple} choice keys are also
+% supported: these allow a selection of values to be chosen at the same time.
+%
+% Mutually-exclusive choices are created by setting the \texttt{.choice:}
+% property:
+% \begin{verbatim}
+% \keys_define:nn { mymodule }
+% { key .choice: }
+% \end{verbatim}
+% For keys which are set up as choices, the valid choices are generated
+% by creating sub-keys of the choice key. This can be carried out in
+% two ways.
+%
+% In many cases, choices execute similar code which is dependant only
+% on the name of the choice or the position of the choice in the
+% list of all possibilities. Here, the keys can share the same code, and can
+% be rapidly created using the \texttt{.choices:nn} property.
+% \begin{verbatim}
+% \keys_define:nn { mymodule }
+% {
+% key .choices:nn =
+% { choice-a, choice-b, choice-c }
+% {
+% You~gave~choice~'\tl_use:N \l_keys_choice_tl',~
+% which~is~in~position~\int_use:N \l_keys_choice_int \c_space_tl
+% in~the~list.
+% }
+% }
+% \end{verbatim}
+% The index \cs{l_keys_choice_int} in the list of choices starts at~$1$.
+%
+% \begin{variable}{\l_keys_choice_int, \l_keys_choice_tl}
+% Inside the code block for a choice generated using \texttt{.choices:nn},
+% the variables \cs{l_keys_choice_tl} and \cs{l_keys_choice_int} are
+% available to indicate the name of the current choice, and its position in
+% the comma list. The position is indexed from~$1$. Note that, as with
+% standard key code generated using \texttt{.code:n}, the value passed to
+% the key (i.e.~the choice name) is also available as |#1|.
+% \end{variable}
+%
+% On the other hand, it is sometimes useful to create choices which
+% use entirely different code from one another. This can be achieved
+% by setting the \texttt{.choice:} property of a key, then manually
+% defining sub-keys.
+% \begin{verbatim}
+% \keys_define:nn { mymodule }
+% {
+% key .choice:,
+% key / choice-a .code:n = code-a,
+% key / choice-b .code:n = code-b,
+% key / choice-c .code:n = code-c,
+% }
+% \end{verbatim}
+%
+% It is possible to mix the two methods, but manually-created choices
+% should \emph{not} use \cs{l_keys_choice_tl} or \cs{l_keys_choice_int}.
+% These variables do not have defined behaviour when used outside of
+% code created using \texttt{.choices:nn}
+% (\emph{i.e.}~anything might happen).
+%
+% It is possible to allow choice keys to take values which have not previously
+% been defined by adding code for the special \texttt{unknown} choice. The
+% general behavior of the \texttt{unknown} key is described in
+% Section~\ref{sec:l3keys:unknown}. A typical example in the case of a choice
+% would be to issue a custom error message:
+% \begin{verbatim}
+% \keys_define:nn { mymodule }
+% {
+% key .choice:,
+% key / choice-a .code:n = code-a,
+% key / choice-b .code:n = code-b,
+% key / choice-c .code:n = code-c,
+% key / unknown .code:n =
+% \msg_error:nneee { mymodule } { unknown-choice }
+% { key } % Name of choice key
+% { choice-a , choice-b , choice-c } % Valid choices
+% { \exp_not:n {#1} } % Invalid choice given
+% %
+% %
+% }
+% \end{verbatim}
+%
+% Multiple choices are created in a very similar manner to mutually-exclusive
+% choices, using the properties \texttt{.multichoice:} and
+% \texttt{.multichoices:nn}. As with mutually exclusive choices, multiple
+% choices are define as sub-keys. Thus both
+% \begin{verbatim}
+% \keys_define:nn { mymodule }
+% {
+% key .multichoices:nn =
+% { choice-a, choice-b, choice-c }
+% {
+% You~gave~choice~'\tl_use:N \l_keys_choice_tl',~
+% which~is~in~position~
+% \int_use:N \l_keys_choice_int \c_space_tl
+% in~the~list.
+% }
+% }
+% \end{verbatim}
+% and
+% \begin{verbatim}
+% \keys_define:nn { mymodule }
+% {
+% key .multichoice:,
+% key / choice-a .code:n = code-a,
+% key / choice-b .code:n = code-b,
+% key / choice-c .code:n = code-c,
+% }
+% \end{verbatim}
+% are valid.
+%
+% When a multiple choice key is set
+% \begin{verbatim}
+% \keys_set:nn { mymodule }
+% {
+% key = { a , b , c } % 'key' defined as a multiple choice
+% }
+% \end{verbatim}
+% each choice is applied in turn, equivalent to a \texttt{clist} mapping or
+% to applying each value individually:
+% \begin{verbatim}
+% \keys_set:nn { mymodule }
+% {
+% key = a ,
+% key = b ,
+% key = c ,
+% }
+% \end{verbatim}
+% Thus each separate choice will have passed to it the
+% \cs{l_keys_choice_tl} and \cs{l_keys_choice_int} in exactly
+% the same way as described for \texttt{.choices:nn}.
+%
+% \section{Key usage scope}
+%
+% Some keys will be used as settings which have a strictly limited scope
+% of usage. Some will be only available once, others will only be valid
+% until typesetting begins. To allow formats to support this in a structured
+% way, \pkg{l3keys} allows this information to be specified using the
+% \texttt{.usage:n} property.
+%
+% \begin{function}[added = 2022-01-10]{.usage:n}
+% \begin{syntax}
+% \meta{key} .usage:n = \meta{scope}
+% \end{syntax}
+% Defines the \meta{key} to have usage within the \meta{scope}, which
+% should be one of \texttt{general}, \texttt{preamble} or \texttt{load}.
+% \end{function}
+%
+% \begin{variable}[added = 2022-01-10]
+% {\l_keys_usage_load_prop, \l_keys_usage_preamble_prop}
+% \pkg{l3keys} itself does \emph{not} attempt to redefine keys based on the
+% usage scope. Rather, this information is made available with these
+% two property lists. These hold an entry for each module (prefix); the
+% value of each entry is a comma-separated list of the usage-restricted
+% key(s).
+% \end{variable}
+%
+% \section{Setting keys}
+%
+% \begin{function}[updated = 2017-11-14]
+% {
+% \keys_set:nn, \keys_set:nV, \keys_set:nv, \keys_set:ne,
+% \keys_set:no,
+% }
+% \begin{syntax}
+% \cs{keys_set:nn} \Arg{module} \Arg{keyval list}
+% \end{syntax}
+% Parses the \meta{keyval list}, and sets those keys which are defined
+% for \meta{module}. The behaviour on finding an unknown key can be set
+% by defining a special \texttt{unknown} key: this is illustrated
+% later.
+% \end{function}
+%
+% \begin{variable}[updated = 2020-02-08]
+% {\l_keys_path_str, \l_keys_key_str, \l_keys_value_tl}
+% For each key processed, information of the full \emph{path} of the
+% key, the \emph{name} of the key and the \emph{value} of the key is
+% available within two string and one token list variables.
+% These may be used within the code of the key.
+%
+% The \emph{path} of the key is a \enquote{full} description of the key,
+% and is unique for each key. It consists of the module and full key name,
+% thus for example
+% \begin{verbatim}
+% \keys_set:nn { mymodule } { key-a = some-value }
+% \end{verbatim}
+% has path \texttt{mymodule/key-a} while
+% \begin{verbatim}
+% \keys_set:nn { mymodule } { subset / key-a = some-value }
+% \end{verbatim}
+% has path \texttt{mymodule/subset/key-a}. This information is stored in
+% \cs{l_keys_path_str}.
+%
+% The \emph{name} of the key is the part of the path after the last
+% \texttt{/}, and thus is not unique. In the preceding examples, both keys
+% have name \texttt{key-a} despite having different paths. This information
+% is stored in \cs{l_keys_key_str}.
+%
+% The \emph{value} is everything after the \texttt{=}, which may be
+% empty if no value was given. This is stored in \cs{l_keys_value_tl}, and
+% is not processed in any way by \cs{keys_set:nn}.
+% \end{variable}
+%
+% \section{Handling of unknown keys}
+% \label{sec:l3keys:unknown}
+%
+% If a key has not previously been defined (is unknown), \cs{keys_set:nn}
+% looks for a special \texttt{unknown} key for the same module, and if this is
+% not defined raises an error indicating that the key name was unknown. This
+% mechanism can be used for example to issue custom error texts. The
+% \texttt{unknown} key also supports the \texttt{.default:n} property.
+% \begin{verbatim}
+% \keys_define:nn { mymodule }
+% {
+% unknown .code:n =
+% You~tried~to~set~key~'\l_keys_key_str'~to~'#1'. ,
+% unknown .default:V = \c_novalue_tl
+% }
+% \end{verbatim}
+%
+% \begin{function}[added = 2011-08-23, updated = 2019-01-29]
+% {
+% \keys_set_known:nn, \keys_set_known:nV,
+% \keys_set_known:nv, \keys_set_known:ne,
+% \keys_set_known:no,
+% \keys_set_known:nnN, \keys_set_known:nVN,
+% \keys_set_known:nvN, \keys_set_known:neN,
+% \keys_set_known:noN,
+% \keys_set_known:nnnN, \keys_set_known:nVnN,
+% \keys_set_known:nvnN, \keys_set_known:nenN,
+% \keys_set_known:nonN
+% }
+% \begin{syntax}
+% \cs{keys_set_known:nn} \Arg{module} \Arg{keyval list}
+% \cs{keys_set_known:nnN} \Arg{module} \Arg{keyval list} \meta{tl}
+% \cs{keys_set_known:nnnN} \Arg{module} \Arg{keyval list} \Arg{root} \meta{tl}
+% \end{syntax}
+% These functions set keys which are known for the \meta{module}, and
+% simply ignore other keys. The \cs{keys_set_known:nn} function parses the
+% \meta{keyval list}, and sets those keys which are defined for
+% \meta{module}. Any keys which are unknown are not processed further by
+% the parser. In addition, \cs{keys_set_known:nnN} stores the key--value
+% pairs in the \meta{tl} in comma-separated form (\emph{i.e.}~an edited
+% version of the \meta{keyval list}). When a \meta{root} is given
+% (\cs{keys_set_known:nnnN}), the key--value entries are returned
+% relative to this point in the key tree. When it is absent, only the
+% key name and value are provided. The correct list is returned by
+% nested calls.
+% \end{function}
+%
+% \section{Selective key setting}
+% \label{sec:l3keys:selective}
+%
+% In some cases it may be useful to be able to select only some keys for
+% setting, even though these keys have the same path. For example, with
+% a set of keys defined using
+% \begin{verbatim}
+% \keys_define:nn { mymodule }
+% {
+% key-one .code:n = { \my_func:n {#1} } ,
+% key-two .tl_set:N = \l_my_a_tl ,
+% key-three .tl_set:N = \l_my_b_tl ,
+% key-four .fp_set:N = \l_my_a_fp ,
+% }
+% \end{verbatim}
+% the use of \cs{keys_set:nn} attempts to set all four keys. However, in
+% some contexts it may only be sensible to set some keys, or to control the
+% order of setting. To do this, keys may be assigned to \emph{groups}:
+% arbitrary sets which are independent of the key tree. Thus modifying the
+% example to read
+% \begin{verbatim}
+% \keys_define:nn { mymodule }
+% {
+% key-one .code:n = { \my_func:n {#1} } ,
+% key-one .groups:n = { first } ,
+% key-two .tl_set:N = \l_my_a_tl ,
+% key-two .groups:n = { first } ,
+% key-three .tl_set:N = \l_my_b_tl ,
+% key-three .groups:n = { second } ,
+% key-four .fp_set:N = \l_my_a_fp ,
+% }
+% \end{verbatim}
+% assigns \texttt{key-one} and \texttt{key-two} to group \texttt{first},
+% \texttt{key-three} to group \texttt{second}, while \texttt{key-four} is
+% not assigned to a group.
+%
+% Selective key setting may be achieved either by selecting one or more
+% groups to be made \enquote{active}, or by marking one or more groups to
+% be ignored in key setting.
+%
+% \begin{function}[added = 2024-01-10]
+% {
+% \keys_set_exclude_groups:nnn, \keys_set_exclude_groups:nnV,
+% \keys_set_exclude_groups:nnv, \keys_set_exclude_groups:nno,
+% \keys_set_exclude_groups:nnnN, \keys_set_exclude_groups:nnVN,
+% \keys_set_exclude_groups:nnvN, \keys_set_exclude_groups:nnoN,
+% \keys_set_exclude_groups:nnnnN, \keys_set_exclude_groups:nnVnN,
+% \keys_set_exclude_groups:nnvnN, \keys_set_exclude_groups:nnonN,
+% }
+% \begin{syntax}
+% \cs{keys_set_exclude_groups:nnn} \Arg{module} \Arg{groups} \Arg{keyval list}
+% \cs{keys_set_exclude_groups:nnnN} \Arg{module} \Arg{groups} \Arg{keyval list} \meta{tl}
+% \cs{keys_set_exclude_groups:nnnnN} \Arg{module} \Arg{groups} \Arg{keyval list} \meta{root} \meta{tl}
+% \end{syntax}
+% Sets keys by excluding those in the specificied \meta{groups}.
+% The \meta{groups} are
+% given as a comma-separated list. Unknown keys are not assigned to any
+% group and are thus always set. The key--value pairs for each
+% key which is filtered out are stored in the \meta{tl} in a
+% comma-separated form (\emph{i.e.}~an edited version of the \meta{keyval
+% list}). The \cs{keys_set_exclude_groups:nnn} version skips this stage.
+%
+% Use of \cs{keys_set_exclude_groups:nnnN} can be nested, with the correct residual
+% \meta{keyval list} returned at each stage. In the version which takes
+% a \meta{root} argument, the key list is returned relative to that point
+% in the key tree. In the cases without a \meta{root} argument, only
+% the key names and values are returned.
+% \end{function}
+%
+% \begin{function}[added = 2013-07-14, updated = 2017-05-27]
+% {
+% \keys_set_groups:nnn, \keys_set_groups:nnV,
+% \keys_set_groups:nnv, \keys_set_groups:nno
+% }
+% \begin{syntax}
+% \cs{keys_set_groups:nnn} \Arg{module} \Arg{groups} \Arg{keyval list}
+% \end{syntax}
+% Activates key filtering in an \enquote{opt-in} sense: only keys assigned to
+% one or more of the \meta{groups} specified are set. The \meta{groups} are
+% given as a comma-separated list. Unknown keys are not assigned to any
+% group and are thus never set.
+% \end{function}
+%
+% \section{Digesting keys}
+%
+% \begin{function}[added = 2022-03-09]{\keys_precompile:nnN}
+% \begin{syntax}
+% \cs{keys_precompile:nnN} \Arg{module} \Arg{keyval list} \meta{tl}
+% \end{syntax}
+% Parses the \meta{keyval list} as for \cs{keys_set:nn}, placing the
+% resulting code for those which set variables or functions into the
+% \meta{tl}. Thus this function \enquote{precompiles} the keyval list into
+% a set of results which can be applied rapidly.
+% \end{function}
+%
+% \section{Utility functions for keys}
+%
+% \begin{function}[EXP, pTF, updated = 2022-01-10]
+% {\keys_if_exist:nn, \keys_if_exist:ne}
+% \begin{syntax}
+% \cs{keys_if_exist_p:nn} \Arg{module} \Arg{key} \\
+% \cs{keys_if_exist:nnTF} \Arg{module} \Arg{key} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{key} exists for \meta{module}, \emph{i.e.}~if any code
+% has been defined for \meta{key}.
+% \end{function}
+%
+% \begin{function}[added = 2011-08-21,EXP,pTF, updated = 2017-11-14]
+% {\keys_if_choice_exist:nnn}
+% \begin{syntax}
+% \cs{keys_if_choice_exist_p:nnn} \Arg{module} \Arg{key} \Arg{choice} \\
+% \cs{keys_if_choice_exist:nnnTF} \Arg{module} \Arg{key} \Arg{choice} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{choice} is defined for the \meta{key} within the
+% \meta{module}, \emph{i.e.}~if any code has been defined for
+% \meta{key}/\meta{choice}. The test is \texttt{false} if the \meta{key}
+% itself is not defined.
+% \end{function}
+%
+% \begin{function}[updated = 2015-08-09]{\keys_show:nn}
+% \begin{syntax}
+% \cs{keys_show:nn} \Arg{module} \Arg{key}
+% \end{syntax}
+% Displays in the terminal
+% the information associated to the \meta{key} for a \meta{module},
+% including the function which is used to actually implement it.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2015-08-09]{\keys_log:nn}
+% \begin{syntax}
+% \cs{keys_log:nn} \Arg{module} \Arg{key}
+% \end{syntax}
+% Writes in the log file the information associated to the \meta{key}
+% for a \meta{module}. See also \cs{keys_show:nn} which displays the
+% result in the terminal.
+% \end{function}
+%
+% \section{Low-level interface for parsing key--val lists}
+%
+% To re-cap from earlier, a key--value list is input of the form
+% \begin{verbatim}
+% KeyOne = ValueOne ,
+% KeyTwo = ValueTwo ,
+% KeyThree
+% \end{verbatim}
+% where each key--value pair is separated by a comma from the rest of
+% the list, and each key--value pair does not necessarily contain an
+% equals sign or a value! Processing this type of input correctly
+% requires a number of careful steps, to correctly account for
+% braces, spaces and the category codes of separators.
+%
+% While the functions described earlier are used as a high-level interface
+% for processing such input, in special circumstances you may wish to use
+% a lower-level approach.
+% The low-level parsing system converts a \meta{key--value list}
+% into \meta{keys} and associated \meta{values}. After the parsing phase
+% is completed, the resulting keys and values (or keys alone) are
+% available for further processing. This processing is not carried out by the
+% low-level parser itself, and so the parser requires the names of
+% two functions along with the key--value list. One function is
+% needed to process key--value pairs (it receives two arguments),
+% and a second function is required for keys given without any value
+% (it is called with a single argument).
+%
+% The parser does not double |#| tokens or expand any input. Active
+% tokens |=| and |,| appearing at the outer level of braces are converted
+% to category \enquote{other} (12) so that the parser does not \enquote{miss}
+% any due to category code changes. Spaces are removed from the ends
+% of the keys and values. Keys and values which are given in braces
+% have exactly one set removed (after space trimming), thus
+% \begin{verbatim}
+% key = {value here},
+% \end{verbatim}
+% and
+% \begin{verbatim}
+% key = value here,
+% \end{verbatim}
+% are treated identically.
+%
+% \begin{function}[rEXP, added=2020-12-19, updated = 2021-05-10]
+% {\keyval_parse:nnn, \keyval_parse:nnV, \keyval_parse:nnv}
+% \begin{syntax}
+% \cs{keyval_parse:nnn} \Arg{code_1} \Arg{code_2} \Arg{key--value list}
+% \end{syntax}
+% Parses the \meta{key--value list} into a series of \meta{keys} and
+% associated \meta{values}, or keys alone (if no \meta{value} was
+% given). \meta{code_1} receives each \meta{key} (with no \meta{value}) as a
+% trailing brace group, whereas \meta{code_2} is appended by two brace groups,
+% the \meta{key} and \meta{value}.
+% The order of the \meta{keys} in the \meta{key--value list}
+% is preserved. Thus
+% \begin{verbatim}
+% \keyval_parse:nnn
+% { \use_none:nn { code 1 } }
+% { \use_none:nnn { code 2 } }
+% { key1 = value1 , key2 = value2, key3 = , key4 }
+% \end{verbatim}
+% is converted into an input stream
+% \begin{verbatim}
+% \use_none:nnn { code 2 } { key1 } { value1 }
+% \use_none:nnn { code 2 } { key2 } { value2 }
+% \use_none:nnn { code 2 } { key3 } { }
+% \use_none:nn { code 1 } { key4 }
+% \end{verbatim}
+% Note that there is a difference between an empty value (an equals
+% sign followed by nothing) and a missing value (no equals sign at
+% all). Spaces are trimmed from the ends of the \meta{key} and \meta{value},
+% then one \emph{outer} set of braces is removed from the \meta{key}
+% and \meta{value} as part of the processing. If you need exactly the output
+% shown above, you'll need to either \texttt{e}-type or \texttt{x}-type expand
+% the function.
+% \begin{texnote}
+% The result of each list element is returned within \cs{exp_not:n}, which
+% means that the converted input stream does not expand further when
+% appearing in an \texttt{e}-type or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[rEXP, updated = 2021-05-10]
+% {\keyval_parse:NNn, \keyval_parse:NNV, \keyval_parse:NNv}
+% \begin{syntax}
+% \cs{keyval_parse:NNn} \meta{function_1} \meta{function_2} \Arg{key--value list}
+% \end{syntax}
+% Parses the \meta{key--value list} into a series of \meta{keys} and
+% associated \meta{values}, or keys alone (if no \meta{value} was
+% given). \meta{function_1} should take one argument, while
+% \meta{function_2} should absorb two arguments. After
+% \cs{keyval_parse:NNn} has parsed the \meta{key--value list},
+% \meta{function_1} is used to process keys given with no value
+% and \meta{function_2} is used to process keys given with a
+% value. The order of the \meta{keys} in the \meta{key--value list}
+% is preserved. Thus
+% \begin{verbatim}
+% \keyval_parse:NNn \function:n \function:nn
+% { key1 = value1 , key2 = value2, key3 = , key4 }
+% \end{verbatim}
+% is converted into an input stream
+% \begin{verbatim}
+% \function:nn { key1 } { value1 }
+% \function:nn { key2 } { value2 }
+% \function:nn { key3 } { }
+% \function:n { key4 }
+% \end{verbatim}
+% Note that there is a difference between an empty value (an equals
+% sign followed by nothing) and a missing value (no equals sign at
+% all). Spaces are trimmed from the ends of the \meta{key} and \meta{value},
+% then one \emph{outer} set of braces is removed from the \meta{key}
+% and \meta{value} as part of the processing.
+%
+% This shares the implementation of \cs{keyval_parse:nnn}, the difference is
+% only semantically.
+% \begin{texnote}
+% The result is returned within \cs{exp_not:n}, which means that the
+% converted input stream does not expand further when appearing in an
+% \texttt{e}-type or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3keys} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \subsection{Low-level interface}
+%
+% The low-level key parser's implementation is based heavily on \pkg{expkv}.
+% Compared to \pkg{keyval} it adds a number of additional \enquote{safety}
+% requirements and allows to process the parsed list of key--value pairs in a
+% variety of ways. The net result is that this code needs around one and a half
+% the amount of time as \pkg{keyval} to parse the same list of keys. To optimise
+% speed as far as reasonably practical, a number of lower-level approaches are
+% taken rather than using the higher-level \pkg{expl3} interfaces.
+%
+% \begin{macrocode}
+%<@@=keyval>
+% \end{macrocode}
+%
+% \begin{variable}{\s_@@_nil,\s_@@_mark,\s_@@_stop,\s_@@_tail}
+% \begin{macrocode}
+\scan_new:N \s_@@_nil
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+\scan_new:N \s_@@_tail
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l__kernel_keyval_allow_blank_keys_bool}
+% The general behavior of the \pkg{l3keys} module is to throw an error on
+% blank key names. However to support the usage of \cs{keyval_parse:nnn} in
+% the \pkg{l3prop} module we allow this error to be switched off temporarily
+% and just ignore blank names.
+% \begin{macrocode}
+\bool_new:N \l__kernel_keyval_allow_blank_keys_bool
+% \end{macrocode}
+% \end{variable}
+%
+% This temporary macro will be used since some of the definitions will need an
+% active comma or equals sign. Inside of this macro |#1| will be the active
+% comma and |#2| will be the active equals sign.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \@@_tmp:w #1#2
+ {
+% \end{macrocode}
+%
+% \begin{macro}[EXP]
+% {
+% \keyval_parse:nnn, \keyval_parse:nnV, \keyval_parse:nnv,
+% \keyval_parse:NNn, \keyval_parse:NNV, \keyval_parse:NNv
+% }
+% The main function starts the first of two loops. The outer loop splits the
+% key--value list at active commas, the inner loop will do so at other commas.
+% The use of \cs{s_@@_mark} here prevents loss of braces from the key
+% argument.
+% \begin{macrocode}
+ \cs_new:Npn \keyval_parse:nnn ##1 ##2 ##3
+ {
+ \__kernel_exp_not:w \tex_expanded:D
+ {
+ {
+ \@@_loop_active:nnw {##1} {##2}
+ \s_@@_mark ##3 #1 \s_@@_tail #1
+ }
+ }
+ }
+ \cs_new_eq:NN \keyval_parse:NNn \keyval_parse:nnn
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_loop_active:nnw}
+% First a fast test for the end of the loop is done, it'll gobble everything
+% up to a \cs{s_@@_tail}. The loop ending macro will gobble everything to the
+% last comma in this definition.
+% If the end isn't reached yet, start the second loop splitting at other
+% commas, the next iteration of this first loop will be inserted by the end of
+% \cs{@@_loop_other:nnw}.
+% \begin{macrocode}
+ \cs_new:Npn \@@_loop_active:nnw ##1 ##2 ##3 #1
+ {
+ \@@_if_recursion_tail:w ##3
+ \@@_end_loop_active:w \s_@@_tail
+ \@@_loop_other:nnw {##1} {##2} ##3 , \s_@@_tail ,
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_split_other:w, \@@_split_active:w}
+% These two macros allow to split at the first equals sign of category 12 or
+% 13. At the same time they also execute branching by inserting the first
+% token following \cs{s_@@_mark} that followed the equals sign. Hence they
+% also test for the presence of such an equals sign simultaneously.
+% \begin{macrocode}
+ \cs_new:Npn \@@_split_other:w ##1 = ##2 \s_@@_mark ##3
+ { ##3 ##1 \s_@@_stop \s_@@_mark ##2 }
+ \cs_new:Npn \@@_split_active:w ##1 #2 ##2 \s_@@_mark ##3
+ { ##3 ##1 \s_@@_stop \s_@@_mark ##2 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_loop_other:nnw}
+% The second loop uses the same test for its end as the first loop, next it
+% splits at the first active equals sign using \cs{@@_split_active:w}. The
+% \cs{s_@@_nil} prevents accidental brace stripping and acts as a delimiter in
+% the next steps. First testing for an active equals sign will reduce the
+% number of necessary expansion steps for the expected average use case of
+% other equals signs and hence perform better on average.
+% \begin{macrocode}
+ \cs_new:Npn \@@_loop_other:nnw ##1 ##2 ##3 ,
+ {
+ \@@_if_recursion_tail:w ##3
+ \@@_end_loop_other:w \s_@@_tail
+ \@@_split_active:w ##3 \s_@@_nil
+ \s_@@_mark \@@_split_active_auxi:w
+ #2 \s_@@_mark \@@_clean_up_active:w
+ {##1} {##2}
+ \s_@@_mark
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_split_active_auxi:w}
+% \begin{macro}[EXP]{\@@_split_active_auxii:w}
+% \begin{macro}[EXP]{\@@_split_active_auxiii:w}
+% \begin{macro}[EXP]{\@@_split_active_auxiv:w}
+% \begin{macro}[EXP]{\@@_split_active_auxv:w}
+% After \cs{@@_split_active:w} the following will only be called if there was
+% at least one active equals sign in the current key--value pair. Therefore
+% this is the execution branch for a key--value pair with an active equals
+% sign. |##1| will be everything up to the first active equals sign. First it
+% tests for other equals signs in the key name, which will eventually throw an
+% error via \cs{@@_misplaced_equal_after_active_error:w}. If none was found we
+% forward the key to \cs{@@_split_active_auxii:w}.
+% \begin{macrocode}
+ \cs_new:Npn \@@_split_active_auxi:w ##1 \s_@@_stop
+ {
+ \@@_split_other:w ##1 \s_@@_nil
+ \s_@@_mark \@@_misplaced_equal_after_active_error:w
+ = \s_@@_mark \@@_split_active_auxii:w
+ }
+% \end{macrocode}
+% \cs{@@_split_active_auxii:w} gets the correct key name with a leading
+% \cs{s_@@_mark} as |##1|. It has to sanitise the remainder of the previous
+% test and trims the key name which will be forwarded to
+% \cs{@@_split_active_auxiii:w}.
+% \begin{macrocode}
+ \cs_new:Npn \@@_split_active_auxii:w
+ ##1 \s_@@_nil \s_@@_mark \@@_misplaced_equal_after_active_error:w
+ \s_@@_stop \s_@@_mark
+ ##2 \s_@@_nil #2 \s_@@_mark \@@_clean_up_active:w
+ { \@@_trim:nN {##1} \@@_split_active_auxiii:w ##2 \s_@@_nil }
+% \end{macrocode}
+% Next we test for a misplaced active equals sign in the value, if none is
+% found \cs{@@_split_active_auxiv:w} will be called.
+% \begin{macrocode}
+ \cs_new:Npn \@@_split_active_auxiii:w ##1 ##2 \s_@@_nil
+ {
+ \@@_split_active:w ##2 \s_@@_nil
+ \s_@@_mark \@@_misplaced_equal_in_split_error:w
+ #2 \s_@@_mark \@@_split_active_auxiv:w
+ {##1}
+ }
+% \end{macrocode}
+% This runs the last test after sanitising the remainder of the previous one.
+% This time test for a misplaced equals sign of category 12 in the value.
+% Finally the last auxiliary macro will be called.
+% \begin{macrocode}
+ \cs_new:Npn \@@_split_active_auxiv:w
+ ##1 \s_@@_nil \s_@@_mark \@@_misplaced_equal_in_split_error:w
+ \s_@@_stop \s_@@_mark
+ {
+ \@@_split_other:w ##1 \s_@@_nil
+ \s_@@_mark \@@_misplaced_equal_in_split_error:w
+ = \s_@@_mark \@@_split_active_auxv:w
+ }
+% \end{macrocode}
+% This last macro in this execution branch sanitises the last test, trims the
+% value and passes it to \cs{@@_pair:nnnn}.
+% \begin{macrocode}
+ \cs_new:Npn \@@_split_active_auxv:w
+ ##1 \s_@@_nil \s_@@_mark \@@_misplaced_equal_in_split_error:w
+ \s_@@_stop \s_@@_mark
+ { \@@_trim:nN { ##1 } \@@_pair:nnnn }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_clean_up_active:w}
+% The following is the branch taken if the key--value pair doesn't contain an
+% active equals sign. The remainder of that test will be cleaned up by
+% \cs{@@_clean_up_active:w} which will then split at an equals sign of
+% category other.
+% \begin{macrocode}
+ \cs_new:Npn \@@_clean_up_active:w
+ ##1 \s_@@_nil \s_@@_mark \@@_split_active_auxi:w \s_@@_stop \s_@@_mark
+ {
+ \@@_split_other:w ##1 \s_@@_nil
+ \s_@@_mark \@@_split_other_auxi:w
+ = \s_@@_mark \@@_clean_up_other:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_split_other_auxi:w}
+% \begin{macro}[EXP]{\@@_split_other_auxii:w}
+% \begin{macro}[EXP]{\@@_split_other_auxiii:w}
+% This is executed if the key--value pair doesn't contain an active equals
+% sign but at least one other. |##1| of \cs{@@_split_other_auxi:w} will
+% contain the complete key name, which is trimmed and forwarded to the next
+% auxiliary macro.
+% \begin{macrocode}
+ \cs_new:Npn \@@_split_other_auxi:w ##1 \s_@@_stop
+ { \@@_trim:nN { ##1 } \@@_split_other_auxii:w }
+% \end{macrocode}
+% We know that the value doesn't contain misplaced active equals signs but we
+% have to test for others. Also we need to sanitise the previous test, which
+% is done here and not earlier to avoid superfluous argument grabbing.
+% \begin{macrocode}
+ \cs_new:Npn \@@_split_other_auxii:w
+ ##1 ##2 \s_@@_nil = \s_@@_mark \@@_clean_up_other:w
+ {
+ \@@_split_other:w ##2 \s_@@_nil
+ \s_@@_mark \@@_misplaced_equal_in_split_error:w
+ = \s_@@_mark \@@_split_other_auxiii:w
+ { ##1 }
+ }
+% \end{macrocode}
+% \cs{@@_split_other_auxiii:w} sanitises the test for other equals signs,
+% trims the value and forwards it to \cs{@@_pair:nnnn}.
+% \begin{macrocode}
+ \cs_new:Npn \@@_split_other_auxiii:w
+ ##1 \s_@@_nil \s_@@_mark \@@_misplaced_equal_in_split_error:w
+ \s_@@_stop \s_@@_mark
+ { \@@_trim:nN { ##1 } \@@_pair:nnnn }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_clean_up_other:w}
+% \cs{@@_clean_up_other:w} is the last branch that might exist. It is called
+% if no equals sign was found, hence the only possibilities left are a blank
+% list element, which is to be skipped, or a lonely key. If it's no empty list
+% element this will trim the key name and forward it to \cs{@@_key:nn}.
+% \begin{macrocode}
+ \cs_new:Npn \@@_clean_up_other:w
+ ##1 \s_@@_nil \s_@@_mark \@@_split_other_auxi:w \s_@@_stop \s_@@_mark
+ {
+ \@@_if_blank:w ##1 \s_@@_nil \s_@@_stop \@@_blank_true:w
+ \s_@@_mark \s_@@_stop
+ \@@_trim:nN { ##1 } \@@_key:nn
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_misplaced_equal_after_active_error:w}
+% \begin{macro}[EXP]{\@@_misplaced_equal_in_split_error:w}
+% All these two macros do is gobble the remainder of the current other loop
+% execution and throw an error. Afterwards they have to insert the next loop
+% iteration.
+% \begin{macrocode}
+ \cs_new:Npn \@@_misplaced_equal_after_active_error:w
+ \s_@@_mark ##1 \s_@@_stop \s_@@_mark ##2 \s_@@_nil
+ = \s_@@_mark \@@_split_active_auxii:w
+ \s_@@_mark ##3 \s_@@_nil
+ #2 \s_@@_mark \@@_clean_up_active:w
+ {
+ \msg_expandable_error:nn
+ { keyval } { misplaced-equals-sign }
+ \@@_loop_other:nnw
+ }
+ \cs_new:Npn \@@_misplaced_equal_in_split_error:w
+ \s_@@_mark ##1 \s_@@_stop \s_@@_mark ##2 \s_@@_nil
+ ##3 \s_@@_mark ##4 ##5
+ {
+ \msg_expandable_error:nn
+ { keyval } { misplaced-equals-sign }
+ \@@_loop_other:nnw
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_end_loop_other:w, \@@_end_loop_active:w}
+% All that's left for the parsing loops are the macros which end the
+% recursion. Both just gobble the remaining tokens of the respective loop
+% including the next recursion call. \cs{@@_end_loop_other:w} also has to
+% insert the next iteration of the active loop.
+% \begin{macrocode}
+ \cs_new:Npn \@@_end_loop_other:w
+ \s_@@_tail
+ \@@_split_active:w
+ \s_@@_mark \s_@@_tail
+ \s_@@_nil \s_@@_mark
+ \@@_split_active_auxi:w
+ #2 \s_@@_mark \@@_clean_up_active:w
+ { \@@_loop_active:nnw }
+ \cs_new:Npn \@@_end_loop_active:w
+ \s_@@_tail
+ \@@_loop_other:nnw ##1 \s_@@_mark \s_@@_tail , \s_@@_tail ,
+ { }
+% \end{macrocode}
+% \end{macro}
+%
+% The parsing loops are done, so here ends the definition of \cs{@@_tmp:w},
+% which will finally set up the macros.
+% \begin{macrocode}
+ }
+ \char_set_catcode_active:n { `\, }
+ \char_set_catcode_active:n { `\= }
+ \@@_tmp:w , =
+\group_end:
+\cs_generate_variant:Nn \keyval_parse:NNn { NNV , NNv }
+\cs_generate_variant:Nn \keyval_parse:nnn { nnV , nnv }
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\@@_pair:nnnn, \@@_key:nn}
+% These macros will be called on the parsed keys and values of the key--value
+% list. All arguments are completely trimmed. They test for blank key names
+% and call the functions passed to \cs{keyval_parse:nnn} inside of
+% \cs{exp_not:n} with the correct arguments. Afterwards they insert the next
+% iteration of the other loop.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \@@_tmp:w #1#2
+ {
+ \cs_new:Npn \@@_pair:nnnn ##1 ##2 ##3 ##4
+ {
+ \@@_if_blank:w \s_@@_mark ##2 \s_@@_nil \s_@@_stop \@@_blank_key_error:w
+ \s_@@_mark \s_@@_stop
+ #1
+ \exp_not:n { ##4 {##2} {##1} }
+ #2
+ \@@_loop_other:nnw {##3} {##4}
+ }
+ \cs_new:Npn \@@_key:nn ##1 ##2
+ {
+ \@@_if_blank:w \s_@@_mark ##1 \s_@@_nil \s_@@_stop \@@_blank_key_error:w
+ \s_@@_mark \s_@@_stop
+ #1
+ \exp_not:n { ##2 {##1} }
+ #2
+ \@@_loop_other:nnw {##2}
+ }
+ }
+ \@@_tmp:w { } { }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_if_empty:w,\@@_if_blank:w,\@@_if_recursion_tail:w}
+% All these tests work by gobbling tokens until a certain combination is met,
+% which makes them pretty fast. The test for a blank argument should be called
+% with an arbitrary token following the argument. Each of these utilize the
+% fact that the argument will contain a leading \cs{s_@@_mark}.
+% \begin{macrocode}
+\cs_new:Npn \@@_if_empty:w #1 \s_@@_mark \s_@@_stop { }
+\cs_new:Npn \@@_if_blank:w \s_@@_mark #1 { \@@_if_empty:w \s_@@_mark }
+\cs_new:Npn \@@_if_recursion_tail:w \s_@@_mark #1 \s_@@_tail { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_blank_true:w,\@@_blank_key_error:w}
+% These macros will be called if the tests above didn't gobble them, they
+% execute the branching.
+% \begin{macrocode}
+\cs_new:Npn \@@_blank_true:w \s_@@_mark \s_@@_stop \@@_trim:nN #1 \@@_key:nn
+ { \@@_loop_other:nnw }
+\cs_new:Npn \@@_blank_key_error:w \s_@@_mark \s_@@_stop #1 \@@_loop_other:nnw
+ {
+ \bool_if:NTF \l__kernel_keyval_allow_blank_keys_bool
+ { #1 }
+ { \msg_expandable_error:nn { keyval } { blank-key-name } }
+ \@@_loop_other:nnw
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% Two messages for the low level parsing system.
+% \begin{macrocode}
+\msg_new:nnn { keyval } { misplaced-equals-sign }
+ { Misplaced~'='~in~key-value~input~\msg_line_context: }
+\msg_new:nnn { keyval } { blank-key-name }
+ { Blank~key~name~in~key-value~input~\msg_line_context: }
+\prop_gput:Nnn \g_msg_module_name_prop { keyval } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { keyval } { }
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\@@_trim:nN}
+% \begin{macro}[EXP]
+% {\@@_trim_auxi:w,\@@_trim_auxii:w,\@@_trim_auxiii:w,\@@_trim_auxiv:w}
+% And an adapted version of \cs{__tl_trim_spaces:nn} which is a bit faster for
+% our use case, as it can strip the braces at the end. This is pretty much the
+% same concept, so I won't comment on it here. The speed gain by using this
+% instead of \cs{tl_trim_spaces_apply:nN} is about 10\,\% of the total time for
+% \cs{keyval_parse:NNn} with one key and one key--value pair, so I think it's
+% worth it.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_new:Npn \@@_trim:nN ##1
+ {
+ \@@_trim_auxi:w
+ ##1
+ \s_@@_nil
+ \s_@@_mark #1 { }
+ \s_@@_mark \@@_trim_auxii:w
+ \@@_trim_auxiii:w
+ #1 \s_@@_nil
+ \@@_trim_auxiv:w
+ }
+ \cs_new:Npn \@@_trim_auxi:w ##1 \s_@@_mark #1 ##2 \s_@@_mark ##3
+ {
+ ##3
+ \@@_trim_auxi:w
+ \s_@@_mark
+ ##2
+ \s_@@_mark #1 {##1}
+ }
+ \cs_new:Npn \@@_trim_auxii:w \@@_trim_auxi:w \s_@@_mark \s_@@_mark ##1
+ {
+ \@@_trim_auxiii:w
+ ##1
+ }
+ \cs_new:Npn \@@_trim_auxiii:w ##1 #1 \s_@@_nil ##2
+ {
+ ##2
+ ##1 \s_@@_nil
+ \@@_trim_auxiii:w
+ }
+% \end{macrocode}
+% This is the one macro which differs from the original definition.
+% \begin{macrocode}
+ \cs_new:Npn \@@_trim_auxiv:w
+ \s_@@_mark ##1 \s_@@_nil
+ \@@_trim_auxiii:w \s_@@_nil \@@_trim_auxiii:w
+ ##2
+ { ##2 { ##1 } }
+ }
+ \@@_tmp:w { ~ }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Constants and variables}
+%
+% \begin{macrocode}
+%<@@=keys>
+% \end{macrocode}
+%
+% \begin{variable}
+% {
+% \c_@@_code_root_str ,
+% \c_@@_check_root_str ,
+% \c_@@_default_root_str ,
+% \c_@@_groups_root_str ,
+% \c_@@_inherit_root_str ,
+% \c_@@_type_root_str
+% }
+% Various storage areas for the different data which make up keys.
+% \begin{macrocode}
+\str_const:Nn \c_@@_code_root_str { key~code~>~ }
+\str_const:Nn \c_@@_check_root_str { key~check~>~ }
+\str_const:Nn \c_@@_default_root_str { key~default~>~ }
+\str_const:Nn \c_@@_groups_root_str { key~groups~>~ }
+\str_const:Nn \c_@@_inherit_root_str { key~inherit~>~ }
+\str_const:Nn \c_@@_type_root_str { key~type~>~ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_props_root_str}
+% The prefix for storing properties.
+% \begin{macrocode}
+\str_const:Nn \c_@@_props_root_str { key~prop~>~ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_keys_choice_int, \l_keys_choice_tl}
+% Publicly accessible data on which choice is being used when several
+% are generated as a set.
+% \begin{macrocode}
+\int_new:N \l_keys_choice_int
+\tl_new:N \l_keys_choice_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_groups_clist}
+% Used for storing and recovering the list of groups which apply to a key:
+% set as a comma list but at one point we have to use this for a token
+% list recovery.
+% \begin{macrocode}
+\clist_new:N \l_@@_groups_clist
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_inherit_clist}
+% For normalisation.
+% \begin{macrocode}
+\clist_new:N \l_@@_inherit_clist
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_keys_key_str}
+% The name of a key itself: needed when setting keys.
+% \begin{macrocode}
+\str_new:N \l_keys_key_str
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}[deprecated]{\l_keys_key_tl}
+% The |tl| version is deprecated but has to be handled manually.
+% \begin{macrocode}
+\tl_new:N \l_keys_key_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_module_str}
+% The module for an entire set of keys.
+% \begin{macrocode}
+\str_new:N \l_@@_module_str
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_no_value_bool}
+% A marker is needed internally to show if only a key or a key plus a
+% value was seen: this is recorded here.
+% \begin{macrocode}
+\bool_new:N \l_@@_no_value_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_only_known_bool}
+% Used to track if only \enquote{known} keys are being set.
+% \begin{macrocode}
+\bool_new:N \l_@@_only_known_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_keys_path_str}
+% The \enquote{path} of the current key is stored here: this is
+% available to the programmer and so is public.
+% \begin{macrocode}
+\str_new:N \l_keys_path_str
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}[deprecated]{\l_keys_path_tl}
+% The older version is deprecated but has to be handled manually.
+% \begin{macrocode}
+\tl_new:N \l_keys_path_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_inherit_str}
+% \begin{macrocode}
+\str_new:N \l_@@_inherit_str
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_relative_tl}
+% The relative path for passing keys back to the user. As this can
+% be explicitly no-value, it must be a token list.
+% \begin{macrocode}
+\tl_new:N \l_@@_relative_tl
+\tl_set:Nn \l_@@_relative_tl { \q_@@_no_value }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_property_str}
+% The \enquote{property} begin set for a key at definition time is
+% stored here.
+% \begin{macrocode}
+\str_new:N \l_@@_property_str
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_selective_bool, \l_@@_filtered_bool}
+% Two booleans for using key groups: one to indicate that \enquote{selective}
+% setting is active, a second to specify which type (\enquote{opt-in}
+% or \enquote{opt-out}).
+% \begin{macrocode}
+\bool_new:N \l_@@_selective_bool
+\bool_new:N \l_@@_filtered_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_selective_seq}
+% The list of key groups being filtered in or out during selective setting.
+% \begin{macrocode}
+\seq_new:N \l_@@_selective_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_unused_clist}
+% Used when setting only some keys to store those left over.
+% \begin{macrocode}
+\clist_new:N \l_@@_unused_clist
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_keys_value_tl}
+% The value given for a key: may be empty if no value was given.
+% \begin{macrocode}
+\tl_new:N \l_keys_value_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_tmp_bool, \l_@@_tmpa_tl, \l_@@_tmpb_tl}
+% Scratch space.
+% \begin{macrocode}
+\bool_new:N \l_@@_tmp_bool
+\tl_new:N \l_@@_tmpa_tl
+\tl_new:N \l_@@_tmpb_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_precompile_bool, \l_@@_precompile_tl}
+% For digesting keys.
+% \begin{macrocode}
+\bool_new:N \l_@@_precompile_bool
+\tl_new:N \l_@@_precompile_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_keys_usage_load_prop, \l_keys_usage_preamble_prop}
+% Global data for document-level information.
+% \begin{macrocode}
+\prop_new:N \l_keys_usage_load_prop
+\prop_new:N \l_keys_usage_preamble_prop
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Internal auxiliaries}
+%
+% \begin{variable}{\s_@@_nil,\s_@@_mark,\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_nil
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\q_@@_no_value}
+% Internal quarks.
+% \begin{macrocode}
+\quark_new:N \q_@@_no_value
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[pTF]{\@@_quark_if_no_value:N}
+% Branching quark conditional.
+% \begin{macrocode}
+\__kernel_quark_new_conditional:Nn \@@_quark_if_no_value:N { TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_precompile:n}
+% An auxiliary to allow cleaner showing of code.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_precompile:n #1
+ {
+ \bool_if:NTF \l_@@_precompile_bool
+ { \tl_put_right:Nn \l_@@_precompile_tl }
+ { \use:n }
+ {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_cs_undefine:c}
+% Local version of \cs{cs_undefine:c} to avoid sprinkling
+% \cs{tex_undefined:D} everywhere.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_cs_undefine:c #1
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \else:
+ \use_i:nnnn
+ \fi:
+ \cs_set_eq:cN {#1} \tex_undefined:D
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{The key defining mechanism}
+%
+% \begin{macro}{\keys_define:nn, \keys_define:ne, \keys_define:nx}
+% \begin{macro}{\@@_define:nnn, \@@_define:onn}
+% The public function for definitions is just a wrapper for the lower
+% level mechanism, more or less. The outer function is designed to
+% keep a track of the current module, to allow safe nesting. The module is set
+% removing any leading |/| (which is not needed here).
+% \begin{macrocode}
+\cs_new_protected:Npn \keys_define:nn
+ { \@@_define:onn \l_@@_module_str }
+\cs_generate_variant:Nn \keys_define:nn { ne , nx }
+\cs_new_protected:Npn \@@_define:nnn #1#2#3
+ {
+ \str_set:Ne \l_@@_module_str { \@@_trim_spaces:n {#2} }
+ \keyval_parse:NNn \@@_define:n \@@_define:nn {#3}
+ \str_set:Nn \l_@@_module_str {#1}
+ }
+\cs_generate_variant:Nn \@@_define:nnn { o }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_define:n}
+% \begin{macro}{\@@_define:nn}
+% \begin{macro}{\@@_define_aux:nn}
+% The outer functions here record whether a value was given and then
+% converge on a common internal mechanism. There is first a search for
+% a property in the current key name, then a check to make sure it is
+% known before the code hands off to the next step.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_define:n #1
+ {
+ \bool_set_true:N \l_@@_no_value_bool
+ \@@_define_aux:nn {#1} { }
+ }
+\cs_new_protected:Npn \@@_define:nn #1#2
+ {
+ \bool_set_false:N \l_@@_no_value_bool
+ \@@_define_aux:nn {#1} {#2}
+ }
+\cs_new_protected:Npn \@@_define_aux:nn #1#2
+ {
+ \@@_property_find:n {#1}
+ \cs_if_exist:cTF { \c_@@_props_root_str \l_@@_property_str }
+ { \@@_define_code:n {#2} }
+ {
+ \str_if_empty:NF \l_@@_property_str
+ {
+ \msg_error:nnee { keys } { property-unknown }
+ \l_@@_property_str \l_keys_path_str
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_property_find:n}
+% \begin{macro}[EXP]{\@@_property_find_auxi:w}
+% \begin{macro}{\@@_property_find_auxii:w}
+% \begin{macro}[EXP]
+% {
+% \@@_property_find_auxiii:w ,
+% \@@_property_find_auxiv:w
+% }
+% \begin{macro}{\@@_property_find_err:w}
+% Searching for a property means finding the last |.| in the input,
+% and storing the text before and after it. Everything is first turned into
+% strings, so there is no problem using \cs{cs_set_nopar:Npe} instead of
+% \cs{str_set:Ne} to set \cs{l_keys_path_str}. To gain further speed, brace
+% tricks are used and \cs{@@_property_find_auxiv:w} is defined as expandable.
+% Since spaces will already be trimmed from the module we can omit it from the
+% argument to \cs{@@_trim_spaces:n}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_property_find:n #1
+ {
+ \exp_after:wN \@@_property_find_auxi:w \tl_to_str:n {#1}
+ \s_@@_nil \@@_property_find_auxii:w
+ . \s_@@_nil \@@_property_find_err:w
+ }
+\cs_new:Npn \@@_property_find_auxi:w #1 . #2 \s_@@_nil #3
+ {
+ #3 #1 \s_@@_mark #2 \s_@@_nil #3
+ }
+\cs_new_protected:Npn \@@_property_find_auxii:w
+ #1 \s_@@_mark #2 \s_@@_nil \@@_property_find_auxii:w . \s_@@_nil
+ \@@_property_find_err:w
+ {
+ \cs_set_nopar:Npe \l_keys_path_str
+ {
+ \str_if_empty:NF \l_@@_module_str { \l_@@_module_str / }
+ \exp_after:wN \@@_trim_spaces:n \tex_expanded:D {{
+ #1
+ \if_false: }}} \fi:
+ \@@_property_find_auxi:w #2 \s_@@_nil \@@_property_find_auxiii:w
+ . \s_@@_nil \@@_property_find_auxiv:w
+ }
+\cs_new:Npn \@@_property_find_auxiii:w #1 \s_@@_mark #2 . #3 \s_@@_nil #4
+ {
+ . #1 #4 #2 \s_@@_mark #3 \s_@@_nil #4
+ }
+\cs_new:Npn \@@_property_find_auxiv:w
+ #1 \s_@@_nil \@@_property_find_auxiii:w
+ \s_@@_mark \s_@@_nil \@@_property_find_auxiv:w
+ {
+ \if_false: {{{ \fi: }}}
+ \cs_set_nopar:Npe \l_@@_property_str { . #1 }
+ \tl_set_eq:NN \l_keys_path_tl \l_keys_path_str
+ }
+\cs_new_protected:Npn \@@_property_find_err:w
+ #1 \s_@@_nil #2 \@@_property_find_err:w
+ {
+ \str_clear:N \l_@@_property_str
+ \msg_error:nnn { keys } { no-property } {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_define_code:n}
+% \begin{macro}[EXP]{\@@_define_code:w}
+% Two possible cases. If there is a value for the key, then just use
+% the function. If not, then a check to make sure there is no need for
+% a value with the property. If there should be one then complain,
+% otherwise execute it. There is no need to check for a |:| as if it
+% was missing the earlier tests would have failed.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_define_code:n #1
+ {
+ \bool_if:NTF \l_@@_no_value_bool
+ {
+ \exp_after:wN \@@_define_code:w
+ \l_@@_property_str \s_@@_stop
+ { \use:c { \c_@@_props_root_str \l_@@_property_str } }
+ {
+ \msg_error:nnee { keys } { property-requires-value }
+ \l_@@_property_str \l_keys_path_str
+ }
+ }
+ { \use:c { \c_@@_props_root_str \l_@@_property_str } {#1} }
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new:Npn \@@_define_code:w #1 \c_colon_str #2 \s_@@_stop
+ { \tl_if_empty:nTF {#2} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Turning properties into actions}
+%
+% \begin{macro}
+% {
+% \@@_bool_set:Nn, \@@_bool_set:cn,
+% \@@_bool_set_inverse:Nn, \@@_bool_set_inverse:cn
+% }
+% \begin{macro}{\@@_bool_set:Nnnn}
+% Boolean keys are really just choices, but all done by hand. The
+% second argument here is the scope: either empty or \texttt{ g } for
+% global.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_bool_set:Nn #1#2
+ { \@@_bool_set:Nnnn #1 {#2} { true } { false } }
+\cs_generate_variant:Nn \@@_bool_set:Nn { c }
+\cs_new_protected:Npn \@@_bool_set_inverse:Nn #1#2
+ { \@@_bool_set:Nnnn #1 {#2} { false } { true } }
+\cs_generate_variant:Nn \@@_bool_set_inverse:Nn { c }
+\cs_new_protected:Npn \@@_bool_set:Nnnn #1#2#3#4
+ {
+ \bool_if_exist:NF #1 { \bool_new:N #1 }
+ \@@_choice_make:
+ \@@_cmd_set:ne { \l_keys_path_str / true }
+ { \exp_not:c { bool_ #2 set_ #3 :N } \exp_not:N #1 }
+ \@@_cmd_set:ne { \l_keys_path_str / false }
+ { \exp_not:c { bool_ #2 set_ #4 :N } \exp_not:N #1 }
+ \@@_cmd_set_direct:nn { \l_keys_path_str / unknown }
+ {
+ \msg_error:nne { keys } { boolean-values-only }
+ \l_keys_path_str
+ }
+ \@@_default_set:n { true }
+ }
+\cs_generate_variant:Nn \@@_bool_set:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_choice_make:, \@@_multichoice_make:}
+% \begin{macro}{\@@_choice_make:N}
+% \begin{macro}{\@@_choice_make_aux:N}
+% To make a choice from a key, two steps: set the code, and set the
+% unknown key. As multichoices and choices are essentially the same bar one
+% function, the code is given together.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_choice_make:
+ { \@@_choice_make:N \@@_choice_find:n }
+\cs_new_protected:Npn \@@_multichoice_make:
+ { \@@_choice_make:N \@@_multichoice_find:n }
+\cs_new_protected:Npn \@@_choice_make:N #1
+ {
+ \cs_if_exist:cTF
+ { \c_@@_type_root_str \@@_parent:o \l_keys_path_str }
+ {
+ \str_if_eq:vnTF
+ { \c_@@_type_root_str \@@_parent:o \l_keys_path_str }
+ { choice }
+ {
+ \msg_error:nnee { keys } { nested-choice-key }
+ \l_keys_path_tl { \@@_parent:o \l_keys_path_str }
+ }
+ { \@@_choice_make_aux:N #1 }
+ }
+ { \@@_choice_make_aux:N #1 }
+ }
+\cs_new_protected:Npn \@@_choice_make_aux:N #1
+ {
+ \cs_set_nopar:cpn { \c_@@_type_root_str \l_keys_path_str }
+ { choice }
+ \@@_cmd_set_direct:nn \l_keys_path_str { #1 {##1} }
+ \@@_cmd_set_direct:nn { \l_keys_path_str / unknown }
+ {
+ \msg_error:nnee { keys } { choice-unknown }
+ \l_keys_path_str {##1}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_choices_make:nn, \@@_multichoices_make:nn}
+% \begin{macro}{\@@_choices_make:Nnn}
+% Auto-generating choices means setting up the root key as a choice, then
+% defining each choice in turn.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_choices_make:nn
+ { \@@_choices_make:Nnn \@@_choice_make: }
+\cs_new_protected:Npn \@@_multichoices_make:nn
+ { \@@_choices_make:Nnn \@@_multichoice_make: }
+\cs_new_protected:Npn \@@_choices_make:Nnn #1#2#3
+ {
+ #1
+ \int_zero:N \l_keys_choice_int
+ \clist_map_inline:nn {#2}
+ {
+ \int_incr:N \l_keys_choice_int
+ \@@_cmd_set:ne
+ { \l_keys_path_str / \@@_trim_spaces:n {##1} }
+ {
+ \tl_set:Nn \exp_not:N \l_keys_choice_tl {##1}
+ \int_set:Nn \exp_not:N \l_keys_choice_int
+ { \int_use:N \l_keys_choice_int }
+ \exp_not:n {#3}
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_cmd_set:nn, \@@_cmd_set:Vn, \@@_cmd_set:ne, \@@_cmd_set:Vo,
+% \@@_cmd_set_direct:nn
+% }
+% Setting the code for a key first logs if appropriate that we are
+% defining a new key, then saves the code.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_cmd_set:nn #1#2
+ { \@@_cmd_set_direct:nn {#1} { \@@_precompile:n {#2} } }
+\cs_generate_variant:Nn \@@_cmd_set:nn { ne , Vn , Vo }
+\cs_new_protected:Npn \@@_cmd_set_direct:nn #1#2
+ { \cs_set_protected:cpn { \c_@@_code_root_str #1 } ##1 {#2} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_cs_set:NNpn, \@@_cs_set:Ncpn}
+% Creating control sequences is a bit more tricky than other cases as
+% we need to pick up the |p| argument. To make the internals look clearer,
+% the trailing |n| argument here is just for appearance.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_cs_set:NNpn #1#2#3#
+ {
+ \cs_set_protected:cpe { \c_@@_code_root_str \l_keys_path_str } ##1
+ {
+ \@@_precompile:n
+ { #1 \exp_not:N #2 \exp_not:n {#3} {##1} }
+ }
+ \use_none:n
+ }
+\cs_generate_variant:Nn \@@_cs_set:NNpn { Nc }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_default_set:n}
+% Setting a default value is easy. These are stored using \cs{cs_set_nopar:cpe} as this
+% avoids any worries about whether a token list exists.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_default_set:n #1
+ {
+ \tl_if_empty:nTF {#1}
+ {
+ \@@_cs_undefine:c
+ { \c_@@_default_root_str \l_keys_path_str }
+ }
+ {
+ \cs_set_nopar:cpe
+ { \c_@@_default_root_str \l_keys_path_str }
+ { \exp_not:n {#1} }
+ \@@_value_requirement:nn { required } { false }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_groups_set:n}
+% Assigning a key to one or more groups uses comma lists. As the list of
+% groups only exists if there is anything to do, the setting is done using
+% a scratch list. For the usual grouping reasons we use the low-level
+% approach to undefining a list. We also use the low-level approach for
+% the other case to avoid tripping up the |check-declarations| code.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_groups_set:n #1
+ {
+ \clist_set:Ne \l_@@_groups_clist { \tl_to_str:n {#1} }
+ \clist_if_empty:NTF \l_@@_groups_clist
+ {
+ \@@_cs_undefine:c
+ { \c_@@_groups_root_str \l_keys_path_str }
+ }
+ {
+ \cs_set_eq:cN { \c_@@_groups_root_str \l_keys_path_str }
+ \l_@@_groups_clist
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_inherit:n}
+% Inheritance means ignoring anything already said about the key:
+% zap the lot and set up.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_inherit:n #1
+ {
+ \@@_undefine:
+ \clist_set:Nn \l_@@_inherit_clist {#1}
+ \cs_set_eq:cN { \c_@@_inherit_root_str \l_keys_path_str }
+ \l_@@_inherit_clist
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_initialise:n}
+% A set up for initialisation: just run the code if it exists.
+% We need to set the key string here, using the deprecated \texttt{tl}
+% as a piece of scratch space.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_initialise:n #1
+ {
+ \cs_if_exist:cTF
+ { \c_@@_inherit_root_str \@@_parent:o \l_keys_path_str }
+ { \@@_execute_inherit: }
+ {
+ \str_clear:N \l_@@_inherit_str
+ \cs_if_exist:cT { \c_@@_code_root_str \l_keys_path_str }
+ {
+ \exp_after:wN \@@_find_key_module:wNN
+ \l_keys_path_str \s_@@_stop
+ \l_keys_key_tl \l_keys_key_str
+ \tl_set_eq:NN \l_keys_key_tl \l_keys_key_str
+ \tl_set:Nn \l_keys_value_tl {#1}
+ \@@_execute:no \l_keys_path_str \l_keys_value_tl
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_legacy_if_set:nn, \@@_legacy_if_inverse:nn}
+% \begin{macro}{\@@_legacy_if_inverse:nnnn}
+% Much the same as \pkg{expl3} booleans, except we assume that the switch
+% exists.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_legacy_if_set:nn #1#2
+ { \@@_legacy_if_set:nnnn {#1} {#2} { true } { false } }
+\cs_new_protected:Npn \@@_legacy_if_set_inverse:nn #1#2
+ { \@@_legacy_if_set:nnnn {#1} {#2} { false } { true } }
+\cs_new_protected:Npn \@@_legacy_if_set:nnnn #1#2#3#4
+ {
+ \@@_choice_make:
+ \@@_cmd_set:ne { \l_keys_path_str / true }
+ { \exp_not:c { legacy_if_#2 set_ #3 :n } { \exp_not:n {#1} } }
+ \@@_cmd_set:ne { \l_keys_path_str / false }
+ { \exp_not:c { legacy_if_#2 set_ #4 :n } { \exp_not:n {#1} } }
+ \@@_cmd_set:nn { \l_keys_path_str / unknown }
+ {
+ \msg_error:nne { keys } { boolean-values-only }
+ \l_keys_path_str
+ }
+ \@@_default_set:n { true }
+ \cs_if_exist:cF { if#1 }
+ {
+ \cs:w newif \exp_after:wN \cs_end:
+ \cs:w if#1 \cs_end:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_meta_make:n}
+% \begin{macro}{\@@_meta_make:nn}
+% To create a meta-key, simply set up to pass data through. The internal
+% function is used here as a meta key should respect the prevailing
+% filtering, etc.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_meta_make:n #1
+ {
+ \exp_args:NVo \@@_cmd_set_direct:nn \l_keys_path_str
+ {
+ \exp_after:wN \@@_set:nn \exp_after:wN
+ { \l_@@_module_str } {#1}
+ }
+ }
+\cs_new_protected:Npn \@@_meta_make:nn #1#2
+ {
+ \exp_args:NV \@@_cmd_set_direct:nn
+ \l_keys_path_str { \@@_set:nn {#1} {#2} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_prop_put:Nn, \@@_prop_put:cn}
+% Much the same as other variables, but needs a dedicated auxiliary.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_prop_put:Nn #1#2
+ {
+ \prop_if_exist:NF #1 { \prop_new:N #1 }
+ \exp_after:wN \@@_find_key_module:wNN \l_keys_path_str \s_@@_stop
+ \l_@@_tmpa_tl \l_@@_tmpb_tl
+ \@@_cmd_set:ne \l_keys_path_str
+ {
+ \exp_not:c { prop_ #2 put:Nnn }
+ \exp_not:N #1
+ { \l_@@_tmpb_tl }
+ \exp_not:n { {##1} }
+ }
+ }
+\cs_generate_variant:Nn \@@_prop_put:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_undefine:}
+% Undefining a key has to be done without \cs{cs_undefine:c} as that
+% function acts globally.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_undefine:
+ {
+ \clist_map_inline:nn
+ { code , default , groups , inherit , type , check }
+ {
+ \@@_cs_undefine:c
+ { \tl_use:c { c_@@_ ##1 _root_str } \l_keys_path_str }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_value_requirement:nn}
+% \begin{macro}{\@@_check_forbidden:, \@@_check_required:}
+% Validating key input is done using a second function which runs before
+% the main key code. Setting that up means setting it equal to a generic
+% stub which does the check. This approach makes the lookup very fast at
+% the cost of one additional csname per key that needs it. The cleanup here
+% has to know the structure of the following code.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_value_requirement:nn #1#2
+ {
+ \str_case:nnF {#2}
+ {
+ { true }
+ {
+ \cs_set_eq:cc
+ { \c_@@_check_root_str \l_keys_path_str }
+ { @@_check_ #1 : }
+ }
+ { false }
+ {
+ \cs_if_eq:ccT
+ { \c_@@_check_root_str \l_keys_path_str }
+ { @@_check_ #1 : }
+ {
+ \@@_cs_undefine:c
+ { \c_@@_check_root_str \l_keys_path_str }
+ }
+ }
+ }
+ {
+ \msg_error:nne { keys }
+ { boolean-values-only }
+ { .value_ #1 :n }
+ }
+ }
+\cs_new_protected:Npn \@@_check_forbidden:
+ {
+ \bool_if:NF \l_@@_no_value_bool
+ {
+ \msg_error:nnee { keys } { value-forbidden }
+ \l_keys_path_str \l_keys_value_tl
+ \use_none:nnn
+ }
+ }
+\cs_new_protected:Npn \@@_check_required:
+ {
+ \bool_if:NT \l_@@_no_value_bool
+ {
+ \msg_error:nne { keys } { value-required }
+ \l_keys_path_str
+ \use_none:nnn
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_usage:n}
+% \begin{macro}{\@@_usage:NN}
+% \begin{macro}{\@@_usage:w}
+% Save the relevant data.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_usage:n #1
+ {
+ \str_case:nnF {#1}
+ {
+ { general }
+ {
+ \@@_usage:NN \l_keys_usage_load_prop
+ \c_false_bool
+ \@@_usage:NN \l_keys_usage_preamble_prop
+ \c_false_bool
+ }
+ { load }
+ {
+ \@@_usage:NN \l_keys_usage_load_prop
+ \c_true_bool
+ \@@_usage:NN \l_keys_usage_preamble_prop
+ \c_false_bool
+ }
+ { preamble }
+ {
+ \@@_usage:NN \l_keys_usage_load_prop
+ \c_false_bool
+ \@@_usage:NN \l_keys_usage_preamble_prop
+ \c_true_bool
+ }
+ }
+ {
+ \msg_error:nnnn { keys }
+ { choice-unknown }
+ { .usage:n }
+ {#1}
+ }
+ }
+\cs_new_protected:Npn \@@_usage:NN #1#2
+ {
+ \prop_get:NVNF #1 \l_@@_module_str \l_@@_tmpa_tl
+ { \tl_clear:N \l_@@_tmpa_tl }
+ \tl_set:Ne \l_@@_tmpb_tl
+ { \exp_after:wN \@@_usage:w \l_keys_path_str \s_@@_stop }
+ \bool_if:NTF #2
+ { \clist_put_right:NV \l_@@_tmpa_tl \l_@@_tmpb_tl }
+ { \clist_remove_all:NV \l_@@_tmpa_tl \l_@@_tmpb_tl }
+ \prop_put:NVV #1 \l_@@_module_str
+ \l_@@_tmpa_tl
+ }
+\cs_new:Npn \@@_usage:w #1 / #2 \s_@@_stop {#2}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_variable_set:NnnN, \@@_variable_set:cnnN}
+% \begin{macro}{\@@_variable_set_required:NnnN, \@@_variable_set_required:cnnN}
+% Setting a variable takes the type and scope separately so that
+% it is easy to make a new variable if needed.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_variable_set:NnnN #1#2#3#4
+ {
+ \use:c { #2_if_exist:NF } #1 { \use:c { #2 _new:N } #1 }
+ \@@_cmd_set:ne \l_keys_path_str
+ {
+ \exp_not:c { #2 _ #3 set:N #4 }
+ \exp_not:N #1
+ \exp_not:n { {##1} }
+ }
+ }
+\cs_generate_variant:Nn \@@_variable_set:NnnN { c }
+\cs_new_protected:Npn \@@_variable_set_required:NnnN #1#2#3#4
+ {
+ \@@_variable_set:NnnN #1 {#2} {#3} #4
+ \@@_value_requirement:nn { required } { true }
+ }
+\cs_generate_variant:Nn \@@_variable_set_required:NnnN { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Creating key properties}
+%
+% The key property functions are all wrappers for internal functions,
+% meaning that things stay readable and can also be altered later on.
+%
+% Importantly, while key properties have \enquote{normal} argument specs, the
+% underlying code always supplies one braced argument to these. As such, argument
+% expansion is handled by hand rather than using the standard tools. This shows
+% up particularly for the two-argument properties, where things would otherwise
+% go badly wrong.
+%
+% \begin{macro}{.bool_set:N, .bool_set:c}
+% \begin{macro}{.bool_gset:N, .bool_gset:c}
+% One function for this.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .bool_set:N } #1
+ { \@@_bool_set:Nn #1 { } }
+\cs_new_protected:cpn { \c_@@_props_root_str .bool_set:c } #1
+ { \@@_bool_set:cn {#1} { } }
+\cs_new_protected:cpn { \c_@@_props_root_str .bool_gset:N } #1
+ { \@@_bool_set:Nn #1 { g } }
+\cs_new_protected:cpn { \c_@@_props_root_str .bool_gset:c } #1
+ { \@@_bool_set:cn {#1} { g } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{.bool_set_inverse:N, .bool_set_inverse:c}
+% \begin{macro}{.bool_gset_inverse:N, .bool_gset_inverse:c}
+% One function for this.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .bool_set_inverse:N } #1
+ { \@@_bool_set_inverse:Nn #1 { } }
+\cs_new_protected:cpn { \c_@@_props_root_str .bool_set_inverse:c } #1
+ { \@@_bool_set_inverse:cn {#1} { } }
+\cs_new_protected:cpn { \c_@@_props_root_str .bool_gset_inverse:N } #1
+ { \@@_bool_set_inverse:Nn #1 { g } }
+\cs_new_protected:cpn { \c_@@_props_root_str .bool_gset_inverse:c } #1
+ { \@@_bool_set_inverse:cn {#1} { g } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{.choice:}
+% Making a choice is handled internally, as it is also needed by
+% \texttt{.generate_choices:n}.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .choice: }
+ { \@@_choice_make: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{.choices:nn, .choices:Vn, .choices:en, .choices:on, .choices:xn}
+% For auto-generation of a series of mutually-exclusive choices.
+% Here, |#1| consists of two separate
+% arguments, hence the slightly odd-looking implementation.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .choices:nn } #1
+ { \@@_choices_make:nn #1 }
+\cs_new_protected:cpn { \c_@@_props_root_str .choices:Vn } #1
+ { \exp_args:NV \@@_choices_make:nn #1 }
+\cs_new_protected:cpn { \c_@@_props_root_str .choices:en } #1
+ { \exp_args:Ne \@@_choices_make:nn #1 }
+\cs_new_protected:cpn { \c_@@_props_root_str .choices:on } #1
+ { \exp_args:No \@@_choices_make:nn #1 }
+\cs_new_protected:cpn { \c_@@_props_root_str .choices:xn } #1
+ { \exp_args:Nx \@@_choices_make:nn #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{.code:n}
+% Creating code is simply a case of passing through to the underlying
+% \texttt{set} function.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .code:n } #1
+ { \@@_cmd_set:nn \l_keys_path_str {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{.clist_set:N, .clist_set:c}
+% \begin{macro}{.clist_gset:N, .clist_gset:c}
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .clist_set:N } #1
+ { \@@_variable_set:NnnN #1 { clist } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .clist_set:c } #1
+ { \@@_variable_set:cnnN {#1} { clist } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .clist_gset:N } #1
+ { \@@_variable_set:NnnN #1 { clist } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .clist_gset:c } #1
+ { \@@_variable_set:cnnN {#1} { clist } { g } n }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {.cs_set:Np, .cs_set:cp, .cs_set_protected:Np, .cs_set_protected:cp}
+% \begin{macro}
+% {.cs_gset:Np, .cs_gset:cp, .cs_gset_protected:Np, .cs_gset_protected:cp}
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .cs_set:Np } #1
+ { \@@_cs_set:NNpn \cs_set:Npn #1 { } }
+\cs_new_protected:cpn { \c_@@_props_root_str .cs_set:cp } #1
+ { \@@_cs_set:Ncpn \cs_set:Npn #1 { } }
+\cs_new_protected:cpn { \c_@@_props_root_str .cs_set_protected:Np } #1
+ { \@@_cs_set:NNpn \cs_set_protected:Npn #1 { } }
+\cs_new_protected:cpn { \c_@@_props_root_str .cs_set_protected:cp } #1
+ { \@@_cs_set:Ncpn \cs_set_protected:Npn #1 { } }
+\cs_new_protected:cpn { \c_@@_props_root_str .cs_gset:Np } #1
+ { \@@_cs_set:NNpn \cs_gset:Npn #1 { } }
+\cs_new_protected:cpn { \c_@@_props_root_str .cs_gset:cp } #1
+ { \@@_cs_set:Ncpn \cs_gset:Npn #1 { } }
+\cs_new_protected:cpn { \c_@@_props_root_str .cs_gset_protected:Np } #1
+ { \@@_cs_set:NNpn \cs_gset_protected:Npn #1 { } }
+\cs_new_protected:cpn { \c_@@_props_root_str .cs_gset_protected:cp } #1
+ { \@@_cs_set:Ncpn \cs_gset_protected:Npn #1 { } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{.default:n, .default:V, .default:e, .default:o, .default:x}
+% Expansion is left to the internal functions.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .default:n } #1
+ { \@@_default_set:n {#1} }
+\cs_new_protected:cpn { \c_@@_props_root_str .default:V } #1
+ { \exp_args:NV \@@_default_set:n #1 }
+\cs_new_protected:cpn { \c_@@_props_root_str .default:e } #1
+ { \exp_args:Ne \@@_default_set:n {#1} }
+\cs_new_protected:cpn { \c_@@_props_root_str .default:o } #1
+ { \exp_args:No \@@_default_set:n {#1} }
+\cs_new_protected:cpn { \c_@@_props_root_str .default:x } #1
+ { \exp_args:Nx \@@_default_set:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{.dim_set:N, .dim_set:c}
+% \begin{macro}{.dim_gset:N, .dim_gset:c}
+% Setting a variable is very easy: just pass the data along.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .dim_set:N } #1
+ { \@@_variable_set_required:NnnN #1 { dim } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .dim_set:c } #1
+ { \@@_variable_set_required:cnnN {#1} { dim } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .dim_gset:N } #1
+ { \@@_variable_set_required:NnnN #1 { dim } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .dim_gset:c } #1
+ { \@@_variable_set_required:cnnN {#1} { dim } { g } n }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{.fp_set:N, .fp_set:c}
+% \begin{macro}{.fp_gset:N, .fp_gset:c}
+% Setting a variable is very easy: just pass the data along.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .fp_set:N } #1
+ { \@@_variable_set_required:NnnN #1 { fp } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .fp_set:c } #1
+ { \@@_variable_set_required:cnnN {#1} { fp } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .fp_gset:N } #1
+ { \@@_variable_set_required:NnnN #1 { fp } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .fp_gset:c } #1
+ { \@@_variable_set_required:cnnN {#1} { fp } { g } n }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{.groups:n}
+% A single property to create groups of keys.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .groups:n } #1
+ { \@@_groups_set:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{.inherit:n}
+% Nothing complex: only one variant at the moment!
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .inherit:n } #1
+ { \@@_inherit:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{.initial:n, .initial:V, .initial:e, .initial:o, .initial:x}
+% The standard hand-off approach.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .initial:n } #1
+ { \@@_initialise:n {#1} }
+\cs_new_protected:cpn { \c_@@_props_root_str .initial:V } #1
+ { \exp_args:NV \@@_initialise:n #1 }
+\cs_new_protected:cpn { \c_@@_props_root_str .initial:e } #1
+ { \exp_args:Ne \@@_initialise:n {#1} }
+\cs_new_protected:cpn { \c_@@_props_root_str .initial:o } #1
+ { \exp_args:No \@@_initialise:n {#1} }
+\cs_new_protected:cpn { \c_@@_props_root_str .initial:x } #1
+ { \exp_args:Nx \@@_initialise:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{.int_set:N, .int_set:c}
+% \begin{macro}{.int_gset:N, .int_gset:c}
+% Setting a variable is very easy: just pass the data along.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .int_set:N } #1
+ { \@@_variable_set_required:NnnN #1 { int } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .int_set:c } #1
+ { \@@_variable_set_required:cnnN {#1} { int } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .int_gset:N } #1
+ { \@@_variable_set_required:NnnN #1 { int } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .int_gset:c } #1
+ { \@@_variable_set_required:cnnN {#1} { int } { g } n }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% .legacy_if_set:n, .legacy_if_gset:n,
+% .legacy_if_set_inverse:n, .legacy_if_gset_inverse:n
+% }
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .legacy_if_set:n } #1
+ { \@@_legacy_if_set:nn {#1} { } }
+\cs_new_protected:cpn { \c_@@_props_root_str .legacy_if_gset:n } #1
+ { \@@_legacy_if_set:nn {#1} { g } }
+\cs_new_protected:cpn { \c_@@_props_root_str .legacy_if_set_inverse:n } #1
+ { \@@_legacy_if_set_inverse:nn {#1} { } }
+\cs_new_protected:cpn { \c_@@_props_root_str .legacy_if_gset_inverse:n } #1
+ { \@@_legacy_if_set_inverse:nn {#1} { g } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{.meta:n}
+% Making a meta is handled internally.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .meta:n } #1
+ { \@@_meta_make:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{.meta:nn}
+% Meta with path: potentially lots of variants, but for the moment
+% no so many defined.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .meta:nn } #1
+ { \@@_meta_make:nn #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{.multichoice:}
+% \begin{macro}{.multichoices:nn, .multichoices:Vn, .multichoices:en, .multichoices:on, .multichoices:xn}
+% The same idea as \texttt{.choice:} and \texttt{.choices:nn}, but
+% where more than one choice is allowed.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .multichoice: }
+ { \@@_multichoice_make: }
+\cs_new_protected:cpn { \c_@@_props_root_str .multichoices:nn } #1
+ { \@@_multichoices_make:nn #1 }
+\cs_new_protected:cpn { \c_@@_props_root_str .multichoices:Vn } #1
+ { \exp_args:NV \@@_multichoices_make:nn #1 }
+\cs_new_protected:cpn { \c_@@_props_root_str .multichoices:en } #1
+ { \exp_args:Ne \@@_multichoices_make:nn #1 }
+\cs_new_protected:cpn { \c_@@_props_root_str .multichoices:on } #1
+ { \exp_args:No \@@_multichoices_make:nn #1 }
+\cs_new_protected:cpn { \c_@@_props_root_str .multichoices:xn } #1
+ { \exp_args:Nx \@@_multichoices_make:nn #1 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{.muskip_set:N, .muskip_set:c, .muskip_gset:N, .muskip_gset:c}
+% Setting a variable is very easy: just pass the data along.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .muskip_set:N } #1
+ { \@@_variable_set_required:NnnN #1 { muskip } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .muskip_set:c } #1
+ { \@@_variable_set_required:cnnN {#1} { muskip } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .muskip_gset:N } #1
+ { \@@_variable_set_required:NnnN #1 { muskip } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .muskip_gset:c } #1
+ { \@@_variable_set_required:cnnN {#1} { muskip } { g } n }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{.prop_put:N, .prop_put:c, .prop_gput:N, .prop_gput:c}
+% Setting a variable is very easy: just pass the data along.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .prop_put:N } #1
+ { \@@_prop_put:Nn #1 { } }
+\cs_new_protected:cpn { \c_@@_props_root_str .prop_put:c } #1
+ { \@@_prop_put:cn {#1} { } }
+\cs_new_protected:cpn { \c_@@_props_root_str .prop_gput:N } #1
+ { \@@_prop_put:Nn #1 { g } }
+\cs_new_protected:cpn { \c_@@_props_root_str .prop_gput:c } #1
+ { \@@_prop_put:cn {#1} { g } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{.skip_set:N, .skip_set:c}
+% \begin{macro}{.skip_gset:N, .skip_gset:c}
+% Setting a variable is very easy: just pass the data along.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .skip_set:N } #1
+ { \@@_variable_set_required:NnnN #1 { skip } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .skip_set:c } #1
+ { \@@_variable_set_required:cnnN {#1} { skip } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .skip_gset:N } #1
+ { \@@_variable_set_required:NnnN #1 { skip } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .skip_gset:c } #1
+ { \@@_variable_set_required:cnnN {#1} { skip } { g } n }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{.str_set:N, .str_set:c}
+% \begin{macro}{.str_gset:N, .str_gset:c}
+% \begin{macro}{.str_set_e:N, .str_set_e:c}
+% \begin{macro}{.str_gset_e:N, .str_gset_e:c}
+% Setting a variable is very easy: just pass the data along.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .str_set:N } #1
+ { \@@_variable_set:NnnN #1 { str } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .str_set:c } #1
+ { \@@_variable_set:cnnN {#1} { str } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .str_set_e:N } #1
+ { \@@_variable_set:NnnN #1 { str } { } e }
+\cs_new_protected:cpn { \c_@@_props_root_str .str_set_e:c } #1
+ { \@@_variable_set:cnnN {#1} { str } { } e }
+\cs_new_protected:cpn { \c_@@_props_root_str .str_gset:N } #1
+ { \@@_variable_set:NnnN #1 { str } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .str_gset:c } #1
+ { \@@_variable_set:cnnN {#1} { str } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .str_gset_e:N } #1
+ { \@@_variable_set:NnnN #1 { str } { g } e }
+\cs_new_protected:cpn { \c_@@_props_root_str .str_gset_e:c } #1
+ { \@@_variable_set:cnnN {#1} { str } { g } e }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{.tl_set:N, .tl_set:c}
+% \begin{macro}{.tl_gset:N, .tl_gset:c}
+% \begin{macro}{.tl_set_e:N, .tl_set_e:c}
+% \begin{macro}{.tl_gset_e:N, .tl_gset_e:c}
+% Setting a variable is very easy: just pass the data along.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .tl_set:N } #1
+ { \@@_variable_set:NnnN #1 { tl } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .tl_set:c } #1
+ { \@@_variable_set:cnnN {#1} { tl } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .tl_set_e:N } #1
+ { \@@_variable_set:NnnN #1 { tl } { } e }
+\cs_new_protected:cpn { \c_@@_props_root_str .tl_set_e:c } #1
+ { \@@_variable_set:cnnN {#1} { tl } { } e }
+\cs_new_protected:cpn { \c_@@_props_root_str .tl_gset:N } #1
+ { \@@_variable_set:NnnN #1 { tl } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .tl_gset:c } #1
+ { \@@_variable_set:cnnN {#1} { tl } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_str .tl_gset_e:N } #1
+ { \@@_variable_set:NnnN #1 { tl } { g } e }
+\cs_new_protected:cpn { \c_@@_props_root_str .tl_gset_e:c } #1
+ { \@@_variable_set:cnnN {#1} { tl } { g } e }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{.undefine:}
+% Another simple wrapper.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .undefine: }
+ { \@@_undefine: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{.usage:n}
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .usage:n } #1
+ { \@@_usage:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{.value_forbidden:n}
+% \begin{macro}{.value_required:n}
+% These are very similar, so both call the same function.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .value_forbidden:n } #1
+ { \@@_value_requirement:nn { forbidden } {#1} }
+\cs_new_protected:cpn { \c_@@_props_root_str .value_required:n } #1
+ { \@@_value_requirement:nn { required } {#1} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Setting keys}
+%
+% \begin{macro}
+% {
+% \keys_set:nn, \keys_set:nV, \keys_set:nv, \keys_set:ne,
+% \keys_set:no, \keys_set:nx,
+% \@@_set:nn
+% }
+% \begin{macro}{\@@_set:nnn}
+% A simple wrapper allowing for nesting.
+% \begin{macrocode}
+\cs_new_protected:Npn \keys_set:nn #1#2
+ {
+ \use:e
+ {
+ \bool_set_false:N \exp_not:N \l_@@_only_known_bool
+ \bool_set_false:N \exp_not:N \l_@@_filtered_bool
+ \bool_set_false:N \exp_not:N \l_@@_selective_bool
+ \tl_set:Nn \exp_not:N \l_@@_relative_tl
+ { \exp_not:N \q_@@_no_value }
+ \@@_set:nn \exp_not:n { {#1} {#2} }
+ \bool_if:NT \l_@@_only_known_bool
+ { \bool_set_true:N \exp_not:N \l_@@_only_known_bool }
+ \bool_if:NT \l_@@_filtered_bool
+ { \bool_set_true:N \exp_not:N \l_@@_filtered_bool }
+ \bool_if:NT \l_@@_selective_bool
+ { \bool_set_true:N \exp_not:N \l_@@_selective_bool }
+ \tl_set:Nn \exp_not:N \l_@@_relative_tl
+ { \exp_not:o \l_@@_relative_tl }
+ }
+ }
+\cs_generate_variant:Nn \keys_set:nn { nV , nv , ne , no , nx }
+\cs_new_protected:Npn \@@_set:nn #1#2
+ { \exp_args:No \@@_set:nnn \l_@@_module_str {#1} {#2} }
+\cs_new_protected:Npn \@@_set:nnn #1#2#3
+ {
+ \str_set:Ne \l_@@_module_str { \@@_trim_spaces:n {#2} }
+ \keyval_parse:NNn \@@_set_keyval:n \@@_set_keyval:nn {#3}
+ \str_set:Nn \l_@@_module_str {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \keys_set_known:nnN, \keys_set_known:nVN,
+% \keys_set_known:nvN, \keys_set_known:neN,
+% \keys_set_known:noN
+% }
+% \begin{macro}
+% {
+% \keys_set_known:nnnN, \keys_set_known:nVnN,
+% \keys_set_known:nvnN, \keys_set_known:nenN,
+% \keys_set_known:nonN
+% }
+% \begin{macro}{\@@_set_known:nnnnN}
+% \begin{macro}
+% {
+% \keys_set_known:nn, \keys_set_known:nV,
+% \keys_set_known:nv, \keys_set_known:ne,
+% \keys_set_known:no
+% }
+% \begin{macro}{\@@_set_known:nnn}
+% Setting known keys simply means setting the appropriate boolean, then
+% running the standard code. To allow for nested setting, any existing
+% value of \cs{l_@@_unused_clist} is saved on the stack and reset
+% afterwards. Note that for speed/simplicity reasons we use a \texttt{tl}
+% operation to set the \texttt{clist} here!
+% \begin{macrocode}
+\cs_new_protected:Npn \keys_set_known:nnN #1#2#3
+ {
+ \exp_args:No \@@_set_known:nnnnN
+ \l_@@_unused_clist \q_@@_no_value {#1} {#2} #3
+ }
+\cs_generate_variant:Nn \keys_set_known:nnN { nV , nv , ne , no }
+\cs_new_protected:Npn \keys_set_known:nnnN #1#2#3#4
+ {
+ \exp_args:No \@@_set_known:nnnnN
+ \l_@@_unused_clist {#3} {#1} {#2} #4
+ }
+\cs_generate_variant:Nn \keys_set_known:nnnN { nV , nv , ne , no }
+\cs_new_protected:Npn \@@_set_known:nnnnN #1#2#3#4#5
+ {
+ \clist_clear:N \l_@@_unused_clist
+ \@@_set_known:nnn {#2} {#3} {#4}
+ \__kernel_tl_set:Nx #5 { \exp_not:o \l_@@_unused_clist }
+ \__kernel_tl_set:Nx \l_@@_unused_clist { \exp_not:n {#1} }
+ }
+\cs_new_protected:Npn \keys_set_known:nn #1#2
+ { \@@_set_known:nnn \q_@@_no_value {#1} {#2} }
+\cs_generate_variant:Nn \keys_set_known:nn { nV , nv , ne , no }
+\cs_new_protected:Npn \@@_set_known:nnn #1#2#3
+ {
+ \use:e
+ {
+ \bool_set_true:N \exp_not:N \l_@@_only_known_bool
+ \bool_set_false:N \exp_not:N \l_@@_filtered_bool
+ \bool_set_false:N \exp_not:N \l_@@_selective_bool
+ \tl_set:Nn \exp_not:N \l_@@_relative_tl { \exp_not:n {#1} }
+ \@@_set:nn \exp_not:n { {#2} {#3} }
+ \bool_if:NF \l_@@_only_known_bool
+ { \bool_set_false:N \exp_not:N \l_@@_only_known_bool }
+ \bool_if:NT \l_@@_filtered_bool
+ { \bool_set_true:N \exp_not:N \l_@@_filtered_bool }
+ \bool_if:NT \l_@@_selective_bool
+ { \bool_set_true:N \exp_not:N \l_@@_selective_bool }
+ \tl_set:Nn \exp_not:N \l_@@_relative_tl
+ { \exp_not:o \l_@@_relative_tl }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \keys_set_exclude_groups:nnnN, \keys_set_exclude_groups:nnVN,
+% \keys_set_exclude_groups:nnvN, \keys_set_exclude_groups:nnoN
+% }
+% \begin{macro}
+% {
+% \keys_set_exclude_groups:nnnnN, \keys_set_exclude_groups:nnVnN,
+% \keys_set_exclude_groups:nnvnN, \keys_set_exclude_groups:nnonN
+% }
+% \begin{macro}{\@@_set_exclude_groups:nnnnnN}
+% \begin{macro}
+% {
+% \keys_set_exclude_groups:nnn, \keys_set_exclude_groups:nnV,
+% \keys_set_exclude_groups:nnv, \keys_set_exclude_groups:nno
+% }
+% \begin{macro}{\@@_set_exclude_groups:nnnn}
+% \begin{macro}
+% {
+% \keys_set_groups:nnn, \keys_set_groups:nnV, \keys_set_groups:nnv,
+% \keys_set_groups:nno
+% }
+% \begin{macro}{\@@_set_selective:nnn}
+% \begin{macro}{\@@_set_selective:nnnn}
+% The idea of setting keys in a selective manner again uses booleans
+% wrapped around the basic code. The comments on \cs{keys_set_known:nnN}
+% also apply here. We have a bit more shuffling to do to keep everything
+% nestable.
+% \begin{macrocode}
+\cs_new_protected:Npn \keys_set_exclude_groups:nnnN #1#2#3#4
+ {
+ \exp_args:No \@@_set_exclude_groups:nnnnnN
+ \l_@@_unused_clist
+ \q_@@_no_value {#1} {#2} {#3} #4
+ }
+\cs_generate_variant:Nn \keys_set_exclude_groups:nnnN { nnV , nnv , nno }
+\cs_new_protected:Npn \keys_set_exclude_groups:nnnnN #1#2#3#4#5
+ {
+ \exp_args:No \@@_set_exclude_groups:nnnnnN
+ \l_@@_unused_clist {#4} {#1} {#2} {#3} #5
+ }
+\cs_generate_variant:Nn \keys_set_exclude_groups:nnnnN { nnV , nnv , nno }
+\cs_new_protected:Npn \@@_set_exclude_groups:nnnnnN #1#2#3#4#5#6
+ {
+ \clist_clear:N \l_@@_unused_clist
+ \@@_set_exclude_groups:nnnn {#2} {#3} {#4} {#5}
+ \__kernel_tl_set:Nx #6 { \exp_not:o \l_@@_unused_clist }
+ \__kernel_tl_set:Nx \l_@@_unused_clist { \exp_not:n {#1} }
+ }
+\cs_new_protected:Npn \keys_set_exclude_groups:nnn #1#2#3
+ {\@@_set_exclude_groups:nnnn \q_@@_no_value {#1} {#2} {#3} }
+\cs_generate_variant:Nn \keys_set_exclude_groups:nnn { nnV , nnv , nno }
+\cs_new_protected:Npn \@@_set_exclude_groups:nnnn #1#2#3#4
+ {
+ \use:e
+ {
+ \bool_set_false:N \exp_not:N \l_@@_only_known_bool
+ \bool_set_true:N \exp_not:N \l_@@_filtered_bool
+ \bool_set_true:N \exp_not:N \l_@@_selective_bool
+ \tl_set:Nn \exp_not:N \l_@@_relative_tl { \exp_not:n {#1} }
+ \@@_set_selective:nnn \exp_not:n { {#2} {#3} {#4} }
+ \bool_if:NT \l_@@_only_known_bool
+ { \bool_set_true:N \exp_not:N \l_@@_only_known_bool }
+ \bool_if:NF \l_@@_filtered_bool
+ { \bool_set_false:N \exp_not:N \l_@@_filtered_bool }
+ \bool_if:NF \l_@@_selective_bool
+ { \bool_set_false:N \exp_not:N \l_@@_selective_bool }
+ \tl_set:Nn \exp_not:N \l_@@_relative_tl
+ { \exp_not:o \l_@@_relative_tl }
+ }
+ }
+\cs_new_protected:Npn \keys_set_groups:nnn #1#2#3
+ {
+ \use:e
+ {
+ \bool_set_false:N \exp_not:N \l_@@_only_known_bool
+ \bool_set_false:N \exp_not:N \l_@@_filtered_bool
+ \bool_set_true:N \exp_not:N \l_@@_selective_bool
+ \tl_set:Nn \exp_not:N \l_@@_relative_tl
+ { \exp_not:N \q_@@_no_value }
+ \@@_set_selective:nnn \exp_not:n { {#1} {#2} {#3} }
+ \bool_if:NT \l_@@_only_known_bool
+ { \bool_set_true:N \exp_not:N \l_@@_only_known_bool }
+ \bool_if:NF \l_@@_filtered_bool
+ { \bool_set_true:N \exp_not:N \l_@@_filtered_bool }
+ \bool_if:NF \l_@@_selective_bool
+ { \bool_set_false:N \exp_not:N \l_@@_selective_bool }
+ \tl_set:Nn \exp_not:N \l_@@_relative_tl
+ { \exp_not:o \l_@@_relative_tl }
+ }
+ }
+\cs_generate_variant:Nn \keys_set_groups:nnn { nnV , nnv , nno }
+\cs_new_protected:Npn \@@_set_selective:nnn
+ { \exp_args:No \@@_set_selective:nnnn \l_@@_selective_seq }
+\cs_new_protected:Npn \@@_set_selective:nnnn #1#2#3#4
+ {
+ \exp_args:NNe \seq_set_from_clist:Nn
+ \l_@@_selective_seq { \tl_to_str:n {#3} }
+ \@@_set:nn {#2} {#4}
+ \tl_set:Nn \l_@@_selective_seq {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\keys_precompile:nnN}
+% A simple wrapper.
+% \begin{macrocode}
+\cs_new_protected:Npn \keys_precompile:nnN #1#2#3
+ {
+ \bool_set_true:N \l_@@_precompile_bool
+ \tl_clear:N \l_@@_precompile_tl
+ \keys_set:nn {#1} {#2}
+ \bool_set_false:N \l_@@_precompile_bool
+ \tl_set_eq:NN #3 \l_@@_precompile_tl
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_set_keyval:n, \@@_set_keyval:nn}
+% \begin{macro}{\@@_set_keyval:nnn, \@@_set_keyval:onn}
+% \begin{macro}{\@@_find_key_module:wNN}
+% \begin{macro}
+% {
+% \@@_find_key_module_auxi:Nw ,
+% \@@_find_key_module_auxii:Nw ,
+% \@@_find_key_module_auxiii:Nn ,
+% \@@_find_key_module_auxiv:Nw
+% }
+% \begin{macro}{\@@_set_selective:}
+% A shared system once again. First, set the current path and add a
+% default if needed. There are then checks to see if a value is
+% required or forbidden. If everything passes, move on to execute the
+% code.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_set_keyval:n #1
+ {
+ \bool_set_true:N \l_@@_no_value_bool
+ \@@_set_keyval:onn \l_@@_module_str {#1} { }
+ }
+\cs_new_protected:Npn \@@_set_keyval:nn #1#2
+ {
+ \bool_set_false:N \l_@@_no_value_bool
+ \@@_set_keyval:onn \l_@@_module_str {#1} {#2}
+ }
+% \end{macrocode}
+% The key path here can be fully defined, after which there is a search
+% for the key and module names: the user may have passed them with part
+% of what is actually the module (for our purposes) in the key name. As
+% that happens on a per-key basis, we use the stack approach to restore
+% the module name without a group.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_set_keyval:nnn #1#2#3
+ {
+ \__kernel_tl_set:Nx \l_keys_path_str
+ {
+ \tl_if_blank:nF {#1}
+ { #1 / }
+ \@@_trim_spaces:n {#2}
+ }
+ \str_clear:N \l_@@_module_str
+ \str_clear:N \l_@@_inherit_str
+ \exp_after:wN \@@_find_key_module:wNN \l_keys_path_str \s_@@_stop
+ \l_@@_module_str \l_keys_key_str
+ \tl_set_eq:NN \l_keys_key_tl \l_keys_key_str
+ \@@_value_or_default:n {#3}
+ \bool_if:NTF \l_@@_selective_bool
+ \@@_set_selective:
+ \@@_execute:
+ \str_set:Nn \l_@@_module_str {#1}
+ }
+\cs_generate_variant:Nn \@@_set_keyval:nnn { o }
+% \end{macrocode}
+% This function uses \cs{cs_set_nopar:Npe} internally for performance reasons,
+% the argument |#1| is already a string in every usage, so turning it into a
+% string again seems unnecessary.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_find_key_module:wNN #1 \s_@@_stop #2 #3
+ {
+ \@@_find_key_module_auxi:Nw #2 #1 \s_@@_nil \@@_find_key_module_auxii:Nw
+ / \s_@@_nil \@@_find_key_module_auxiv:Nw #3
+ }
+\cs_new_protected:Npn \@@_find_key_module_auxi:Nw #1 #2 / #3 \s_@@_nil #4
+ {
+ #4 #1 #2 \s_@@_mark #3 \s_@@_nil #4
+ }
+\cs_new_protected:Npn \@@_find_key_module_auxii:Nw
+ #1 #2 \s_@@_mark #3 \s_@@_nil \@@_find_key_module_auxii:Nw
+ {
+ \cs_set_nopar:Npe #1 { \tl_if_empty:NF #1 { #1 / } #2 }
+ \@@_find_key_module_auxi:Nw #1 #3 \s_@@_nil \@@_find_key_module_auxiii:Nw
+ }
+\cs_new_protected:Npn \@@_find_key_module_auxiii:Nw #1 #2 \s_@@_mark
+ {
+ \cs_set_nopar:Npe #1 { \tl_if_empty:NF #1 { #1 / } #2 }
+ \@@_find_key_module_auxi:Nw #1
+ }
+\cs_new_protected:Npn \@@_find_key_module_auxiv:Nw
+ #1 #2 \s_@@_nil #3 \s_@@_mark
+ \s_@@_nil \@@_find_key_module_auxiv:Nw #4
+ {
+ \cs_set_nopar:Npn #4 { #2 }
+ }
+% \end{macrocode}
+% If selective setting is active, there are a number of possible sub-cases
+% to consider. The key name may not be known at all or if it is, it may not
+% have any groups assigned. There is then the question of whether the
+% selection is opt-in or opt-out.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_set_selective:
+ {
+ \cs_if_exist:cTF { \c_@@_groups_root_str \l_keys_path_str }
+ {
+ \clist_set_eq:Nc \l_@@_groups_clist
+ { \c_@@_groups_root_str \l_keys_path_str }
+ \@@_check_groups:
+ }
+ {
+ \bool_if:NTF \l_@@_filtered_bool
+ \@@_execute:
+ \@@_store_unused:
+ }
+ }
+% \end{macrocode}
+% In the case where selective setting requires a comparison of the list
+% of groups which apply to a key with the list of those which have been
+% set active. That requires two mappings, and again a different outcome
+% depending on whether opt-in or opt-out is set.
+% It is safe to use \cs{clist_if_in:NnTF} because
+% both \cs{l_@@_selective_seq} and \cs{l_@@_groups_clist} contain the
+% groups as strings, without leading/trailing spaces in any item,
+% since the \pkg{l3clist} functions were applied to the result of
+% applying \cs{tl_to_str:n}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_check_groups:
+ {
+ \bool_set_false:N \l_@@_tmp_bool
+ \seq_map_inline:Nn \l_@@_selective_seq
+ {
+ \clist_if_in:NnT \l_@@_groups_clist {##1}
+ {
+ \bool_set_true:N \l_@@_tmp_bool
+ \seq_map_break:
+ }
+ }
+ \bool_if:NTF \l_@@_tmp_bool
+ {
+ \bool_if:NTF \l_@@_filtered_bool
+ \@@_store_unused:
+ \@@_execute:
+ }
+ {
+ \bool_if:NTF \l_@@_filtered_bool
+ \@@_execute:
+ \@@_store_unused:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_value_or_default:n}
+% \begin{macro}{\@@_default_inherit:}
+% If a value is given, return it as |#1|, otherwise send a default if
+% available.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_value_or_default:n #1
+ {
+ \bool_if:NTF \l_@@_no_value_bool
+ {
+ \cs_if_exist:cTF { \c_@@_default_root_str \l_keys_path_str }
+ {
+ \tl_set_eq:Nc
+ \l_keys_value_tl
+ { \c_@@_default_root_str \l_keys_path_str }
+ }
+ {
+ \tl_clear:N \l_keys_value_tl
+ \cs_if_exist:cT
+ { \c_@@_inherit_root_str \@@_parent:o \l_keys_path_str }
+ { \@@_default_inherit: }
+ }
+ }
+ { \tl_set:Nn \l_keys_value_tl {#1} }
+ }
+\cs_new_protected:Npn \@@_default_inherit:
+ {
+ \clist_map_inline:cn
+ { \c_@@_inherit_root_str \@@_parent:o \l_keys_path_str }
+ {
+ \cs_if_exist:cT
+ { \c_@@_default_root_str ##1 / \l_keys_key_str }
+ {
+ \tl_set_eq:Nc
+ \l_keys_value_tl
+ { \c_@@_default_root_str ##1 / \l_keys_key_str }
+ \clist_map_break:
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_execute:, \@@_execute_inherit:, \@@_execute_unknown:}
+% \begin{macro}[EXP]{\@@_execute:nn, \@@_execute:no}
+% \begin{macro}{\@@_store_unused:,\@@_store_unused_aux:}
+% Actually executing a key is done in two parts. First, look for the
+% key itself, then look for the \texttt{unknown} key with the same
+% path. If both of these fail, complain. What exactly happens if a key
+% is unknown depends on whether unknown keys are being skipped or if
+% an error should be raised.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_execute:
+ {
+ \cs_if_exist:cTF { \c_@@_code_root_str \l_keys_path_str }
+ {
+ \cs_if_exist_use:c { \c_@@_check_root_str \l_keys_path_str }
+ \@@_execute:no \l_keys_path_str \l_keys_value_tl
+ }
+ {
+ \cs_if_exist:cTF
+ { \c_@@_inherit_root_str \@@_parent:o \l_keys_path_str }
+ { \@@_execute_inherit: }
+ { \@@_execute_unknown: }
+ }
+ }
+% \end{macrocode}
+% To deal with the case where there is no hit, we leave
+% \cs{@@_execute_unknown:} in the input stream and clean it up using the
+% break function: that avoids needing a boolean.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_execute_inherit:
+ {
+ \clist_map_inline:cn
+ { \c_@@_inherit_root_str \@@_parent:o \l_keys_path_str }
+ {
+ \cs_if_exist:cT
+ { \c_@@_code_root_str ##1 / \l_keys_key_str }
+ {
+ \str_set:Nn \l_@@_inherit_str {##1}
+ \cs_if_exist_use:c { \c_@@_check_root_str ##1 / \l_keys_key_str }
+ \@@_execute:no { ##1 / \l_keys_key_str } \l_keys_value_tl
+ \clist_map_break:n \use_none:n
+ }
+ }
+ \@@_execute_unknown:
+ }
+\cs_new_protected:Npn \@@_execute_unknown:
+ {
+ \bool_if:NTF \l_@@_only_known_bool
+ { \@@_store_unused: }
+ {
+ \cs_if_exist:cTF
+ { \c_@@_code_root_str \l_@@_module_str / unknown }
+ {
+ \bool_if:NT \l_@@_no_value_bool
+ {
+ \cs_if_exist:cT
+ { \c_@@_default_root_str \l_@@_module_str / unknown }
+ {
+ \tl_set_eq:Nc
+ \l_keys_value_tl
+ { \c_@@_default_root_str \l_@@_module_str / unknown }
+ }
+ }
+ \@@_execute:no { \l_@@_module_str / unknown } \l_keys_value_tl
+ }
+ {
+ \msg_error:nnee { keys } { unknown }
+ \l_keys_path_str \l_@@_module_str
+ }
+ }
+ }
+% \end{macrocode}
+% A key's code is in the control sequence with csname
+% \cs{c_@@_code_root_str} |#1|. We expand it once to get the
+% replacement text (with argument |#2|) and call \cs{use:n}
+% with this replacement as its argument. This ensures that any
+% undefined control sequence error in the key's code will lead to an
+% error message of the form |<argument>|\ldots{}\meta{control
+% sequence} in which one can read the (undefined) \meta{control
+% sequence} in full, rather than an error message that starts with the
+% potentially very long key name, which would make the (undefined)
+% \meta{control sequence} be truncated or sometimes completely hidden.
+% See \url{https://github.com/latex3/latex2e/issues/351}.
+% \begin{macrocode}
+\cs_new:Npn \@@_execute:nn #1#2
+ { \@@_execute:no {#1} { \prg_do_nothing: #2 } }
+\cs_new:Npn \@@_execute:no #1#2
+ {
+ \exp_args:NNo \exp_args:No \use:n
+ {
+ \cs:w \c_@@_code_root_str #1 \exp_after:wN \cs_end:
+ \exp_after:wN {#2}
+ }
+ }
+% \end{macrocode}
+% When there is no relative path, things here are easy: just save the key
+% name and value. When we are working with a relative path, first we
+% need to turn it into a string: that can't happen earlier as we need
+% to store \cs{q_@@_no_value}. Then, use a standard delimited approach to fish
+% out the partial path.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_store_unused:
+ {
+ \@@_quark_if_no_value:NTF \l_@@_relative_tl
+ {
+ \clist_put_right:Ne \l_@@_unused_clist
+ {
+ \l_keys_key_str
+ \bool_if:NF \l_@@_no_value_bool
+ { = { \exp_not:o \l_keys_value_tl } }
+ }
+ }
+ {
+ \tl_if_empty:NTF \l_@@_relative_tl
+ {
+ \clist_put_right:Ne \l_@@_unused_clist
+ {
+ \l_keys_path_str
+ \bool_if:NF \l_@@_no_value_bool
+ { = { \exp_not:o \l_keys_value_tl } }
+ }
+ }
+ { \@@_store_unused_aux: }
+ }
+ }
+\cs_new_protected:Npn \@@_store_unused_aux:
+ {
+ \__kernel_tl_set:Nx \l_@@_relative_tl
+ { \exp_args:No \@@_trim_spaces:n \l_@@_relative_tl }
+ \use:e
+ {
+ \cs_set_protected:Npn \@@_store_unused:w
+ ##1 \l_@@_relative_tl /
+ ##2 \l_@@_relative_tl /
+ ##3 \s_@@_stop
+ }
+ {
+ \tl_if_blank:nF {##1}
+ {
+ \msg_error:nnee { keys } { bad-relative-key-path }
+ \l_keys_path_str
+ \l_@@_relative_tl
+ }
+ \clist_put_right:Ne \l_@@_unused_clist
+ {
+ \exp_not:n {##2}
+ \bool_if:NF \l_@@_no_value_bool
+ { = { \exp_not:o \l_keys_value_tl } }
+ }
+ }
+ \use:e
+ {
+ \@@_store_unused:w \l_keys_path_str
+ \l_@@_relative_tl / \l_@@_relative_tl /
+ \s_@@_stop
+ }
+ }
+\cs_new_protected:Npn \@@_store_unused:w { }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_choice_find:n}
+% \begin{macro}[EXP]{\@@_choice_find:nn}
+% \begin{macro}[EXP]{\@@_multichoice_find:n}
+% Executing a choice has two parts. First, try the choice given, then
+% if that fails call the unknown key. That always exists, as it is created
+% when a choice is first made. So there is no need for any escape code.
+% For multiple choices, the same code ends up used in a mapping.
+% \begin{macrocode}
+\cs_new:Npn \@@_choice_find:n #1
+ {
+ \str_if_empty:NTF \l_@@_inherit_str
+ { \@@_choice_find:nn \l_keys_path_str {#1} }
+ {
+ \@@_choice_find:nn
+ { \l_@@_inherit_str / \l_keys_key_str } {#1}
+ }
+ }
+\cs_new:Npn \@@_choice_find:nn #1#2
+ {
+ \cs_if_exist:cTF { \c_@@_code_root_str #1 / \@@_trim_spaces:n {#2} }
+ { \@@_execute:nn { #1 / \@@_trim_spaces:n {#2} } {#2} }
+ { \@@_execute:nn { #1 / unknown } {#2} }
+ }
+\cs_new:Npn \@@_multichoice_find:n #1
+ { \clist_map_function:nN {#1} \@@_choice_find:n }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Utilities}
+%
+% \begin{macro}[EXP]{\@@_parent:o}
+% \begin{macro}[EXP]
+% {
+% \@@_parent_auxi:w ,
+% \@@_parent_auxii:w ,
+% \@@_parent_auxiii:n ,
+% \@@_parent_auxiv:w
+% }
+% Used to strip off the ending part of the key path after the last~|/|.
+% \begin{macrocode}
+\cs_new:Npn \@@_parent:o #1
+ {
+ \exp_after:wN \@@_parent_auxi:w #1 \q_nil \@@_parent_auxii:w
+ / \q_nil \@@_parent_auxiv:w
+ }
+\cs_new:Npn \@@_parent_auxi:w #1 / #2 \q_nil #3
+ {
+ #3 { #1 } #2 \q_nil #3
+ }
+\cs_new:Npn \@@_parent_auxii:w #1 #2 \q_nil \@@_parent_auxii:w
+ {
+ #1 \@@_parent_auxi:w #2 \q_nil \@@_parent_auxiii:n
+ }
+\cs_new:Npn \@@_parent_auxiii:n #1
+ {
+ / #1 \@@_parent_auxi:w
+ }
+\cs_new:Npn \@@_parent_auxiv:w #1 \q_nil \@@_parent_auxiv:w
+ {
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_trim_spaces:n}
+% \begin{macro}[EXP]
+% {
+% \@@_trim_spaces_auxi:w ,
+% \@@_trim_spaces_auxii:w ,
+% \@@_trim_spaces_auxiii:w
+% }
+% Space stripping has to allow for the fact that the key here might have
+% several parts, and spaces need to be stripped from each part. Since the key
+% name is turned into a string groups can't be stripped accidentally and the
+% precautions of \cs{tl_trim_spaces:n} aren't necessary, in this case it is
+% much faster to just directly strip spaces around |/|.
+% \begin{macrocode}
+\group_begin:
+ \cs_set:Npn \@@_tmp:w #1
+ {
+ \cs_new:Npn \@@_trim_spaces:n ##1
+ {
+ \exp_after:wN \@@_trim_spaces_auxi:w \tl_to_str:n { / ##1 } /
+ \s_@@_nil \@@_trim_spaces_auxi:w
+ \s_@@_mark \@@_trim_spaces_auxii:w
+ #1 / #1
+ \s_@@_nil \@@_trim_spaces_auxii:w
+ \s_@@_mark \@@_trim_spaces_auxiii:w
+ }
+ }
+ \@@_tmp:w { ~ }
+\group_end:
+\cs_new:Npn \@@_trim_spaces_auxi:w #1 ~ / #2 \s_@@_nil #3
+ {
+ #3 #1 / #2 \s_@@_nil #3
+ }
+\cs_new:Npn \@@_trim_spaces_auxii:w #1 / ~ #2 \s_@@_mark #3
+ {
+ #3 #1 / #2 \s_@@_mark #3
+ }
+\cs_new:Npn \@@_trim_spaces_auxiii:w
+ / #1 /
+ \s_@@_nil \@@_trim_spaces_auxi:w
+ \s_@@_mark \@@_trim_spaces_auxii:w
+ /
+ \s_@@_nil \@@_trim_spaces_auxii:w
+ \s_@@_mark \@@_trim_spaces_auxiii:w
+ {
+ #1
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP,pTF]{\keys_if_exist:nn}
+% A utility for others to see if a key exists.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \keys_if_exist:nn #1#2 { p , T , F , TF }
+ {
+ \cs_if_exist:cTF
+ { \c_@@_code_root_str \@@_trim_spaces:n { #1 / #2 } }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\prg_generate_conditional_variant:Nnn \keys_if_exist:nn { ne } { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP,pTF]{\keys_if_choice_exist:nnn}
+% Just an alternative view on \cs{keys_if_exist:nnTF}.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \keys_if_choice_exist:nnn #1#2#3
+ { p , T , F , TF }
+ {
+ \cs_if_exist:cTF
+ { \c_@@_code_root_str \@@_trim_spaces:n { #1 / #2 / #3 } }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\keys_show:nn, \keys_log:nn}
+% \begin{macro}{\@@_show:Nnn}
+% \begin{macro}{\@@_show:n}
+% \begin{macro}{\@@_show:w}
+% \begin{macro}{\@@_show:Nw}
+% To show a key, show its code using a message.
+% \begin{macrocode}
+\cs_new_protected:Npn \keys_show:nn
+ { \@@_show:Nnn \msg_show:nneeee }
+\cs_new_protected:Npn \keys_log:nn
+ { \@@_show:Nnn \msg_log:nneeee }
+\cs_new_protected:Npn \@@_show:Nnn #1#2#3
+ {
+ #1 { keys } { show-key }
+ { \@@_trim_spaces:n { #2 / #3 } }
+ {
+ \keys_if_exist:nnT {#2} {#3}
+ {
+ \exp_args:Nnf \msg_show_item_unbraced:nn { code }
+ {
+ \exp_args:Ne \@@_show:n
+ {
+ \exp_args:Nc \cs_replacement_spec:N
+ {
+ \c_@@_code_root_str
+ \@@_trim_spaces:n { #2 / #3 }
+ }
+ }
+ }
+ }
+ }
+ { } { }
+ }
+\cs_new:Npe \@@_show:n #1
+ {
+ \exp_not:N \@@_show:w
+ #1
+ \tl_to_str:n { \@@_precompile:n }
+ #1
+ \tl_to_str:n { \@@_precompile:n }
+ \exp_not:N \s_@@_stop
+ }
+\use:e
+ {
+ \cs_new:Npn \exp_not:N \@@_show:w
+ #1 \tl_to_str:n { \@@_precompile:n }
+ #2 \tl_to_str:n { \@@_precompile:n }
+ #3 \exp_not:N \s_@@_stop
+ }
+ {
+ \tl_if_blank:nTF {#2}
+ {#1}
+ { \@@_show:Nw #2 \s_@@_stop }
+ }
+\use:e
+ {
+ \cs_new:Npn \exp_not:N \@@_show:Nw #1#2
+ \c_right_brace_str \exp_not:N \s_@@_stop
+ }
+ {#2}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+% For when there is a need to complain.
+% \begin{macrocode}
+\msg_new:nnnn { keys } { bad-relative-key-path }
+ { The~key~'#1'~is~not~inside~the~'#2'~path. }
+ { The~key~'#1'~cannot~be~expressed~relative~to~path~'#2'. }
+\msg_new:nnnn { keys } { boolean-values-only }
+ { Key~'#1'~accepts~boolean~values~only. }
+ { The~key~'#1'~only~accepts~the~values~'true'~and~'false'. }
+\msg_new:nnnn { keys } { choice-unknown }
+ { Key~'#1'~accepts~only~a~fixed~set~of~choices. }
+ {
+ The~key~'#1'~only~accepts~predefined~values,~
+ and~'#2'~is~not~one~of~these.
+ }
+\msg_new:nnnn { keys } { unknown }
+ { The~key~'#1'~is~unknown~and~is~being~ignored. }
+ {
+ The~module~'#2'~does~not~have~a~key~called~'#1'.\\
+ Check~that~you~have~spelled~the~key~name~correctly.
+ }
+\msg_new:nnnn { keys } { nested-choice-key }
+ { Attempt~to~define~'#1'~as~a~nested~choice~key. }
+ {
+ The~key~'#1'~cannot~be~defined~as~a~choice~as~the~parent~key~'#2'~is~
+ itself~a~choice.
+ }
+\msg_new:nnnn { keys } { value-forbidden }
+ { The~key~'#1'~does~not~take~a~value. }
+ {
+ The~key~'#1'~should~be~given~without~a~value.\\
+ The~value~'#2'~was~present:~the~key~will~be~ignored.
+ }
+\msg_new:nnnn { keys } { value-required }
+ { The~key~'#1'~requires~a~value. }
+ {
+ The~key~'#1'~must~have~a~value.\\
+ No~value~was~present:~the~key~will~be~ignored.
+ }
+\msg_new:nnn { keys } { show-key }
+ {
+ The~key~#1~
+ \tl_if_empty:nTF {#2}
+ { is~undefined. }
+ { has~the~properties: #2 . }
+ }
+\prop_gput:Nnn \g_msg_module_name_prop { keys } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { keys } { }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+%\end{implementation}
+%
+%\PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3legacy.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3legacy.dtx
new file mode 100644
index 00000000000..ed7dadf2220
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3legacy.dtx
@@ -0,0 +1,161 @@
+% \iffalse meta-comment
+%
+%% File: l3legacy.dtx
+%
+% Copyright (C) 2019-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3legacy} module\\ Interfaces to legacy concepts^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% There are a small number of \TeX{} or \LaTeXe{} concepts which are not used
+% in \pkg{expl3} code but which need to be manipulated when working as a \LaTeXe{}
+% package. To allow these to be integrated cleanly into \pkg{expl3} code, a set
+% of legacy interfaces are provided here.
+%
+% \begin{function}[EXP,pTF]{\legacy_if:n}
+% \begin{syntax}
+% \cs{legacy_if_p:n} \Arg{name}
+% \cs{legacy_if:nTF} \Arg{name} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \LaTeXe{}/plain \TeX{} conditional (generated by \tn{newif})
+% is \texttt{true} or \texttt{false} and branches accordingly. The
+% \meta{name} of the conditional should \emph{omit} the leading \texttt{if}.
+% \end{function}
+%
+% \begin{function}[added = 2021-05-10]
+% {
+% \legacy_if_set_true:n, \legacy_if_set_false:n,
+% \legacy_if_gset_true:n, \legacy_if_gset_false:n
+% }
+% \begin{syntax}
+% \cs{legacy_if_set_true:n} \Arg{name}
+% \cs{legacy_if_set_false:n} \Arg{name}
+% \end{syntax}
+% Sets the \LaTeXe{}/plain \TeX{} conditional |\if|\meta{name}
+% (generated by \tn{newif}) to be \texttt{true} or \texttt{false}.
+% \end{function}
+%
+% \begin{function}[added = 2021-05-10]{\legacy_if_set:nn, \legacy_if_gset:nn}
+% \begin{syntax}
+% \cs{legacy_if_set:nn} \Arg{name} \Arg{boolexpr}
+% \end{syntax}
+% Sets the \LaTeXe{}/plain \TeX{} conditional |\if|\meta{name}
+% (generated by \tn{newif}) to the result of evaluating the
+% \meta{boolean expression}.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3legacy} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=legacy>
+% \end{macrocode}
+%
+% \begin{macro}[EXP,pTF]{\legacy_if:n}
+% A friendly wrapper. We need to use the \cs{if:w} approach here, rather than
+% testing against \tn{iftrue}/\tn{iffalse} as the latter approach fails for
+% primitive conditionals such as \tn{ifmmode}. The \cs{reverse_if:N} here
+% means that we get a slightly more useful error if the name is undefined.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \legacy_if:n #1 { p , T , F , TF }
+ {
+ \exp_after:wN \reverse_if:N
+ \cs:w if#1 \cs_end:
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \legacy_if_set_true:n, \legacy_if_set_false:n,
+% \legacy_if_gset_true:n, \legacy_if_gset_false:n
+% }
+% A friendly wrapper.
+% \begin{macrocode}
+\cs_new_protected:Npn \legacy_if_set_true:n #1
+ { \cs_set_eq:cN { if#1 } \if_true: }
+\cs_new_protected:Npn \legacy_if_set_false:n #1
+ { \cs_set_eq:cN { if#1 } \if_false: }
+\cs_new_protected:Npn \legacy_if_gset_true:n #1
+ { \cs_gset_eq:cN { if#1 } \if_true: }
+\cs_new_protected:Npn \legacy_if_gset_false:n #1
+ { \cs_gset_eq:cN { if#1 } \if_false: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\legacy_if_set:nn, \legacy_if_gset:nn}
+% A more elaborate wrapper.
+% \begin{macrocode}
+\cs_new_protected:Npn \legacy_if_set:nn #1#2
+ {
+ \bool_if:nTF {#2} \legacy_if_set_true:n \legacy_if_set_false:n
+ {#1}
+ }
+\cs_new_protected:Npn \legacy_if_gset:nn #1#2
+ {
+ \bool_if:nTF {#2} \legacy_if_gset_true:n \legacy_if_gset_false:n
+ {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3luatex.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3luatex.dtx
new file mode 100644
index 00000000000..f0107d465be
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3luatex.dtx
@@ -0,0 +1,750 @@
+% \iffalse meta-comment
+%
+%% File: l3luatex.dtx
+%
+% Copyright (C) 2010-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3luatex} module\\ \LuaTeX-specific functions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% The \LuaTeX{} engine provides access to the \Lua{} programming language,
+% and with it access to the \enquote{internals} of \TeX{}. In order to use
+% this within the framework provided here, a family of functions is
+% available. When used with \pdfTeX{}, \pTeX{}, \upTeX{} or \XeTeX{} these raise an
+% error: use \cs{sys_if_engine_luatex:T} to avoid this. Details on using
+% \Lua{} with the \LuaTeX{} engine are given in the \LuaTeX{} manual.
+%
+% \section{Breaking out to \Lua{}}
+%
+% \begin{function}[EXP, added = 2018-06-18]{\lua_now:n, \lua_now:e}
+% \begin{syntax}
+% \cs{lua_now:n} \Arg{token list}
+% \end{syntax}
+% The \meta{token list} is first tokenized by \TeX{}, which includes
+% converting line ends to spaces in the usual \TeX{} manner and which
+% respects currently-applicable \TeX{} category codes. The resulting
+% \meta{\Lua{} input} is passed to the \Lua{} interpreter for processing.
+% Each \cs{lua_now:n} block is treated by \Lua{} as a separate chunk.
+% The \Lua{} interpreter executes the \meta{\Lua{} input} immediately,
+% and in an expandable manner.
+% \begin{texnote}
+% \cs{lua_now:e} is a macro wrapper around \tn{directlua}: when
+% \LuaTeX{} is in use two expansions are required to yield the
+% result of the \Lua{} code.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2018-06-18]{\lua_shipout_e:n, \lua_shipout:n}
+% \begin{syntax}
+% \cs{lua_shipout:n} \Arg{token list}
+% \end{syntax}
+% The \meta{token list} is first tokenized by \TeX{}, which includes
+% converting line ends to spaces in the usual \TeX{} manner and which
+% respects currently-applicable \TeX{} category codes. The resulting
+% \meta{\Lua{} input} is passed to the \Lua{} interpreter when the
+% current page is finalised (\emph{i.e.}~at shipout). Each
+% \cs{lua_shipout:n} block is treated by \Lua{} as a separate chunk.
+% The \Lua{} interpreter will execute the \meta{\Lua{} input} during the
+% page-building routine: no \TeX{} expansion of the \meta{\Lua{} input}
+% will occur at this stage.
+%
+% In the case of the \cs{lua_shipout_e:n} version the input is fully
+% expanded by \TeX{} in an \texttt{e}-type manner during the shipout
+% operation.
+% \begin{texnote}
+% At a \TeX{} level, the \meta{\Lua{} input} is stored as a
+% \enquote{whatsit}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2015-06-29]{\lua_escape:n, \lua_escape:e}
+% \begin{syntax}
+% \cs{lua_escape:n} \Arg{token list}
+% \end{syntax}
+% Converts the \meta{token list} such that it can safely be passed to
+% \Lua{}: embedded backslashes, double and single quotes, and newlines
+% and carriage returns are escaped. This is done by prepending an extra
+% token consisting of a backslash with category code~$12$, and for the line
+% endings, converting them to |\n| and |\r|, respectively.
+% \begin{texnote}
+% \cs{lua_escape:e} is a macro wrapper around \tn{luaescapestring}:
+% when \LuaTeX{} is in use two expansions are required to yield the
+% result of the \Lua{} code.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2022-05-14]{\lua_load_module:n}
+% \begin{syntax}
+% \cs{lua_load_module:n} \Arg{Lua module name}
+% \end{syntax}
+% Loads a Lua module into the Lua interpreter.
+%
+% \cs{lua_now:n} passes its \Arg{token list} argument to the Lua interpreter
+% as a single line, with characters interpreted under the current catcode
+% regime. These two facts mean that \cs{lua_now:n} rarely behaves as expected
+% for larger pieces of code. Therefore, package authors should \textbf{not}
+% write significant amounts of Lua code in the arguments to \cs{lua_now:n}.
+% Instead, it is strongly recommended that they write the majorty of their Lua
+% code in a separate file, and then load it using \cs{lua_load_module:n}.
+% \begin{texnote}
+% This is a wrapper around the Lua call |require '|\meta{module}|'|.
+% \end{texnote}
+% \end{function}
+%
+% \section{Lua interfaces}
+%
+% As well as interfaces for \TeX{}, there are a small number of Lua functions
+% provided here.
+%
+% \begin{function}{ltx.utils}
+% Most public interfaces provided by the module are stored within the
+% |ltx.utils| table.
+% \end{function}
+%
+% \begin{function}{ltx.utils.filedump}
+% \begin{syntax}
+% \meta{dump}| = ltx.utils.filedump(|\meta{file}|,|\meta{offset}|,|\meta{length}|)| \\
+% \end{syntax}
+% Returns the uppercase hexadecimal representation of the content of the
+% \meta{file} read as bytes. If the \meta{length} is given, only this part
+% of the file is returned; similarly, one may specify the \meta{offset} from
+% the start of the file. If the \meta{length} is not given, the entire file
+% is read starting at the \meta{offset}.
+% \end{function}
+%
+% \begin{function}{ltx.utils.filemd5sum}
+% \begin{syntax}
+% \meta{hash}| = ltx.utils.filemd5sum(|\meta{file}|)| \\
+% \end{syntax}
+% Returns the MD5 sum of the file contents read as bytes; note that
+% the result will depend on the nature of the line endings used in the file,
+% in contrast to normal \TeX{} behaviour. If the \meta{file} is not found,
+% nothing is returned with \emph{no error raised}.
+% \end{function}
+%
+% \begin{function}{ltx.utils.filemoddate}
+% \begin{syntax}
+% \meta{date}| = ltx.utils.filemoddate(|\meta{file}|)| \\
+% \end{syntax}
+% Returns the date/time of last modification of the \meta{file} in the
+% format
+% \begin{quote}
+% |D:|\meta{year}\meta{month}\meta{day}\meta{hour}\meta{minute}^^A
+% \meta{second}\meta{offset}
+% \end{quote}
+% where the latter may be |Z| (UTC) or
+% \meta{plus-minus}\meta{hours}|'|\meta{minutes}|'|. If the \meta{file} is
+% not found, nothing is returned with \emph{no error raised}.
+% \end{function}
+%
+% \begin{function}{ltx.utils.filesize}
+% \begin{syntax}
+% |size = ltx.utils.filesize(|\meta{file}|)| \\
+% \end{syntax}
+% Returns the size of the \meta{file} in bytes. If the \meta{file} is not
+% found, nothing is returned with \emph{no error raised}.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3luatex} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \subsection{Breaking out to \Lua{}}
+%
+% \begin{macrocode}
+%<*tex>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=lua>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\@@_escape:n, \@@_now:n, \@@_shipout:n}
+% Copies of primitives.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_escape:n \tex_luaescapestring:D
+\cs_new_eq:NN \@@_now:n \tex_directlua:D
+\cs_new_eq:NN \@@_shipout:n \tex_latelua:D
+% \end{macrocode}
+% \end{macro}
+%
+% These functions are set up in \pkg{l3str} for bootstrapping: we want to
+% replace them with a \enquote{proper} version at this stage, so clean up.
+% \begin{macrocode}
+\cs_undefine:N \lua_escape:e
+\cs_undefine:N \lua_now:e
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\lua_now:n, \lua_now:e}
+% \begin{macro}{\lua_shipout_e:n, \lua_shipout:n}
+% \begin{macro}[EXP]{\lua_escape:n, \lua_escape:e}
+% Wrappers around the primitives.
+% \begin{macrocode}
+\cs_new:Npn \lua_now:e #1 { \@@_now:n {#1} }
+\cs_new:Npn \lua_now:n #1 { \lua_now:e { \exp_not:n {#1} } }
+\cs_new_protected:Npn \lua_shipout_e:n #1 { \@@_shipout:n {#1} }
+\cs_new_protected:Npn \lua_shipout:n #1
+ { \lua_shipout_e:n { \exp_not:n {#1} } }
+\cs_new:Npn \lua_escape:e #1 { \@@_escape:n {#1} }
+\cs_new:Npn \lua_escape:n #1 { \lua_escape:e { \exp_not:n {#1} } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\lua_load_module:n}
+% Wrapper around |require'|\meta{module}|'|.
+% \begin{macrocode}
+\str_new:N \l_@@_err_msg_str
+\cs_new_protected:Npn \lua_load_module:n #1
+ {
+ \bool_if:nF { \@@_load_module_p:n { #1 } }
+ {
+ \msg_error:nnnV
+ { luatex } { module-not-found } { #1 } \l_@@_err_msg_str
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% As with engines other than \LuaTeX{}
+% these have to be macros, we give them the same status in all cases.
+% When \LuaTeX{} is not in use, simply give an error message/
+% \begin{macrocode}
+\sys_if_engine_luatex:F
+ {
+ \clist_map_inline:nn
+ {
+ \lua_escape:n , \lua_escape:e ,
+ \lua_now:n , \lua_now:e
+ }
+ {
+ \cs_gset:Npn #1 ##1
+ {
+ \msg_expandable_error:nnn
+ { luatex } { luatex-required } { #1 }
+ }
+ }
+ \clist_map_inline:nn
+ { \lua_shipout_e:n , \lua_shipout:n, \lua_load_module:n }
+ {
+ \cs_gset_protected:Npn #1 ##1
+ {
+ \msg_error:nnn
+ { luatex } { luatex-required } { #1 }
+ }
+ }
+ }
+% \end{macrocode}
+% \subsection{Messages}
+%
+% \begin{macrocode}
+\msg_new:nnnn { luatex } { luatex-required }
+ { LuaTeX~engine~not~in~use!~Ignoring~#1. }
+ {
+ The~feature~you~are~using~is~only~available~
+ with~the~LuaTeX~engine.~LaTeX3~ignored~'#1'.
+ }
+
+\msg_new:nnnn { luatex } { module-not-found }
+ { Lua~module~`#1'~not~found. }
+ {
+ The~file~`#1.lua'~could~not~be~found.~Please~ensure~
+ that~the~file~was~properly~installed~and~that~the~
+ filename~database~is~current. \\ \\
+ The~Lua~loader~provided~this~additional~information: \\
+ #2
+ }
+
+\prop_gput:Nnn \g_msg_module_name_prop { luatex } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { luatex } { }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</tex>
+% \end{macrocode}
+%
+% \subsection{\Lua{} functions for internal use}
+%
+% \begin{macrocode}
+%<*lua>
+% \end{macrocode}
+%
+% Most of the emulation of \pdfTeX{} here is based heavily on Heiko Oberdiek's
+% \pkg{pdftexcmds} package.
+%
+% \begin{macro}{ltx.utils}
+% Create a table for the kernel's own use.
+% \begin{macrocode}
+ltx = ltx or {utils={}}
+ltx.utils = ltx.utils or { }
+local ltxutils = ltx.utils
+% \end{macrocode}
+% \end{macro}
+%
+% Local copies of global tables.
+% \begin{macrocode}
+local io = io
+local kpse = kpse
+local lfs = lfs
+local math = math
+local md5 = md5
+local os = os
+local string = string
+local tex = tex
+local texio = texio
+local tonumber = tonumber
+% \end{macrocode}
+%
+% Local copies of standard functions.
+% \begin{macrocode}
+local abs = math.abs
+local byte = string.byte
+local floor = math.floor
+local format = string.format
+local gsub = string.gsub
+local lfs_attr = lfs.attributes
+local open = io.open
+local os_date = os.date
+local setcatcode = tex.setcatcode
+local sprint = tex.sprint
+local cprint = tex.cprint
+local write = tex.write
+local write_nl = texio.write_nl
+local utf8_char = utf8.char
+local package_loaded = package.loaded
+local package_searchers = package.searchers
+local table_concat = table.concat
+
+local scan_int = token.scan_int or token.scan_integer
+local scan_string = token.scan_string
+local scan_keyword = token.scan_keyword
+local put_next = token.put_next
+local token_create = token.create
+local token_new = token.new
+local set_macro = token.set_macro
+% \end{macrocode}
+%
+% Since token.create only returns useful values after the tokens
+% has been added to TeX's hash table, we define a variant which
+% defines it first if necessary.
+% \begin{macrocode}
+local token_create_safe
+do
+ local is_defined = token.is_defined
+ local set_char = token.set_char
+ local runtoks = tex.runtoks
+ local let_token = token_create'let'
+
+ function token_create_safe(s)
+ local orig_token = token_create(s)
+ if is_defined(s, true) then
+ return orig_token
+ end
+ set_char(s, 0)
+ local new_token = token_create(s)
+ runtoks(function()
+ put_next(let_token, new_token, orig_token)
+ end)
+ return new_token
+ end
+end
+
+local true_tok = token_create_safe'prg_return_true:'
+local false_tok = token_create_safe'prg_return_false:'
+% \end{macrocode}
+% In Con\TeX{}t lmtx \texttt{token.command_id} does not exist,
+% but it can easily be emulated with Con\TeX{}t's \texttt{tokens.commands}.
+% \begin{macrocode}
+local command_id = token.command_id
+if not command_id and tokens and tokens.commands then
+ local id_map = tokens.commands
+ function command_id(name)
+ return id_map[name]
+ end
+end
+% \end{macrocode}
+%
+% Deal with Con\TeX{}t: doesn't use |kpse| library.
+% \begin{macrocode}
+local kpse_find = (resolvers and resolvers.findfile) or kpse.find_file
+% \end{macrocode}
+%
+% \begin{macro}[int]{escapehex}
+% An internal auxiliary to convert a string to the matching hex escape.
+% This works on a byte basis: extension to handled UTF-8 input is
+% covered in \pkg{pdftexcmds} but is not currently required here.
+% \begin{macrocode}
+local function escapehex(str)
+ return (gsub(str, ".",
+ function (ch) return format("%02X", byte(ch)) end))
+end
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{ltx.utils.filedump}
+% Similar comments here to the next function: read the file in binary mode
+% to avoid any line-end weirdness.
+% \begin{macrocode}
+local function filedump(name,offset,length)
+ local file = kpse_find(name,"tex",true)
+ if not file then return end
+ local f = open(file,"rb")
+ if not f then return end
+ if offset and offset > 0 then
+ f:seek("set", offset)
+ end
+ local data = f:read(length or 'a')
+ f:close()
+ return escapehex(data)
+end
+ltxutils.filedump = filedump
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]{md5.HEX}
+% Hash a string and return the hash in uppercase hexadecimal format.
+% In some engines, this is build-in. For traditional \LuaTeX{}, the conversion
+% to hexadecimal has to be done by us.
+% \begin{macrocode}
+local md5_HEX = md5.HEX
+if not md5_HEX then
+ local md5_sum = md5.sum
+ function md5_HEX(data)
+ return escapehex(md5_sum(data))
+ end
+ md5.HEX = md5_HEX
+end
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{ltx.utils.filemd5sum}
+% Read an entire file and hash it: the hash function itself is a built-in.
+% As Lua is byte-based there is no work needed here in terms of UTF-8
+% (see \pkg{pdftexcmds} and how it handles strings that have passed through
+% \LuaTeX{}). The file is read in binary mode so that no line ending
+% normalisation occurs.
+% \begin{macrocode}
+local function filemd5sum(name)
+ local file = kpse_find(name, "tex", true) if not file then return end
+ local f = open(file, "rb") if not f then return end
+
+ local data = f:read("*a")
+ f:close()
+ return md5_HEX(data)
+end
+ltxutils.filemd5sum = filemd5sum
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{ltx.utils.filemoddate}
+% There are two cases: If the C standard library is C99 compliant,
+% we can use |%z| to get the timezone in almost the right format.
+% We only have to add primes and replace a zero or missing offset
+% with |Z|.
+%
+% Of course this would be boring, so Windows does things differently.
+% There we have to manually calculate the offset.
+% See procedure \texttt{makepdftime} in \texttt{utils.c} of
+% \pdfTeX{}.
+% \begin{macrocode}
+local filemoddate
+if os_date'%z':match'^[+-]%d%d%d%d$' then
+ local pattern = lpeg.Cs(16 *
+ (lpeg.Cg(lpeg.S'+-' * '0000' * lpeg.Cc'Z')
+ + 3 * lpeg.Cc"'" * 2 * lpeg.Cc"'"
+ + lpeg.Cc'Z')
+ * -1)
+ function filemoddate(name)
+ local file = kpse_find(name, "tex", true)
+ if not file then return end
+ local date = lfs_attr(file, "modification")
+ if not date then return end
+ return pattern:match(os_date("D:%Y%m%d%H%M%S%z", date))
+ end
+else
+ local function filemoddate(name)
+ local file = kpse_find(name, "tex", true)
+ if not file then return end
+ local date = lfs_attr(file, "modification")
+ if not date then return end
+ local d = os_date("*t", date)
+ local u = os_date("!*t", date)
+ local off = 60 * (d.hour - u.hour) + d.min - u.min
+ if d.year ~= u.year then
+ if d.year > u.year then
+ off = off + 1440
+ else
+ off = off - 1440
+ end
+ elseif d.yday ~= u.yday then
+ if d.yday > u.yday then
+ off = off + 1440
+ else
+ off = off - 1440
+ end
+ end
+ local timezone
+ if off == 0 then
+ timezone = "Z"
+ else
+ if off < 0 then
+ timezone = "-"
+ off = -off
+ else
+ timezone = "+"
+ end
+ timezone = format("%s%02d'%02d'", timezone, hours // 60, hours % 60)
+ end
+ return format("D:%04d%02d%02d%02d%02d%02d%s",
+ d.year, d.month, d.day, d.hour, d.min, d.sec, timezone)
+ end
+end
+ltxutils.filemoddate = filemoddate
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{ltx.utils.filesize}
+% A simple disk lookup.
+% \begin{macrocode}
+local function filesize(name)
+ local file = kpse_find(name, "tex", true)
+ if file then
+ local size = lfs_attr(file, "size")
+ if size then
+ return size
+ end
+ end
+end
+ltxutils.filesize = filesize
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]{luadef}
+% An internal function for defining control sequences form Lua which behave
+% like primitives. This acts as a wrapper around |token.set_lua| which accepts
+% a function instead of an index into the functions table.
+% \begin{macrocode}
+local luacmd do
+ local set_lua = token.set_lua
+ local undefined_cs = command_id'undefined_cs'
+
+ if not context and not luatexbase then require'ltluatex' end
+ if luatexbase then
+ local new_luafunction = luatexbase.new_luafunction
+ local functions = lua.get_functions_table()
+ function luacmd(name, func, ...)
+ local id
+ local tok = token_create(name)
+ if tok.command == undefined_cs then
+ id = new_luafunction(name)
+ set_lua(name, id, ...)
+ else
+ id = tok.index or tok.mode
+ end
+ functions[id] = func
+ end
+ elseif context then
+ local register = context.functions.register
+ local functions = context.functions.known
+ function luacmd(name, func, ...)
+ local tok = token_create(name)
+ if tok.command == undefined_cs then
+ token.set_lua(name, register(func), ...)
+ else
+ functions[tok.index or tok.mode] = func
+ end
+ end
+ end
+end
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]{try_require}
+% Loads a Lua module. This function loads the module similarly to the standard
+% Lua global function |require|, with a few differences. On success,
+% |try_require| returns |true, module|. If the module cannot be found, it
+% returns |false, err_msg|. If the module is found, but something goes wrong
+% when loading it, the function throws an error.
+% \begin{macrocode}
+local function try_require(name)
+ if package_loaded[name] then
+ return true, package_loaded[name]
+ end
+
+ local failure_details = {}
+ for _, searcher in ipairs(package_searchers) do
+ local loader, data = searcher(name)
+ if type(loader) == 'function' then
+ package_loaded[name] = loader(name, data) or true
+ return true, package_loaded[name]
+ elseif type(loader) == 'string' then
+ failure_details[#failure_details + 1] = loader
+ end
+ end
+
+ return false, table_concat(failure_details, '\n')
+end
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_load_module_p:n}
+% Check to see if we can load a module using |require|. If we
+% can load the module, then we load it immediately. Otherwise, we save the
+% error message in |\l_@@_err_msg_str|.
+% \begin{macrocode}
+local char_given = command_id'char_given'
+local c_true_bool = token_create(1, char_given)
+local c_false_bool = token_create(0, char_given)
+local c_str_cctab = token_create('c_str_cctab').mode
+
+luacmd('@@_load_module_p:n', function()
+ local success, result = try_require(scan_string())
+ if success then
+ set_macro(c_str_cctab, 'l_@@_err_msg_str', '')
+ put_next(c_true_bool)
+ else
+ set_macro(c_str_cctab, 'l_@@_err_msg_str', result)
+ put_next(c_false_bool)
+ end
+end)
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Preserving iniTeX Lua data for runs}
+%
+% \begin{macrocode}
+%<@@=lua>
+% \end{macrocode}
+%
+% The Lua state is not dumped when a forat is written, therefore any Lua
+% variables filled doing format building need to be restored in order to
+% be accessible during normal runs.
+%
+% We provide some kernel-internal helpers for this. They will only be available if
+% \texttt{luatexbase} is available. This is not a big restriction though, because
+% Con\TeX{}t (which does not use \texttt{luatexbase}) does not load \pkg{expl3}
+% in the format.
+%
+% \begin{macrocode}
+local register_luadata, get_luadata
+
+if luatexbase then
+ local register = token_create'@expl@luadata@bytecode'.index
+ if status.ini_version then
+% \end{macrocode}
+%
+% \begin{macro}[int]{register_luadata}
+% \texttt{register_luadata} is only available during format generation.
+% It accept a string which uniquely identifies the data object and has to be
+% provided to retrieve it later. Additionally it accepts a function which is
+% called in the \texttt{pre_dump} callback and which has to return a string that
+% evaluates to a valid Lua object to be preserved.
+% \begin{macrocode}
+ local luadata, luadata_order = {}, {}
+
+ function register_luadata(name, func)
+ if luadata[name] then
+ error(format("LaTeX error: data name %q already in use", name))
+ end
+ luadata[name] = func
+ luadata_order[#luadata_order + 1] = func and name
+ end
+% \end{macrocode}
+% \end{macro}
+%
+% The actual work is done in \texttt{pre_dump}. The \texttt{luadata_order} is used
+% to ensure that the order is consistent over multiple runs.
+% \begin{macrocode}
+ luatexbase.add_to_callback("pre_dump", function()
+ if next(luadata) then
+ local str = "return {"
+ for i=1, #luadata_order do
+ local name = luadata_order[i]
+ str = format('%s[%q]=%s,', str, name, luadata[name]())
+ end
+ lua.bytecode[register] = assert(load(str .. "}"))
+ end
+ end, "ltx.luadata")
+ else
+% \end{macrocode}
+%
+% \begin{macro}[int]{get_luadata}
+% \texttt{get_luadata} is only available if data should be restored.
+% It accept the identifier which was used when the data object was registered and
+% returns the associated object. Every object can only be retrieved once.
+% \begin{macrocode}
+ local luadata = lua.bytecode[register]
+ if luadata then
+ lua.bytecode[register] = nil
+ luadata = luadata()
+ end
+ function get_luadata(name)
+ if not luadata then return end
+ local data = luadata[name]
+ luadata[name] = nil
+ return data
+ end
+ end
+end
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</lua>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+%\end{implementation}
+%
+%\PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3msg.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3msg.dtx
new file mode 100644
index 00000000000..ab4d882dcbf
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3msg.dtx
@@ -0,0 +1,2443 @@
+% \iffalse meta-comment
+%
+%% File: l3msg.dtx
+%
+% Copyright (C) 2009-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3msg} module\\ Messages^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% Messages need to be passed to the user by modules, either when errors
+% occur or to indicate how the code is proceeding. The \pkg{l3msg}
+% module provides a consistent method for doing this (as opposed to
+% writing directly to the terminal or log).
+%
+% The system used by \pkg{l3msg} to create messages divides the process
+% into two distinct parts. Named messages are created in the first part
+% of the process; at this stage, no decision is made about the type of
+% output that the message will produce. The second part of the process
+% is actually producing a message. At this stage a choice of message
+% \emph{class} has to be made, for example \texttt{error},
+% \texttt{warning} or \texttt{info}.
+%
+% By separating out the creation and use of messages, several benefits
+% are available. First, the messages can be altered later without
+% needing details of where they are used in the code. This makes it
+% possible to alter the language used, the detail level and so on.
+% Secondly, the output which results from a given message can be
+% altered. This can be done on a message class, module or message name
+% basis. In this way, message behaviour can be altered and messages can
+% be entirely suppressed.
+%
+% \section{Creating new messages}
+%
+% All messages have to be created before they can be used.
+% The text of messages is automatically wrapped to the length
+% available in the console. As a result, formatting is only needed
+% where it helps to show meaning. In particular, |\\| may be
+% used to force a new line and \verb*|\ | forces an explicit space.
+% Additionally, |\{|, |\#|, |\}|, |\%| and |\~| can be used to produce
+% the corresponding character.
+%
+% Messages may be subdivided \emph{by one level} using the~|/|
+% character. This is used within the message filtering system to allow
+% for example the \LaTeX{} kernel messages to belong to the module
+% \texttt{LaTeX} while still being filterable at a more granular level.
+% Thus for example
+% \begin{verbatim}
+% \msg_new:nnnn { mymodule } { submodule / message } ...
+% \end{verbatim}
+% will allow to filter out specifically messages from the \texttt{submodule}.
+%
+% Some authors may find the need to include spaces as |~| characters
+% tedious. This can be avoided by locally reseting the cateogry code
+% of \verb*| |.
+% \begin{verbatim}
+% \char_set_catcode_space:n { `\ }
+% \msg_new:nnn { foo } { bar }
+% {Some message text using '#1' and usual message shorthands \{ \ \ \}.}
+% \char_set_catcode_ignore:n { `\ }
+% \end{verbatim}
+% although in general this may be confusing; simply writing the messages
+% using |~| characters is the method favored by the team.
+%
+% \begin{function}[updated = 2011-08-16]
+% {
+% \msg_new:nnnn, \msg_new:nnee,
+% \msg_new:nnn, \msg_new:nne
+% }
+% \begin{syntax}
+% \cs{msg_new:nnnn} \Arg{module} \Arg{message} \Arg{text} \Arg{more text}
+% \end{syntax}
+% Creates a \meta{message} for a given \meta{module}.
+% The message is defined to first give \meta{text} and then
+% \meta{more text} if the user requests it. If no \meta{more text} is
+% available then a standard text is given instead. Within \meta{text}
+% and \meta{more text} four parameters (|#1| to |#4|) can be used:
+% these will be supplied at the time the message is
+% used. An error is raised if the \meta{message} already exists.
+% \end{function}
+%
+% \begin{function}{\msg_set:nnnn, \msg_set:nnn}
+% \begin{syntax}
+% \cs{msg_set:nnnn} \Arg{module} \Arg{message} \Arg{text} \Arg{more text}
+% \end{syntax}
+% Sets up the text for a \meta{message} for a given \meta{module}.
+% The message is defined to first give \meta{text} and then
+% \meta{more text} if the user requests it. If no \meta{more text} is
+% available then a standard text is given instead. Within \meta{text}
+% and \meta{more text} four parameters (|#1| to |#4|) can be used:
+% these will be supplied at the time the message is used.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added = 2012-03-03]{\msg_if_exist:nn}
+% \begin{syntax}
+% \cs{msg_if_exist_p:nn} \Arg{module} \Arg{message}
+% \cs{msg_if_exist:nnTF} \Arg{module} \Arg{message} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{message} for the \meta{module} is currently
+% defined.
+% \end{function}
+%
+% \section{Customizable information for message modules}
+%
+% \begin{function}[EXP, added = 2018-10-10]{\msg_module_name:n}
+% \begin{syntax}
+% \cs{msg_module_name:n} \Arg{module}
+% \end{syntax}
+% Expands to the public name of the \meta{module} as defined by
+% \cs{g_msg_module_name_prop} (or otherwise leaves the \meta{module}
+% unchanged).
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-10-10]{\msg_module_type:n}
+% \begin{syntax}
+% \cs{msg_module_type:n} \Arg{module}
+% \end{syntax}
+% Expands to the description which applies to the \meta{module},
+% for example a |Package| or |Class|. The information here is defined
+% in \cs{g_msg_module_type_prop}, and will default to |Package| if an
+% entry is not present.
+% \end{function}
+%
+% \begin{variable}[added = 2018-10-10]{\g_msg_module_name_prop}
+% Provides a mapping between the module name used for messages, and that
+% for documentation.
+% \end{variable}
+%
+% \begin{variable}[added = 2018-10-10]{\g_msg_module_type_prop}
+% Provides a mapping between the module name used for messages, and that
+% type of module. For example, for \LaTeX{}3 core messages, an empty entry
+% is set here meaning that they are not described using the standard
+% |Package| text.
+% \end{variable}
+%
+% \section{Contextual information for messages}
+%
+% \begin{function}[rEXP]{\msg_line_context:}
+% \begin{syntax}
+% \cs{msg_line_context:}
+% \end{syntax}
+% Prints the current line number when a message is given, and
+% thus suitable for giving context to messages. The number itself
+% is proceeded by the text |on line|.
+% \end{function}
+%
+% \begin{function}[EXP]{\msg_line_number:}
+% \begin{syntax}
+% \cs{msg_line_number:}
+% \end{syntax}
+% Prints the current line number when a message is given.
+% \end{function}
+%
+% \begin{function}[EXP]{\msg_fatal_text:n}
+% \begin{syntax}
+% \cs{msg_fatal_text:n} \Arg{module}
+% \end{syntax}
+% Produces the standard text
+% \begin{quote}
+% \ttfamily
+% Fatal Package \meta{module} Error
+% \end{quote}
+% This function can be redefined to alter the language in which the
+% message is given, using |#1| as the name of the \meta{module} to
+% be included.
+% \end{function}
+%
+% \begin{function}[EXP]{\msg_critical_text:n}
+% \begin{syntax}
+% \cs{msg_critical_text:n} \Arg{module}
+% \end{syntax}
+% Produces the standard text
+% \begin{quote}
+% \ttfamily
+% Critical Package \meta{module} Error
+% \end{quote}
+% This function can be redefined to alter the language in which the
+% message is given, using |#1| as the name of the \meta{module} to
+% be included.
+% \end{function}
+%
+% \begin{function}[EXP]{\msg_error_text:n}
+% \begin{syntax}
+% \cs{msg_error_text:n} \Arg{module}
+% \end{syntax}
+% Produces the standard text
+% \begin{quote}
+% \ttfamily
+% Package \meta{module} Error
+% \end{quote}
+% This function can be redefined to alter the language in which the
+% message is given, using |#1| as the name of the \meta{module} to
+% be included.
+% \end{function}
+%
+% \begin{function}[EXP]{\msg_warning_text:n}
+% \begin{syntax}
+% \cs{msg_warning_text:n} \Arg{module}
+% \end{syntax}
+% Produces the standard text
+% \begin{quote}
+% \ttfamily
+% Package \meta{module} Warning
+% \end{quote}
+% This function can be redefined to alter the language in which the
+% message is given, using |#1| as the name of the \meta{module} to
+% be included. The \meta{type} of \meta{module} may be adjusted:
+% |Package| is the standard outcome: see \cs{msg_module_type:n}.
+% \end{function}
+%
+% \begin{function}[EXP]{\msg_info_text:n}
+% \begin{syntax}
+% \cs{msg_info_text:n} \Arg{module}
+% \end{syntax}
+% Produces the standard text:
+% \begin{quote}
+% \ttfamily
+% Package \meta{module} Info
+% \end{quote}
+% This function can be redefined to alter the language in which the
+% message is given, using |#1| as the name of the \meta{module} to
+% be included. The \meta{type} of \meta{module} may be adjusted:
+% |Package| is the standard outcome: see \cs{msg_module_type:n}.
+% \end{function}
+%
+% \begin{function}[EXP,updated = 2018-09-30]{\msg_see_documentation_text:n}
+% \begin{syntax}
+% \cs{msg_see_documentation_text:n} \Arg{module}
+% \end{syntax}
+% Produces the standard text
+% \begin{quote}
+% \ttfamily
+%
+% See the \meta{module} documentation for further information.
+% \end{quote}
+% This function can be redefined to alter the language in which the
+% message is given, using |#1| as the name of the \meta{module} to
+% be included. The name of the \meta{module} is produced using
+% \cs{msg_module_name:n}.
+% \end{function}
+%
+% \section{Issuing messages}
+%
+% Messages behave differently depending on the message class. In all cases,
+% the message may be issued supplying~$0$ to~$4$ arguments. If the number of
+% arguments supplied here does not match the number in the definition of the
+% message, extra arguments are ignored, or empty arguments added (of
+% course the sense of the message may be impaired). The four arguments are
+% converted to strings before being added to the message text: the
+% \texttt{e}-type variants should be used to expand material.
+% Note that this expansion takes place with the standard definitions in
+% effect, which means that shorthands such as |\~| or |\\| are
+% \emph{not} available; instead one should use \cs{iow_char:N} |\~| and
+% \cs{iow_newline:}, respectively.
+% The following message classes exist:
+% \begin{itemize}
+% \item \texttt{fatal}, ending the \TeX{} run;
+% \item \texttt{critical}, ending the file being input;
+% \item \texttt{error}, interrupting the \TeX{} run without ending it;
+% \item \texttt{warning}, written to terminal and log file, for
+% important messages that may require corrections by the user;
+% \item \texttt{note} (less common than \texttt{info}) for important
+% information messages written to the terminal and log file;
+% \item \texttt{info} for normal information messages written to the log
+% file only;
+% \item \texttt{term} and \texttt{log} for un-decorated messages written
+% to the terminal and log file, or to the log file only;
+% \item \texttt{none} for suppressed messages.
+% \end{itemize}
+%
+% \begin{function}[updated = 2012-08-11]
+% {
+% \msg_fatal:nnnnnn ,
+% \msg_fatal:nnnnn ,
+% \msg_fatal:nnnn ,
+% \msg_fatal:nnn ,
+% \msg_fatal:nn ,
+% \msg_fatal:nnVV ,
+% \msg_fatal:nnVn ,
+% \msg_fatal:nnnV ,
+% \msg_fatal:nnV ,
+% \msg_fatal:nneeee ,
+% \msg_fatal:nneee ,
+% \msg_fatal:nnnee ,
+% \msg_fatal:nnee ,
+% \msg_fatal:nnne ,
+% \msg_fatal:nne
+% }
+% \begin{syntax}
+% \cs{msg_fatal:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \end{syntax}
+% Issues \meta{module} error \meta{message}, passing \meta{arg one} to
+% \meta{arg four} to the text-creating functions. After issuing a
+% fatal error the \TeX{} run halts. No PDF file will be produced in
+% this case (DVI mode runs may produce a truncated DVI file).
+% \end{function}
+%
+% \begin{function}[updated = 2012-08-11]
+% {
+% \msg_critical:nnnnnn ,
+% \msg_critical:nnnnn ,
+% \msg_critical:nnnn ,
+% \msg_critical:nnn ,
+% \msg_critical:nn ,
+% \msg_critical:nnVV ,
+% \msg_critical:nnVn ,
+% \msg_critical:nnnV ,
+% \msg_critical:nnV ,
+% \msg_critical:nneeee ,
+% \msg_critical:nneee ,
+% \msg_critical:nnnee ,
+% \msg_critical:nnee ,
+% \msg_critical:nnne ,
+% \msg_critical:nne
+% }
+% \begin{syntax}
+% \cs{msg_critical:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \end{syntax}
+% Issues \meta{module} error \meta{message}, passing \meta{arg one} to
+% \meta{arg four} to the text-creating functions. After issuing a
+% critical error, \TeX{} stops reading the current input file.
+% This may halt the \TeX{} run (if the current file is the main file)
+% or may abort reading a sub-file.
+% \begin{texnote}
+% The \TeX{} \tn{endinput} primitive is used to exit the file. In
+% particular, the rest of the current line remains in the input
+% stream.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[updated = 2012-08-11]
+% {
+% \msg_error:nnnnnn ,
+% \msg_error:nnnnn ,
+% \msg_error:nnnn ,
+% \msg_error:nnn ,
+% \msg_error:nn ,
+% \msg_error:nnVV ,
+% \msg_error:nnVn ,
+% \msg_error:nnnV ,
+% \msg_error:nnV ,
+% \msg_error:nneeee ,
+% \msg_error:nneee ,
+% \msg_error:nnnee ,
+% \msg_error:nnee ,
+% \msg_error:nnne ,
+% \msg_error:nne
+% }
+% \begin{syntax}
+% \cs{msg_error:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \end{syntax}
+% Issues \meta{module} error \meta{message}, passing \meta{arg one} to
+% \meta{arg four} to the text-creating functions. The error
+% interrupts processing and issues the text at the terminal. After user
+% input, the run continues.
+% \end{function}
+%
+% \begin{function}[updated = 2012-08-11]
+% {
+% \msg_warning:nnnnnn ,
+% \msg_warning:nnnnn ,
+% \msg_warning:nnnn ,
+% \msg_warning:nnn ,
+% \msg_warning:nn ,
+% \msg_warning:nnVV ,
+% \msg_warning:nnVn ,
+% \msg_warning:nnnV ,
+% \msg_warning:nnV ,
+% \msg_warning:nneeee ,
+% \msg_warning:nneee ,
+% \msg_warning:nnnee ,
+% \msg_warning:nnee ,
+% \msg_warning:nnne ,
+% \msg_warning:nne
+% }
+% \begin{syntax}
+% \cs{msg_warning:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \end{syntax}
+% Issues \meta{module} warning \meta{message}, passing \meta{arg one}
+% to \meta{arg four} to the text-creating functions. The warning text
+% is added to the log file and the terminal, but the \TeX{} run
+% is not interrupted.
+% \end{function}
+%
+% \begin{function}[added = 2021-05-18]
+% {
+% \msg_note:nnnnnn ,
+% \msg_note:nnnnn ,
+% \msg_note:nnnn ,
+% \msg_note:nnn ,
+% \msg_note:nn ,
+% \msg_note:nnVV ,
+% \msg_note:nnVn ,
+% \msg_note:nnnV ,
+% \msg_note:nnV ,
+% \msg_note:nneeee ,
+% \msg_note:nneee ,
+% \msg_note:nnnee ,
+% \msg_note:nnee ,
+% \msg_note:nnne ,
+% \msg_note:nne ,
+% \msg_info:nnnnnn ,
+% \msg_info:nnnnn ,
+% \msg_info:nnnn ,
+% \msg_info:nnn ,
+% \msg_info:nn ,
+% \msg_info:nnVV ,
+% \msg_info:nnVn ,
+% \msg_info:nnnV ,
+% \msg_info:nnV ,
+% \msg_info:nneeee ,
+% \msg_info:nneee ,
+% \msg_info:nnnee ,
+% \msg_info:nnee ,
+% \msg_info:nnne ,
+% \msg_info:nne
+% }
+% \begin{syntax}
+% \cs{msg_note:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \cs{msg_info:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \end{syntax}
+% Issues \meta{module} information \meta{message}, passing
+% \meta{arg one} to \meta{arg four} to the text-creating functions.
+% For the more common \cs{msg_info:nnnnnn}, the information text is
+% added to the log file only, while \cs{msg_note:nnnnnn} adds the
+% info text to both the log file and the terminal. The \TeX{} run is
+% not interrupted.
+% \end{function}
+%
+% \begin{function}[updated = 2012-08-11]
+% {
+% \msg_term:nnnnnn ,
+% \msg_term:nnnnn ,
+% \msg_term:nnnn ,
+% \msg_term:nnn ,
+% \msg_term:nn ,
+% \msg_term:nnVV ,
+% \msg_term:nnVn ,
+% \msg_term:nnnV ,
+% \msg_term:nnV ,
+% \msg_term:nneeee ,
+% \msg_term:nneee ,
+% \msg_term:nnnee ,
+% \msg_term:nnee ,
+% \msg_term:nnne ,
+% \msg_term:nne ,
+% \msg_log:nnnnnn ,
+% \msg_log:nnnnn ,
+% \msg_log:nnnn ,
+% \msg_log:nnn ,
+% \msg_log:nn ,
+% \msg_log:nnVV ,
+% \msg_log:nnVn ,
+% \msg_log:nnnV ,
+% \msg_log:nnV ,
+% \msg_log:nneeee ,
+% \msg_log:nneee ,
+% \msg_log:nnnee ,
+% \msg_log:nnee ,
+% \msg_log:nnne ,
+% \msg_log:nne
+% }
+% \begin{syntax}
+% \cs{msg_term:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \cs{msg_log:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \end{syntax}
+% Issues \meta{module} information \meta{message}, passing
+% \meta{arg one} to \meta{arg four} to the text-creating functions.
+% The output is briefer than \cs{msg_info:nnnnnn}, omitting for
+% instance the module name. It is added to the log file by
+% \cs{msg_log:nnnnnn} while \cs{msg_term:nnnnnn} also prints it on the
+% terminal.
+% \end{function}
+%
+% \begin{function}[updated = 2012-08-11]
+% {
+% \msg_none:nnnnnn ,
+% \msg_none:nnnnn ,
+% \msg_none:nnnn ,
+% \msg_none:nnn ,
+% \msg_none:nn ,
+% \msg_none:nnVV ,
+% \msg_none:nnVn ,
+% \msg_none:nnnV ,
+% \msg_none:nnV ,
+% \msg_none:nneeee ,
+% \msg_none:nneee ,
+% \msg_none:nnnee ,
+% \msg_none:nnee ,
+% \msg_none:nnne ,
+% \msg_none:nne
+% }
+% \begin{syntax}
+% \cs{msg_none:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \end{syntax}
+% Does nothing: used as a message class to prevent any output at
+% all (see the discussion of message redirection).
+% \end{function}
+%
+% \subsection{Messages for showing material}
+%
+% \begin{function}[added = 2017-12-04]
+% {
+% \msg_show:nnnnnn ,
+% \msg_show:nnnnn ,
+% \msg_show:nnnn ,
+% \msg_show:nnn ,
+% \msg_show:nn ,
+% \msg_show:nnVV ,
+% \msg_show:nnVn ,
+% \msg_show:nnnV ,
+% \msg_show:nnV ,
+% \msg_show:nneeee ,
+% \msg_show:nneee ,
+% \msg_show:nnnee ,
+% \msg_show:nnee ,
+% \msg_show:nnne ,
+% \msg_show:nne
+% }
+% \begin{syntax}
+% \cs{msg_show:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \end{syntax}
+% Issues \meta{module} information \meta{message}, passing \meta{arg
+% one} to \meta{arg four} to the text-creating functions. The
+% information text is shown on the terminal and the \TeX{} run is
+% interrupted in a manner similar to \cs{tl_show:n}. This is used in
+% conjunction with \cs{msg_show_item:n} and similar functions to print
+% complex variable contents completely. If the formatted text does
+% not contain |>~| at the start of a line, an additional line |>~.|
+% will be put at the end. In addition, a final period is added if not
+% present.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2017-12-04]
+% {\msg_show_item:n, \msg_show_item_unbraced:n, \msg_show_item:nn, \msg_show_item_unbraced:nn}
+% \begin{syntax}
+% \cs{seq_map_function:NN} \meta{seq} \cs{msg_show_item:n}
+% \cs{prop_map_function:NN} \meta{prop} \cs{msg_show_item:nn}
+% \end{syntax}
+% Used in the text of messages for \cs{msg_show:nnnnnn} to show or log
+% a list of items or key--value pairs. The output of
+% \cs{msg_show_item:n} produces a newline, the prefix |>|, two spaces,
+% then the braced string representation of its argument.
+% The two-argument versions separates the key and value using
+% \verb*| => |, and the \texttt{unbraced} versions don't print the
+% surrounding braces.
+%
+% These functions are suitable for usage with iterator functions like
+% \cs{seq_map_function:NN}, \cs{prop_map_function:NN}, etc. For
+% example, with a sequence \cs[no-index]{l_tmpa_seq} containing |a|,
+% |{b}| and |\c|,
+% \begin{verbatim}
+% \seq_map_function:NN \l_tmpa_seq \msg_show_item:n
+% \end{verbatim}
+% would expand to three lines:
+% \begin{quotation}
+% \noindent\verb*|> {a}|\\
+% \verb*|> {{b}}|\\
+% \verb*|> {\c }|
+% \end{quotation}
+% \end{function}
+%
+% \subsection{Expandable error messages}
+%
+% In very rare cases it may be necessary to produce errors in an
+% expansion-only context. The functions in this section should only be
+% used if there is no alternative approach using \cs{msg_error:nnnnnn}
+% or other non-expandable commands from the previous section. Despite
+% having a similar interface as non-expandable messages, expandable
+% errors must be handled internally very differently from normal error
+% messages, as none of the tools to print to the terminal or the log
+% file are expandable. As a result, short-hands such as |\{| or |\\| do
+% not work, and messages must be very short (with default settings,
+% they are truncated after approximately 50 characters). It is
+% advisable to ensure that the message is understandable even when
+% truncated, by putting the most important information up front.
+% Another particularity of expandable messages is that they
+% cannot be redirected or turned off by the user.
+%
+% \begin{function}[EXP, added = 2015-08-06, updated = 2019-02-28]
+% {
+% \msg_expandable_error:nnnnnn ,
+% \msg_expandable_error:nnnnn ,
+% \msg_expandable_error:nnnn ,
+% \msg_expandable_error:nnn ,
+% \msg_expandable_error:nn ,
+% \msg_expandable_error:nnffff ,
+% \msg_expandable_error:nnfff ,
+% \msg_expandable_error:nnff ,
+% \msg_expandable_error:nnf ,
+% }
+% \begin{syntax}
+% \cs{msg_expandable_error:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \end{syntax}
+% Issues an \enquote{Undefined error} message from \TeX{} itself
+% using the undefined control sequence \cs{???} then prints
+% \enquote{! \meta{module}: }\meta{error message}, which should be
+% short. With default settings, anything beyond approximately $60$
+% characters long (or bytes in some engines) is cropped. A leading
+% space might be removed as well.
+% \end{function}
+%
+% \section{Redirecting messages}
+%
+% Each message has a \enquote{name}, which can be used to alter the behaviour
+% of the message when it is given. Thus we might have
+% \begin{verbatim}
+% \msg_new:nnnn { module } { my-message } { Some~text } { Some~more~text }
+% \end{verbatim}
+% to define a message, with
+% \begin{verbatim}
+% \msg_error:nn { module } { my-message }
+% \end{verbatim}
+% when it is used. With no filtering, this raises an error. However, we
+% could alter the behaviour with
+% \begin{verbatim}
+% \msg_redirect_class:nn { error } { warning }
+% \end{verbatim}
+% to turn all errors into warnings, or with
+% \begin{verbatim}
+% \msg_redirect_module:nnn { module } { error } { warning }
+% \end{verbatim}
+% to alter only messages from that module, or even
+% \begin{verbatim}
+% \msg_redirect_name:nnn { module } { my-message } { warning }
+% \end{verbatim}
+% to target just one message. Redirection applies first to individual messages,
+% then to messages from one module and finally to messages of one class. Thus
+% it is possible to select out an individual message for special treatment
+% even if the entire class is already redirected.
+%
+% Multiple redirections are possible. Redirections can be cancelled by
+% providing an empty argument for the target class. Redirection to a
+% missing class raises an error immediately. Infinite loops are
+% prevented by eliminating the redirection starting from the target of
+% the redirection that caused the loop to appear. Namely, if
+% redirections are requested as $A \to B$, $B \to C$ and $C \to A$ in
+% this order, then the $A \to B$ redirection is cancelled.
+%
+% \begin{function}[updated = 2012-04-27]{\msg_redirect_class:nn}
+% \begin{syntax}
+% \cs{msg_redirect_class:nn} \Arg{class one} \Arg{class two}
+% \end{syntax}
+% Changes the behaviour of messages of \meta{class one} so that they
+% are processed using the code for those of \meta{class two}.
+% Each \meta{class} can be one of \texttt{fatal}, \texttt{critical},
+% \texttt{error}, \texttt{warning}, \texttt{note}, \texttt{info},
+% \texttt{term}, \texttt{log}, \texttt{none}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-04-27]{\msg_redirect_module:nnn}
+% \begin{syntax}
+% \cs{msg_redirect_module:nnn} \Arg{module} \Arg{class one} \Arg{class two}
+% \end{syntax}
+% Redirects message of \meta{class one} for \meta{module} to act as
+% though they were from \meta{class two}. Messages of \meta{class one}
+% from sources other than \meta{module} are not affected by this
+% redirection. This function can be used to make some messages
+% \enquote{silent} by default. For example, all of the
+% \texttt{warning} messages of \meta{module} could be turned off with:
+% \begin{verbatim}
+% \msg_redirect_module:nnn { module } { warning } { none }
+% \end{verbatim}
+% \end{function}
+%
+% \begin{function}[updated = 2012-04-27]{\msg_redirect_name:nnn}
+% \begin{syntax}
+% \cs{msg_redirect_name:nnn} \Arg{module} \Arg{message} \Arg{class}
+% \end{syntax}
+% Redirects a specific \meta{message} from a specific \meta{module} to
+% act as a member of \meta{class} of messages. No further redirection
+% is performed. This function can be used to make a selected message
+% \enquote{silent} without changing global parameters:
+% \begin{verbatim}
+% \msg_redirect_name:nnn { module } { annoying-message } { none }
+% \end{verbatim}
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3msg} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=msg>
+% \end{macrocode}
+%
+% \begin{variable}{\l_@@_internal_tl}
+% A general scratch for the module.
+% \begin{macrocode}
+\tl_new:N \l_@@_internal_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_name_str, \l_@@_text_str}
+% Used to save module info when creating messages.
+% \begin{macrocode}
+\str_new:N \l_@@_name_str
+\str_new:N \l_@@_text_str
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Internal auxiliaries}
+%
+% \begin{variable}{\s_@@_mark,\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_use_none_delimit_by_s_stop:w}
+% Functions to gobble up to a scan mark.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_none_delimit_by_s_stop:w #1 \s_@@_stop { }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Creating messages}
+%
+% Messages are created and used separately, so there two parts to
+% the code here. First, a mechanism for creating message text.
+% This is pretty simple, as there is not actually a lot to do.
+%
+% \begin{variable}{\c_@@_text_prefix_tl, \c_@@_more_text_prefix_tl}
+% Locations for the text of messages.
+% \begin{macrocode}
+\tl_const:Nn \c_@@_text_prefix_tl { msg~text~>~ }
+\tl_const:Nn \c_@@_more_text_prefix_tl { msg~extra~text~>~ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP,pTF]{\msg_if_exist:nn}
+% Test whether the control sequence containing the message text exists
+% or not.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \msg_if_exist:nn #1#2 { p , T , F , TF }
+ {
+ \cs_if_exist:cTF { \c_@@_text_prefix_tl #1 / #2 }
+ { \prg_return_true: } { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_chk_if_free:nn}
+% This auxiliary is similar to \cs{__kernel_chk_if_free_cs:N}, and is used when
+% defining messages with \cs{msg_new:nnnn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_chk_free:nn #1#2
+ {
+ \msg_if_exist:nnT {#1} {#2}
+ {
+ \msg_error:nnnn { msg } { already-defined }
+ {#1} {#2}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \msg_new:nnnn, \msg_new:nnee, \msg_new:nnxx,
+% \msg_new:nnn, \msg_new:nne, \msg_new:nnx
+% }
+% \begin{macro}{\msg_set:nnnn, \msg_set:nnn}
+% Setting a message simply means saving the appropriate text
+% into two functions. A sanity check first.
+% \begin{macrocode}
+\cs_new_protected:Npn \msg_new:nnnn #1#2#3#4
+ {
+ \@@_chk_free:nn {#1} {#2}
+ \cs_gset:cpn { \c_@@_text_prefix_tl #1 / #2 }
+ ##1##2##3##4 {#3}
+ \cs_gset:cpn { \c_@@_more_text_prefix_tl #1 / #2 }
+ ##1##2##3##4 {#4}
+ }
+\cs_generate_variant:Nn \msg_new:nnnn { nnee , nnxx }
+\cs_new_protected:Npn \msg_new:nnn #1#2#3
+ { \msg_new:nnnn {#1} {#2} {#3} { } }
+\cs_generate_variant:Nn \msg_new:nnn { nne , nnx }
+\cs_new_protected:Npn \msg_set:nnnn #1#2#3#4
+ {
+ \cs_set:cpn { \c_@@_text_prefix_tl #1 / #2 }
+ ##1##2##3##4 {#3}
+ \cs_set:cpn { \c_@@_more_text_prefix_tl #1 / #2 }
+ ##1##2##3##4 {#4}
+ }
+\cs_new_protected:Npn \msg_set:nnn #1#2#3
+ { \msg_set:nnnn {#1} {#2} {#3} { } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Messages: support functions and text}
+%
+% \begin{variable}
+% {
+% \c_@@_coding_error_text_tl ,
+% \c_@@_continue_text_tl ,
+% \c_@@_critical_text_tl ,
+% \c_@@_fatal_text_tl ,
+% \c_@@_help_text_tl ,
+% \c_@@_no_info_text_tl ,
+% \c_@@_on_line_text_tl ,
+% \c_@@_return_text_tl ,
+% \c_@@_trouble_text_tl
+% }
+% Simple pieces of text for messages.
+% \begin{macrocode}
+\tl_const:Nn \c_@@_coding_error_text_tl
+ {
+ This~is~a~coding~error.
+ \\ \\
+ }
+\tl_const:Nn \c_@@_continue_text_tl
+ { Type~<return>~to~continue }
+\tl_const:Nn \c_@@_critical_text_tl
+ { Reading~the~current~file~'\g_file_curr_name_str'~will~stop. }
+\tl_const:Nn \c_@@_fatal_text_tl
+ { This~is~a~fatal~error:~LaTeX~will~abort. }
+\tl_const:Nn \c_@@_help_text_tl
+ { For~immediate~help~type~H~<return> }
+\tl_const:Nn \c_@@_no_info_text_tl
+ {
+ LaTeX~does~not~know~anything~more~about~this~error,~sorry.
+ \c_@@_return_text_tl
+ }
+\tl_const:Nn \c_@@_on_line_text_tl { on~line }
+\tl_const:Nn \c_@@_return_text_tl
+ {
+ \\ \\
+ Try~typing~<return>~to~proceed.
+ \\
+ If~that~doesn't~work,~type~X~<return>~to~quit.
+ }
+\tl_const:Nn \c_@@_trouble_text_tl
+ {
+ \\ \\
+ More~errors~will~almost~certainly~follow: \\
+ the~LaTeX~run~should~be~aborted.
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\msg_line_number:, \msg_line_context:}
+% For writing the line number nicely. \cs{msg_line_context:} was set up
+% earlier, so this is not \texttt{new}.
+% \begin{macrocode}
+\cs_new:Npn \msg_line_number: { \int_use:N \tex_inputlineno:D }
+\cs_gset:Npn \msg_line_context:
+ {
+ \c_@@_on_line_text_tl
+ \c_space_tl
+ \msg_line_number:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Showing messages: low level mechanism}
+%
+% \begin{macro}{\@@_interrupt:Nnnn}
+% \begin{macro}{\@@_no_more_text:nnnn}
+% The low-level interruption macro is rather opaque, unfortunately.
+% Depending on the availability of more information there is a choice
+% of how to set up the further help. We feed the extra help text and
+% the message itself to a wrapping auxiliary, in this order because we
+% must first setup \TeX{}'s \tn{errhelp} register before issuing an
+% \tn{errmessage}. To deal with the various cases of critical or fatal
+% errors with and without help text, there is a bit of argument-passing
+% to do.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_interrupt:NnnnN #1#2#3#4#5
+ {
+ \str_set:Ne \l_@@_text_str { #1 {#2} }
+ \str_set:Ne \l_@@_name_str { \msg_module_name:n {#2} }
+ \cs_if_eq:cNTF
+ { \c_@@_more_text_prefix_tl #2 / #3 }
+ \@@_no_more_text:nnnn
+ {
+ \@@_interrupt_wrap:nnn
+ { \use:c { \c_@@_text_prefix_tl #2 / #3 } #4 }
+ { \c_@@_continue_text_tl }
+ {
+ \c_@@_no_info_text_tl
+ \tl_if_empty:NF #5
+ { \\ \\ #5 }
+ }
+ }
+ {
+ \@@_interrupt_wrap:nnn
+ { \use:c { \c_@@_text_prefix_tl #2 / #3 } #4 }
+ { \c_@@_help_text_tl }
+ {
+ \use:c { \c_@@_more_text_prefix_tl #2 / #3 } #4
+ \tl_if_empty:NF #5
+ { \\ \\ #5 }
+ }
+ }
+ }
+\cs_new:Npn \@@_no_more_text:nnnn #1#2#3#4 { }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_interrupt_wrap:nnn}
+% \begin{macro}{\@@_interrupt_text:n, \@@_interrupt_more_text:n}
+% First setup \TeX{}'s \tn{errhelp} register with the extra help |#1|,
+% then build a nice-looking error message with |#2|. Everything is
+% done using \texttt{e}-type expansion as the new line markers are
+% different for the two type of text and need to be correctly set up.
+% The auxiliary \cs{@@_interrupt_more_text:n} receives its argument
+% as a line-wrapped string, which is thus unaffected by expansion.
+% We ave to split the main text into two parts as only the \enquote{message}
+% itself is wrapped with a leader: the generic help is wrapped at full
+% width. We also have to allow for the two characters used by \tn{errmessage}
+% itself.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_interrupt_wrap:nnn #1#2#3
+ {
+ \iow_wrap:nnnN { \\ #3 } { } { } \@@_interrupt_more_text:n
+ \group_begin:
+ \int_sub:Nn \l_iow_line_count_int { 2 }
+ \iow_wrap:nenN { \l_@@_text_str : ~ #1 }
+ {
+ ( \l_@@_name_str )
+ \prg_replicate:nn
+ {
+ \str_count:N \l_@@_text_str
+ - \str_count:N \l_@@_name_str
+ + 2
+ }
+ { ~ }
+ }
+ { } \@@_interrupt_text:n
+ \iow_wrap:nnnN { \l_@@_internal_tl \\ \\ #2 } { } { }
+ \@@_interrupt:n
+ }
+\cs_new_protected:Npn \@@_interrupt_text:n #1
+ {
+ \group_end:
+ \tl_set:Nn \l_@@_internal_tl {#1}
+ }
+\cs_new_protected:Npn \@@_interrupt_more_text:n #1
+ { \exp_args:Ne \tex_errhelp:D { #1 \iow_newline: } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_interrupt:n}
+% The business end of the process starts by producing some visual
+% separation of the message from the main part of the log. The error
+% message needs to be printed with everything made
+% \enquote{invisible}: \TeX{}'s own information involves the macro in
+% which \tn{errmessage} is called, and the end of the argument of the
+% \tn{errmessage}, including the closing brace. We use an active |!|
+% to call the \tn{errmessage} primitive, and end its argument with
+% \cs{use_none:n} \Arg{spaces} which fills the output with spaces. Two
+% trailing closing braces are turned into spaces to hide them as well.
+% The group in which we alter the definition of the active |!| is
+% closed before producing the message: this ensures that tokens
+% inserted by typing |I| in the command-line are inserted after
+% the message is entirely cleaned up.
+%
+% The \cs{__kernel_iow_with:Nnn} auxiliary, defined in \pkg{l3file}, expects
+% an \meta{integer variable}, an integer \meta{value}, and some
+% \meta{code}. It runs the \meta{code} after ensuring that the
+% \meta{integer variable} takes the given \meta{value}, then restores
+% the former value of the \meta{integer variable} if needed. We use
+% it to ensure that the \tn{newlinechar} is $10$, as needed for
+% \cs{iow_newline:} to work, and that \tn{errorcontextlines} is $-1$,
+% to avoid showing irrelevant context. Note that restoring the former
+% value of these integers requires inserting tokens after the
+% \tn{errmessage}, which go in the way of tokens which could be
+% inserted by the user. This is unavoidable.
+% \begin{macrocode}
+\group_begin:
+ \char_set_lccode:nn { 38 } { 32 } % &
+ \char_set_lccode:nn { 46 } { 32 } % .
+ \char_set_lccode:nn { 123 } { 32 } % {
+ \char_set_lccode:nn { 125 } { 32 } % }
+ \char_set_catcode_active:N \&
+\tex_lowercase:D
+ {
+ \group_end:
+ \cs_new_protected:Npn \@@_interrupt:n #1
+ {
+ \iow_term:n { }
+ \__kernel_iow_with:Nnn \tex_newlinechar:D { `\^^J }
+ {
+ \__kernel_iow_with:Nnn \tex_errorcontextlines:D { -1 }
+ {
+ \group_begin:
+ \cs_set_protected:Npn &
+ {
+ \tex_errmessage:D
+ {
+ #1
+ \use_none:n
+ { ............................................ }
+ }
+ }
+ \exp_after:wN
+ \group_end:
+ &
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Displaying messages}
+%
+% \LaTeX{} is handling error messages and so the \TeX{} ones are disabled.
+% \begin{macrocode}
+\int_gset:Nn \tex_errorcontextlines:D { -1 }
+% \end{macrocode}
+%
+% \begin{macro}[EXP]
+% {
+% \msg_fatal_text:n ,
+% \msg_critical_text:n ,
+% \msg_error_text:n ,
+% \msg_warning_text:n ,
+% \msg_info_text:n
+% }
+% \begin{macro}[EXP]{\@@_text:nn}
+% \begin{macro}[EXP]{\@@_text:n}
+% A function for issuing messages: both the text and order could
+% in principle vary. The module name may be empty for kernel messages,
+% hence the slightly contorted code path for a space.
+% \begin{macrocode}
+\cs_new:Npn \msg_fatal_text:n #1
+ {
+ Fatal ~
+ \msg_error_text:n {#1}
+ }
+\cs_new:Npn \msg_critical_text:n #1
+ {
+ Critical ~
+ \msg_error_text:n {#1}
+ }
+\cs_new:Npn \msg_error_text:n #1
+ { \@@_text:nn {#1} { Error } }
+\cs_new:Npn \msg_warning_text:n #1
+ { \@@_text:nn {#1} { Warning } }
+\cs_new:Npn \msg_info_text:n #1
+ { \@@_text:nn {#1} { Info } }
+\cs_new:Npn \@@_text:nn #1#2
+ {
+ \exp_args:Nf \@@_text:n { \msg_module_type:n {#1} }
+ \exp_args:Nf \@@_text:n { \msg_module_name:n {#1} }
+ #2
+ }
+\cs_new:Npn \@@_text:n #1
+ {
+ \tl_if_blank:nF {#1}
+ { #1 ~ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\g_msg_module_name_prop, \g_msg_module_type_prop}
+% For storing public module information: the kernel data is set up
+% in advance.
+% \begin{macrocode}
+\prop_new:N \g_msg_module_name_prop
+\prop_new:N \g_msg_module_type_prop
+\prop_gput:Nnn \g_msg_module_type_prop { LaTeX } { }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\msg_module_type:n}
+% Contextual footer information, with the potential to give modules an
+% alternative name.
+% \begin{macrocode}
+\cs_new:Npn \msg_module_type:n #1
+ {
+ \prop_if_in:NnTF \g_msg_module_type_prop {#1}
+ { \prop_item:Nn \g_msg_module_type_prop {#1} }
+ { Package }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\msg_module_name:n, \msg_see_documentation_text:n}
+% Contextual footer information, with the potential to give modules an
+% alternative name.
+% \begin{macrocode}
+\cs_new:Npn \msg_module_name:n #1
+ {
+ \prop_if_in:NnTF \g_msg_module_name_prop {#1}
+ { \prop_item:Nn \g_msg_module_name_prop {#1} }
+ {#1}
+ }
+\cs_new:Npn \msg_see_documentation_text:n #1
+ {
+ See~the~ \msg_module_name:n {#1} ~
+ documentation~for~further~information.
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_class_new:nn}
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \@@_class_new:nn #1#2
+ {
+ \prop_new:c { l_@@_redirect_ #1 _prop }
+ \cs_new_protected:cpn { @@_ #1 _code:nnnnnn }
+ ##1##2##3##4##5##6 {#2}
+ \cs_new_protected:cpn { msg_ #1 :nnnnnn } ##1##2##3##4##5##6
+ {
+ \use:e
+ {
+ \exp_not:n { \@@_use:nnnnnnn {#1} {##1} {##2} }
+ { \tl_to_str:n {##3} } { \tl_to_str:n {##4} }
+ { \tl_to_str:n {##5} } { \tl_to_str:n {##6} }
+ }
+ }
+ \cs_new_protected:cpe { msg_ #1 :nnnnn } ##1##2##3##4##5
+ { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} {##3} {##4} {##5} { } }
+ \cs_new_protected:cpe { msg_ #1 :nnnn } ##1##2##3##4
+ { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} {##3} {##4} { } { } }
+ \cs_new_protected:cpe { msg_ #1 :nnn } ##1##2##3
+ { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} {##3} { } { } { } }
+ \cs_new_protected:cpe { msg_ #1 :nn } ##1##2
+ { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} { } { } { } { } }
+ \cs_generate_variant:cn { msg_ #1 :nnn }
+ { nnV , nne , nnx }
+ \cs_generate_variant:cn { msg_ #1 :nnnn }
+ { nnVV , nnVn , nnnV , nnne , nnnx , nnee , nnxx }
+ \cs_generate_variant:cn { msg_ #1 :nnnnn }
+ { nnnee , nnnxx , nneee , nnxxx }
+ \cs_generate_variant:cn { msg_ #1 :nnnnnn } { nneeee , nnxxxx }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \msg_fatal:nnnnnn ,
+% \msg_fatal:nnnnn ,
+% \msg_fatal:nnnn ,
+% \msg_fatal:nnn ,
+% \msg_fatal:nn ,
+% \msg_fatal:nnVV ,
+% \msg_fatal:nnVn ,
+% \msg_fatal:nnnV ,
+% \msg_fatal:nnV ,
+% \msg_fatal:nneeee ,
+% \msg_fatal:nneee ,
+% \msg_fatal:nnxxxx ,
+% \msg_fatal:nnxxx ,
+% \msg_fatal:nnnee ,
+% \msg_fatal:nnee ,
+% \msg_fatal:nnnxx ,
+% \msg_fatal:nnxx ,
+% \msg_fatal:nnnx ,
+% \msg_fatal:nnne ,
+% \msg_fatal:nne ,
+% \msg_fatal:nnx
+% }
+% \begin{macro}{\@@_fatal_exit:}
+% For fatal errors, after the error message \TeX{} bails out. We force
+% a bail out rather than using \tn{end} as this means it does not
+% matter if we are in a context where normally the run cannot end.
+% \begin{macrocode}
+ \@@_class_new:nn { fatal }
+ {
+ \@@_interrupt:NnnnN
+ \msg_fatal_text:n {#1} {#2}
+ { {#3} {#4} {#5} {#6} }
+ \c_@@_fatal_text_tl
+ \@@_fatal_exit:
+ }
+ \cs_new_protected:Npn \@@_fatal_exit:
+ {
+ \tex_batchmode:D
+ \tex_read:D -1 to \l_@@_internal_tl
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \msg_critical:nnnnnn ,
+% \msg_critical:nnnnn ,
+% \msg_critical:nnnn ,
+% \msg_critical:nnn ,
+% \msg_critical:nn ,
+% \msg_critical:nnVV ,
+% \msg_critical:nnVn ,
+% \msg_critical:nnnV ,
+% \msg_critical:nnV ,
+% \msg_critical:nneeee ,
+% \msg_critical:nneee ,
+% \msg_critical:nnxxxx ,
+% \msg_critical:nnxxx ,
+% \msg_critical:nnnee ,
+% \msg_critical:nnee ,
+% \msg_critical:nnnxx ,
+% \msg_critical:nnxx ,
+% \msg_critical:nnnx ,
+% \msg_critical:nnne ,
+% \msg_critical:nne ,
+% \msg_critical:nnx
+% }
+% Not quite so bad: just end the current file.
+% \begin{macrocode}
+ \@@_class_new:nn { critical }
+ {
+ \@@_interrupt:NnnnN
+ \msg_critical_text:n {#1} {#2}
+ { {#3} {#4} {#5} {#6} }
+ \c_@@_critical_text_tl
+ \tex_endinput:D
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \msg_error:nnnnnn ,
+% \msg_error:nnnnn ,
+% \msg_error:nnnn ,
+% \msg_error:nnn ,
+% \msg_error:nn ,
+% \msg_error:nnVV ,
+% \msg_error:nnVn ,
+% \msg_error:nnnV ,
+% \msg_error:nnV ,
+% \msg_error:nneeee ,
+% \msg_error:nneee ,
+% \msg_error:nnxxxx ,
+% \msg_error:nnxxx ,
+% \msg_error:nnnee ,
+% \msg_error:nnee ,
+% \msg_error:nnnxx ,
+% \msg_error:nnxx ,
+% \msg_error:nnnx ,
+% \msg_error:nnne ,
+% \msg_error:nne ,
+% \msg_error:nnx
+% }
+% For an error, the interrupt routine is called. We check if there is
+% a \enquote{more text} by comparing that control sequence with a
+% permanently empty text. We have to undefine the bootstrap versions
+% here.
+% \begin{macrocode}
+ \cs_undefine:N \msg_error:nnee
+ \cs_undefine:N \msg_error:nne
+ \cs_undefine:N \msg_error:nn
+ \@@_class_new:nn { error }
+ {
+ \@@_interrupt:NnnnN
+ \msg_error_text:n {#1} {#2}
+ { {#3} {#4} {#5} {#6} }
+ \c_empty_tl
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_info_aux:NNnnnnnn ,
+% \msg_warning:nnnnnn ,
+% \msg_warning:nnnnn ,
+% \msg_warning:nnnn ,
+% \msg_warning:nnn ,
+% \msg_warning:nn ,
+% \msg_warning:nnVV ,
+% \msg_warning:nnVn ,
+% \msg_warning:nnnV ,
+% \msg_warning:nnV ,
+% \msg_warning:nneeee ,
+% \msg_warning:nneee ,
+% \msg_warning:nnxxxx ,
+% \msg_warning:nnxxx ,
+% \msg_warning:nnnee ,
+% \msg_warning:nnee ,
+% \msg_warning:nnnxx ,
+% \msg_warning:nnxx ,
+% \msg_warning:nnnx ,
+% \msg_warning:nnne ,
+% \msg_warning:nne ,
+% \msg_warning:nnx ,
+% \msg_note:nnnnnn ,
+% \msg_note:nnnnn ,
+% \msg_note:nnnn ,
+% \msg_note:nnn ,
+% \msg_note:nn ,
+% \msg_note:nnVV ,
+% \msg_note:nnVn ,
+% \msg_note:nnnV ,
+% \msg_note:nnV ,
+% \msg_note:nneeee ,
+% \msg_note:nneee ,
+% \msg_note:nnxxxx ,
+% \msg_note:nnxxx ,
+% \msg_note:nnnee ,
+% \msg_note:nnee ,
+% \msg_note:nnnxx ,
+% \msg_note:nnxx ,
+% \msg_note:nnnx ,
+% \msg_note:nnne ,
+% \msg_note:nne ,
+% \msg_note:nnx ,
+% \msg_info:nnnnnn ,
+% \msg_info:nnnnn ,
+% \msg_info:nnnn ,
+% \msg_info:nnn ,
+% \msg_info:nn ,
+% \msg_info:nnVV ,
+% \msg_info:nnVn ,
+% \msg_info:nnnV ,
+% \msg_info:nnV ,
+% \msg_info:nneeee ,
+% \msg_info:nneee ,
+% \msg_info:nnxxxx ,
+% \msg_info:nnxxx ,
+% \msg_info:nnnee ,
+% \msg_info:nnee ,
+% \msg_info:nnnxx ,
+% \msg_info:nnxx ,
+% \msg_info:nnnx ,
+% \msg_info:nnne ,
+% \msg_info:nne ,
+% \msg_info:nnx
+% }
+% Warnings and information messages have no decoration. Warnings are
+% printed to the terminal while information can either go to the log
+% or both log and terminal.
+% \begin{macrocode}
+ \cs_new_protected:Npn \@@_info_aux:NNnnnnnn #1#2#3#4#5#6#7#8
+ {
+ \str_set:Ne \l_@@_text_str { #2 {#3} }
+ \str_set:Ne \l_@@_name_str { \msg_module_name:n {#3} }
+ #1 { }
+ \iow_wrap:nenN
+ {
+ \l_@@_text_str : ~
+ \use:c { \c_@@_text_prefix_tl #3 / #4 } {#5} {#6} {#7} {#8}
+ }
+ {
+ ( \l_@@_name_str )
+ \prg_replicate:nn
+ {
+ \str_count:N \l_@@_text_str
+ - \str_count:N \l_@@_name_str
+ }
+ { ~ }
+ }
+ { } #1
+ #1 { }
+ }
+ \@@_class_new:nn { warning }
+ {
+ \@@_info_aux:NNnnnnnn \iow_term:n \msg_warning_text:n
+ {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+ \@@_class_new:nn { note }
+ {
+ \@@_info_aux:NNnnnnnn \iow_term:n \msg_info_text:n
+ {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+ \@@_class_new:nn { info }
+ {
+ \@@_info_aux:NNnnnnnn \iow_log:n \msg_info_text:n
+ {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \msg_term:nnnnnn ,
+% \msg_term:nnnnn ,
+% \msg_term:nnnn ,
+% \msg_term:nnn ,
+% \msg_term:nn ,
+% \msg_term:nnVV ,
+% \msg_term:nnVn ,
+% \msg_term:nnnV ,
+% \msg_term:nnV ,
+% \msg_term:nneeee ,
+% \msg_term:nneee ,
+% \msg_term:nnxxxx ,
+% \msg_term:nnxxx ,
+% \msg_term:nnnee ,
+% \msg_term:nnee ,
+% \msg_term:nnnxx ,
+% \msg_term:nnxx ,
+% \msg_term:nnnx ,
+% \msg_term:nnne ,
+% \msg_term:nne ,
+% \msg_term:nnx ,
+% \msg_log:nnnnnn ,
+% \msg_log:nnnnn ,
+% \msg_log:nnnn ,
+% \msg_log:nnn ,
+% \msg_log:nn ,
+% \msg_log:nnVV ,
+% \msg_log:nnVn ,
+% \msg_log:nnnV ,
+% \msg_log:nnV ,
+% \msg_log:nneeee ,
+% \msg_log:nneee ,
+% \msg_log:nnxxxx ,
+% \msg_log:nnxxx ,
+% \msg_log:nnnee ,
+% \msg_log:nnee ,
+% \msg_log:nnnxx ,
+% \msg_log:nnxx ,
+% \msg_log:nnnx ,
+% \msg_log:nnne ,
+% \msg_log:nne ,
+% \msg_log:nnx
+% }
+% \enquote{Log} data is very similar to information, but with no extras
+% added.
+% \enquote{Term} is used for communicating with the user through the
+% terminal, like diagnostic messages, and debugging. This is similar
+% to \enquote{log} messages, but uses the terminal output.
+% \begin{macrocode}
+ \@@_class_new:nn { log }
+ {
+ \iow_wrap:nnnN
+ { \use:c { \c_@@_text_prefix_tl #1 / #2 } {#3} {#4} {#5} {#6} }
+ { } { } \iow_log:n
+ }
+ \@@_class_new:nn { term }
+ {
+ \iow_wrap:nnnN
+ { \use:c { \c_@@_text_prefix_tl #1 / #2 } {#3} {#4} {#5} {#6} }
+ { } { } \iow_term:n
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \msg_none:nnnnnn ,
+% \msg_none:nnnnn ,
+% \msg_none:nnnn ,
+% \msg_none:nnn ,
+% \msg_none:nn ,
+% \msg_none:nnVV ,
+% \msg_none:nnVn ,
+% \msg_none:nnnV ,
+% \msg_none:nnV ,
+% \msg_none:nneeee ,
+% \msg_none:nneee ,
+% \msg_none:nnxxxx ,
+% \msg_none:nnxxx ,
+% \msg_none:nnnee ,
+% \msg_none:nnee ,
+% \msg_none:nnnxx ,
+% \msg_none:nnxx ,
+% \msg_none:nnnx ,
+% \msg_none:nnne ,
+% \msg_none:nne ,
+% \msg_none:nnx
+% }
+% The \texttt{none} message type is needed so that input can be gobbled.
+% \begin{macrocode}
+ \@@_class_new:nn { none } { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \msg_show:nnnnnn ,
+% \msg_show:nnnnn ,
+% \msg_show:nnnn ,
+% \msg_show:nnn ,
+% \msg_show:nn ,
+% \msg_show:nnVV ,
+% \msg_show:nnVn ,
+% \msg_show:nnnV ,
+% \msg_show:nnV ,
+% \msg_show:nneeee ,
+% \msg_show:nneee ,
+% \msg_show:nnxxxx ,
+% \msg_show:nnxxx ,
+% \msg_show:nnnee ,
+% \msg_show:nnee ,
+% \msg_show:nnnxx ,
+% \msg_show:nnxx ,
+% \msg_show:nnnx ,
+% \msg_show:nnne ,
+% \msg_show:nne ,
+% \msg_show:nnx
+% }
+% \begin{macro}{\@@_show:n, \@@_show:w, \@@_show_dot:w, \@@_show:nn}
+% The \texttt{show} message type is used for \cs{seq_show:N} and
+% similar complicated data structures. Wrap the given text with a
+% trailing dot (important later) then pass it to \cs{@@_show:n}. If
+% there is |\\>~| (or if the whole thing starts with |>~|) we split
+% there, print the first part and show the second part using
+% \tn{showtokens} (the \cs{exp_after:wN} ensure a nice display). Note
+% that this primitive adds a leading |>~| and trailing dot. That is
+% why we included a trailing dot before wrapping and removed it
+% afterwards. If there is no |\\>~| do the same but with an empty
+% second part which adds a spurious but inevitable |>~.|
+% \begin{macrocode}
+ \@@_class_new:nn { show }
+ {
+ \iow_wrap:nnnN
+ { \use:c { \c_@@_text_prefix_tl #1 / #2 } {#3} {#4} {#5} {#6} }
+ { } { } \@@_show:n
+ }
+ \cs_new_protected:Npn \@@_show:n #1
+ {
+ \tl_if_in:nnTF { ^^J #1 } { ^^J > ~ }
+ {
+ \tl_if_in:nnTF { #1 \s_@@_mark } { . \s_@@_mark }
+ { \@@_show_dot:w } { \@@_show:w }
+ ^^J #1 \s_@@_stop
+ }
+ { \@@_show:nn { ? #1 } { } }
+ }
+ \cs_new:Npn \@@_show_dot:w #1 ^^J > ~ #2 . \s_@@_stop
+ { \@@_show:nn {#1} {#2} }
+ \cs_new:Npn \@@_show:w #1 ^^J > ~ #2 \s_@@_stop
+ { \@@_show:nn {#1} {#2} }
+ \cs_new_protected:Npn \@@_show:nn #1#2
+ {
+ \tl_if_empty:nF {#1}
+ { \exp_args:No \iow_term:n { \use_none:n #1 } }
+ \tl_set:Nn \l_@@_internal_tl {#2}
+ \__kernel_iow_with:Nnn \tex_newlinechar:D { 10 }
+ {
+ \__kernel_iow_with:Nnn \tex_errorcontextlines:D { -1 }
+ {
+ \tex_showtokens:D \exp_after:wN \exp_after:wN \exp_after:wN
+ { \exp_after:wN \l_@@_internal_tl }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% End the group to eliminate \cs{@@_class_new:nn}.
+% \begin{macrocode}
+\group_end:
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\msg_show_item:n}
+% \begin{macro}[EXP]{\msg_show_item_unbraced:n}
+% \begin{macro}[EXP]{\msg_show_item:nn}
+% \begin{macro}[EXP]{\msg_show_item_unbraced:nn}
+% Each item in the variable is formatted using one of the following
+% functions. We cannot use |\\| and so on because these short-hands
+% cannot be used inside the arguments of messages, only when defining
+% the messages. We need to use |^^J| here directly as \pkg{l3file} is
+% not yet loaded.
+% \begin{macrocode}
+\cs_new:Npe \msg_show_item:n #1
+ { ^^J > ~ \c_space_tl \exp_not:N \tl_to_str:n { {#1} } }
+\cs_new:Npe \msg_show_item_unbraced:n #1
+ { ^^J > ~ \c_space_tl \exp_not:N \tl_to_str:n {#1} }
+\cs_new:Npe \msg_show_item:nn #1#2
+ {
+ ^^J > \use:nn { ~ } { ~ }
+ \exp_not:N \tl_to_str:n { {#1} }
+ \use:nn { ~ } { ~ } => \use:nn { ~ } { ~ }
+ \exp_not:N \tl_to_str:n { {#2} }
+ }
+\cs_new:Npe \msg_show_item_unbraced:nn #1#2
+ {
+ ^^J > \use:nn { ~ } { ~ }
+ \exp_not:N \tl_to_str:n {#1}
+ \use:nn { ~ } { ~ } => \use:nn { ~ } { ~ }
+ \exp_not:N \tl_to_str:n {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_class_chk_exist:nT}
+% Checking that a message class exists. We build this from
+% \cs{cs_if_free:cTF} rather than \cs{cs_if_exist:cTF} because that
+% avoids reading the second argument earlier than necessary.
+% \begin{macrocode}
+\cs_new:Npn \@@_class_chk_exist:nT #1
+ {
+ \cs_if_free:cTF { @@_ #1 _code:nnnnnn }
+ { \msg_error:nnn { msg } { class-unknown } {#1} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_class_tl, \l_@@_current_class_tl}
+% Support variables needed for the redirection system.
+% \begin{macrocode}
+\tl_new:N \l_@@_class_tl
+\tl_new:N \l_@@_current_class_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_redirect_prop}
+% For redirection of individually-named messages
+% \begin{macrocode}
+\prop_new:N \l_@@_redirect_prop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_hierarchy_seq}
+% During redirection, split the message name into a sequence:
+% |{/module/submodule}|, |{/module}|, and |{}|.
+% \begin{macrocode}
+\seq_new:N \l_@@_hierarchy_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_class_loop_seq}
+% Classes encountered when following redirections to check for loops.
+% \begin{macrocode}
+\seq_new:N \l_@@_class_loop_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_use:nnnnnnn}
+% \begin{macro}
+% {
+% \@@_use_redirect_name:n , \@@_use_hierarchy:nwwN ,
+% \@@_use_redirect_module:n, \@@_use_code:
+% }
+% Actually using a message is a multi-step process. First, some
+% safety checks on the message and class requested. The code and
+% arguments are then stored to avoid passing them around. The
+% assignment to \cs{@@_use_code:} is similar to \cs{tl_set:Nn}.
+% The message is eventually produced with whatever \cs{l_@@_class_tl}
+% is when \cs{@@_use_code:} is called.
+% Here is also a good place to suppress tracing output if the
+% \pkg{trace} package is loaded since all (non-expandable) messages go
+% through this auxiliary.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_use:nnnnnnn #1#2#3#4#5#6#7
+ {
+ \cs_if_exist_use:N \conditionally@traceoff
+ \msg_if_exist:nnTF {#2} {#3}
+ {
+ \@@_class_chk_exist:nT {#1}
+ {
+ \tl_set:Nn \l_@@_current_class_tl {#1}
+ \cs_set_protected:Npe \@@_use_code:
+ {
+ \exp_not:n
+ {
+ \use:c { @@_ \l_@@_class_tl _code:nnnnnn }
+ {#2} {#3} {#4} {#5} {#6} {#7}
+ }
+ }
+ \@@_use_redirect_name:n { #2 / #3 }
+ }
+ }
+ { \msg_error:nnnn { msg } { unknown } {#2} {#3} }
+ \cs_if_exist_use:N \conditionally@traceon
+ }
+\cs_new_protected:Npn \@@_use_code: { }
+% \end{macrocode}
+% The first check is for a individual message redirection. If this
+% applies then no further redirection is attempted. Otherwise, split
+% the message name into \meta{module}, \meta{submodule} and \meta{message}
+% (with an
+% arbitrary number of slashes), and store |{/module/submodule}|,
+% |{/module}| and |{}| into \cs{l_@@_hierarchy_seq}. We then
+% map through this sequence, applying the most specific redirection.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_use_redirect_name:n #1
+ {
+ \prop_get:NnNTF \l_@@_redirect_prop { / #1 } \l_@@_class_tl
+ { \@@_use_code: }
+ {
+ \seq_clear:N \l_@@_hierarchy_seq
+ \@@_use_hierarchy:nwwN { }
+ #1 \s_@@_mark \@@_use_hierarchy:nwwN
+ / \s_@@_mark \@@_use_none_delimit_by_s_stop:w
+ \s_@@_stop
+ \@@_use_redirect_module:n { }
+ }
+ }
+\cs_new_protected:Npn \@@_use_hierarchy:nwwN #1#2 / #3 \s_@@_mark #4
+ {
+ \seq_put_left:Nn \l_@@_hierarchy_seq {#1}
+ #4 { #1 / #2 } #3 \s_@@_mark #4
+ }
+% \end{macrocode}
+% At this point, the items of \cs{l_@@_hierarchy_seq} are the
+% various levels at which we should look for a redirection.
+% Redirections which are less specific than the argument of
+% \cs{@@_use_redirect_module:n} are not attempted. This argument is
+% empty for a class redirection, \texttt{/module} for a module
+% redirection, \emph{etc.} Loop through the sequence to find the most
+% specific redirection, with module |##1|. The loop is interrupted
+% after testing for a redirection for |##1| equal to the argument |#1|
+% (least specific redirection allowed). When a redirection is found,
+% break the mapping, then if the redirection targets the same class,
+% output the code with that class, and otherwise set the target as the
+% new current class, and search for further redirections. Those
+% redirections should be at least as specific as |##1|.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_use_redirect_module:n #1
+ {
+ \seq_map_inline:Nn \l_@@_hierarchy_seq
+ {
+ \prop_get:cnNTF { l_@@_redirect_ \l_@@_current_class_tl _prop }
+ {##1} \l_@@_class_tl
+ {
+ \seq_map_break:n
+ {
+ \tl_if_eq:NNTF \l_@@_current_class_tl \l_@@_class_tl
+ { \@@_use_code: }
+ {
+ \tl_set_eq:NN \l_@@_current_class_tl \l_@@_class_tl
+ \@@_use_redirect_module:n {##1}
+ }
+ }
+ }
+ {
+ \str_if_eq:nnT {##1} {#1}
+ {
+ \tl_set_eq:NN \l_@@_class_tl \l_@@_current_class_tl
+ \seq_map_break:n { \@@_use_code: }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\msg_redirect_name:nnn}
+% Named message always use the given class even if that class is
+% redirected further. An empty target class cancels any existing
+% redirection for that message.
+% \begin{macrocode}
+\cs_new_protected:Npn \msg_redirect_name:nnn #1#2#3
+ {
+ \tl_if_empty:nTF {#3}
+ { \prop_remove:Nn \l_@@_redirect_prop { / #1 / #2 } }
+ {
+ \@@_class_chk_exist:nT {#3}
+ { \prop_put:Nnn \l_@@_redirect_prop { / #1 / #2 } {#3} }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\msg_redirect_class:nn, \msg_redirect_module:nnn}
+% \begin{macro}{\@@_redirect:nnn, \@@_redirect_loop_chk:nnn}
+% \begin{macro}{\@@_redirect_loop_list:n}
+% If the target class is empty, eliminate the corresponding
+% redirection. Otherwise, add the redirection. We must then check
+% for a loop: as an initialization, we start by storing the initial
+% class in \cs{l_@@_current_class_tl}.
+% \begin{macrocode}
+\cs_new_protected:Npn \msg_redirect_class:nn
+ { \@@_redirect:nnn { } }
+\cs_new_protected:Npn \msg_redirect_module:nnn #1
+ { \@@_redirect:nnn { / #1 } }
+\cs_new_protected:Npn \@@_redirect:nnn #1#2#3
+ {
+ \@@_class_chk_exist:nT {#2}
+ {
+ \tl_if_empty:nTF {#3}
+ { \prop_remove:cn { l_@@_redirect_ #2 _prop } {#1} }
+ {
+ \@@_class_chk_exist:nT {#3}
+ {
+ \prop_put:cnn { l_@@_redirect_ #2 _prop } {#1} {#3}
+ \tl_set:Nn \l_@@_current_class_tl {#2}
+ \seq_clear:N \l_@@_class_loop_seq
+ \@@_redirect_loop_chk:nnn {#2} {#3} {#1}
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% Since multiple redirections can only happen with increasing
+% specificity, a loop requires that all steps are of the same
+% specificity. The new redirection can thus only create a loop with
+% other redirections for the exact same module, |#1|, and not
+% submodules. After some initialization above, follow redirections
+% with \cs{l_@@_class_tl}, and keep track in
+% \cs{l_@@_class_loop_seq} of the various classes encountered. A
+% redirection from a class to itself, or the absence of redirection
+% both mean that there is no loop. A redirection to the initial class
+% marks a loop. To break it, we must decide which redirection to
+% cancel. The user most likely wants the newly added redirection to
+% hold with no further redirection. We thus remove the redirection
+% starting from |#2|, target of the new redirection. Note that no
+% message is emitted by any of the underlying functions: otherwise we
+% may get an infinite loop because of a message from the message
+% system itself.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_redirect_loop_chk:nnn #1#2#3
+ {
+ \seq_put_right:Nn \l_@@_class_loop_seq {#1}
+ \prop_get:cnNT { l_@@_redirect_ #1 _prop } {#3} \l_@@_class_tl
+ {
+ \str_if_eq:VnF \l_@@_class_tl {#1}
+ {
+ \tl_if_eq:NNTF \l_@@_class_tl \l_@@_current_class_tl
+ {
+ \prop_put:cnn { l_@@_redirect_ #2 _prop } {#3} {#2}
+ \msg_warning:nneeee
+ { msg } { redirect-loop }
+ { \seq_item:Nn \l_@@_class_loop_seq { 1 } }
+ { \seq_item:Nn \l_@@_class_loop_seq { 2 } }
+ {#3}
+ {
+ \seq_map_function:NN \l_@@_class_loop_seq
+ \@@_redirect_loop_list:n
+ { \seq_item:Nn \l_@@_class_loop_seq { 1 } }
+ }
+ }
+ { \@@_redirect_loop_chk:onn \l_@@_class_tl {#2} {#3} }
+ }
+ }
+ }
+\cs_generate_variant:Nn \@@_redirect_loop_chk:nnn { o }
+\cs_new:Npn \@@_redirect_loop_list:n #1 { {#1} ~ => ~ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Kernel-specific functions}
+%
+% \begin{macro}{\__kernel_msg_show_eval:Nn, \__kernel_msg_log_eval:Nn, \@@_show_eval:nnN}
+% A short-hand used for \cs{int_show:n} and similar functions that
+% passes to \cs{tl_show:n} the result of applying |#1| (a
+% function such as \cs{int_eval:n}) to the expression |#2|. The use of
+% \texttt{f}-expansion ensures that |#1| is expanded in the scope in which the
+% show command is called, rather than in the group created by
+% \cs{iow_wrap:nnnN}. This is only important for expressions
+% involving the \tn{currentgrouplevel} or \tn{currentgrouptype}.
+% On the other hand we want the expression to be converted to a string
+% with the usual escape character, hence within the wrapping code.
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_msg_show_eval:Nn #1#2
+ { \exp_args:Nf \@@_show_eval:nnN { #1 {#2} } {#2} \tl_show:n }
+\cs_new_protected:Npn \__kernel_msg_log_eval:Nn #1#2
+ { \exp_args:Nf \@@_show_eval:nnN { #1 {#2} } {#2} \tl_log:n }
+\cs_new_protected:Npn \@@_show_eval:nnN #1#2#3 { #3 { #2 = #1 } }
+% \end{macrocode}
+% \end{macro}
+%
+% These are all retained purely for older \pkg{xparse} support.
+%
+% \begin{macro}{\__kernel_msg_new:nnnn, \__kernel_msg_new:nnn}
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_msg_new:nnnn #1
+ { \msg_new:nnnn { LaTeX / #1 } }
+\cs_new_protected:Npn \__kernel_msg_new:nnn #1
+ { \msg_new:nnn { LaTeX / #1 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \__kernel_msg_info:nnxx ,
+% \__kernel_msg_warning:nnx ,
+% \__kernel_msg_warning:nnxx ,
+% \__kernel_msg_error:nnx ,
+% \__kernel_msg_error:nnxx ,
+% \__kernel_msg_error:nnxxx
+% }
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_msg_info:nnxx #1
+ { \msg_info:nnee { LaTeX / #1 } }
+\cs_new_protected:Npn \__kernel_msg_warning:nnx #1
+ { \msg_warning:nne { LaTeX / #1 } }
+\cs_new_protected:Npn \__kernel_msg_warning:nnxx #1
+ { \msg_warning:nnee { LaTeX / #1 } }
+\cs_new_protected:Npn \__kernel_msg_error:nnx #1
+ { \msg_error:nne { LaTeX / #1 } }
+\cs_new_protected:Npn \__kernel_msg_error:nnxx #1
+ { \msg_error:nnee { LaTeX / #1 } }
+\cs_new_protected:Npn \__kernel_msg_error:nnxxx #1
+ { \msg_error:nneee { LaTeX / #1 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \__kernel_msg_expandable_error:nnn ,
+% \__kernel_msg_expandable_error:nnf ,
+% \__kernel_msg_expandable_error:nnff
+% }
+% \begin{macrocode}
+\cs_new:Npn \__kernel_msg_expandable_error:nnn #1
+ { \msg_expandable_error:nnn { LaTeX / #1 } }
+\cs_new:Npn \__kernel_msg_expandable_error:nnf #1
+ { \msg_expandable_error:nnf { LaTeX / #1 } }
+\cs_new:Npn \__kernel_msg_expandable_error:nnff #1
+ { \msg_expandable_error:nnff { LaTeX / #1 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Internal messages}
+%
+% Error messages needed to actually implement the message system
+% itself.
+% \begin{macrocode}
+\msg_new:nnnn { msg } { already-defined }
+ { Message~'#2'~for~module~'#1'~already~defined. }
+ {
+ \c_@@_coding_error_text_tl
+ LaTeX~was~asked~to~define~a~new~message~called~'#2'\\
+ by~the~module~'#1':~this~message~already~exists.
+ \c_@@_return_text_tl
+ }
+\msg_new:nnnn { msg } { unknown }
+ { Unknown~message~'#2'~for~module~'#1'. }
+ {
+ \c_@@_coding_error_text_tl
+ LaTeX~was~asked~to~display~a~message~called~'#2'\\
+ by~the~module~'#1':~this~message~does~not~exist.
+ \c_@@_return_text_tl
+ }
+\msg_new:nnnn { msg } { class-unknown }
+ { Unknown~message~class~'#1'. }
+ {
+ LaTeX~has~been~asked~to~redirect~messages~to~a~class~'#1':\\
+ this~was~never~defined.
+ \c_@@_return_text_tl
+ }
+\msg_new:nnnn { msg } { redirect-loop }
+ {
+ Message~redirection~loop~caused~by~ {#1} ~=>~ {#2}
+ \tl_if_empty:nF {#3} { ~for~module~' \use_none:n #3 ' } .
+ }
+ {
+ Adding~the~message~redirection~ {#1} ~=>~ {#2}
+ \tl_if_empty:nF {#3} { ~for~the~module~' \use_none:n #3 ' } ~
+ created~an~infinite~loop\\\\
+ \iow_indent:n { #4 \\\\ }
+ }
+% \end{macrocode}
+%
+% Messages for earlier kernel modules plus a few for \pkg{l3keys} which
+% cover coding errors.
+% \begin{macrocode}
+\msg_new:nnnn { kernel } { bad-number-of-arguments }
+ { Function~'#1'~cannot~be~defined~with~#2~arguments. }
+ {
+ \c_@@_coding_error_text_tl
+ LaTeX~has~been~asked~to~define~a~function~'#1'~with~
+ #2~arguments.~
+ TeX~allows~between~0~and~9~arguments~for~a~single~function.
+ }
+\msg_new:nnnn { kernel } { command-already-defined }
+ { Control~sequence~#1~already~defined. }
+ {
+ \c_@@_coding_error_text_tl
+ LaTeX~has~been~asked~to~create~a~new~control~sequence~'#1'~
+ but~this~name~has~already~been~used~elsewhere. \\ \\
+ The~current~meaning~is:\\
+ \ \ #2
+ }
+\msg_new:nnnn { kernel } { command-not-defined }
+ { Control~sequence~#1~undefined. }
+ {
+ \c_@@_coding_error_text_tl
+ LaTeX~has~been~asked~to~use~a~control~sequence~'#1':\\
+ this~has~not~been~defined~yet.
+ }
+\msg_new:nnnn { kernel } { empty-search-pattern }
+ { Empty~search~pattern. }
+ {
+ \c_@@_coding_error_text_tl
+ LaTeX~has~been~asked~to~replace~an~empty~pattern~by~'#1':~that~
+ would~lead~to~an~infinite~loop!
+ }
+\cs_if_exist:NF \tex_elapsedtime:D
+ {
+ \msg_new:nnnn { kernel } { no-elapsed-time }
+ { No~clock~detected~for~#1. }
+ { The~current~engine~provides~no~way~to~access~the~system~time. }
+ }
+\msg_new:nnnn { kernel } { non-base-function }
+ { Function~'#1'~is~not~a~base~function }
+ {
+ \c_@@_coding_error_text_tl
+ Functions~defined~through~\iow_char:N\\cs_new:Nn~must~have~
+ a~signature~consisting~of~only~normal~arguments~'N'~and~'n'.~
+ The~signature~'#2'~of~'#1'~contains~other~arguments~'#3'.~
+ To~define~variants~use~\iow_char:N\\cs_generate_variant:Nn~
+ and~to~define~other~functions~use~\iow_char:N\\cs_new:Npn.
+ }
+\msg_new:nnnn { kernel } { missing-colon }
+ { Function~'#1'~contains~no~':'. }
+ {
+ \c_@@_coding_error_text_tl
+ Code-level~functions~must~contain~':'~to~separate~the~
+ argument~specification~from~the~function~name.~This~is~
+ needed~when~defining~conditionals~or~variants,~or~when~building~a~
+ parameter~text~from~the~number~of~arguments~of~the~function.
+ }
+\msg_new:nnnn { kernel } { overflow }
+ { Integers~larger~than~2^{30}-1~cannot~be~stored~in~arrays. }
+ {
+ An~attempt~was~made~to~store~#3~
+ \tl_if_empty:nF {#2} { at~position~#2~ } in~the~array~'#1'.~
+ The~largest~allowed~value~#4~will~be~used~instead.
+ }
+\msg_new:nnnn { kernel } { out-of-bounds }
+ { Access~to~an~entry~beyond~an~array's~bounds. }
+ {
+ An~attempt~was~made~to~access~or~store~data~at~position~#2~of~the~
+ array~'#1',~but~this~array~has~entries~at~positions~from~1~to~#3.
+ }
+\msg_new:nnnn { kernel } { protected-predicate }
+ { Predicate~'#1'~must~be~expandable. }
+ {
+ \c_@@_coding_error_text_tl
+ LaTeX~has~been~asked~to~define~'#1'~as~a~protected~predicate.~
+ Only~expandable~tests~can~have~a~predicate~version.
+ }
+\msg_new:nnn { kernel } { randint-backward-range }
+ { Wrong~order~of~bounds~in~\iow_char:N\\int_rand:nn{#1}{#2}. }
+\msg_new:nnnn { kernel } { conditional-form-unknown }
+ { Conditional~form~'#1'~for~function~'#2'~unknown. }
+ {
+ \c_@@_coding_error_text_tl
+ LaTeX~has~been~asked~to~define~the~conditional~form~'#1'~of~
+ the~function~'#2',~but~only~'TF',~'T',~'F',~and~'p'~forms~exist.
+ }
+\msg_new:nnnn { kernel } { variant-too-long }
+ { Variant~form~'#1'~longer~than~base~signature~of~'#2'. }
+ {
+ \c_@@_coding_error_text_tl
+ LaTeX~has~been~asked~to~create~a~variant~of~the~function~'#2'~
+ with~a~signature~starting~with~'#1',~but~that~is~longer~than~
+ the~signature~(part~after~the~colon)~of~'#2'.
+ }
+\msg_new:nnnn { kernel } { invalid-variant }
+ { Variant~form~'#1'~invalid~for~base~form~'#2'. }
+ {
+ \c_@@_coding_error_text_tl
+ LaTeX~has~been~asked~to~create~a~variant~of~the~function~'#2'~
+ with~a~signature~starting~with~'#1',~but~cannot~change~an~argument~
+ from~type~'#3'~to~type~'#4'.
+ }
+\msg_new:nnnn { kernel } { invalid-exp-args }
+ { Invalid~variant~specifier~'#1'~in~'#2'. }
+ {
+ \c_@@_coding_error_text_tl
+ LaTeX~has~been~asked~to~create~an~\iow_char:N\\exp_args:N...~
+ function~with~signature~'N#2'~but~'#1'~is~not~a~valid~argument~
+ specifier.
+ }
+\msg_new:nnn { kernel } { deprecated-variant }
+ {
+ Variant~form~'#1'~deprecated~for~base~form~'#2'.~
+ One~should~not~change~an~argument~from~type~'#3'~to~type~'#4'
+ \str_case:nnF {#3}
+ {
+ { n } { :~use~a~'\token_if_eq_charcode:NNTF #4 c v V'~variant? }
+ { N } { :~base~form~only~accepts~a~single~token~argument. }
+ {#4} { :~base~form~is~already~a~variant. }
+ } { . }
+ }
+\msg_new:nnn { char } { active }
+ { Cannot~generate~active~chars. }
+\msg_new:nnn { char } { invalid-catcode }
+ { Invalid~catcode~for~char~generation. }
+\msg_new:nnn { char } { null-space }
+ { Cannot~generate~null~char~as~a~space. }
+\msg_new:nnn { char } { out-of-range }
+ { Charcode~requested~out~of~engine~range. }
+\msg_new:nnn { dim } { zero-unit }
+ { Zero~unit~in~conversion. }
+\msg_new:nnnn { kernel } { quote-in-shell }
+ { Quotes~in~shell~command~'#1'. }
+ { Shell~commands~cannot~contain~quotes~("). }
+\msg_new:nnnn { keys } { no-property }
+ { No~property~given~in~definition~of~key~'#1'. }
+ {
+ \c_@@_coding_error_text_tl
+ Inside~\keys_define:nn each~key~name~
+ needs~a~property: \\ \\
+ \iow_indent:n { #1 .<property> } \\ \\
+ LaTeX~did~not~find~a~'.'~to~indicate~the~start~of~a~property.
+ }
+\msg_new:nnnn { keys } { property-boolean-values-only }
+ { The~property~'#1'~accepts~boolean~values~only. }
+ {
+ \c_@@_coding_error_text_tl
+ The~property~'#1'~only~accepts~the~values~'true'~and~'false'.
+ }
+\msg_new:nnnn { keys } { property-requires-value }
+ { The~property~'#1'~requires~a~value. }
+ {
+ \c_@@_coding_error_text_tl
+ LaTeX~was~asked~to~set~property~'#1'~for~key~'#2'.\\
+ No~value~was~given~for~the~property,~and~one~is~required.
+ }
+\msg_new:nnnn { keys } { property-unknown }
+ { The~key~property~'#1'~is~unknown. }
+ {
+ \c_@@_coding_error_text_tl
+ LaTeX~has~been~asked~to~set~the~property~'#1'~for~key~'#2':~
+ this~property~is~not~defined.
+ }
+\msg_new:nnnn { quark } { invalid-function }
+ { Quark~test~function~'#1'~is~invalid. }
+ {
+ \c_@@_coding_error_text_tl
+ LaTeX~has~been~asked~to~create~quark~test~function~'#1'~
+ \tl_if_empty:nTF {#2}
+ { but~that~name~ }
+ { with~signature~'#2',~but~that~signature~ }
+ is~not~valid.
+ }
+\__kernel_msg_new:nnn { quark } { invalid }
+ { Invalid~quark~variable~'#1'. }
+\msg_new:nnnn { scanmark } { already-defined }
+ { Scan~mark~#1~already~defined. }
+ {
+ \c_@@_coding_error_text_tl
+ LaTeX~has~been~asked~to~create~a~new~scan~mark~'#1'~
+ but~this~name~has~already~been~used~for~a~scan~mark.
+ }
+\msg_new:nnnn { seq } { item-too-large }
+ { Sequence~'#1'~does~not~have~an~item~#3 }
+ {
+ An~attempt~was~made~to~push~or~pop~the~item~at~position~#3~
+ of~'#1',~but~this~
+ \int_compare:nTF { #3 = 0 }
+ { position~does~not~exist. }
+ { sequence~only~has~#2~item \int_compare:nF { #2 = 1 } {s}. }
+ }
+\msg_new:nnnn { seq } { shuffle-too-large }
+ { The~sequence~#1~is~too~long~to~be~shuffled~by~TeX. }
+ {
+ TeX~has~ \int_eval:n { \c_max_register_int + 1 } ~
+ toks~registers:~this~only~allows~to~shuffle~up~to~
+ \int_use:N \c_max_register_int \ items.~
+ The~list~will~not~be~shuffled.
+ }
+\msg_new:nnnn { kernel } { variable-not-defined }
+ { Variable~#1~undefined. }
+ {
+ \c_@@_coding_error_text_tl
+ LaTeX~has~been~asked~to~show~a~variable~#1,~but~this~has~not~
+ been~defined~yet.
+ }
+\msg_new:nnnn { kernel } { bad-type }
+ { Variable~'#1'~is~not~a~valid~#3. }
+ {
+ \c_@@_coding_error_text_tl
+ The~variable~'#1'~with~\tl_if_empty:nTF {#4} {meaning} {value}\\\\
+ \iow_indent:n {#2}\\\\
+ should~be~a~#3~variable,~but~
+ \tl_if_empty:nTF {#4}
+ { it~is~not \str_if_eq:nnF {#3} { bool } { ~a~short~macro } . }
+ {
+ it~does~not~have~the~correct~
+ \str_if_eq:nnTF {#2} {#4}
+ { category~codes. }
+ { internal~structure:\\\\\iow_indent:n {#4} }
+ }
+ }
+\msg_new:nnnn { prop } { bad-link }
+ { Variable~'#1'~is~not~a~valid~(linked)~prop. }
+ {
+ \c_@@_coding_error_text_tl
+ The~variable~'#1'~has~an~incorrect~internal~structure.~
+ Its~internal~entry~'#2'~points~to~'#3',~whose~name~is~not~of~the~
+ form~'#4~<key>'.
+ }
+\msg_new:nnnn { clist } { non-clist }
+ { Variable~'#1'~is~not~a~valid~clist. }
+ {
+ \c_@@_coding_error_text_tl
+ The~variable~'#1'~with~value\\\\
+ \iow_indent:n {#2}\\\\
+ should~be~a~clist~variable,~but~it~includes~empty~or~blank~items~
+ without~braces.
+ }
+\msg_new:nnnn { prop } { misused }
+ { A~property~list~was~misused. }
+ {
+ \c_@@_coding_error_text_tl
+ A~property~list~variable~was~used~without~an~accessor~function.~
+ It~
+ \tl_if_empty:nTF {#1}
+ { is~empty. }
+ { contains~the~key-value~pairs \use_none:n #1 . }
+ }
+\msg_new:nnnn { prop } { inner-make }
+ { '#1'~ cannot~ be~ used~ in~ a~ group. }
+ {
+ \c_@@_coding_error_text_tl
+ The~ command~ '#1'~ was~ applied~ to~ the~ property~ list~
+ variable~ '#2', but~ the~ storage~ type~ can~ only~ be~ changed~
+ at~ the~ outermost~ group~ level.
+ }
+% \end{macrocode}
+%
+% Some errors only appear in expandable settings,
+% hence don't need a \enquote{more-text} argument.
+% \begin{macrocode}
+\msg_new:nnn { kernel } { bad-exp-end-f }
+ { Misused~\exp_end_continue_f:w or~:nw }
+\msg_new:nnn { kernel } { bad-variable }
+ { Erroneous~variable~#1 used! }
+\msg_new:nnn { seq } { misused }
+ { A~sequence~was~misused. }
+\msg_new:nnn { prg } { negative-replication }
+ { Negative~argument~for~\iow_char:N\\prg_replicate:nn. }
+\msg_new:nnn { prop } { prop-keyval }
+ { Missing~'='~in~'#1'~(in~'..._keyval:Nn') }
+\msg_new:nnn { kernel } { unknown-comparison }
+ { Relation~'#1'~not~among~=,<,>,==,!=,<=,>=. }
+\msg_new:nnn { kernel } { zero-step }
+ { Zero~step~size~for~function~#1. }
+% \end{macrocode}
+%
+% Messages used by the \enquote{\texttt{show}} functions.
+% \begin{macrocode}
+\msg_new:nnn { clist } { show }
+ {
+ The~comma~list~ \tl_if_empty:nF {#1} { #1 ~ }
+ \tl_if_empty:nTF {#2}
+ { is~empty \\>~ . }
+ { contains~the~items~(without~outer~braces): #2 . }
+ }
+\msg_new:nnn { intarray } { show }
+ { The~integer~array~#1~contains~#2~items: \\ #3 . }
+\msg_new:nnn { prop } { show }
+ {
+ The~ \str_if_eq:nnF {#3} { flat } { #3~ }
+ property~list~#1~
+ \tl_if_empty:nTF {#2}
+ { is~empty \\>~ . }
+ { contains~the~pairs~(without~outer~braces): #2 . }
+ }
+\msg_new:nnn { seq } { show }
+ {
+ The~sequence~#1~
+ \tl_if_empty:nTF {#2}
+ { is~empty \\>~ . }
+ { contains~the~items~(without~outer~braces): #2 . }
+ }
+\msg_new:nnn { kernel } { show-streams }
+ {
+ \tl_if_empty:nTF {#2} { No~ } { The~following~ }
+ \str_case:nn {#1}
+ {
+ { ior } { input ~ }
+ { iow } { output ~ }
+ }
+ streams~are~
+ \tl_if_empty:nTF {#2} { open } { in~use: #2 . }
+ }
+% \end{macrocode}
+%
+% System layer messages
+% \begin{macrocode}
+\msg_new:nnnn { sys } { backend-set }
+ { Backend~configuration~already~set. }
+ {
+ Run-time~backend~selection~may~only~be~carried~out~once~during~a~run.~
+ This~second~attempt~to~set~them~will~be~ignored.
+ }
+\msg_new:nnnn { sys } { load-debug-in-preamble }
+ { Load~debug~support~in~the~preamble. }
+ {
+ Debugging~requires~support~loaded~in~the~preamble: \\
+ Use~\sys_load_debug:~before~\begin{document}.
+ }
+\msg_new:nnnn { sys } { wrong-backend }
+ { Backend~request~inconsistent~with~engine:~using~'#2'~backend. }
+ {
+ You~have~requested~backend~'#1',~but~this~is~not~suitable~for~use~with~the~
+ active~engine.~LaTeX~will~use~the~'#2'~backend~instead.
+ }
+% \end{macrocode}
+%
+% \subsection{Expandable errors}
+%
+% \begin{macro}{\@@_expandable_error:nn}
+% In expansion only context, we cannot use the normal means of
+% reporting errors. Instead, we rely on a low-level \TeX{} error
+% caused by expanding a macro \cs{???} with parameter text ``|?|''
+% (this could be any token) which we used followed by something else
+% (here, a space). This shows
+% the context, which thanks to the odd-looking \cs{use:n} is
+% \begin{verbatim}
+% <argument> \???
+% ! mypkg Error: The error message.
+% \end{verbatim}
+% In other words, \TeX{} is processing the argument of \cs{use:n},
+% which is \cs{???} \meta{space} |!| \meta{error type} |:| \meta{error message}.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_new:Npn #1 ? { }
+ \cs_new:Npn \@@_expandable_error:nn ##1##2
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_use_none_delimit_by_s_stop:w
+ \use:n { #1 ~ ! ~ ##2 : ~ ##1 } \s_@@_stop
+ }
+ }
+\exp_args:Nc \@@_tmp:w { ??? }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \msg_expandable_error:nnnnnn ,
+% \msg_expandable_error:nnnnn ,
+% \msg_expandable_error:nnnn ,
+% \msg_expandable_error:nnn ,
+% \msg_expandable_error:nn ,
+% \msg_expandable_error:nnffff ,
+% \msg_expandable_error:nnfff ,
+% \msg_expandable_error:nnff ,
+% \msg_expandable_error:nnf
+% }
+% The command built from the csname
+% \cs{c_@@_text_prefix_tl} |#1 / #2|
+% takes four arguments and builds the error text, which is fed to
+% \cs{@@_expandable_error:n} with appropriate expansion: just as for
+% usual messages the arguments are first turned to strings, then the
+% message is fully expanded. The module name also has to be determined.
+% \begin{macrocode}
+\exp_args_generate:n { oooo }
+\cs_new:Npn \msg_expandable_error:nnnnnn #1#2#3#4#5#6
+ {
+ \exp_args:Nee \@@_expandable_error:nn
+ {
+ \exp_args:Nc \exp_args:Noooo
+ { \c_@@_text_prefix_tl #1 / #2 }
+ { \tl_to_str:n {#3} }
+ { \tl_to_str:n {#4} }
+ { \tl_to_str:n {#5} }
+ { \tl_to_str:n {#6} }
+ }
+ { \msg_error_text:n {#1} }
+ }
+\cs_new:Npn \msg_expandable_error:nnnnn #1#2#3#4#5
+ { \msg_expandable_error:nnnnnn {#1} {#2} {#3} {#4} {#5} { } }
+\cs_new:Npn \msg_expandable_error:nnnn #1#2#3#4
+ { \msg_expandable_error:nnnnnn {#1} {#2} {#3} {#4} { } { } }
+\cs_new:Npn \msg_expandable_error:nnn #1#2#3
+ { \msg_expandable_error:nnnnnn {#1} {#2} {#3} { } { } { } }
+\cs_new:Npn \msg_expandable_error:nn #1#2
+ { \msg_expandable_error:nnnnnn {#1} {#2} { } { } { } { } }
+\cs_generate_variant:Nn \msg_expandable_error:nnnnnn { nnffff }
+\cs_generate_variant:Nn \msg_expandable_error:nnnnn { nnfff }
+\cs_generate_variant:Nn \msg_expandable_error:nnnn { nnff }
+\cs_generate_variant:Nn \msg_expandable_error:nnn { nnf }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Message formatting}
+%
+% \begin{macrocode}
+\prop_gput:Nnn \g_msg_module_name_prop { kernel } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { kernel } { }
+\clist_map_inline:nn
+ {
+ char , clist , coffin , debug , deprecation , dim, msg ,
+ quark , prg , prop , scanmark , seq , sys
+ }
+ {
+ \prop_gput:Nnn \g_msg_module_name_prop {#1} { LaTeX }
+ \prop_gput:Nnn \g_msg_module_type_prop {#1} { }
+ }
+\prop_gput:Nnn \g_msg_module_name_prop { LaTeX / cmd } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { LaTeX / cmd } { }
+\prop_gput:Nnn \g_msg_module_name_prop { LaTeX / ltcmd } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { LaTeX / ltcmd } { }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3names.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3names.dtx
new file mode 100644
index 00000000000..468481c28c6
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3names.dtx
@@ -0,0 +1,1568 @@
+% \iffalse meta-comment
+%
+%% File: l3names.dtx
+%
+% Copyright (C) 1990-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3names} module\\ Namespace for primitives^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{Setting up the \LaTeX3 programming language}
+%
+% This module is at the core of the \LaTeX3 programming language. It
+% performs the following tasks:
+% \begin{itemize}
+% \item defines new names for all \TeX{} primitives;
+% \item emulate required primitives not provided by default in \LuaTeX;
+% \item switches to the category code r{\'e}gime for programming;
+% \end{itemize}
+%
+% This module is entirely dedicated to primitives (and emulations of
+% these), which should not be used directly within \LaTeX3 code
+% (outside of \enquote{kernel-level} code). As such, the primitives
+% are not documented here: \emph{The \TeX{}book}, \emph{\TeX{} by
+% Topic} and the manuals for \pdfTeX{}, \XeTeX{}, \LuaTeX{}, \pTeX{}
+% and \upTeX{} should be consulted for details of the primitives.
+% These are named \cs[no-index]{tex_\meta{name}:D}, typically based
+% on the primitive's \meta{name} in \pdfTeX{} and omitting a leading
+% |pdf| when the primitive is not related to pdf output.
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3names} implementation}
+%
+% \begin{macrocode}
+%<*package&tex>
+% \end{macrocode}
+%
+% The prefix here is \texttt{kernel}. A few places need |@@| to be left
+% as is; this is obtained as |@@@@|.
+% \begin{macrocode}
+%<@@=kernel>
+% \end{macrocode}
+%
+% The code here simply renames all of the primitives to new, internal,
+% names.
+%
+% The \tn{let} primitive is renamed by hand first as it is essential for
+% the entire process to follow. This also uses \tn{global}, as that way
+% we avoid leaving an unneeded csname in the hash table.
+% \begin{macrocode}
+\let \tex_global:D \global
+\let \tex_let:D \let
+% \end{macrocode}
+%
+% Everything is inside a (rather long) group, which keeps
+% \cs{@@_primitive:NN} trapped.
+% \begin{macrocode}
+\begingroup
+% \end{macrocode}
+%
+% \begin{macro}{\@@_primitive:NN}
+% A temporary function to actually do the renaming.
+% \begin{macrocode}
+ \long \def \@@_primitive:NN #1#2
+ { \tex_global:D \tex_let:D #2 #1 }
+% \end{macrocode}
+%\end{macro}
+%
+% To allow extracting \enquote{just the names}, a bit of DocStrip fiddling.
+% \begin{macrocode}
+%</package&tex>
+%<*names|tex>
+%<*names|package>
+% \end{macrocode}
+%
+% In the current incarnation of this module, all \TeX{} primitives
+% are given a new name of the form |\tex_|\emph{oldname}|:D|.
+% But first three special cases which have symbolic original names.
+% These are given modified new names, so that they may be entered
+% without catcode tricks.
+% \begin{macrocode}
+ \@@_primitive:NN \ \tex_space:D
+ \@@_primitive:NN \/ \tex_italiccorrection:D
+ \@@_primitive:NN \- \tex_hyphen:D
+% \end{macrocode}
+%
+% Now all the other primitives.
+% \begin{macrocode}
+ \@@_primitive:NN \above \tex_above:D
+ \@@_primitive:NN \abovedisplayshortskip \tex_abovedisplayshortskip:D
+ \@@_primitive:NN \abovedisplayskip \tex_abovedisplayskip:D
+ \@@_primitive:NN \abovewithdelims \tex_abovewithdelims:D
+ \@@_primitive:NN \accent \tex_accent:D
+ \@@_primitive:NN \adjdemerits \tex_adjdemerits:D
+ \@@_primitive:NN \advance \tex_advance:D
+ \@@_primitive:NN \afterassignment \tex_afterassignment:D
+ \@@_primitive:NN \aftergroup \tex_aftergroup:D
+ \@@_primitive:NN \atop \tex_atop:D
+ \@@_primitive:NN \atopwithdelims \tex_atopwithdelims:D
+ \@@_primitive:NN \badness \tex_badness:D
+ \@@_primitive:NN \baselineskip \tex_baselineskip:D
+ \@@_primitive:NN \batchmode \tex_batchmode:D
+ \@@_primitive:NN \begingroup \tex_begingroup:D
+ \@@_primitive:NN \belowdisplayshortskip \tex_belowdisplayshortskip:D
+ \@@_primitive:NN \belowdisplayskip \tex_belowdisplayskip:D
+ \@@_primitive:NN \binoppenalty \tex_binoppenalty:D
+ \@@_primitive:NN \botmark \tex_botmark:D
+ \@@_primitive:NN \box \tex_box:D
+ \@@_primitive:NN \boxmaxdepth \tex_boxmaxdepth:D
+ \@@_primitive:NN \brokenpenalty \tex_brokenpenalty:D
+ \@@_primitive:NN \catcode \tex_catcode:D
+ \@@_primitive:NN \char \tex_char:D
+ \@@_primitive:NN \chardef \tex_chardef:D
+ \@@_primitive:NN \cleaders \tex_cleaders:D
+ \@@_primitive:NN \closein \tex_closein:D
+ \@@_primitive:NN \closeout \tex_closeout:D
+ \@@_primitive:NN \clubpenalty \tex_clubpenalty:D
+ \@@_primitive:NN \copy \tex_copy:D
+ \@@_primitive:NN \count \tex_count:D
+ \@@_primitive:NN \countdef \tex_countdef:D
+ \@@_primitive:NN \cr \tex_cr:D
+ \@@_primitive:NN \crcr \tex_crcr:D
+ \@@_primitive:NN \csname \tex_csname:D
+ \@@_primitive:NN \day \tex_day:D
+ \@@_primitive:NN \deadcycles \tex_deadcycles:D
+ \@@_primitive:NN \def \tex_def:D
+ \@@_primitive:NN \defaulthyphenchar \tex_defaulthyphenchar:D
+ \@@_primitive:NN \defaultskewchar \tex_defaultskewchar:D
+ \@@_primitive:NN \delcode \tex_delcode:D
+ \@@_primitive:NN \delimiter \tex_delimiter:D
+ \@@_primitive:NN \delimiterfactor \tex_delimiterfactor:D
+ \@@_primitive:NN \delimitershortfall \tex_delimitershortfall:D
+ \@@_primitive:NN \dimen \tex_dimen:D
+ \@@_primitive:NN \dimendef \tex_dimendef:D
+ \@@_primitive:NN \discretionary \tex_discretionary:D
+ \@@_primitive:NN \displayindent \tex_displayindent:D
+ \@@_primitive:NN \displaylimits \tex_displaylimits:D
+ \@@_primitive:NN \displaystyle \tex_displaystyle:D
+ \@@_primitive:NN \displaywidowpenalty \tex_displaywidowpenalty:D
+ \@@_primitive:NN \displaywidth \tex_displaywidth:D
+ \@@_primitive:NN \divide \tex_divide:D
+ \@@_primitive:NN \doublehyphendemerits \tex_doublehyphendemerits:D
+ \@@_primitive:NN \dp \tex_dp:D
+ \@@_primitive:NN \dump \tex_dump:D
+ \@@_primitive:NN \edef \tex_edef:D
+ \@@_primitive:NN \else \tex_else:D
+ \@@_primitive:NN \emergencystretch \tex_emergencystretch:D
+ \@@_primitive:NN \end \tex_end:D
+ \@@_primitive:NN \endcsname \tex_endcsname:D
+ \@@_primitive:NN \endgroup \tex_endgroup:D
+ \@@_primitive:NN \endinput \tex_endinput:D
+ \@@_primitive:NN \endlinechar \tex_endlinechar:D
+ \@@_primitive:NN \eqno \tex_eqno:D
+ \@@_primitive:NN \errhelp \tex_errhelp:D
+ \@@_primitive:NN \errmessage \tex_errmessage:D
+ \@@_primitive:NN \errorcontextlines \tex_errorcontextlines:D
+ \@@_primitive:NN \errorstopmode \tex_errorstopmode:D
+ \@@_primitive:NN \escapechar \tex_escapechar:D
+ \@@_primitive:NN \everycr \tex_everycr:D
+ \@@_primitive:NN \everydisplay \tex_everydisplay:D
+ \@@_primitive:NN \everyhbox \tex_everyhbox:D
+ \@@_primitive:NN \everyjob \tex_everyjob:D
+ \@@_primitive:NN \everymath \tex_everymath:D
+ \@@_primitive:NN \everypar \tex_everypar:D
+ \@@_primitive:NN \everyvbox \tex_everyvbox:D
+ \@@_primitive:NN \exhyphenpenalty \tex_exhyphenpenalty:D
+ \@@_primitive:NN \expandafter \tex_expandafter:D
+ \@@_primitive:NN \fam \tex_fam:D
+ \@@_primitive:NN \fi \tex_fi:D
+ \@@_primitive:NN \finalhyphendemerits \tex_finalhyphendemerits:D
+ \@@_primitive:NN \firstmark \tex_firstmark:D
+ \@@_primitive:NN \floatingpenalty \tex_floatingpenalty:D
+ \@@_primitive:NN \font \tex_font:D
+ \@@_primitive:NN \fontdimen \tex_fontdimen:D
+ \@@_primitive:NN \fontname \tex_fontname:D
+ \@@_primitive:NN \futurelet \tex_futurelet:D
+ \@@_primitive:NN \gdef \tex_gdef:D
+ \@@_primitive:NN \global \tex_global:D
+ \@@_primitive:NN \globaldefs \tex_globaldefs:D
+ \@@_primitive:NN \halign \tex_halign:D
+ \@@_primitive:NN \hangafter \tex_hangafter:D
+ \@@_primitive:NN \hangindent \tex_hangindent:D
+ \@@_primitive:NN \hbadness \tex_hbadness:D
+ \@@_primitive:NN \hbox \tex_hbox:D
+ \@@_primitive:NN \hfil \tex_hfil:D
+ \@@_primitive:NN \hfill \tex_hfill:D
+ \@@_primitive:NN \hfilneg \tex_hfilneg:D
+ \@@_primitive:NN \hfuzz \tex_hfuzz:D
+ \@@_primitive:NN \hoffset \tex_hoffset:D
+ \@@_primitive:NN \holdinginserts \tex_holdinginserts:D
+ \@@_primitive:NN \hrule \tex_hrule:D
+ \@@_primitive:NN \hsize \tex_hsize:D
+ \@@_primitive:NN \hskip \tex_hskip:D
+ \@@_primitive:NN \hss \tex_hss:D
+ \@@_primitive:NN \ht \tex_ht:D
+ \@@_primitive:NN \hyphenation \tex_hyphenation:D
+ \@@_primitive:NN \hyphenchar \tex_hyphenchar:D
+ \@@_primitive:NN \hyphenpenalty \tex_hyphenpenalty:D
+ \@@_primitive:NN \if \tex_if:D
+ \@@_primitive:NN \ifcase \tex_ifcase:D
+ \@@_primitive:NN \ifcat \tex_ifcat:D
+ \@@_primitive:NN \ifdim \tex_ifdim:D
+ \@@_primitive:NN \ifeof \tex_ifeof:D
+ \@@_primitive:NN \iffalse \tex_iffalse:D
+ \@@_primitive:NN \ifhbox \tex_ifhbox:D
+ \@@_primitive:NN \ifhmode \tex_ifhmode:D
+ \@@_primitive:NN \ifinner \tex_ifinner:D
+ \@@_primitive:NN \ifmmode \tex_ifmmode:D
+ \@@_primitive:NN \ifnum \tex_ifnum:D
+ \@@_primitive:NN \ifodd \tex_ifodd:D
+ \@@_primitive:NN \iftrue \tex_iftrue:D
+ \@@_primitive:NN \ifvbox \tex_ifvbox:D
+ \@@_primitive:NN \ifvmode \tex_ifvmode:D
+ \@@_primitive:NN \ifvoid \tex_ifvoid:D
+ \@@_primitive:NN \ifx \tex_ifx:D
+ \@@_primitive:NN \ignorespaces \tex_ignorespaces:D
+ \@@_primitive:NN \immediate \tex_immediate:D
+ \@@_primitive:NN \indent \tex_indent:D
+ \@@_primitive:NN \input \tex_input:D
+ \@@_primitive:NN \inputlineno \tex_inputlineno:D
+ \@@_primitive:NN \insert \tex_insert:D
+ \@@_primitive:NN \insertpenalties \tex_insertpenalties:D
+ \@@_primitive:NN \interlinepenalty \tex_interlinepenalty:D
+ \@@_primitive:NN \jobname \tex_jobname:D
+ \@@_primitive:NN \kern \tex_kern:D
+ \@@_primitive:NN \language \tex_language:D
+ \@@_primitive:NN \lastbox \tex_lastbox:D
+ \@@_primitive:NN \lastkern \tex_lastkern:D
+ \@@_primitive:NN \lastpenalty \tex_lastpenalty:D
+ \@@_primitive:NN \lastskip \tex_lastskip:D
+ \@@_primitive:NN \lccode \tex_lccode:D
+ \@@_primitive:NN \leaders \tex_leaders:D
+ \@@_primitive:NN \left \tex_left:D
+ \@@_primitive:NN \lefthyphenmin \tex_lefthyphenmin:D
+ \@@_primitive:NN \leftskip \tex_leftskip:D
+ \@@_primitive:NN \leqno \tex_leqno:D
+ \@@_primitive:NN \let \tex_let:D
+ \@@_primitive:NN \limits \tex_limits:D
+ \@@_primitive:NN \linepenalty \tex_linepenalty:D
+ \@@_primitive:NN \lineskip \tex_lineskip:D
+ \@@_primitive:NN \lineskiplimit \tex_lineskiplimit:D
+ \@@_primitive:NN \long \tex_long:D
+ \@@_primitive:NN \looseness \tex_looseness:D
+ \@@_primitive:NN \lower \tex_lower:D
+ \@@_primitive:NN \lowercase \tex_lowercase:D
+ \@@_primitive:NN \mag \tex_mag:D
+ \@@_primitive:NN \mark \tex_mark:D
+ \@@_primitive:NN \mathaccent \tex_mathaccent:D
+ \@@_primitive:NN \mathbin \tex_mathbin:D
+ \@@_primitive:NN \mathchar \tex_mathchar:D
+ \@@_primitive:NN \mathchardef \tex_mathchardef:D
+ \@@_primitive:NN \mathchoice \tex_mathchoice:D
+ \@@_primitive:NN \mathclose \tex_mathclose:D
+ \@@_primitive:NN \mathcode \tex_mathcode:D
+ \@@_primitive:NN \mathinner \tex_mathinner:D
+ \@@_primitive:NN \mathop \tex_mathop:D
+ \@@_primitive:NN \mathopen \tex_mathopen:D
+ \@@_primitive:NN \mathord \tex_mathord:D
+ \@@_primitive:NN \mathpunct \tex_mathpunct:D
+ \@@_primitive:NN \mathrel \tex_mathrel:D
+ \@@_primitive:NN \mathsurround \tex_mathsurround:D
+ \@@_primitive:NN \maxdeadcycles \tex_maxdeadcycles:D
+ \@@_primitive:NN \maxdepth \tex_maxdepth:D
+ \@@_primitive:NN \meaning \tex_meaning:D
+ \@@_primitive:NN \medmuskip \tex_medmuskip:D
+ \@@_primitive:NN \message \tex_message:D
+ \@@_primitive:NN \mkern \tex_mkern:D
+ \@@_primitive:NN \month \tex_month:D
+ \@@_primitive:NN \moveleft \tex_moveleft:D
+ \@@_primitive:NN \moveright \tex_moveright:D
+ \@@_primitive:NN \mskip \tex_mskip:D
+ \@@_primitive:NN \multiply \tex_multiply:D
+ \@@_primitive:NN \muskip \tex_muskip:D
+ \@@_primitive:NN \muskipdef \tex_muskipdef:D
+ \@@_primitive:NN \newlinechar \tex_newlinechar:D
+ \@@_primitive:NN \noalign \tex_noalign:D
+ \@@_primitive:NN \noboundary \tex_noboundary:D
+ \@@_primitive:NN \noexpand \tex_noexpand:D
+ \@@_primitive:NN \noindent \tex_noindent:D
+ \@@_primitive:NN \nolimits \tex_nolimits:D
+ \@@_primitive:NN \nonscript \tex_nonscript:D
+ \@@_primitive:NN \nonstopmode \tex_nonstopmode:D
+ \@@_primitive:NN \nulldelimiterspace \tex_nulldelimiterspace:D
+ \@@_primitive:NN \nullfont \tex_nullfont:D
+ \@@_primitive:NN \number \tex_number:D
+ \@@_primitive:NN \omit \tex_omit:D
+ \@@_primitive:NN \openin \tex_openin:D
+ \@@_primitive:NN \openout \tex_openout:D
+ \@@_primitive:NN \or \tex_or:D
+ \@@_primitive:NN \outer \tex_outer:D
+ \@@_primitive:NN \output \tex_output:D
+ \@@_primitive:NN \outputpenalty \tex_outputpenalty:D
+ \@@_primitive:NN \over \tex_over:D
+ \@@_primitive:NN \overfullrule \tex_overfullrule:D
+ \@@_primitive:NN \overline \tex_overline:D
+ \@@_primitive:NN \overwithdelims \tex_overwithdelims:D
+ \@@_primitive:NN \pagedepth \tex_pagedepth:D
+ \@@_primitive:NN \pagefilllstretch \tex_pagefilllstretch:D
+ \@@_primitive:NN \pagefillstretch \tex_pagefillstretch:D
+ \@@_primitive:NN \pagefilstretch \tex_pagefilstretch:D
+ \@@_primitive:NN \pagegoal \tex_pagegoal:D
+ \@@_primitive:NN \pageshrink \tex_pageshrink:D
+ \@@_primitive:NN \pagestretch \tex_pagestretch:D
+ \@@_primitive:NN \pagetotal \tex_pagetotal:D
+ \@@_primitive:NN \par \tex_par:D
+ \@@_primitive:NN \parfillskip \tex_parfillskip:D
+ \@@_primitive:NN \parindent \tex_parindent:D
+ \@@_primitive:NN \parshape \tex_parshape:D
+ \@@_primitive:NN \parskip \tex_parskip:D
+ \@@_primitive:NN \patterns \tex_patterns:D
+ \@@_primitive:NN \pausing \tex_pausing:D
+ \@@_primitive:NN \penalty \tex_penalty:D
+ \@@_primitive:NN \postdisplaypenalty \tex_postdisplaypenalty:D
+ \@@_primitive:NN \predisplaypenalty \tex_predisplaypenalty:D
+ \@@_primitive:NN \predisplaysize \tex_predisplaysize:D
+ \@@_primitive:NN \pretolerance \tex_pretolerance:D
+ \@@_primitive:NN \prevdepth \tex_prevdepth:D
+ \@@_primitive:NN \prevgraf \tex_prevgraf:D
+ \@@_primitive:NN \radical \tex_radical:D
+ \@@_primitive:NN \raise \tex_raise:D
+ \@@_primitive:NN \read \tex_read:D
+ \@@_primitive:NN \relax \tex_relax:D
+ \@@_primitive:NN \relpenalty \tex_relpenalty:D
+ \@@_primitive:NN \right \tex_right:D
+ \@@_primitive:NN \righthyphenmin \tex_righthyphenmin:D
+ \@@_primitive:NN \rightskip \tex_rightskip:D
+ \@@_primitive:NN \romannumeral \tex_romannumeral:D
+ \@@_primitive:NN \scriptfont \tex_scriptfont:D
+ \@@_primitive:NN \scriptscriptfont \tex_scriptscriptfont:D
+ \@@_primitive:NN \scriptscriptstyle \tex_scriptscriptstyle:D
+ \@@_primitive:NN \scriptspace \tex_scriptspace:D
+ \@@_primitive:NN \scriptstyle \tex_scriptstyle:D
+ \@@_primitive:NN \scrollmode \tex_scrollmode:D
+ \@@_primitive:NN \setbox \tex_setbox:D
+ \@@_primitive:NN \setlanguage \tex_setlanguage:D
+ \@@_primitive:NN \sfcode \tex_sfcode:D
+ \@@_primitive:NN \shipout \tex_shipout:D
+ \@@_primitive:NN \show \tex_show:D
+ \@@_primitive:NN \showbox \tex_showbox:D
+ \@@_primitive:NN \showboxbreadth \tex_showboxbreadth:D
+ \@@_primitive:NN \showboxdepth \tex_showboxdepth:D
+ \@@_primitive:NN \showlists \tex_showlists:D
+ \@@_primitive:NN \showthe \tex_showthe:D
+ \@@_primitive:NN \skewchar \tex_skewchar:D
+ \@@_primitive:NN \skip \tex_skip:D
+ \@@_primitive:NN \skipdef \tex_skipdef:D
+ \@@_primitive:NN \spacefactor \tex_spacefactor:D
+ \@@_primitive:NN \spaceskip \tex_spaceskip:D
+ \@@_primitive:NN \span \tex_span:D
+ \@@_primitive:NN \special \tex_special:D
+ \@@_primitive:NN \splitbotmark \tex_splitbotmark:D
+ \@@_primitive:NN \splitfirstmark \tex_splitfirstmark:D
+ \@@_primitive:NN \splitmaxdepth \tex_splitmaxdepth:D
+ \@@_primitive:NN \splittopskip \tex_splittopskip:D
+ \@@_primitive:NN \string \tex_string:D
+ \@@_primitive:NN \tabskip \tex_tabskip:D
+ \@@_primitive:NN \textfont \tex_textfont:D
+ \@@_primitive:NN \textstyle \tex_textstyle:D
+ \@@_primitive:NN \the \tex_the:D
+ \@@_primitive:NN \thickmuskip \tex_thickmuskip:D
+ \@@_primitive:NN \thinmuskip \tex_thinmuskip:D
+ \@@_primitive:NN \time \tex_time:D
+ \@@_primitive:NN \toks \tex_toks:D
+ \@@_primitive:NN \toksdef \tex_toksdef:D
+ \@@_primitive:NN \tolerance \tex_tolerance:D
+ \@@_primitive:NN \topmark \tex_topmark:D
+ \@@_primitive:NN \topskip \tex_topskip:D
+ \@@_primitive:NN \tracingcommands \tex_tracingcommands:D
+ \@@_primitive:NN \tracinglostchars \tex_tracinglostchars:D
+ \@@_primitive:NN \tracingmacros \tex_tracingmacros:D
+ \@@_primitive:NN \tracingonline \tex_tracingonline:D
+ \@@_primitive:NN \tracingoutput \tex_tracingoutput:D
+ \@@_primitive:NN \tracingpages \tex_tracingpages:D
+ \@@_primitive:NN \tracingparagraphs \tex_tracingparagraphs:D
+ \@@_primitive:NN \tracingrestores \tex_tracingrestores:D
+ \@@_primitive:NN \tracingstats \tex_tracingstats:D
+ \@@_primitive:NN \uccode \tex_uccode:D
+ \@@_primitive:NN \uchyph \tex_uchyph:D
+ \@@_primitive:NN \underline \tex_underline:D
+ \@@_primitive:NN \unhbox \tex_unhbox:D
+ \@@_primitive:NN \unhcopy \tex_unhcopy:D
+ \@@_primitive:NN \unkern \tex_unkern:D
+ \@@_primitive:NN \unpenalty \tex_unpenalty:D
+ \@@_primitive:NN \unskip \tex_unskip:D
+ \@@_primitive:NN \unvbox \tex_unvbox:D
+ \@@_primitive:NN \unvcopy \tex_unvcopy:D
+ \@@_primitive:NN \uppercase \tex_uppercase:D
+ \@@_primitive:NN \vadjust \tex_vadjust:D
+ \@@_primitive:NN \valign \tex_valign:D
+ \@@_primitive:NN \vbadness \tex_vbadness:D
+ \@@_primitive:NN \vbox \tex_vbox:D
+ \@@_primitive:NN \vcenter \tex_vcenter:D
+ \@@_primitive:NN \vfil \tex_vfil:D
+ \@@_primitive:NN \vfill \tex_vfill:D
+ \@@_primitive:NN \vfilneg \tex_vfilneg:D
+ \@@_primitive:NN \vfuzz \tex_vfuzz:D
+ \@@_primitive:NN \voffset \tex_voffset:D
+ \@@_primitive:NN \vrule \tex_vrule:D
+ \@@_primitive:NN \vsize \tex_vsize:D
+ \@@_primitive:NN \vskip \tex_vskip:D
+ \@@_primitive:NN \vsplit \tex_vsplit:D
+ \@@_primitive:NN \vss \tex_vss:D
+ \@@_primitive:NN \vtop \tex_vtop:D
+ \@@_primitive:NN \wd \tex_wd:D
+ \@@_primitive:NN \widowpenalty \tex_widowpenalty:D
+ \@@_primitive:NN \write \tex_write:D
+ \@@_primitive:NN \xdef \tex_xdef:D
+ \@@_primitive:NN \xleaders \tex_xleaders:D
+ \@@_primitive:NN \xspaceskip \tex_xspaceskip:D
+ \@@_primitive:NN \year \tex_year:D
+% \end{macrocode}
+% Primitives introduced by \eTeX{}.
+% \begin{macrocode}
+ \@@_primitive:NN \beginL \tex_beginL:D
+ \@@_primitive:NN \beginR \tex_beginR:D
+ \@@_primitive:NN \botmarks \tex_botmarks:D
+ \@@_primitive:NN \clubpenalties \tex_clubpenalties:D
+ \@@_primitive:NN \currentgrouplevel \tex_currentgrouplevel:D
+ \@@_primitive:NN \currentgrouptype \tex_currentgrouptype:D
+ \@@_primitive:NN \currentifbranch \tex_currentifbranch:D
+ \@@_primitive:NN \currentiflevel \tex_currentiflevel:D
+ \@@_primitive:NN \currentiftype \tex_currentiftype:D
+ \@@_primitive:NN \detokenize \tex_detokenize:D
+ \@@_primitive:NN \dimexpr \tex_dimexpr:D
+ \@@_primitive:NN \displaywidowpenalties \tex_displaywidowpenalties:D
+ \@@_primitive:NN \endL \tex_endL:D
+ \@@_primitive:NN \endR \tex_endR:D
+ \@@_primitive:NN \eTeXrevision \tex_eTeXrevision:D
+ \@@_primitive:NN \eTeXversion \tex_eTeXversion:D
+ \@@_primitive:NN \everyeof \tex_everyeof:D
+ \@@_primitive:NN \firstmarks \tex_firstmarks:D
+ \@@_primitive:NN \fontchardp \tex_fontchardp:D
+ \@@_primitive:NN \fontcharht \tex_fontcharht:D
+ \@@_primitive:NN \fontcharic \tex_fontcharic:D
+ \@@_primitive:NN \fontcharwd \tex_fontcharwd:D
+ \@@_primitive:NN \glueexpr \tex_glueexpr:D
+ \@@_primitive:NN \glueshrink \tex_glueshrink:D
+ \@@_primitive:NN \glueshrinkorder \tex_glueshrinkorder:D
+ \@@_primitive:NN \gluestretch \tex_gluestretch:D
+ \@@_primitive:NN \gluestretchorder \tex_gluestretchorder:D
+ \@@_primitive:NN \gluetomu \tex_gluetomu:D
+ \@@_primitive:NN \ifcsname \tex_ifcsname:D
+ \@@_primitive:NN \ifdefined \tex_ifdefined:D
+ \@@_primitive:NN \iffontchar \tex_iffontchar:D
+ \@@_primitive:NN \interactionmode \tex_interactionmode:D
+ \@@_primitive:NN \interlinepenalties \tex_interlinepenalties:D
+ \@@_primitive:NN \lastlinefit \tex_lastlinefit:D
+ \@@_primitive:NN \lastnodetype \tex_lastnodetype:D
+ \@@_primitive:NN \marks \tex_marks:D
+ \@@_primitive:NN \middle \tex_middle:D
+ \@@_primitive:NN \muexpr \tex_muexpr:D
+ \@@_primitive:NN \mutoglue \tex_mutoglue:D
+ \@@_primitive:NN \numexpr \tex_numexpr:D
+ \@@_primitive:NN \pagediscards \tex_pagediscards:D
+ \@@_primitive:NN \parshapedimen \tex_parshapedimen:D
+ \@@_primitive:NN \parshapeindent \tex_parshapeindent:D
+ \@@_primitive:NN \parshapelength \tex_parshapelength:D
+ \@@_primitive:NN \predisplaydirection \tex_predisplaydirection:D
+ \@@_primitive:NN \protected \tex_protected:D
+ \@@_primitive:NN \readline \tex_readline:D
+ \@@_primitive:NN \savinghyphcodes \tex_savinghyphcodes:D
+ \@@_primitive:NN \savingvdiscards \tex_savingvdiscards:D
+ \@@_primitive:NN \scantokens \tex_scantokens:D
+ \@@_primitive:NN \showgroups \tex_showgroups:D
+ \@@_primitive:NN \showifs \tex_showifs:D
+ \@@_primitive:NN \showtokens \tex_showtokens:D
+ \@@_primitive:NN \splitbotmarks \tex_splitbotmarks:D
+ \@@_primitive:NN \splitdiscards \tex_splitdiscards:D
+ \@@_primitive:NN \splitfirstmarks \tex_splitfirstmarks:D
+ \@@_primitive:NN \TeXXeTstate \tex_TeXXeTstate:D
+ \@@_primitive:NN \topmarks \tex_topmarks:D
+ \@@_primitive:NN \tracingassigns \tex_tracingassigns:D
+ \@@_primitive:NN \tracinggroups \tex_tracinggroups:D
+ \@@_primitive:NN \tracingifs \tex_tracingifs:D
+ \@@_primitive:NN \tracingnesting \tex_tracingnesting:D
+ \@@_primitive:NN \tracingscantokens \tex_tracingscantokens:D
+ \@@_primitive:NN \unexpanded \tex_unexpanded:D
+ \@@_primitive:NN \unless \tex_unless:D
+ \@@_primitive:NN \widowpenalties \tex_widowpenalties:D
+% \end{macrocode}
+% Post-\eTeX{} primitives do not always end up with the same name in all
+% engines, if indeed they are available cross-engine anyway. We therefore
+% take the approach of preferring the shortest name that makes sense. First,
+% we deal with the primitives introduced by \pdfTeX{} which directly
+% relate to PDF output: these are copied with the names unchanged.
+% \begin{macrocode}
+ \@@_primitive:NN \pdfannot \tex_pdfannot:D
+ \@@_primitive:NN \pdfcatalog \tex_pdfcatalog:D
+ \@@_primitive:NN \pdfcompresslevel \tex_pdfcompresslevel:D
+ \@@_primitive:NN \pdfcolorstack \tex_pdfcolorstack:D
+ \@@_primitive:NN \pdfcolorstackinit \tex_pdfcolorstackinit:D
+ \@@_primitive:NN \pdfdecimaldigits \tex_pdfdecimaldigits:D
+ \@@_primitive:NN \pdfdest \tex_pdfdest:D
+ \@@_primitive:NN \pdfdestmargin \tex_pdfdestmargin:D
+ \@@_primitive:NN \pdfendlink \tex_pdfendlink:D
+ \@@_primitive:NN \pdfendthread \tex_pdfendthread:D
+ \@@_primitive:NN \pdffakespace \tex_pdffakespace:D
+ \@@_primitive:NN \pdffontattr \tex_pdffontattr:D
+ \@@_primitive:NN \pdffontname \tex_pdffontname:D
+ \@@_primitive:NN \pdffontobjnum \tex_pdffontobjnum:D
+ \@@_primitive:NN \pdfgamma \tex_pdfgamma:D
+ \@@_primitive:NN \pdfgentounicode \tex_pdfgentounicode:D
+ \@@_primitive:NN \pdfglyphtounicode \tex_pdfglyphtounicode:D
+ \@@_primitive:NN \pdfhorigin \tex_pdfhorigin:D
+ \@@_primitive:NN \pdfimageapplygamma \tex_pdfimageapplygamma:D
+ \@@_primitive:NN \pdfimagegamma \tex_pdfimagegamma:D
+ \@@_primitive:NN \pdfimagehicolor \tex_pdfimagehicolor:D
+ \@@_primitive:NN \pdfimageresolution \tex_pdfimageresolution:D
+ \@@_primitive:NN \pdfincludechars \tex_pdfincludechars:D
+ \@@_primitive:NN \pdfinclusioncopyfonts \tex_pdfinclusioncopyfonts:D
+ \@@_primitive:NN \pdfinclusionerrorlevel
+ \tex_pdfinclusionerrorlevel:D
+ \@@_primitive:NN \pdfinfo \tex_pdfinfo:D
+ \@@_primitive:NN \pdfinfoomitdate \tex_pdfinfoomitdate:D
+ \@@_primitive:NN \pdfinterwordspaceoff \tex_pdfinterwordspaceoff:D
+ \@@_primitive:NN \pdfinterwordspaceon \tex_pdfinterwordspaceon:D
+ \@@_primitive:NN \pdflastannot \tex_pdflastannot:D
+ \@@_primitive:NN \pdflastlink \tex_pdflastlink:D
+ \@@_primitive:NN \pdflastobj \tex_pdflastobj:D
+ \@@_primitive:NN \pdflastxform \tex_pdflastxform:D
+ \@@_primitive:NN \pdflastximage \tex_pdflastximage:D
+ \@@_primitive:NN \pdflastximagecolordepth
+ \tex_pdflastximagecolordepth:D
+ \@@_primitive:NN \pdflastximagepages \tex_pdflastximagepages:D
+ \@@_primitive:NN \pdflinkmargin \tex_pdflinkmargin:D
+ \@@_primitive:NN \pdfliteral \tex_pdfliteral:D
+ \@@_primitive:NN \pdfmapfile \tex_pdfmapfile:D
+ \@@_primitive:NN \pdfmapline \tex_pdfmapline:D
+ \@@_primitive:NN \pdfmajorversion \tex_pdfmajorversion:D
+ \@@_primitive:NN \pdfminorversion \tex_pdfminorversion:D
+ \@@_primitive:NN \pdfnames \tex_pdfnames:D
+ \@@_primitive:NN \pdfnobuiltintounicode \tex_pdfnobuiltintounicode:D
+ \@@_primitive:NN \pdfobj \tex_pdfobj:D
+ \@@_primitive:NN \pdfobjcompresslevel \tex_pdfobjcompresslevel:D
+ \@@_primitive:NN \pdfomitcharset \tex_pdfomitcharset:D
+ \@@_primitive:NN \pdfoutline \tex_pdfoutline:D
+ \@@_primitive:NN \pdfoutput \tex_pdfoutput:D
+ \@@_primitive:NN \pdfpageattr \tex_pdfpageattr:D
+ \@@_primitive:NN \pdfpagesattr \tex_pdfpagesattr:D
+ \@@_primitive:NN \pdfpagebox \tex_pdfpagebox:D
+ \@@_primitive:NN \pdfpageref \tex_pdfpageref:D
+ \@@_primitive:NN \pdfpageresources \tex_pdfpageresources:D
+ \@@_primitive:NN \pdfpagesattr \tex_pdfpagesattr:D
+ \@@_primitive:NN \pdfrefobj \tex_pdfrefobj:D
+ \@@_primitive:NN \pdfrefxform \tex_pdfrefxform:D
+ \@@_primitive:NN \pdfrefximage \tex_pdfrefximage:D
+ \@@_primitive:NN \pdfrestore \tex_pdfrestore:D
+ \@@_primitive:NN \pdfretval \tex_pdfretval:D
+ \@@_primitive:NN \pdfrunninglinkoff \tex_pdfrunninglinkoff:D
+ \@@_primitive:NN \pdfrunninglinkon \tex_pdfrunninglinkon:D
+ \@@_primitive:NN \pdfsave \tex_pdfsave:D
+ \@@_primitive:NN \pdfsetmatrix \tex_pdfsetmatrix:D
+ \@@_primitive:NN \pdfstartlink \tex_pdfstartlink:D
+ \@@_primitive:NN \pdfstartthread \tex_pdfstartthread:D
+ \@@_primitive:NN \pdfsuppressptexinfo \tex_pdfsuppressptexinfo:D
+ \@@_primitive:NN \pdfsuppresswarningdupdest
+ \tex_pdfsuppresswarningdupdest:D
+ \@@_primitive:NN \pdfsuppresswarningdupmap
+ \tex_pdfsuppresswarningdupmap:D
+ \@@_primitive:NN \pdfsuppresswarningpagegroup
+ \tex_pdfsuppresswarningpagegroup:D
+ \@@_primitive:NN \pdfthread \tex_pdfthread:D
+ \@@_primitive:NN \pdfthreadmargin \tex_pdfthreadmargin:D
+ \@@_primitive:NN \pdftrailer \tex_pdftrailer:D
+ \@@_primitive:NN \pdftrailerid \tex_pdftrailerid:D
+ \@@_primitive:NN \pdfuniqueresname \tex_pdfuniqueresname:D
+ \@@_primitive:NN \pdfvorigin \tex_pdfvorigin:D
+ \@@_primitive:NN \pdfxform \tex_pdfxform:D
+ \@@_primitive:NN \pdfxformname \tex_pdfxformname:D
+ \@@_primitive:NN \pdfximage \tex_pdfximage:D
+ \@@_primitive:NN \pdfximagebbox \tex_pdfximagebbox:D
+% \end{macrocode}
+% These are not related to PDF output and either already appear in other
+% engines without the |\pdf| prefix, or might reasonably do so at some
+% future stage. We therefore drop the leading |pdf| here.
+% \begin{macrocode}
+ \@@_primitive:NN \ifpdfabsdim \tex_ifabsdim:D
+ \@@_primitive:NN \ifpdfabsnum \tex_ifabsnum:D
+ \@@_primitive:NN \ifpdfprimitive \tex_ifprimitive:D
+ \@@_primitive:NN \pdfadjustinterwordglue
+ \tex_adjustinterwordglue:D
+ \@@_primitive:NN \pdfadjustspacing \tex_adjustspacing:D
+ \@@_primitive:NN \pdfappendkern \tex_appendkern:D
+ \@@_primitive:NN \pdfcopyfont \tex_copyfont:D
+ \@@_primitive:NN \pdfcreationdate \tex_creationdate:D
+ \@@_primitive:NN \pdfdraftmode \tex_draftmode:D
+ \@@_primitive:NN \pdfeachlinedepth \tex_eachlinedepth:D
+ \@@_primitive:NN \pdfeachlineheight \tex_eachlineheight:D
+ \@@_primitive:NN \pdfelapsedtime \tex_elapsedtime:D
+ \@@_primitive:NN \pdfescapehex \tex_escapehex:D
+ \@@_primitive:NN \pdfescapename \tex_escapename:D
+ \@@_primitive:NN \pdfescapestring \tex_escapestring:D
+ \@@_primitive:NN \pdffirstlineheight \tex_firstlineheight:D
+ \@@_primitive:NN \pdffontexpand \tex_fontexpand:D
+ \@@_primitive:NN \pdffontsize \tex_fontsize:D
+ \@@_primitive:NN \pdfignoreddimen \tex_ignoreddimen:D
+ \@@_primitive:NN \pdfinsertht \tex_insertht:D
+ \@@_primitive:NN \pdflastlinedepth \tex_lastlinedepth:D
+ \@@_primitive:NN \pdflastmatch \tex_lastmatch:D
+ \@@_primitive:NN \pdflastxpos \tex_lastxpos:D
+ \@@_primitive:NN \pdflastypos \tex_lastypos:D
+ \@@_primitive:NN \pdfmatch \tex_match:D
+ \@@_primitive:NN \pdfnoligatures \tex_noligatures:D
+ \@@_primitive:NN \pdfnormaldeviate \tex_normaldeviate:D
+ \@@_primitive:NN \pdfpageheight \tex_pageheight:D
+ \@@_primitive:NN \pdfpagewidth \tex_pagewidth:D
+ \@@_primitive:NN \pdfpkmode \tex_pkmode:D
+ \@@_primitive:NN \pdfpkresolution \tex_pkresolution:D
+ \@@_primitive:NN \pdfprimitive \tex_primitive:D
+ \@@_primitive:NN \pdfprependkern \tex_prependkern:D
+ \@@_primitive:NN \pdfprotrudechars \tex_protrudechars:D
+ \@@_primitive:NN \pdfpxdimen \tex_pxdimen:D
+ \@@_primitive:NN \pdfrandomseed \tex_randomseed:D
+ \@@_primitive:NN \pdfresettimer \tex_resettimer:D
+ \@@_primitive:NN \pdfsavepos \tex_savepos:D
+ \@@_primitive:NN \pdfsetrandomseed \tex_setrandomseed:D
+ \@@_primitive:NN \pdfshellescape \tex_shellescape:D
+ \@@_primitive:NN \pdftracingfonts \tex_tracingfonts:D
+ \@@_primitive:NN \pdfunescapehex \tex_unescapehex:D
+ \@@_primitive:NN \pdfuniformdeviate \tex_uniformdeviate:D
+% \end{macrocode}
+% The version primitives are not related to PDF mode but are
+% \pdfTeX{}-specific, so again are carried forward unchanged.
+% \begin{macrocode}
+ \@@_primitive:NN \pdftexbanner \tex_pdftexbanner:D
+ \@@_primitive:NN \pdftexrevision \tex_pdftexrevision:D
+ \@@_primitive:NN \pdftexversion \tex_pdftexversion:D
+% \end{macrocode}
+% These ones appear in \pdfTeX{} but don't have |pdf| in the name at all:
+% no decisions to make.
+% \begin{macrocode}
+ \@@_primitive:NN \efcode \tex_efcode:D
+ \@@_primitive:NN \ifincsname \tex_ifincsname:D
+ \@@_primitive:NN \knaccode \tex_knaccode:D
+ \@@_primitive:NN \knbccode \tex_knbccode:D
+ \@@_primitive:NN \knbscode \tex_knbscode:D
+ \@@_primitive:NN \leftmarginkern \tex_leftmarginkern:D
+ \@@_primitive:NN \letterspacefont \tex_letterspacefont:D
+ \@@_primitive:NN \lpcode \tex_lpcode:D
+ \@@_primitive:NN \quitvmode \tex_quitvmode:D
+ \@@_primitive:NN \rightmarginkern \tex_rightmarginkern:D
+ \@@_primitive:NN \rpcode \tex_rpcode:D
+ \@@_primitive:NN \shbscode \tex_shbscode:D
+ \@@_primitive:NN \stbscode \tex_stbscode:D
+ \@@_primitive:NN \synctex \tex_synctex:D
+ \@@_primitive:NN \tagcode \tex_tagcode:D
+% \end{macrocode}
+% Post \pdfTeX{} primitive availability gets more complex. Both \XeTeX{} and
+% \LuaTeX{} have varying names for some primitives from \pdfTeX{}. Particularly
+% for \LuaTeX{} tracking all of that would be hard. Instead, we now check that
+% we only save primitives if they actually exist.
+% \begin{macrocode}
+%</names|package>
+%<*package>
+ \tex_long:D \tex_def:D \use_ii:nn #1#2 {#2}
+ \tex_long:D \tex_def:D \use_none:n #1 { }
+ \tex_long:D \tex_def:D \@@_primitive:NN #1#2
+ {
+ \tex_ifdefined:D #1
+ \tex_expandafter:D \use_ii:nn
+ \tex_fi:D
+ \use_none:n { \tex_global:D \tex_let:D #2 #1 }
+ }
+%</package>
+%<*names|package>
+% \end{macrocode}
+% Some \pdfTeX{} primitives are handled here because they got dropped in
+% \LuaTeX{} but the corresponding internal names are emulated later. The
+% Lua code is already loaded at this point, so we shouldn't overwrite them.
+% \begin{macrocode}
+ \@@_primitive:NN \pdfstrcmp \tex_strcmp:D
+ \@@_primitive:NN \pdffilesize \tex_filesize:D
+ \@@_primitive:NN \pdfmdfivesum \tex_mdfivesum:D
+ \@@_primitive:NN \pdffilemoddate \tex_filemoddate:D
+ \@@_primitive:NN \pdffiledump \tex_filedump:D
+% \end{macrocode}
+% \XeTeX{}-specific primitives. Note that \XeTeX{}'s \tn{strcmp} is
+% handled earlier and is \enquote{rolled up} into \tn{pdfstrcmp}.
+% A few cross-compatibility names which lack
+% the |pdf| of the original are handled later.
+% \begin{macrocode}
+ \@@_primitive:NN \suppressfontnotfounderror
+ \tex_suppressfontnotfounderror:D
+ \@@_primitive:NN \XeTeXcharclass \tex_XeTeXcharclass:D
+ \@@_primitive:NN \XeTeXcharglyph \tex_XeTeXcharglyph:D
+ \@@_primitive:NN \XeTeXcountfeatures \tex_XeTeXcountfeatures:D
+ \@@_primitive:NN \XeTeXcountglyphs \tex_XeTeXcountglyphs:D
+ \@@_primitive:NN \XeTeXcountselectors \tex_XeTeXcountselectors:D
+ \@@_primitive:NN \XeTeXcountvariations \tex_XeTeXcountvariations:D
+ \@@_primitive:NN \XeTeXdefaultencoding \tex_XeTeXdefaultencoding:D
+ \@@_primitive:NN \XeTeXdashbreakstate \tex_XeTeXdashbreakstate:D
+ \@@_primitive:NN \XeTeXfeaturecode \tex_XeTeXfeaturecode:D
+ \@@_primitive:NN \XeTeXfeaturename \tex_XeTeXfeaturename:D
+ \@@_primitive:NN \XeTeXfindfeaturebyname
+ \tex_XeTeXfindfeaturebyname:D
+ \@@_primitive:NN \XeTeXfindselectorbyname
+ \tex_XeTeXfindselectorbyname:D
+ \@@_primitive:NN \XeTeXfindvariationbyname
+ \tex_XeTeXfindvariationbyname:D
+ \@@_primitive:NN \XeTeXfirstfontchar \tex_XeTeXfirstfontchar:D
+ \@@_primitive:NN \XeTeXfonttype \tex_XeTeXfonttype:D
+ \@@_primitive:NN \XeTeXgenerateactualtext
+ \tex_XeTeXgenerateactualtext:D
+ \@@_primitive:NN \XeTeXglyph \tex_XeTeXglyph:D
+ \@@_primitive:NN \XeTeXglyphbounds \tex_XeTeXglyphbounds:D
+ \@@_primitive:NN \XeTeXglyphindex \tex_XeTeXglyphindex:D
+ \@@_primitive:NN \XeTeXglyphname \tex_XeTeXglyphname:D
+ \@@_primitive:NN \XeTeXinputencoding \tex_XeTeXinputencoding:D
+ \@@_primitive:NN \XeTeXinputnormalization
+ \tex_XeTeXinputnormalization:D
+ \@@_primitive:NN \XeTeXinterchartokenstate
+ \tex_XeTeXinterchartokenstate:D
+ \@@_primitive:NN \XeTeXinterchartoks \tex_XeTeXinterchartoks:D
+ \@@_primitive:NN \XeTeXisdefaultselector
+ \tex_XeTeXisdefaultselector:D
+ \@@_primitive:NN \XeTeXisexclusivefeature
+ \tex_XeTeXisexclusivefeature:D
+ \@@_primitive:NN \XeTeXlastfontchar \tex_XeTeXlastfontchar:D
+ \@@_primitive:NN \XeTeXlinebreakskip \tex_XeTeXlinebreakskip:D
+ \@@_primitive:NN \XeTeXlinebreaklocale \tex_XeTeXlinebreaklocale:D
+ \@@_primitive:NN \XeTeXlinebreakpenalty \tex_XeTeXlinebreakpenalty:D
+ \@@_primitive:NN \XeTeXOTcountfeatures \tex_XeTeXOTcountfeatures:D
+ \@@_primitive:NN \XeTeXOTcountlanguages \tex_XeTeXOTcountlanguages:D
+ \@@_primitive:NN \XeTeXOTcountscripts \tex_XeTeXOTcountscripts:D
+ \@@_primitive:NN \XeTeXOTfeaturetag \tex_XeTeXOTfeaturetag:D
+ \@@_primitive:NN \XeTeXOTlanguagetag \tex_XeTeXOTlanguagetag:D
+ \@@_primitive:NN \XeTeXOTscripttag \tex_XeTeXOTscripttag:D
+ \@@_primitive:NN \XeTeXpdffile \tex_XeTeXpdffile:D
+ \@@_primitive:NN \XeTeXpdfpagecount \tex_XeTeXpdfpagecount:D
+ \@@_primitive:NN \XeTeXpicfile \tex_XeTeXpicfile:D
+ \@@_primitive:NN \XeTeXrevision \tex_XeTeXrevision:D
+ \@@_primitive:NN \XeTeXselectorname \tex_XeTeXselectorname:D
+ \@@_primitive:NN \XeTeXtracingfonts \tex_XeTeXtracingfonts:D
+ \@@_primitive:NN \XeTeXupwardsmode \tex_XeTeXupwardsmode:D
+ \@@_primitive:NN \XeTeXuseglyphmetrics \tex_XeTeXuseglyphmetrics:D
+ \@@_primitive:NN \XeTeXvariation \tex_XeTeXvariation:D
+ \@@_primitive:NN \XeTeXvariationdefault \tex_XeTeXvariationdefault:D
+ \@@_primitive:NN \XeTeXvariationmax \tex_XeTeXvariationmax:D
+ \@@_primitive:NN \XeTeXvariationmin \tex_XeTeXvariationmin:D
+ \@@_primitive:NN \XeTeXvariationname \tex_XeTeXvariationname:D
+ \@@_primitive:NN \XeTeXversion \tex_XeTeXversion:D
+ \@@_primitive:NN \XeTeXselectorcode \tex_XeTeXselectorcode:D
+ \@@_primitive:NN \XeTeXinterwordspaceshaping
+ \tex_XeTeXinterwordspaceshaping:D
+ \@@_primitive:NN \XeTeXhyphenatablelength
+ \tex_XeTeXhyphenatablelength:D
+% \end{macrocode}
+% Primitives from \pdfTeX{} that \XeTeX{} renames: also helps with
+% \LuaTeX{}.
+% \begin{macrocode}
+ \@@_primitive:NN \creationdate \tex_creationdate:D
+ \@@_primitive:NN \elapsedtime \tex_elapsedtime:D
+ \@@_primitive:NN \filedump \tex_filedump:D
+ \@@_primitive:NN \filemoddate \tex_filemoddate:D
+ \@@_primitive:NN \filesize \tex_filesize:D
+ \@@_primitive:NN \mdfivesum \tex_mdfivesum:D
+ \@@_primitive:NN \ifprimitive \tex_ifprimitive:D
+ \@@_primitive:NN \primitive \tex_primitive:D
+ \@@_primitive:NN \resettimer \tex_resettimer:D
+ \@@_primitive:NN \shellescape \tex_shellescape:D
+ \@@_primitive:NN \XeTeXprotrudechars \tex_protrudechars:D
+% \end{macrocode}
+% Primitives from \LuaTeX{}, some of which have been ported back to
+% \XeTeX{}.
+% \begin{macrocode}
+ \@@_primitive:NN \alignmark \tex_alignmark:D
+ \@@_primitive:NN \aligntab \tex_aligntab:D
+ \@@_primitive:NN \attribute \tex_attribute:D
+ \@@_primitive:NN \attributedef \tex_attributedef:D
+ \@@_primitive:NN \automaticdiscretionary
+ \tex_automaticdiscretionary:D
+ \@@_primitive:NN \automatichyphenmode \tex_automatichyphenmode:D
+ \@@_primitive:NN \automatichyphenpenalty
+ \tex_automatichyphenpenalty:D
+ \@@_primitive:NN \begincsname \tex_begincsname:D
+ \@@_primitive:NN \bodydir \tex_bodydir:D
+ \@@_primitive:NN \bodydirection \tex_bodydirection:D
+ \@@_primitive:NN \boundary \tex_boundary:D
+ \@@_primitive:NN \boxdir \tex_boxdir:D
+ \@@_primitive:NN \boxdirection \tex_boxdirection:D
+ \@@_primitive:NN \breakafterdirmode \tex_breakafterdirmode:D
+ \@@_primitive:NN \catcodetable \tex_catcodetable:D
+ \@@_primitive:NN \clearmarks \tex_clearmarks:D
+% \@@_primitive:NN \compoundhyphenmode
+% \tex_compoundhyphenmode:D % not documented in manual
+ \@@_primitive:NN \crampeddisplaystyle \tex_crampeddisplaystyle:D
+ \@@_primitive:NN \crampedscriptscriptstyle
+ \tex_crampedscriptscriptstyle:D
+ \@@_primitive:NN \crampedscriptstyle \tex_crampedscriptstyle:D
+ \@@_primitive:NN \crampedtextstyle \tex_crampedtextstyle:D
+ \@@_primitive:NN \csstring \tex_csstring:D
+ \@@_primitive:NN \deferred \tex_deferred:D
+ \@@_primitive:NN \discretionaryligaturemode
+ \tex_discretionaryligaturemode:D
+ \@@_primitive:NN \directlua \tex_directlua:D
+ \@@_primitive:NN \dviextension \tex_dviextension:D
+ \@@_primitive:NN \dvifeedback \tex_dvifeedback:D
+ \@@_primitive:NN \dvivariable \tex_dvivariable:D
+ \@@_primitive:NN \eTeXglueshrinkorder \tex_eTeXglueshrinkorder:D
+ \@@_primitive:NN \eTeXgluestretchorder \tex_eTeXgluestretchorder:D
+ \@@_primitive:NN \endlocalcontrol \tex_endlocalcontrol:D
+ \@@_primitive:NN \etoksapp \tex_etoksapp:D
+ \@@_primitive:NN \etokspre \tex_etokspre:D
+ \@@_primitive:NN \exceptionpenalty \tex_exceptionpenalty:D
+ \@@_primitive:NN \exhyphenchar \tex_exhyphenchar:D
+ \@@_primitive:NN \explicithyphenpenalty \tex_explicithyphenpenalty:D
+ \@@_primitive:NN \expanded \tex_expanded:D
+ \@@_primitive:NN \explicitdiscretionary \tex_explicitdiscretionary:D
+ \@@_primitive:NN \firstvalidlanguage \tex_firstvalidlanguage:D
+% \@@_primitive:NN \fixupboxesmode
+% \tex_fixupboxesmode:D % not documented in manual
+ \@@_primitive:NN \fontid \tex_fontid:D
+ \@@_primitive:NN \formatname \tex_formatname:D
+ \@@_primitive:NN \hjcode \tex_hjcode:D
+ \@@_primitive:NN \hpack \tex_hpack:D
+ \@@_primitive:NN \hyphenationbounds \tex_hyphenationbounds:D
+ \@@_primitive:NN \hyphenationmin \tex_hyphenationmin:D
+ \@@_primitive:NN \hyphenpenaltymode \tex_hyphenpenaltymode:D
+ \@@_primitive:NN \gleaders \tex_gleaders:D
+ \@@_primitive:NN \glet \tex_glet:D
+ \@@_primitive:NN \glyphdimensionsmode \tex_glyphdimensionsmode:D
+ \@@_primitive:NN \gtoksapp \tex_gtoksapp:D
+ \@@_primitive:NN \gtokspre \tex_gtokspre:D
+ \@@_primitive:NN \ifcondition \tex_ifcondition:D
+ \@@_primitive:NN \immediateassigned \tex_immediateassigned:D
+ \@@_primitive:NN \immediateassignment \tex_immediateassignment:D
+ \@@_primitive:NN \initcatcodetable \tex_initcatcodetable:D
+ \@@_primitive:NN \lastnamedcs \tex_lastnamedcs:D
+ \@@_primitive:NN \latelua \tex_latelua:D
+ \@@_primitive:NN \lateluafunction \tex_lateluafunction:D
+ \@@_primitive:NN \leftghost \tex_leftghost:D
+ \@@_primitive:NN \letcharcode \tex_letcharcode:D
+ \@@_primitive:NN \linedir \tex_linedir:D
+ \@@_primitive:NN \linedirection \tex_linedirection:D
+ \@@_primitive:NN \localbrokenpenalty \tex_localbrokenpenalty:D
+ \@@_primitive:NN \localinterlinepenalty \tex_localinterlinepenalty:D
+ \@@_primitive:NN \luabytecode \tex_luabytecode:D
+ \@@_primitive:NN \luabytecodecall \tex_luabytecodecall:D
+ \@@_primitive:NN \luacopyinputnodes \tex_luacopyinputnodes:D
+ \@@_primitive:NN \luadef \tex_luadef:D
+ \@@_primitive:NN \localleftbox \tex_localleftbox:D
+ \@@_primitive:NN \localrightbox \tex_localrightbox:D
+ \@@_primitive:NN \luaescapestring \tex_luaescapestring:D
+ \@@_primitive:NN \luafunction \tex_luafunction:D
+ \@@_primitive:NN \luafunctioncall \tex_luafunctioncall:D
+ \@@_primitive:NN \luatexbanner \tex_luatexbanner:D
+ \@@_primitive:NN \luatexrevision \tex_luatexrevision:D
+ \@@_primitive:NN \luatexversion \tex_luatexversion:D
+ \@@_primitive:NN \mathdefaultsmode \tex_mathdefaultsmode:D
+ \@@_primitive:NN \mathdelimitersmode \tex_mathdelimitersmode:D
+ \@@_primitive:NN \mathdir \tex_mathdir:D
+ \@@_primitive:NN \mathdirection \tex_mathdirection:D
+ \@@_primitive:NN \mathdisplayskipmode \tex_mathdisplayskipmode:D
+ \@@_primitive:NN \matheqdirmode \tex_matheqdirmode:D
+ \@@_primitive:NN \matheqnogapstep \tex_matheqnogapstep:D
+ \@@_primitive:NN \mathflattenmode \tex_mathflattenmode:D
+ \@@_primitive:NN \mathitalicsmode \tex_mathitalicsmode:D
+ \@@_primitive:NN \mathnolimitsmode \tex_mathnolimitsmode:D
+ \@@_primitive:NN \mathoption \tex_mathoption:D
+ \@@_primitive:NN \mathpenaltiesmode \tex_mathpenaltiesmode:D
+ \@@_primitive:NN \mathrulesfam \tex_mathrulesfam:D
+% \@@_primitive:NN \mathrulesmode
+% \tex_mathrulesmode:D % not documented in manual
+% \@@_primitive:NN \mathrulethicknessmode
+% \tex_mathrulethicknessmode:D % not documented in manual
+ \@@_primitive:NN \mathscriptsmode \tex_mathscriptsmode:D
+ \@@_primitive:NN \mathscriptboxmode \tex_mathscriptboxmode:D
+ \@@_primitive:NN \mathscriptcharmode \tex_mathscriptcharmode:D
+ \@@_primitive:NN \mathstyle \tex_mathstyle:D
+ \@@_primitive:NN \mathsurroundmode \tex_mathsurroundmode:D
+ \@@_primitive:NN \mathsurroundskip \tex_mathsurroundskip:D
+ \@@_primitive:NN \nohrule \tex_nohrule:D
+ \@@_primitive:NN \nokerns \tex_nokerns:D
+ \@@_primitive:NN \noligs \tex_noligs:D
+ \@@_primitive:NN \nospaces \tex_nospaces:D
+ \@@_primitive:NN \novrule \tex_novrule:D
+ \@@_primitive:NN \outputbox \tex_outputbox:D
+ \@@_primitive:NN \pagebottomoffset \tex_pagebottomoffset:D
+ \@@_primitive:NN \pagedir \tex_pagedir:D
+ \@@_primitive:NN \pagedirection \tex_pagedirection:D
+ \@@_primitive:NN \pageleftoffset \tex_pageleftoffset:D
+ \@@_primitive:NN \pagerightoffset \tex_pagerightoffset:D
+ \@@_primitive:NN \pagetopoffset \tex_pagetopoffset:D
+ \@@_primitive:NN \pardir \tex_pardir:D
+ \@@_primitive:NN \pardirection \tex_pardirection:D
+ \@@_primitive:NN \pdfextension \tex_pdfextension:D
+ \@@_primitive:NN \pdffeedback \tex_pdffeedback:D
+ \@@_primitive:NN \pdfvariable \tex_pdfvariable:D
+ \@@_primitive:NN \postexhyphenchar \tex_postexhyphenchar:D
+ \@@_primitive:NN \posthyphenchar \tex_posthyphenchar:D
+ \@@_primitive:NN \prebinoppenalty \tex_prebinoppenalty:D
+ \@@_primitive:NN \predisplaygapfactor \tex_predisplaygapfactor:D
+ \@@_primitive:NN \preexhyphenchar \tex_preexhyphenchar:D
+ \@@_primitive:NN \prehyphenchar \tex_prehyphenchar:D
+ \@@_primitive:NN \prerelpenalty \tex_prerelpenalty:D
+ \@@_primitive:NN \protrusionboundary \tex_protrusionboundary:D
+ \@@_primitive:NN \rightghost \tex_rightghost:D
+ \@@_primitive:NN \savecatcodetable \tex_savecatcodetable:D
+ \@@_primitive:NN \scantextokens \tex_scantextokens:D
+ \@@_primitive:NN \setfontid \tex_setfontid:D
+ \@@_primitive:NN \shapemode \tex_shapemode:D
+ \@@_primitive:NN \suppressifcsnameerror \tex_suppressifcsnameerror:D
+ \@@_primitive:NN \suppresslongerror \tex_suppresslongerror:D
+ \@@_primitive:NN \suppressmathparerror \tex_suppressmathparerror:D
+ \@@_primitive:NN \suppressoutererror \tex_suppressoutererror:D
+ \@@_primitive:NN \suppressprimitiveerror
+ \tex_suppressprimitiveerror:D
+ \@@_primitive:NN \textdir \tex_textdir:D
+ \@@_primitive:NN \textdirection \tex_textdirection:D
+ \@@_primitive:NN \toksapp \tex_toksapp:D
+ \@@_primitive:NN \tokspre \tex_tokspre:D
+ \@@_primitive:NN \tpack \tex_tpack:D
+ \@@_primitive:NN \variablefam \tex_variablefam:D
+ \@@_primitive:NN \vpack \tex_vpack:D
+ \@@_primitive:NN \wordboundary \tex_wordboundary:D
+ \@@_primitive:NN \xtoksapp \tex_xtoksapp:D
+ \@@_primitive:NN \xtokspre \tex_xtokspre:D
+% \end{macrocode}
+% Primitives from \pdfTeX{} that \LuaTeX{} renames.
+% \begin{macrocode}
+ \@@_primitive:NN \adjustspacing \tex_adjustspacing:D
+ \@@_primitive:NN \copyfont \tex_copyfont:D
+ \@@_primitive:NN \draftmode \tex_draftmode:D
+ \@@_primitive:NN \expandglyphsinfont \tex_fontexpand:D
+ \@@_primitive:NN \ifabsdim \tex_ifabsdim:D
+ \@@_primitive:NN \ifabsnum \tex_ifabsnum:D
+ \@@_primitive:NN \ignoreligaturesinfont \tex_ignoreligaturesinfont:D
+ \@@_primitive:NN \insertht \tex_insertht:D
+ \@@_primitive:NN \lastsavedboxresourceindex
+ \tex_pdflastxform:D
+ \@@_primitive:NN \lastsavedimageresourceindex
+ \tex_pdflastximage:D
+ \@@_primitive:NN \lastsavedimageresourcepages
+ \tex_pdflastximagepages:D
+ \@@_primitive:NN \lastxpos \tex_lastxpos:D
+ \@@_primitive:NN \lastypos \tex_lastypos:D
+ \@@_primitive:NN \normaldeviate \tex_normaldeviate:D
+ \@@_primitive:NN \outputmode \tex_pdfoutput:D
+ \@@_primitive:NN \pageheight \tex_pageheight:D
+ \@@_primitive:NN \pagewidth \tex_pagewidth:D
+ \@@_primitive:NN \protrudechars \tex_protrudechars:D
+ \@@_primitive:NN \pxdimen \tex_pxdimen:D
+ \@@_primitive:NN \randomseed \tex_randomseed:D
+ \@@_primitive:NN \useboxresource \tex_pdfrefxform:D
+ \@@_primitive:NN \useimageresource \tex_pdfrefximage:D
+ \@@_primitive:NN \savepos \tex_savepos:D
+ \@@_primitive:NN \saveboxresource \tex_pdfxform:D
+ \@@_primitive:NN \saveimageresource \tex_pdfximage:D
+ \@@_primitive:NN \setrandomseed \tex_setrandomseed:D
+ \@@_primitive:NN \tracingfonts \tex_tracingfonts:D
+ \@@_primitive:NN \uniformdeviate \tex_uniformdeviate:D
+% \end{macrocode}
+% The set of Unicode math primitives were introduced by \XeTeX{} and \LuaTeX{}
+% in a somewhat complex fashion: a few first as |\XeTeX...| which were then
+% renamed with \LuaTeX{} having a lot more. These names now all start
+% |\U...| and mainly |\Umath...|.
+% \begin{macrocode}
+ \@@_primitive:NN \Uchar \tex_Uchar:D
+ \@@_primitive:NN \Ucharcat \tex_Ucharcat:D
+ \@@_primitive:NN \Udelcode \tex_Udelcode:D
+ \@@_primitive:NN \Udelcodenum \tex_Udelcodenum:D
+ \@@_primitive:NN \Udelimiter \tex_Udelimiter:D
+ \@@_primitive:NN \Udelimiterover \tex_Udelimiterover:D
+ \@@_primitive:NN \Udelimiterunder \tex_Udelimiterunder:D
+ \@@_primitive:NN \Uhextensible \tex_Uhextensible:D
+ \@@_primitive:NN \Uleft \tex_Uleft:D
+ \@@_primitive:NN \Umathaccent \tex_Umathaccent:D
+ \@@_primitive:NN \Umathaxis \tex_Umathaxis:D
+ \@@_primitive:NN \Umathbinbinspacing \tex_Umathbinbinspacing:D
+ \@@_primitive:NN \Umathbinclosespacing \tex_Umathbinclosespacing:D
+ \@@_primitive:NN \Umathbininnerspacing \tex_Umathbininnerspacing:D
+ \@@_primitive:NN \Umathbinopenspacing \tex_Umathbinopenspacing:D
+ \@@_primitive:NN \Umathbinopspacing \tex_Umathbinopspacing:D
+ \@@_primitive:NN \Umathbinordspacing \tex_Umathbinordspacing:D
+ \@@_primitive:NN \Umathbinpunctspacing \tex_Umathbinpunctspacing:D
+ \@@_primitive:NN \Umathbinrelspacing \tex_Umathbinrelspacing:D
+ \@@_primitive:NN \Umathchar \tex_Umathchar:D
+ \@@_primitive:NN \Umathcharclass \tex_Umathcharclass:D
+ \@@_primitive:NN \Umathchardef \tex_Umathchardef:D
+ \@@_primitive:NN \Umathcharfam \tex_Umathcharfam:D
+ \@@_primitive:NN \Umathcharnum \tex_Umathcharnum:D
+ \@@_primitive:NN \Umathcharnumdef \tex_Umathcharnumdef:D
+ \@@_primitive:NN \Umathcharslot \tex_Umathcharslot:D
+ \@@_primitive:NN \Umathclosebinspacing \tex_Umathclosebinspacing:D
+ \@@_primitive:NN \Umathcloseclosespacing
+ \tex_Umathcloseclosespacing:D
+ \@@_primitive:NN \Umathcloseinnerspacing
+ \tex_Umathcloseinnerspacing:D
+ \@@_primitive:NN \Umathcloseopenspacing \tex_Umathcloseopenspacing:D
+ \@@_primitive:NN \Umathcloseopspacing \tex_Umathcloseopspacing:D
+ \@@_primitive:NN \Umathcloseordspacing \tex_Umathcloseordspacing:D
+ \@@_primitive:NN \Umathclosepunctspacing
+ \tex_Umathclosepunctspacing:D
+ \@@_primitive:NN \Umathcloserelspacing \tex_Umathcloserelspacing:D
+ \@@_primitive:NN \Umathcode \tex_Umathcode:D
+ \@@_primitive:NN \Umathcodenum \tex_Umathcodenum:D
+ \@@_primitive:NN \Umathconnectoroverlapmin
+ \tex_Umathconnectoroverlapmin:D
+ \@@_primitive:NN \Umathfractiondelsize \tex_Umathfractiondelsize:D
+ \@@_primitive:NN \Umathfractiondenomdown
+ \tex_Umathfractiondenomdown:D
+ \@@_primitive:NN \Umathfractiondenomvgap
+ \tex_Umathfractiondenomvgap:D
+ \@@_primitive:NN \Umathfractionnumup \tex_Umathfractionnumup:D
+ \@@_primitive:NN \Umathfractionnumvgap \tex_Umathfractionnumvgap:D
+ \@@_primitive:NN \Umathfractionrule \tex_Umathfractionrule:D
+ \@@_primitive:NN \Umathinnerbinspacing \tex_Umathinnerbinspacing:D
+ \@@_primitive:NN \Umathinnerclosespacing
+ \tex_Umathinnerclosespacing:D
+ \@@_primitive:NN \Umathinnerinnerspacing
+ \tex_Umathinnerinnerspacing:D
+ \@@_primitive:NN \Umathinneropenspacing \tex_Umathinneropenspacing:D
+ \@@_primitive:NN \Umathinneropspacing \tex_Umathinneropspacing:D
+ \@@_primitive:NN \Umathinnerordspacing \tex_Umathinnerordspacing:D
+ \@@_primitive:NN \Umathinnerpunctspacing
+ \tex_Umathinnerpunctspacing:D
+ \@@_primitive:NN \Umathinnerrelspacing \tex_Umathinnerrelspacing:D
+ \@@_primitive:NN \Umathlimitabovebgap \tex_Umathlimitabovebgap:D
+ \@@_primitive:NN \Umathlimitabovekern \tex_Umathlimitabovekern:D
+ \@@_primitive:NN \Umathlimitabovevgap \tex_Umathlimitabovevgap:D
+ \@@_primitive:NN \Umathlimitbelowbgap \tex_Umathlimitbelowbgap:D
+ \@@_primitive:NN \Umathlimitbelowkern \tex_Umathlimitbelowkern:D
+ \@@_primitive:NN \Umathlimitbelowvgap \tex_Umathlimitbelowvgap:D
+ \@@_primitive:NN \Umathnolimitsubfactor \tex_Umathnolimitsubfactor:D
+ \@@_primitive:NN \Umathnolimitsupfactor \tex_Umathnolimitsupfactor:D
+ \@@_primitive:NN \Umathopbinspacing \tex_Umathopbinspacing:D
+ \@@_primitive:NN \Umathopclosespacing \tex_Umathopclosespacing:D
+ \@@_primitive:NN \Umathopenbinspacing \tex_Umathopenbinspacing:D
+ \@@_primitive:NN \Umathopenclosespacing \tex_Umathopenclosespacing:D
+ \@@_primitive:NN \Umathopeninnerspacing \tex_Umathopeninnerspacing:D
+ \@@_primitive:NN \Umathopenopenspacing \tex_Umathopenopenspacing:D
+ \@@_primitive:NN \Umathopenopspacing \tex_Umathopenopspacing:D
+ \@@_primitive:NN \Umathopenordspacing \tex_Umathopenordspacing:D
+ \@@_primitive:NN \Umathopenpunctspacing \tex_Umathopenpunctspacing:D
+ \@@_primitive:NN \Umathopenrelspacing \tex_Umathopenrelspacing:D
+ \@@_primitive:NN \Umathoperatorsize \tex_Umathoperatorsize:D
+ \@@_primitive:NN \Umathopinnerspacing \tex_Umathopinnerspacing:D
+ \@@_primitive:NN \Umathopopenspacing \tex_Umathopopenspacing:D
+ \@@_primitive:NN \Umathopopspacing \tex_Umathopopspacing:D
+ \@@_primitive:NN \Umathopordspacing \tex_Umathopordspacing:D
+ \@@_primitive:NN \Umathoppunctspacing \tex_Umathoppunctspacing:D
+ \@@_primitive:NN \Umathoprelspacing \tex_Umathoprelspacing:D
+ \@@_primitive:NN \Umathordbinspacing \tex_Umathordbinspacing:D
+ \@@_primitive:NN \Umathordclosespacing \tex_Umathordclosespacing:D
+ \@@_primitive:NN \Umathordinnerspacing \tex_Umathordinnerspacing:D
+ \@@_primitive:NN \Umathordopenspacing \tex_Umathordopenspacing:D
+ \@@_primitive:NN \Umathordopspacing \tex_Umathordopspacing:D
+ \@@_primitive:NN \Umathordordspacing \tex_Umathordordspacing:D
+ \@@_primitive:NN \Umathordpunctspacing \tex_Umathordpunctspacing:D
+ \@@_primitive:NN \Umathordrelspacing \tex_Umathordrelspacing:D
+ \@@_primitive:NN \Umathoverbarkern \tex_Umathoverbarkern:D
+ \@@_primitive:NN \Umathoverbarrule \tex_Umathoverbarrule:D
+ \@@_primitive:NN \Umathoverbarvgap \tex_Umathoverbarvgap:D
+ \@@_primitive:NN \Umathoverdelimiterbgap
+ \tex_Umathoverdelimiterbgap:D
+ \@@_primitive:NN \Umathoverdelimitervgap
+ \tex_Umathoverdelimitervgap:D
+ \@@_primitive:NN \Umathpunctbinspacing \tex_Umathpunctbinspacing:D
+ \@@_primitive:NN \Umathpunctclosespacing
+ \tex_Umathpunctclosespacing:D
+ \@@_primitive:NN \Umathpunctinnerspacing
+ \tex_Umathpunctinnerspacing:D
+ \@@_primitive:NN \Umathpunctopenspacing \tex_Umathpunctopenspacing:D
+ \@@_primitive:NN \Umathpunctopspacing \tex_Umathpunctopspacing:D
+ \@@_primitive:NN \Umathpunctordspacing \tex_Umathpunctordspacing:D
+ \@@_primitive:NN \Umathpunctpunctspacing
+ \tex_Umathpunctpunctspacing:D
+ \@@_primitive:NN \Umathpunctrelspacing \tex_Umathpunctrelspacing:D
+ \@@_primitive:NN \Umathquad \tex_Umathquad:D
+ \@@_primitive:NN \Umathradicaldegreeafter
+ \tex_Umathradicaldegreeafter:D
+ \@@_primitive:NN \Umathradicaldegreebefore
+ \tex_Umathradicaldegreebefore:D
+ \@@_primitive:NN \Umathradicaldegreeraise
+ \tex_Umathradicaldegreeraise:D
+ \@@_primitive:NN \Umathradicalkern \tex_Umathradicalkern:D
+ \@@_primitive:NN \Umathradicalrule \tex_Umathradicalrule:D
+ \@@_primitive:NN \Umathradicalvgap \tex_Umathradicalvgap:D
+ \@@_primitive:NN \Umathrelbinspacing \tex_Umathrelbinspacing:D
+ \@@_primitive:NN \Umathrelclosespacing \tex_Umathrelclosespacing:D
+ \@@_primitive:NN \Umathrelinnerspacing \tex_Umathrelinnerspacing:D
+ \@@_primitive:NN \Umathrelopenspacing \tex_Umathrelopenspacing:D
+ \@@_primitive:NN \Umathrelopspacing \tex_Umathrelopspacing:D
+ \@@_primitive:NN \Umathrelordspacing \tex_Umathrelordspacing:D
+ \@@_primitive:NN \Umathrelpunctspacing \tex_Umathrelpunctspacing:D
+ \@@_primitive:NN \Umathrelrelspacing \tex_Umathrelrelspacing:D
+ \@@_primitive:NN \Umathskewedfractionhgap
+ \tex_Umathskewedfractionhgap:D
+ \@@_primitive:NN \Umathskewedfractionvgap
+ \tex_Umathskewedfractionvgap:D
+ \@@_primitive:NN \Umathspaceafterscript \tex_Umathspaceafterscript:D
+ \@@_primitive:NN \Umathstackdenomdown \tex_Umathstackdenomdown:D
+ \@@_primitive:NN \Umathstacknumup \tex_Umathstacknumup:D
+ \@@_primitive:NN \Umathstackvgap \tex_Umathstackvgap:D
+ \@@_primitive:NN \Umathsubshiftdown \tex_Umathsubshiftdown:D
+ \@@_primitive:NN \Umathsubshiftdrop \tex_Umathsubshiftdrop:D
+ \@@_primitive:NN \Umathsubsupshiftdown \tex_Umathsubsupshiftdown:D
+ \@@_primitive:NN \Umathsubsupvgap \tex_Umathsubsupvgap:D
+ \@@_primitive:NN \Umathsubtopmax \tex_Umathsubtopmax:D
+ \@@_primitive:NN \Umathsupbottommin \tex_Umathsupbottommin:D
+ \@@_primitive:NN \Umathsupshiftdrop \tex_Umathsupshiftdrop:D
+ \@@_primitive:NN \Umathsupshiftup \tex_Umathsupshiftup:D
+ \@@_primitive:NN \Umathsupsubbottommax \tex_Umathsupsubbottommax:D
+ \@@_primitive:NN \Umathunderbarkern \tex_Umathunderbarkern:D
+ \@@_primitive:NN \Umathunderbarrule \tex_Umathunderbarrule:D
+ \@@_primitive:NN \Umathunderbarvgap \tex_Umathunderbarvgap:D
+ \@@_primitive:NN \Umathunderdelimiterbgap
+ \tex_Umathunderdelimiterbgap:D
+ \@@_primitive:NN \Umathunderdelimitervgap
+ \tex_Umathunderdelimitervgap:D
+ \@@_primitive:NN \Umiddle \tex_Umiddle:D
+ \@@_primitive:NN \Unosubscript \tex_Unosubscript:D
+ \@@_primitive:NN \Unosuperscript \tex_Unosuperscript:D
+ \@@_primitive:NN \Uoverdelimiter \tex_Uoverdelimiter:D
+ \@@_primitive:NN \Uradical \tex_Uradical:D
+ \@@_primitive:NN \Uright \tex_Uright:D
+ \@@_primitive:NN \Uroot \tex_Uroot:D
+ \@@_primitive:NN \Uskewed \tex_Uskewed:D
+ \@@_primitive:NN \Uskewedwithdelims \tex_Uskewedwithdelims:D
+ \@@_primitive:NN \Ustack \tex_Ustack:D
+ \@@_primitive:NN \Ustartdisplaymath \tex_Ustartdisplaymath:D
+ \@@_primitive:NN \Ustartmath \tex_Ustartmath:D
+ \@@_primitive:NN \Ustopdisplaymath \tex_Ustopdisplaymath:D
+ \@@_primitive:NN \Ustopmath \tex_Ustopmath:D
+ \@@_primitive:NN \Usubscript \tex_Usubscript:D
+ \@@_primitive:NN \Usuperscript \tex_Usuperscript:D
+ \@@_primitive:NN \Uunderdelimiter \tex_Uunderdelimiter:D
+ \@@_primitive:NN \Uvextensible \tex_Uvextensible:D
+% \end{macrocode}
+% Primitives from \pTeX{}.
+% \begin{macrocode}
+ \@@_primitive:NN \autospacing \tex_autospacing:D
+ \@@_primitive:NN \autoxspacing \tex_autoxspacing:D
+ \@@_primitive:NN \currentcjktoken \tex_currentcjktoken:D
+ \@@_primitive:NN \currentspacingmode \tex_currentspacingmode:D
+ \@@_primitive:NN \currentxspacingmode \tex_currentxspacingmode:D
+ \@@_primitive:NN \disinhibitglue \tex_disinhibitglue:D
+ \@@_primitive:NN \dtou \tex_dtou:D
+ \@@_primitive:NN \epTeXinputencoding \tex_epTeXinputencoding:D
+ \@@_primitive:NN \epTeXversion \tex_epTeXversion:D
+ \@@_primitive:NN \euc \tex_euc:D
+ \@@_primitive:NN \hfi \tex_hfi:D
+ \@@_primitive:NN \ifdbox \tex_ifdbox:D
+ \@@_primitive:NN \ifddir \tex_ifddir:D
+ \@@_primitive:NN \ifjfont \tex_ifjfont:D
+ \@@_primitive:NN \ifmbox \tex_ifmbox:D
+ \@@_primitive:NN \ifmdir \tex_ifmdir:D
+ \@@_primitive:NN \iftbox \tex_iftbox:D
+ \@@_primitive:NN \iftfont \tex_iftfont:D
+ \@@_primitive:NN \iftdir \tex_iftdir:D
+ \@@_primitive:NN \ifybox \tex_ifybox:D
+ \@@_primitive:NN \ifydir \tex_ifydir:D
+ \@@_primitive:NN \inhibitglue \tex_inhibitglue:D
+ \@@_primitive:NN \inhibitxspcode \tex_inhibitxspcode:D
+ \@@_primitive:NN \jcharwidowpenalty \tex_jcharwidowpenalty:D
+ \@@_primitive:NN \jfam \tex_jfam:D
+ \@@_primitive:NN \jfont \tex_jfont:D
+ \@@_primitive:NN \jis \tex_jis:D
+ \@@_primitive:NN \kanjiskip \tex_kanjiskip:D
+ \@@_primitive:NN \kansuji \tex_kansuji:D
+ \@@_primitive:NN \kansujichar \tex_kansujichar:D
+ \@@_primitive:NN \kcatcode \tex_kcatcode:D
+ \@@_primitive:NN \kuten \tex_kuten:D
+ \@@_primitive:NN \lastnodechar \tex_lastnodechar:D
+ \@@_primitive:NN \lastnodefont \tex_lastnodefont:D
+ \@@_primitive:NN \lastnodesubtype \tex_lastnodesubtype:D
+ \@@_primitive:NN \noautospacing \tex_noautospacing:D
+ \@@_primitive:NN \noautoxspacing \tex_noautoxspacing:D
+ \@@_primitive:NN \pagefistretch \tex_pagefistretch:D
+ \@@_primitive:NN \postbreakpenalty \tex_postbreakpenalty:D
+ \@@_primitive:NN \prebreakpenalty \tex_prebreakpenalty:D
+ \@@_primitive:NN \ptexfontname \tex_ptexfontname:D
+ \@@_primitive:NN \ptexlineendmode \tex_lineendmode:D
+ \@@_primitive:NN \ptexminorversion \tex_ptexminorversion:D
+ \@@_primitive:NN \ptexrevision \tex_ptexrevision:D
+ \@@_primitive:NN \ptextracingfonts \tex_ptextracingfonts:D
+ \@@_primitive:NN \ptexversion \tex_ptexversion:D
+ \@@_primitive:NN \readpapersizespecial \tex_readpapersizespecial:D
+ \@@_primitive:NN \scriptbaselineshiftfactor
+ \tex_scriptbaselineshiftfactor:D
+ \@@_primitive:NN \scriptscriptbaselineshiftfactor
+ \tex_scriptscriptbaselineshiftfactor:D
+ \@@_primitive:NN \showmode \tex_showmode:D
+ \@@_primitive:NN \sjis \tex_sjis:D
+ \@@_primitive:NN \tate \tex_tate:D
+ \@@_primitive:NN \tbaselineshift \tex_tbaselineshift:D
+ \@@_primitive:NN \textbaselineshiftfactor
+ \tex_textbaselineshiftfactor:D
+ \@@_primitive:NN \tfont \tex_tfont:D
+ \@@_primitive:NN \tojis \tex_tojis:D
+ \@@_primitive:NN \toucs \tex_toucs:D
+ \@@_primitive:NN \ucs \tex_ucs:D
+ \@@_primitive:NN \xkanjiskip \tex_xkanjiskip:D
+ \@@_primitive:NN \xspcode \tex_xspcode:D
+ \@@_primitive:NN \ybaselineshift \tex_ybaselineshift:D
+ \@@_primitive:NN \yoko \tex_yoko:D
+ \@@_primitive:NN \vfi \tex_vfi:D
+% \end{macrocode}
+% Primitives from \upTeX{}.
+% \begin{macrocode}
+ \@@_primitive:NN \currentcjktoken \tex_currentcjktoken:D
+ \@@_primitive:NN \disablecjktoken \tex_disablecjktoken:D
+ \@@_primitive:NN \enablecjktoken \tex_enablecjktoken:D
+ \@@_primitive:NN \forcecjktoken \tex_forcecjktoken:D
+ \@@_primitive:NN \kchar \tex_kchar:D
+ \@@_primitive:NN \kchardef \tex_kchardef:D
+ \@@_primitive:NN \kuten \tex_kuten:D
+ \@@_primitive:NN \uptexrevision \tex_uptexrevision:D
+ \@@_primitive:NN \uptexversion \tex_uptexversion:D
+% \end{macrocode}
+% Omega primitives provided by \pTeX{} (listed separately mainly to allow
+% understanding of their source).
+% \begin{macrocode}
+ \@@_primitive:NN \odelcode \tex_odelcode:D
+ \@@_primitive:NN \odelimiter \tex_odelimiter:D
+ \@@_primitive:NN \omathaccent \tex_omathaccent:D
+ \@@_primitive:NN \omathchar \tex_omathchar:D
+ \@@_primitive:NN \omathchardef \tex_omathchardef:D
+ \@@_primitive:NN \omathcode \tex_omathcode:D
+ \@@_primitive:NN \oradical \tex_oradical:D
+% \end{macrocode}
+% Newer cross-engine primitives.
+% \begin{macrocode}
+ \@@_primitive:NN \partokencontext \tex_partokencontext:D
+ \@@_primitive:NN \partokenname \tex_partokenname:D
+ \@@_primitive:NN \showstream \tex_showstream:D
+ \@@_primitive:NN \tracingstacklevels \tex_tracingstacklevels:D
+% \end{macrocode}
+% End of the \enquote{just the names} part of the source.
+% \begin{macrocode}
+%</names|package>
+%</names|tex>
+%<*package>
+%<*tex>
+% \end{macrocode}
+%
+% The job is done: close the group (using the primitive renamed!).
+% \begin{macrocode}
+\tex_endgroup:D
+% \end{macrocode}
+%
+% \LaTeXe{} moves a few primitives, so these are sorted out.
+% In newer versions of \LaTeXe{}, \pkg{expl3} is loaded rather early, so
+% only some primitives are already renamed, so we need two tests here.
+% At the beginning of the \LaTeXe{} format, the primitives \tn{end} and
+% \tn{input} are renamed, and only later on the other ones.
+% \begin{macrocode}
+\tex_ifdefined:D \@@@@end
+ \tex_let:D \tex_end:D \@@@@end
+ \tex_let:D \tex_input:D \@@@@input
+\tex_fi:D
+% \end{macrocode}
+%
+% If \tn{@@@@hyph} is defined, we are loading \pkg{expl3} in a
+% pre-2020/10/01 release of \LaTeXe{}, so a few other primitives have to
+% be tested as well.
+% \begin{macrocode}
+\tex_ifdefined:D \@@@@hyph
+ \tex_let:D \tex_everydisplay:D \frozen@everydisplay
+ \tex_let:D \tex_everymath:D \frozen@everymath
+ \tex_let:D \tex_hyphen:D \@@@@hyph
+ \tex_let:D \tex_italiccorrection:D \@@@@italiccorr
+ \tex_let:D \tex_underline:D \@@@@underline
+% \end{macrocode}
+% The \tn{shipout} primitive is particularly tricky as a number of packages
+% want to hook in here. First, we see if a sufficiently-new kernel has saved
+% a copy: if it has, just use that. Otherwise, we need to check each of the
+% possible packages/classes that might move it: here, we are looking for those
+% which do \emph{not} delay action to the \tn{AtBeginDocument} hook. (We
+% cannot use \tn{primitive} as that doesn't
+% allow us to make a direct copy of the primitive \emph{itself}.) As we know
+% that \LaTeXe{} is in use, we use it's \tn{@tfor} loop here.
+% \begin{macrocode}
+ \tex_ifdefined:D \@@@@shipout
+ \tex_let:D \tex_shipout:D \@@@@shipout
+ \tex_fi:D
+ \tex_begingroup:D
+ \tex_edef:D \l_tmpa_tl { \tex_string:D \shipout }
+ \tex_edef:D \l_tmpb_tl { \tex_meaning:D \shipout }
+ \tex_ifx:D \l_tmpa_tl \l_tmpb_tl
+ \tex_else:D
+ \tex_expandafter:D \@tfor \tex_expandafter:D \@tempa \tex_string:D :=
+ \CROP@shipout
+ \dup@shipout
+ \GPTorg@shipout
+ \LL@shipout
+ \mem@oldshipout
+ \opem@shipout
+ \pgfpages@originalshipout
+ \pr@shipout
+ \Shipout
+ \verso@orig@shipout
+ \do
+ {
+ \tex_edef:D \l_tmpb_tl
+ { \tex_expandafter:D \tex_meaning:D \@tempa }
+ \tex_ifx:D \l_tmpa_tl \l_tmpb_tl
+ \tex_global:D \tex_expandafter:D \tex_let:D
+ \tex_expandafter:D \tex_shipout:D \@tempa
+ \tex_fi:D
+ }
+ \tex_fi:D
+ \tex_endgroup:D
+% \end{macrocode}
+% Some tidying up is needed for \tn[index=tracingfonts]{(pdf)tracingfonts}.
+% Newer \LuaTeX{} has
+% this simply as \tn{tracingfonts}, but that is overwritten by
+% the \LaTeXe{} kernel. So any spurious definition has to be removed, then
+% the real version saved either from the \pdfTeX{} name or from \LuaTeX{}.
+% In the latter case, we leave \tn{@@tracingfonts} available: this might
+% be useful and almost all \LaTeXe{} users will have \pkg{expl3} loaded by
+% \pkg{fontspec}. (We follow the usual kernel convention that |@@| is used
+% for saved primitives.)
+% \begin{macrocode}
+ \tex_let:D \tex_tracingfonts:D \tex_undefined:D
+ \tex_ifdefined:D \pdftracingfonts
+ \tex_let:D \tex_tracingfonts:D \pdftracingfonts
+ \tex_else:D
+ \tex_ifdefined:D \tex_directlua:D
+ \tex_directlua:D { tex.enableprimitives("@@@@", {"tracingfonts"}) }
+ \tex_let:D \tex_tracingfonts:D \@@@@tracingfonts
+ \tex_fi:D
+ \tex_fi:D
+\tex_fi:D
+% \end{macrocode}
+% Only \pdfTeX{} and \LuaTeX{} define \tn{pdfmapfile} and \tn{pdfmapline}:
+% Tidy up the fact that some format-building processes leave
+% a couple of questionable decisions about that!
+% \begin{macrocode}
+\tex_ifnum:D 0
+ \tex_ifdefined:D \tex_pdftexversion:D 1 \tex_fi:D
+ \tex_ifdefined:D \tex_luatexversion:D 1 \tex_fi:D
+ = 0 %
+ \tex_let:D \tex_pdfmapfile:D \tex_undefined:D
+ \tex_let:D \tex_pdfmapline:D \tex_undefined:D
+\tex_fi:D
+% \end{macrocode}
+% A few packages do unfortunate things to date-related primitives.
+% \begin{macrocode}
+\tex_begingroup:D
+ \tex_edef:D \l_tmpa_tl { \tex_meaning:D \tex_time:D }
+ \tex_edef:D \l_tmpb_tl { \tex_string:D \time }
+ \tex_ifx:D \l_tmpa_tl \l_tmpb_tl
+ \tex_else:D
+ \tex_global:D \tex_let:D \tex_time:D \tex_undefined:D
+ \tex_fi:D
+ \tex_edef:D \l_tmpa_tl { \tex_meaning:D \tex_day:D }
+ \tex_edef:D \l_tmpb_tl { \tex_string:D \day }
+ \tex_ifx:D \l_tmpa_tl \l_tmpb_tl
+ \tex_else:D
+ \tex_global:D \tex_let:D \tex_day:D \tex_undefined:D
+ \tex_fi:D
+ \tex_edef:D \l_tmpa_tl { \tex_meaning:D \tex_month:D }
+ \tex_edef:D \l_tmpb_tl { \tex_string:D \month }
+ \tex_ifx:D \l_tmpa_tl \l_tmpb_tl
+ \tex_else:D
+ \tex_global:D \tex_let:D \tex_month:D \tex_undefined:D
+ \tex_fi:D
+ \tex_edef:D \l_tmpa_tl { \tex_meaning:D \tex_year:D }
+ \tex_edef:D \l_tmpb_tl { \tex_string:D \year }
+ \tex_ifx:D \l_tmpa_tl \l_tmpb_tl
+ \tex_else:D
+ \tex_global:D \tex_let:D \tex_year:D \tex_undefined:D
+ \tex_fi:D
+\tex_endgroup:D
+% \end{macrocode}
+% \pkg{cslatex} moves a couple of primitives which we recover here; as there
+% is no other marker, we can only work by looking for the names.
+% \begin{macrocode}
+\tex_ifdefined:D \orieveryjob
+ \tex_let:D \tex_everyjob:D \orieveryjob
+\tex_fi:D
+\tex_ifdefined:D \oripdfoutput
+ \tex_let:D \tex_pdfoutput:D \oripdfoutput
+\tex_fi:D
+% \end{macrocode}
+%
+% For \ConTeXt{}, two tests are needed. Both Mark~II and Mark~IV move several
+% primitives: these are all covered by the first test, again using \cs{end}
+% as a marker. For Mark~IV, a few more primitives are moved: they are
+% implemented using some Lua code in the current \ConTeXt{}.
+% \begin{macrocode}
+\tex_ifdefined:D \normalend
+ \tex_let:D \tex_end:D \normalend
+ \tex_let:D \tex_everyjob:D \normaleveryjob
+ \tex_let:D \tex_input:D \normalinput
+ \tex_let:D \tex_language:D \normallanguage
+ \tex_let:D \tex_mathop:D \normalmathop
+ \tex_let:D \tex_month:D \normalmonth
+ \tex_let:D \tex_outer:D \normalouter
+ \tex_let:D \tex_over:D \normalover
+ \tex_let:D \tex_vcenter:D \normalvcenter
+ \tex_let:D \tex_unexpanded:D \normalunexpanded
+ \tex_let:D \tex_expanded:D \normalexpanded
+\tex_fi:D
+\tex_ifdefined:D \normalitaliccorrection
+ \tex_let:D \tex_hoffset:D \normalhoffset
+ \tex_let:D \tex_italiccorrection:D \normalitaliccorrection
+ \tex_let:D \tex_voffset:D \normalvoffset
+ \tex_let:D \tex_showtokens:D \normalshowtokens
+ \tex_let:D \tex_bodydir:D \spac_directions_normal_body_dir
+ \tex_let:D \tex_pagedir:D \spac_directions_normal_page_dir
+\tex_fi:D
+\tex_ifdefined:D \normalleft
+ \tex_let:D \tex_left:D \normalleft
+ \tex_let:D \tex_middle:D \normalmiddle
+ \tex_let:D \tex_right:D \normalright
+\tex_fi:D
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</tex>
+% \end{macrocode}
+%
+% In \LuaTeX{}, we additionally emulate some primitives using Lua code.
+% \begin{macrocode}
+%<*lua>
+% \end{macrocode}
+%
+% \begin{macro}[no-user-doc]{\tex_strcmp:D}
+% Compare two strings, expanding to |0| if they are equal,
+% |-1| if the first one is smaller and |1| if the second one is smaller.
+% Here \enquote{smaller} refers to codepoint order which does not correspond to
+% the user expected order for most non-ASCII strings.
+% \begin{macrocode}
+local minus_tok = token_new(string.byte'-', 12)
+local zero_tok = token_new(string.byte'0', 12)
+local one_tok = token_new(string.byte'1', 12)
+luacmd('tex_strcmp:D', function()
+ local first = scan_string()
+ local second = scan_string()
+ if first < second then
+ put_next(minus_tok, one_tok)
+ else
+ put_next(first == second and zero_tok or one_tok)
+ end
+end, 'global')
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[no-user-doc]{\tex_Ucharcat:D}
+% Creating arbitrary chars using |tex.cprint|.
+% The alternative approach using |token.new(...)| is about 10\% slower
+% but needed to create arbitrary space tokens.
+% \begin{macrocode}
+local sprint = tex.sprint
+local cprint = tex.cprint
+luacmd('tex_Ucharcat:D', function()
+ local charcode = scan_int()
+ local catcode = scan_int()
+ if catcode == 10 then
+ sprint(token_new(charcode, 10))
+ else
+ cprint(catcode, utf8_char(charcode))
+ end
+end, 'global')
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[no-user-doc]{\tex_filesize:D}
+% Wrap the function from |ltxutils|.
+% \begin{macrocode}
+luacmd('tex_filesize:D', function()
+ local size = filesize(scan_string())
+ if size then write(size) end
+end, 'global')
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[no-user-doc]{\tex_mdfivesum:D}
+% There are two cases: Either hash a file or a string.
+% Both are already implemented in l3luatex or built-in.
+% \begin{macrocode}
+luacmd('tex_mdfivesum:D', function()
+ local hash
+ if scan_keyword"file" then
+ hash = filemd5sum(scan_string())
+ else
+ hash = md5_HEX(scan_string())
+ end
+ if hash then write(hash) end
+end, 'global')
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[no-user-doc]{\tex_filemoddate:D}
+% A primitive for getting the modification date of a file.
+% \begin{macrocode}
+luacmd('tex_filemoddate:D', function()
+ local date = filemoddate(scan_string())
+ if date then write(date) end
+end, 'global')
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[no-user-doc]{\tex_filedump:D}
+% An emulated primitive for getting a hexdump from a (partial) file.
+% The length has a default of |0|. This is consistent with
+% \pdfTeX, but it effectivly makes the primitive useless without an
+% explicit |length|. Therefore we allow the keyword |whole| to be used
+% instead of a length, indicating that the whole remaining file should
+% be read.
+% \begin{macrocode}
+luacmd('tex_filedump:D', function()
+ local offset = scan_keyword'offset' and scan_int() or nil
+ local length = scan_keyword'length' and scan_int()
+ or not scan_keyword'whole' and 0 or nil
+ local data = filedump(scan_string(), offset, length)
+ if data then write(data) end
+end, 'global')
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</lua>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3pdf.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3pdf.dtx
new file mode 100644
index 00000000000..b0b17f26902
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3pdf.dtx
@@ -0,0 +1,831 @@
+% \iffalse meta-comment
+%
+%% File: l3pdf.dtx
+%
+% Copyright(C) 2019-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% http://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3pdf} module\\ Core PDF support^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{Objects}
+%
+% \subsection{Named objects}
+%
+% An \meta{object} name should fully expand to tokens suitable for use in a
+% label-like context.
+%
+% \begin{function}[added = 2022-08-23]{\pdf_object_new:n}
+% \begin{syntax}
+% \cs{pdf_object_new:n} \Arg{object}
+% \end{syntax}
+% Declares \meta{object} as a PDF object. The object may be referenced
+% from this point on, and written later using \cs{pdf_object_write:nnn}.
+% \end{function}
+%
+% \begin{function}[added = 2022-08-23]
+% {\pdf_object_write:nnn, \pdf_object_write:nne}
+% \begin{syntax}
+% \cs{pdf_object_write:nnn} \Arg{object} \Arg{type} \Arg{content}
+% \end{syntax}
+% Writes the \meta{content} as content of the \meta{object}. Depending on the
+% \meta{type} declared for the object, the format required for the
+% \meta{data} will vary
+% \begin{itemize}
+% \item[\texttt{array}] A space-separated list of values
+% \item[\texttt{dict}] Key--value pairs in the form
+% \texttt{/\meta{key} \meta{value}}
+% \item[\texttt{fstream}] Two brace groups: \meta{file name} and
+% \meta{file content}
+% \item[\texttt{stream}] Two brace groups: \meta{attributes (dictionary)}
+% and \meta{stream contents}
+% \end{itemize}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2021-02-10]{\pdf_object_ref:n}
+% \begin{syntax}
+% \cs{pdf_object_ref:n} \Arg{object}
+% \end{syntax}
+% Inserts the appropriate information to reference the \meta{object}
+% in for example page resource allocation. If the \meta{object} does
+% not exist then the function expands to a reference to object zero;
+% no PDF indirect object ever has this number, so this is a marker for
+% error.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added = 2020-05-15]{\pdf_object_if_exist:n}
+% \begin{syntax}
+% \cs{pdf_object_if_exist_p:n} \Arg{object}
+% \cs{pdf_object_if_exist:nTF} \Arg{object} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether an object with name \Arg{object} has been defined.
+% \end{function}
+%
+% \subsection{Indexed objects}
+%
+% Objects can also be created using a pair of \meta{class} and \emph{index};
+% the \meta{class} argument should expand to character tokens, whilst the
+% \meta{index} is an \meta{int expr} and starts at $1$. For large
+% families of objects, this approach is more efficient than using
+% individual names.
+%
+% \begin{function}[added = 2024-04-01]{\pdf_object_new_indexed:nn}
+% \begin{syntax}
+% \cs{pdf_object_new_indexed:nn} \Arg{class} \Arg{index}
+% \end{syntax}
+% Declares a PDF object of \meta{class} and \meta{index}. The object may be
+% referenced from this point on, and written later using
+% \cs{pdf_object_write_indexed:nnnn}.
+% \end{function}
+%
+% \begin{function}[added = 2024-04-01]
+% {\pdf_object_write_indexed:nnnn, \pdf_object_write_indexed:nnne}
+% \begin{syntax}
+% \cs{pdf_object_write_indexed:nnnn} \Arg{class} \Arg{index} \Arg{type} \Arg{content}
+% \end{syntax}
+% Writes the \meta{content} as content of the object of \meta{class} and
+% \meta{index}. Depending on the
+% \meta{type} declared for the object, the format required for the
+% \meta{content} will vary
+% \begin{itemize}
+% \item[\texttt{array}] A space-separated list of values
+% \item[\texttt{dict}] Key--value pairs in the form
+% \texttt{/\meta{key} \meta{value}}
+% \item[\texttt{fstream}] Two brace groups: \meta{file name} and
+% \meta{file content}
+% \item[\texttt{stream}] Two brace groups: \meta{attributes (dictionary)}
+% and \meta{stream contents}
+% \end{itemize}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2024-04-01]{\pdf_object_ref_indexed:nn}
+% \begin{syntax}
+% \cs{pdf_object_ref_indexed:nn} \Arg{class} \Arg{index}
+% \end{syntax}
+% Inserts the appropriate information to reference the object of \meta{class}
+% and \meta{index} in for example page resource allocation. If the
+% \meta{class}/\meta{index} combination does
+% not exist then the function expands to a reference to object zero;
+% no PDF indirect object ever has this number, so this is a marker for
+% error.
+% \end{function}
+%
+% \subsection{General functions}
+%
+% \begin{function}[added = 2021-02-10]
+% {\pdf_object_unnamed_write:nn, \pdf_object_unnamed_write:ne}
+% \begin{syntax}
+% \cs{pdf_object_unnamed_write:nn} \Arg{type} \Arg{content}
+% \end{syntax}
+% Writes the \meta{content} as content of an anonymous object. Depending on the
+% \meta{type}, the format required for the \meta{data} will vary
+% \begin{itemize}
+% \item[\texttt{array}] A space-separated list of values
+% \item[\texttt{dict}] Key--value pairs in the form
+% \texttt{/\meta{key} \meta{value}}
+% \item[\texttt{fstream}] Two brace groups: \meta{attributes (dictionary)}
+% and \meta{file name}
+% \item[\texttt{stream}] Two brace groups: \meta{attributes (dictionary)}
+% and \meta{stream contents}
+% \end{itemize}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2021-02-10]{\pdf_object_ref_last:}
+% \begin{syntax}
+% \cs{pdf_object_ref_last:}
+% \end{syntax}
+% Inserts the appropriate information to reference the last \meta{object}
+% created. This is particularly useful for anonymous objects.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2021-02-10]{\pdf_pageobject_ref:n}
+% \begin{syntax}
+% \cs{pdf_pagobject_ref:n} \Arg{pageobject}
+% \end{syntax}
+% Inserts the appropriate information to reference the \meta{pageobject}.
+% \end{function}
+%
+% \section{Version}
+%
+% \begin{function}[pTF, EXP, added = 2021-02-10]{\pdf_version_compare:Nn}
+% \begin{syntax}
+% \cs{pdf_version_compare_p:Nn} \meta{comparator} \Arg{version}
+% \cs{pdf_version_compare:NnTF} \meta{comparator} \Arg{version} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Compares the version of the PDF being created with the \meta{version}
+% string specified, using the \meta{comparator}. Either the \meta{true code}
+% or \meta{false code} will be left in the output stream.
+% \end{function}
+%
+% \begin{function}[added = 2021-02-10]
+% {\pdf_version_gset:n, \pdf_version_min_gset:n}
+% \begin{syntax}
+% \cs{pdf_version_gset:n} \Arg{version}
+% \end{syntax}
+% Sets the \meta{version} of the PDF being created. The |min| version will
+% not alter the output version unless it is currently lower than the
+% \meta{version} requested.
+%
+% This function may only be used up to the point where the PDF file is
+% initialised. With dvips it sets \cs{pdf_version_major:} and \cs{pdf_version_minor:}
+% and allows to compare the values with \cs{pdf_version_compare:Nn}, but the
+% PDF version itself still has to be set with the command line option
+% |-dCompatibilityLevel| of |ps2pdf|.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2021-02-10]
+% {\pdf_version:, \pdf_version_major:, \pdf_version_minor:}
+% \begin{syntax}
+% \cs{pdf_version:}
+% \end{syntax}
+% Expands to the currently-active PDF version.
+% \end{function}
+%
+% \section{Page (media) size}
+%
+% \begin{function}[added = 2023-01-14]{\pdf_pagesize_gset:nn}
+% \begin{syntax}
+% \cs{pdf_pagesize_gset:nn} \Arg{width} \Arg{height}
+% \end{syntax}
+% Sets the page size (mediabox) of the PDF being created to the
+% \meta{width} and \meta{height}, both of which are \meta{dimexpr}.
+% \end{function}
+%
+% \section{Compression}
+%
+% \begin{function}[added = 2021-02-10]{\pdf_uncompress:}
+% \begin{syntax}
+% \cs{pdf_uncompress:}
+% \end{syntax}
+% Disables any compression of the PDF, where possible.
+%
+% This function may only be used up to the point where the PDF file is
+% initialised.
+% \end{function}
+%
+% \section{Destinations}
+%
+% Destinations are the places a link jumped too.
+% Unlike the name may suggest they don't described
+% an exact location in the PDF. Instead a destination contains a reference to
+% a page along with an instruction how to display this page.
+% The normally used \enquote{XYZ \textit{top left zoom}} for example instructs
+% the viewer to show the page with the given \textit{zoom} and
+% the top left corner at the \textit{top left} coordinates---which then gives
+% the impression that there is an anchor at this position.
+%
+% If an instruction takes a coordinate, it is calculated by the following
+% commands relative to the location the command is issued.
+% So to get a specific coordinate one has to move the command to the right place.
+%
+% \begin{function}[added = 2021-01-03]
+% {\pdf_destination:nn}
+% \begin{syntax}
+% \cs{pdf_destination:nn} \Arg{name} \Arg{type or integer}
+% \end{syntax}
+% This creates a destination. \Arg{type or integer} can be one of |fit|, |fith|,
+% |fitv|, |fitb|, |fitbh|, |fitbv|, |fitr|, |xyz|
+% or an integer representing a scale factor in percent.
+% |fitr| here gives only a lightweight version of |/FitR|:
+% The backend code defines |fitr| so that it will with pdf\LaTeX{} and
+% Lua\LaTeX{} use the coordinates of the surrounding box,
+% with \texttt{dvips} and \texttt{dvipdfmx} it falls back to |fit|.
+% For full control use \cs{pdf_destination:nnnn}.
+%
+% The keywords match to the PDF names as described in the following tabular.
+%
+% \medskip
+% \noindent\begin{tabular}{ll>{\raggedright\arraybackslash}p{6cm}}
+% \toprule
+% Keyword & PDF & Remarks \\ \midrule
+% |fit| & |/Fit|
+% & Fits the page to the window\\
+% |fith| & |/FitH| \textit{top}
+% & Fits the width of the page to the window \\
+% |fitv| & |/FitV| \textit{left}
+% & Fits the height of the page to the window \\
+% |fitb| & |/FitB|
+% & Fits the page bounding box to the window \\
+% |fitbh|& |/FitBH| \textit{top}
+% & Fits the width of the page bounding box to the window. \\
+% |fitbv|& |/FitBV| \textit{left}
+% & Fits the height of the page bounding box to the window. \\
+% |fitr| & |/FitR| \textit{left bottom right top}
+% & Fits the rectangle specified by the four coordinates to the window
+% (see above for the restrictions)\\
+% |xyz| & |/XYZ| \textit{left top} null
+% & Sets a coordinate but doesn't change the zoom.\\
+% \Arg{integer} & |/XYZ| \textit{left top zoom}
+% & Sets a coordinate and a zoom meaning \Arg{integer}\%.
+% \\\bottomrule
+% \end{tabular}
+%
+% \end{function}
+%
+% \begin{function}[added = 2021-01-17]
+% {\pdf_destination:nnnn}
+% \begin{syntax}
+% \cs{pdf_destination:nnnn} \Arg{name} \Arg{width} \Arg{height} \Arg{depth}
+% \end{syntax}
+% This creates a destination with |/FitR| type with the given dimensions relative
+% to the current location. The destination is in a box of size zero, but it doesn't
+% switch to horizontal mode.
+% \end{function}
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3pdf} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=pdf>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*tex>
+% \end{macrocode}
+%
+% \begin{variable}{\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_init_bool}
+% A boolean so we have some chance of avoiding setting things we are not
+% allowed to. As we are potentially early in the format, we have to work
+% a bit harder than ideal.
+% \begin{macrocode}
+\bool_new:N \g_@@_init_bool
+\bool_lazy_and:nnT
+ { \str_if_eq_p:Vn \fmtname { LaTeX2e } }
+ { \tl_if_exist_p:N \@expl@finalise@setup@@@@ }
+ {
+ \tl_gput_right:Nn \@expl@finalise@setup@@@@
+ {
+ \tl_gput_right:Nn \@kernel@after@begindocument
+ { \bool_gset_true:N \g_@@_init_bool }
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Compression}
+%
+% \begin{macro}{\pdf_uncompress:}
+% Simple to do.
+% \begin{macrocode}
+\cs_new_protected:Npn \pdf_uncompress:
+ {
+ \bool_if:NF \g_@@_init_bool
+ {
+ \@@_backend_compresslevel:n { 0 }
+ \@@_backend_compress_objects:n { \c_false_bool }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Objects}
+%
+% \begin{variable}{\g_@@_backend_object_int}
+% For returning object numbers.
+% \begin{macrocode}
+\int_new:N \g_@@_backend_object_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\pdf_object_new:n}
+% \begin{macro}
+% {\pdf_object_write:nnn, \pdf_object_write:nne, \pdf_object_write:nnx}
+% \begin{macro}{\pdf_object_ref:n}
+% \begin{macro}{\__kernel_pdf_object_id:n}
+% Simple to do: all objects create a constant |int| so it is not a
+% backend-specific name.
+% \begin{macrocode}
+\cs_new_protected:Npn \pdf_object_new:n #1
+ {
+ \@@_backend_object_new:
+ \@@_object_record:nN {#1} \g_@@_backend_object_int
+ }
+\cs_new_protected:Npn \pdf_object_write:nnn #1#2#3
+ {
+ \exp_args:Ne \@@_backend_object_write:nnn
+ { \@@_object_retrieve:n {#1} } {#2} {#3}
+ \bool_gset_true:N \g_@@_init_bool
+ }
+\cs_generate_variant:Nn \pdf_object_write:nnn { nne , nnx }
+\cs_new:Npn \pdf_object_ref:n #1
+ {
+ \exp_args:Ne \@@_backend_object_ref:n
+ { \@@_object_retrieve:n {#1} }
+ }
+\cs_new:Npn \__kernel_pdf_object_id:n #1
+ {
+ \exp_args:Ne \@@_backend_object_id:n
+ { \@@_object_retrieve:n {#1} }
+ }
+%</tex>
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{macro}{\@@_object_record:nN}
+% \begin{macro}[EXP]{\@@_object_retrieve:n}
+% \begin{macro}{ltx.pdf.object_id}
+% Object mappings are tracked in Lua for \LuaTeX{} as this makes retrieving
+% them much easier; as a result, there is a split in approaches. In Lua we
+% store values in a table indexed by name. The Lua function here is set up
+% to deal with both named and indexed objects: fits the Lua idiom well.
+% \begin{macrocode}
+%<*lua>
+
+local scan_int = token.scan_int
+local scan_string = token.scan_string
+local cprint = tex.cprint
+
+local @@_objects_named = {}
+local @@_objects_indexed = {}
+
+luacmd('@@_object_record:nN', function()
+ local name = scan_string()
+ local n = scan_int()
+ @@_objects_named[name] = n
+end,'protected','global')
+
+local function object_id(name,index)
+ if index then
+ return @@_objects_indexed[name][index] or 0
+ else
+ return @@_objects_named[name] or 0
+ end
+end
+
+luacmd('@@_object_retrieve:n', function()
+ local name = scan_string()
+ return cprint(12,tostring(object_id(name)))
+end,'global')
+
+ltx.pdf = ltx.pdf or {}
+ltx.pdf.object_id = object_id
+
+%</lua>
+% \end{macrocode}
+% Whereas in \TeX{} we use integer constants.
+% \begin{macrocode}
+%<*tex>
+\sys_if_engine_luatex:F
+ {
+ \cs_new_protected:Npn \@@_object_record:nN #1#2
+ {
+ \int_const:cn
+ { c_@@_object_ #1 _int } {#2}
+ }
+ \cs_new:Npn \@@_object_retrieve:n #1
+ {
+ \int_if_exist:cTF { c_@@_object_ #1 _int }
+ {
+ \int_use:c
+ { c_@@_object_ #1 _int }
+ }
+ { 0 }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{macro}[pTF,EXP]{\pdf_object_if_exist:n}
+% \begin{macrocode}
+\prg_new_conditional:Npnn \pdf_object_if_exist:n #1 { p , T , F , TF }
+ {
+ \int_compare:nNnTF { \@@_object_retrieve:n {#1} } = 0
+ \prg_return_false:
+ \prg_return_true:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\pdf_object_new_indexed:nn}
+% \begin{macro}{\pdf_object_write_indexed:nnnn, \pdf_object_write_indexed:nnne}
+% \begin{macro}[EXP]{\pdf_object_ref_indexed:nn}
+% \begin{macro}[EXP]{\__kernel_pdf_object_id_indexed:nn}
+% Again we split between the common code and the macro- or Lua-based
+% implementation. To make life easier for the Lua route, all of the
+% potential expressions are expanded to braced numbers.
+% \begin{macrocode}
+\cs_new_protected:Npn \pdf_object_new_indexed:nn #1#2
+ {
+ \@@_backend_object_new:
+ \@@_object_record:neN {#1}
+ { \int_eval:n {#2} } \g_@@_backend_object_int
+ }
+\cs_new_protected:Npn \pdf_object_write_indexed:nnnn #1#2#3#4
+ {
+ \exp_args:Ne \@@_backend_object_write:nnn
+ { \@@_object_retrieve:ne {#1} { \int_eval:n {#2} } } {#3} {#4}
+ \bool_gset_true:N \g_@@_init_bool
+ }
+\cs_generate_variant:Nn \pdf_object_write_indexed:nnnn { nnne }
+\cs_new:Npn \pdf_object_ref_indexed:nn #1#2
+ {
+ \exp_args:Ne \@@_backend_object_ref:n
+ { \@@_object_retrieve:ne {#1} { \int_eval:n {#2} } }
+ }
+\cs_new:Npn \__kernel_pdf_object_id_indexed:nn #1#2
+ {
+ \exp_args:Ne \@@_backend_object_id:n
+ { \@@_object_retrieve:ne {#1} { \int_eval:n {#2} } }
+ }
+%</tex>
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{macro}{\@@_object_record:nnN, \@@_object_record:neN}
+% \begin{macro}[EXP]{\@@_object_retrieve:nn}
+% \begin{macro}{\@@_object_record:NnN}
+% \begin{macro}[EXP]{\@@_object_retrieve:Nn}
+% Again we split for Lua: the same idea as above but with nested tables.
+% As we've arranged above that the \TeX{} code passes a braced number,
+% we can use |tonumber(scan_string())| rather than |scan_int()|
+% for the index.
+% \begin{macrocode}
+%<*lua>
+
+luacmd('@@_object_record:nnN', function()
+ local name = scan_string()
+ local index = tonumber(scan_string())
+ local n = scan_int()
+ @@_objects_indexed[name] = @@_objects_indexed[name] or {}
+ @@_objects_indexed[name][index] = n
+end,'protected','global')
+
+luacmd('@@_object_retrieve:nn', function()
+ local name = scan_string()
+ local index = tonumber(scan_string())
+ return cprint(12,tostring(object_id(name,index)))
+end,'global')
+
+%</lua>
+% \end{macrocode}
+% The non-Lua approach is to divide the range into blocks, and store in integer
+% arrays that can simulate dynamic assignment.
+% \begin{macrocode}
+%<*tex>
+\sys_if_engine_luatex:F
+ {
+ \cs_new_protected:Npn \@@_object_record:nnN #1#2#3
+ {
+ \use:e
+ {
+ \@@_object_record:NnN
+ \@@_object_index_split:nn {#1} {#2}
+ \exp_not:N #3
+ }
+ }
+ \cs_new_protected:Npn \@@_object_record:NnN #1#2#3
+ {
+ \intarray_if_exist:NF #1
+ { \intarray_new:Nn #1 \c_@@_object_block_size_int }
+ \intarray_gset:Nnn #1 {#2} #3
+ }
+ \cs_new:Npn \@@_object_retrieve:nn #1#2
+ {
+ \use:e
+ {
+ \exp_not:N \@@_object_retrieve:Nn
+ \@@_object_index_split:nn {#1} {#2}
+ }
+ }
+ \cs_new:Npn \@@_object_retrieve:Nn #1#2
+ { \intarray_item:Nn #1 {#2} }
+% \end{macrocode}
+% As we want blocks to start from one, and within the block for the top value
+% to be \enquote{in} the block, we do a little bit of manipulation. By shifting
+% down by one, we keep the values \enquote{in} the block, then we adjust the
+% block/index number to get back on track.
+% \begin{macrocode}
+ \cs_new:Npn \@@_object_index_split:nn #1#2
+ {
+ \exp_not:c
+ {
+ g_@@_object_ #1 _
+ \int_eval:n
+ {
+ \int_div_truncate:nn { #2 - 1 }
+ \c_@@_object_block_size_int + 1
+ }
+ _intarray
+ }
+ {
+ \int_eval:n
+ { \int_mod:nn { #2 - 1 } \c_@@_object_block_size_int + 1 }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{variable}{\c_@@_object_block_size_int}
+% Sets the block size used for managing indexed objects.
+% \begin{macrocode}
+ \int_const:Nn \c_@@_object_block_size_int { 10000 }
+ }
+% \end{macrocode}
+% \end{variable}
+% \begin{macro}{\@@_object_record:neN}
+% \begin{macro}[EXP]{\@@_object_retrieve:ne}
+% Common variants.
+% \begin{macrocode}
+\cs_generate_variant:Nn \@@_object_record:nnN { ne }
+\cs_generate_variant:Nn \@@_object_retrieve:nn { ne }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \pdf_object_unnamed_write:nn, \pdf_object_unnamed_write:ne,
+% \pdf_object_unnamed_write:nx
+% }
+% No tracking needed here.
+% \begin{macrocode}
+\cs_new_protected:Npn \pdf_object_unnamed_write:nn #1#2
+ {
+ \exp_args:Ne \@@_backend_object_now:nn {#1} {#2}
+ \bool_gset_true:N \g_@@_init_bool
+ }
+\cs_generate_variant:Nn \pdf_object_unnamed_write:nn { ne , nx }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\pdf_object_ref_last:}
+% A one-step wrapper for consistency.
+% \begin{macrocode}
+\cs_new:Npn \pdf_object_ref_last: { \@@_backend_object_last: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\pdf_pageobject_ref:n}
+% \begin{macrocode}
+\cs_new:Npn \pdf_pageobject_ref:n #1
+ { \@@_backend_pageobject_ref:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Version}
+%
+% \begin{macro}[pTF,EXP]{\pdf_version_compare:Nn}
+% \begin{macro}
+% {
+% @@_version_compare_=:w ,
+% @@_version_compare_<:w ,
+% @@_version_compare_>:w
+% }
+% To compare version, we need to split the given value then deal with both
+% major and minor version
+% \begin{macrocode}
+\prg_new_conditional:Npnn \pdf_version_compare:Nn #1#2 { p , T , F , TF }
+ { \use:c { @@_version_compare_ #1 :w } #2 . . \s_@@_stop }
+\cs_new:cpn { @@_version_compare_=:w } #1 . #2 . #3 \s_@@_stop
+ {
+ \bool_lazy_and:nnTF
+ { \int_compare_p:nNn \@@_backend_version_major: = {#1} }
+ { \int_compare_p:nNn \@@_backend_version_minor: = {#2} }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_new:cpn { @@_version_compare_<:w } #1 . #2 . #3 \s_@@_stop
+ {
+ \bool_lazy_or:nnTF
+ { \int_compare_p:nNn \@@_backend_version_major: < {#1} }
+ {
+ \bool_lazy_and_p:nn
+ { \int_compare_p:nNn \@@_backend_version_major: = {#1} }
+ { \int_compare_p:nNn \@@_backend_version_minor: < {#2} }
+ }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_new:cpn { @@_version_compare_>:w } #1 . #2 . #3 \s_@@_stop
+ {
+ \bool_lazy_or:nnTF
+ { \int_compare_p:nNn \@@_backend_version_major: > {#1} }
+ {
+ \bool_lazy_and_p:nn
+ { \int_compare_p:nNn \@@_backend_version_major: = {#1} }
+ { \int_compare_p:nNn \@@_backend_version_minor: > {#2} }
+ }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\pdf_version_gset:n, \pdf_version_min_gset:n}
+% \begin{macro}{\@@_version_gset:w}
+% Split the version and set.
+% \begin{macrocode}
+\cs_new_protected:Npn \pdf_version_gset:n #1
+ { \@@_version_gset:w #1 . . \s_@@_stop }
+\cs_new_protected:Npn \pdf_version_min_gset:n #1
+ {
+ \pdf_version_compare:NnT < {#1}
+ { \@@_version_gset:w #1 . . \s_@@_stop }
+ }
+\cs_new_protected:Npn \@@_version_gset:w #1 . #2 . #3\s_@@_stop
+ {
+ \bool_if:NF \g_@@_init_bool
+ {
+ \@@_backend_version_major_gset:n {#1}
+ \@@_backend_version_minor_gset:n {#2}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\pdf_version:, \pdf_version_major:, \pdf_version_minor:}
+% Wrappers.
+% \begin{macrocode}
+\cs_new:Npn \pdf_version:
+ { \@@_backend_version_major: . \@@_backend_version_minor: }
+\cs_new:Npn \pdf_version_major: { \@@_backend_version_major: }
+\cs_new:Npn \pdf_version_minor: { \@@_backend_version_minor: }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Page size}
+%
+% \begin{macro}{\pdf_pagesize_gset:nn}
+% \begin{macrocode}
+\cs_new_protected:Npn \pdf_pagesize_gset:nn #1#2
+ { \@@_backend_pagesize_gset:nn {#1} {#2} }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Destinations}
+%
+% \begin{macro}{\pdf_destination:nn}
+% \begin{macrocode}
+\cs_new_protected:Npn \pdf_destination:nn #1#2
+ { \@@_backend_destination:nn {#1} {#2} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\pdf_destination:nnnn}
+% \begin{macrocode}
+\cs_new_protected:Npn \pdf_destination:nnnn #1#2#3#4
+ {
+ \hbox_to_zero:n
+ { \@@_backend_destination:nnnn {#1} {#2} {#3} {#4} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{PDF Page size (media box)}
+%
+% Everything here is delayed to the start of the document so that the
+% backend will definitely be loaded.
+% \begin{macrocode}
+\cs_if_exist:NT \@kernel@before@begindocument
+ {
+ \tl_gput_right:Nn \@kernel@before@begindocument
+ {
+ \bool_lazy_all:nT
+ {
+ { \cs_if_exist_p:N \stockheight }
+ { \cs_if_exist_p:N \stockwidth }
+ { \cs_if_exist_p:N \IfDocumentMetadataTF }
+ { \IfDocumentMetadataTF { \c_true_bool } { \c_false_bool } }
+ { \int_compare_p:nNn \tex_mag:D = { 1000 } }
+ }
+ {
+ \bool_lazy_and:nnTF
+ { \dim_compare_p:nNn \stockheight > { 0pt } }
+ { \dim_compare_p:nNn \stockwidth > { 0pt } }
+ {
+ \@@_backend_pagesize_gset:nn
+ \stockwidth \stockheight
+ }
+ {
+ \bool_lazy_or:nnF
+ { \dim_compare_p:nNn \stockheight < { 0pt } }
+ { \dim_compare_p:nNn \stockwidth < { 0pt } }
+ {
+ \bool_lazy_and:nnT
+ { \dim_compare_p:nNn \paperheight > { 0pt } }
+ { \dim_compare_p:nNn \paperwidth > { 0pt } }
+ {
+ \@@_backend_pagesize_gset:nn
+ \paperwidth \paperheight
+ }
+ }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</tex>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3prg.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3prg.dtx
new file mode 100644
index 00000000000..ea76955d82f
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3prg.dtx
@@ -0,0 +1,1829 @@
+% \iffalse meta-comment
+%
+%% File: l3prg.dtx
+%
+% Copyright (C) 2005-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3prg} module\\ Control structures^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% Conditional processing in \LaTeX3 is defined as something that
+% performs a series of tests, possibly involving assignments and
+% calling other functions that do not read further ahead in the input
+% stream. After processing the input, a \emph{state} is returned. The
+% states returned are \meta{true} and \meta{false}.
+%
+% \LaTeX3 has two forms of conditional flow processing based
+% on these states. The first form is predicate functions that turn the
+% returned state into a boolean \meta{true} or \meta{false}. For
+% example, the function \cs{cs_if_free_p:N} checks whether the control
+% sequence given as its argument is free and then returns the boolean
+% \meta{true} or \meta{false} values to be used in testing with
+% \cs{if_predicate:w} or in functions to be described below. The second form
+% is the kind of functions choosing a particular argument from the
+% input stream based on the result of the testing as in
+% \cs{cs_if_free:NTF} which also takes one argument (the |N|) and then
+% executes either \texttt{true} or \texttt{false} depending on the
+% result.
+%
+% \begin{texnote}
+% The arguments are executed after exiting the underlying
+% |\if...\fi:| structure.
+% \end{texnote}
+%
+% \section{Defining a set of conditional functions}
+% \label{sec:l3prg:new-conditional-functions}
+%
+% \begin{function}[updated = 2022-11-01]
+% {
+% \prg_new_conditional:Npnn, \prg_set_conditional:Npnn, \prg_gset_conditional:Npnn,
+% \prg_new_conditional:Nnn, \prg_set_conditional:Nnn, \prg_gset_conditional:Nnn
+% }
+% \begin{syntax}
+% \cs{prg_new_conditional:Npnn} \cs[no-index]{\meta{name}:\meta{arg spec}} \meta{parameters} \Arg{conditions} \Arg{code} \\
+% \cs{prg_new_conditional:Nnn} \cs[no-index]{\meta{name}:\meta{arg spec}} \Arg{conditions} \Arg{code}
+% \end{syntax}
+% These functions create a family of conditionals using the same
+% \Arg{code} to perform the test created. Those conditionals are
+% expandable if \meta{code} is. The \texttt{new} versions check
+% for existing definitions and perform assignments globally
+% (\emph{cf.}~\cs{cs_new:Npn}) whereas the \texttt{set} versions do no
+% check and perform assignments locally (\emph{cf.}~\cs{cs_set:Npn}).
+% The conditionals created are dependent on the comma-separated list
+% of \meta{conditions}, which should be one or more of \texttt{p},
+% \texttt{T}, \texttt{F} and \texttt{TF}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-02-06]
+% {
+% \prg_new_protected_conditional:Npnn, \prg_set_protected_conditional:Npnn, \prg_gset_protected_conditional:Npnn,
+% \prg_new_protected_conditional:Nnn, \prg_set_protected_conditional:Nnn, \prg_gset_protected_conditional:Nnn
+% }
+% \begin{syntax}
+% \cs{prg_new_protected_conditional:Npnn} \cs[no-index]{\meta{name}:\meta{arg spec}} \meta{parameters} \Arg{conditions} \Arg{code} \\
+% \cs{prg_new_protected_conditional:Nnn} \cs[no-index]{\meta{name}:\meta{arg spec}} \Arg{conditions} \Arg{code}
+% \end{syntax}
+% These functions create a family of protected conditionals using the
+% same \Arg{code} to perform the test created. The \meta{code} does
+% not need to be expandable. The \texttt{new} version check for
+% existing definitions and perform assignments globally
+% (\emph{cf.}~\cs{cs_new:Npn}) whereas the \texttt{set} version do
+% not (\emph{cf.}~\cs{cs_set:Npn}). The conditionals created are
+% depended on the comma-separated list of \meta{conditions}, which
+% should be one or more of \texttt{T}, \texttt{F} and \texttt{TF} (not
+% \texttt{p}).
+% \end{function}
+%
+% The conditionals are defined by \cs{prg_new_conditional:Npnn} and
+% friends as:
+% \begin{itemize}
+% \item \cs[no-index]{\meta{name}_p:\meta{arg spec}} --- a predicate function
+% which will supply either a logical \texttt{true} or logical
+% \texttt{false}. This function is intended for use in cases where
+% one or more logical tests are combined to lead to a final outcome.
+% This function cannot be defined for \texttt{protected}
+% conditionals.
+% \item \cs[no-index]{\meta{name}:\meta{arg spec}T} --- a function with one more
+% argument than the original \meta{arg spec} demands. The \meta{true
+% branch} code in this additional argument will be left on the
+% input stream only if the test is \texttt{true}.
+% \item \cs[no-index]{\meta{name}:\meta{arg spec}F} --- a function with one more
+% argument than the original \meta{arg spec} demands. The
+% \meta{false branch} code in this additional argument will be left
+% on the input stream only if the test is \texttt{false}.
+% \item \cs[no-index]{\meta{name}:\meta{arg spec}TF} --- a function with two
+% more argument than the original \meta{arg spec} demands. The
+% \meta{true branch} code in the first additional argument will be
+% left on the input stream if the test is \texttt{true}, while the
+% \meta{false branch} code in the second argument will be left on
+% the input stream if the test is \texttt{false}.
+% \end{itemize}
+% The \meta{code} of the test may use \meta{parameters} as specified by
+% the second argument to \cs{prg_set_conditional:Npnn}: this should
+% match the \meta{argument specification} but this is not enforced. The
+% |Nnn| versions infer the number of arguments from the argument
+% specification given (\emph{cf.}~\cs{cs_new:Nn}, \emph{etc.}). Within
+% the \meta{code}, the functions \cs{prg_return_true:} and
+% \cs{prg_return_false:} are used to indicate the logical outcomes of
+% the test.
+%
+% An example can easily clarify matters here:
+% \begin{verbatim}
+% \prg_set_conditional:Npnn \foo_if_bar:NN #1#2 { p , T , TF }
+% {
+% \if_meaning:w \l_tmpa_tl #1
+% \prg_return_true:
+% \else:
+% \if_meaning:w \l_tmpa_tl #2
+% \prg_return_true:
+% \else:
+% \prg_return_false:
+% \fi:
+% \fi:
+% }
+% \end{verbatim}
+% This defines the function |\foo_if_bar_p:NN|, |\foo_if_bar:NNTF| and
+% |\foo_if_bar:NNT| but not |\foo_if_bar:NNF| (because |F| is missing
+% from the \meta{conditions} list). The return statements take care of
+% resolving the remaining \cs{else:} and \cs{fi:} before returning the
+% state. There must be a return statement for each branch; failing to do
+% so will result in erroneous output if that branch is executed.
+%
+% The special case where the code of a conditional ends with
+% \cs{prg_return_true:} \cs{else:} \cs{prg_return_false:} \cs{fi:} is
+% optimized.
+%
+% \begin{function}[updated = 2023-05-26]
+% {
+% \prg_new_eq_conditional:NNn,
+% \prg_set_eq_conditional:NNn,
+% \prg_gset_eq_conditional:NNn
+% }
+% \begin{syntax}
+% \cs{prg_new_eq_conditional:NNn} \cs[no-index]{\meta{name_1}:\meta{arg spec_1}} \cs[no-index]{\meta{name_2}:\meta{arg spec_2}} \Arg{conditions}
+% \end{syntax}
+% These functions copy a family of conditionals. The \texttt{new} version
+% checks for existing definitions (\emph{cf.}~\cs{cs_new_eq:NN}) whereas
+% the \texttt{set} version does not (\emph{cf.}~\cs{cs_set_eq:NN}). The
+% conditionals copied are depended on the comma-separated list of
+% \meta{conditions}, which should be one or more of \texttt{p}, \texttt{T},
+% \texttt{F} and \texttt{TF}.
+% \end{function}
+%
+% \begin{function}[EXP]{\prg_return_true:, \prg_return_false:}
+% \begin{syntax}
+% \cs{prg_return_true:}
+% \cs{prg_return_false:}
+% \end{syntax}
+% These \enquote{return} functions define the logical state of a conditional statement.
+% They appear within the code for a conditional
+% function generated by \cs{prg_set_conditional:Npnn}, \emph{etc}, to indicate
+% when a true or false branch should be taken.
+% While they may appear multiple times each within the code of such conditionals,
+% the execution of the conditional must result in the expansion of one of these
+% two functions \emph{exactly once}.
+%
+% The return functions trigger what is internally an \texttt{f}-expansion process to complete
+% the evaluation of the conditional. Therefore, after \cs{prg_return_true:} or \cs{prg_return_false:}
+% there must be no non-expandable material in the input stream for the remainder of
+% the expansion of the conditional code. This includes other instances of either of these functions.
+% \end{function}
+%
+% \begin{function}[added = 2017-12-12]{\prg_generate_conditional_variant:Nnn}
+% \begin{syntax}
+% \cs{prg_generate_conditional_variant:Nnn} \cs[no-index]{\meta{name}:\meta{arg spec}} \Arg{variant argument specifiers} \Arg{condition specifiers}
+% \end{syntax}
+% Defines argument-specifier variants of conditionals. This is
+% equivalent to running \cs{cs_generate_variant:Nn} \meta{conditional}
+% \Arg{variant argument specifiers} on each \meta{conditional}
+% described by the \meta{condition specifiers}. These base-form
+% \meta{conditionals} are obtained from the \meta{name} and \meta{arg
+% spec} as described for \cs{prg_new_conditional:Npnn}, and they
+% should be defined.
+% \end{function}
+%
+% \section{The boolean data type}
+%
+% This section describes a boolean data type which is closely
+% connected to conditional processing as sometimes you want to
+% execute some code depending on the value of a switch
+% (\emph{e.g.},~draft/final) and other times you perhaps want to use it as a
+% predicate function in an \cs{if_predicate:w} test. The problem of the
+% primitive \cs{if_false:} and \cs{if_true:} tokens is that it is not
+% always safe to pass them around as they may interfere with scanning
+% for termination of primitive conditional processing. Therefore, we
+% employ two canonical booleans: \cs{c_true_bool} or
+% \cs{c_false_bool}. Besides preventing problems as described above, it
+% also allows us to implement a simple boolean parser supporting the
+% logical operations And, Or, Not, \emph{etc.}\ which can then be used on
+% both the boolean type and predicate functions.
+%
+% All conditional |\bool_| functions except assignments are expandable
+% and expect the input to also be fully expandable (which generally
+% means being constructed from predicate functions and booleans, possibly nested).
+%
+% \begin{texnote}
+% The \texttt{bool} data type is not implemented using the
+% \tn{iffalse}/\tn{iftrue} primitives, in contrast to \tn{newif},
+% \emph{etc.}, in plain \TeX{}, \LaTeXe{} and so on. Programmers should
+% not base use of \texttt{bool} switches on any particular expectation
+% of the implementation.
+% \end{texnote}
+%
+% \begin{function}{\bool_new:N, \bool_new:c}
+% \begin{syntax}
+% \cs{bool_new:N} \meta{boolean}
+% \end{syntax}
+% Creates a new \meta{boolean} or raises an error if the
+% name is already taken. The declaration is global. The
+% \meta{boolean} is initially \texttt{false}.
+% \end{function}
+%
+% \begin{function}[added = 2017-11-28]{\bool_const:Nn, \bool_const:cn}
+% \begin{syntax}
+% \cs{bool_const:Nn} \meta{boolean} \Arg{boolexpr}
+% \end{syntax}
+% Creates a new constant \meta{boolean} or raises an error if the name
+% is already taken. The value of the \meta{boolean} is set globally to
+% the result of evaluating the \meta{boolexpr}.
+% \end{function}
+%
+% \begin{function}
+% {
+% \bool_set_false:N , \bool_set_false:c ,
+% \bool_gset_false:N, \bool_gset_false:c
+% }
+% \begin{syntax}
+% \cs{bool_set_false:N} \meta{boolean}
+% \end{syntax}
+% Sets \meta{boolean} logically \texttt{false}.
+% \end{function}
+%
+% \begin{function}
+% {
+% \bool_set_true:N , \bool_set_true:c ,
+% \bool_gset_true:N , \bool_gset_true:c
+% }
+% \begin{syntax}
+% \cs{bool_set_true:N} \meta{boolean}
+% \end{syntax}
+% Sets \meta{boolean} logically \texttt{true}.
+% \end{function}
+%
+% \begin{function}
+% {
+% \bool_set_eq:NN , \bool_set_eq:cN , \bool_set_eq:Nc , \bool_set_eq:cc ,
+% \bool_gset_eq:NN, \bool_gset_eq:cN, \bool_gset_eq:Nc, \bool_gset_eq:cc
+% }
+% \begin{syntax}
+% \cs{bool_set_eq:NN} \meta{boolean_1} \meta{boolean_2}
+% \end{syntax}
+% Sets \meta{boolean_1} to the current value of \meta{boolean_2}.
+% \end{function}
+%
+% \begin{function}[updated = 2017-07-15]
+% {\bool_set:Nn, \bool_set:cn, \bool_gset:Nn, \bool_gset:cn}
+% \begin{syntax}
+% \cs{bool_set:Nn} \meta{boolean} \Arg{boolexpr}
+% \end{syntax}
+% Evaluates the \meta{boolean expression} as described for
+% \cs{bool_if:nTF}, and sets the \meta{boolean} variable to
+% the logical truth of this evaluation.
+% \end{function}
+%
+% \begin{function}[added = 2018-05-10]
+% {
+% \bool_set_inverse:N , \bool_set_inverse:c ,
+% \bool_gset_inverse:N, \bool_gset_inverse:c
+% }
+% \begin{syntax}
+% \cs{bool_set_inverse:N} \meta{boolean}
+% \end{syntax}
+% Toggles the \meta{boolean} from \texttt{true} to \texttt{false} and
+% conversely: sets it to the inverse of its current value.
+% \end{function}
+%
+% \begin{function}[EXP,pTF, updated = 2017-07-15]{\bool_if:N, \bool_if:c}
+% \begin{syntax}
+% \cs{bool_if_p:N} \meta{boolean}
+% \cs{bool_if:NTF} \meta{boolean} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests the current truth of \meta{boolean}, and continues expansion
+% based on this result.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2021-11-01, updated = 2023-11-14]
+% {\bool_to_str:N, \bool_to_str:c, \bool_to_str:n}
+% \begin{syntax}
+% \cs{bool_to_str:N} \meta{boolean}
+% \cs{bool_to_str:n} \meta{boolean expression}
+% \end{syntax}
+% Expands to the string \texttt{true} or \texttt{false} depending on
+% the logical truth of the \meta{boolean} or \meta{boolean
+% expression}.
+% \end{function}
+%
+% \begin{function}[added = 2012-02-09, updated = 2021-04-29]{\bool_show:N, \bool_show:c}
+% \begin{syntax}
+% \cs{bool_show:N} \meta{boolean}
+% \end{syntax}
+% Displays the logical truth of the \meta{boolean} on the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2012-02-09, updated = 2017-07-15]{\bool_show:n}
+% \begin{syntax}
+% \cs{bool_show:n} \Arg{boolean expression}
+% \end{syntax}
+% Displays the logical truth of the \meta{boolean expression} on the
+% terminal.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2021-04-29]{\bool_log:N, \bool_log:c}
+% \begin{syntax}
+% \cs{bool_log:N} \meta{boolean}
+% \end{syntax}
+% Writes the logical truth of the \meta{boolean} in the log file.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2017-07-15]{\bool_log:n}
+% \begin{syntax}
+% \cs{bool_log:n} \Arg{boolean expression}
+% \end{syntax}
+% Writes the logical truth of the \meta{boolean expression} in the log
+% file.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added=2012-03-03]
+% {\bool_if_exist:N, \bool_if_exist:c}
+% \begin{syntax}
+% \cs{bool_if_exist_p:N} \meta{boolean}
+% \cs{bool_if_exist:NTF} \meta{boolean} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{boolean} is currently defined. This does not
+% check that the \meta{boolean} really is a boolean variable.
+% \end{function}
+%
+% \subsection{Constant and scratch booleans}
+%
+% \begin{variable}{\c_true_bool, \c_false_bool}
+% Constants that represent |true| and |false|, respectively. Used to
+% implement predicates.
+% \end{variable}
+%
+% \begin{variable}{\l_tmpa_bool, \l_tmpb_bool}
+% A scratch boolean for local assignment. It is never used by
+% the kernel code, and so is safe for use with any \LaTeX3-defined
+% function. However, it may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \begin{variable}{\g_tmpa_bool, \g_tmpb_bool}
+% A scratch boolean for global assignment. It is never used by
+% the kernel code, and so is safe for use with any \LaTeX3-defined
+% function. However, it may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \section{Boolean expressions}
+%
+% As we have a boolean datatype and predicate functions returning
+% boolean \meta{true} or \meta{false} values, it seems only fitting
+% that we also provide a parser for \meta{boolean expressions}.
+%
+% A boolean expression is an expression which given input in the form
+% of predicate functions and boolean variables, return boolean
+% \meta{true} or \meta{false}. It supports the logical operations And,
+% Or and Not as the well-known infix operators |&&| and \verb"||" and prefix~|!|
+% with their usual precedences (namely, |&&| binds more tightly than
+% \verb"||"). In addition to this, parentheses can be used to isolate
+% sub-expressions. For example,
+% \begin{verbatim}
+% \int_compare_p:n { 1 = 1 } &&
+% (
+% \int_compare_p:n { 2 = 3 } ||
+% \int_compare_p:n { 4 <= 4 } ||
+% \str_if_eq_p:nn { abc } { def }
+% ) &&
+% ! \int_compare_p:n { 2 = 4 }
+% \end{verbatim}
+% is a valid boolean expression.
+%
+% Contrarily to some other programming languages, the operators |&&| and
+% \verb"||" evaluate both operands in all cases, even when the first
+% operand is enough to determine the result. This \enquote{eager}
+% evaluation should be contrasted with the \enquote{lazy} evaluation of
+% \cs[no-index]{bool_lazy_\ldots{}} functions.
+%
+% \begin{texnote}
+% The eager evaluation of boolean expressions is unfortunately
+% necessary in \TeX{}. Indeed, a lazy parser can get confused if |&&|
+% or \verb"||" or parentheses appear as (unbraced) arguments of some
+% predicates. For instance, the innocuous-looking expression below
+% would break (in a lazy parser) if |#1| were a closing parenthesis
+% and \cs[no-index]{l_tmpa_bool} were \texttt{true}.
+% \begin{verbatim}
+% ( \l_tmpa_bool || \token_if_eq_meaning_p:NN X #1 )
+% \end{verbatim}
+% \end{texnote}
+%
+% Minimal (lazy) evaluation can be obtained using the conditionals
+% \cs{bool_lazy_all:nTF}, \cs{bool_lazy_and:nnTF}, \cs{bool_lazy_any:nTF}, or
+% \cs{bool_lazy_or:nnTF}, which only evaluate their boolean expression
+% arguments when they are needed to determine the resulting truth
+% value. For example, when evaluating the boolean expression
+% \begin{verbatim}
+% \bool_lazy_and_p:nn
+% {
+% \bool_lazy_any_p:n
+% {
+% { \int_compare_p:n { 2 = 3 } }
+% { \int_compare_p:n { 4 <= 4 } }
+% { \int_compare_p:n { 1 = \error } } % skipped
+% }
+% }
+% { ! \int_compare_p:n { 2 = 4 } }
+% \end{verbatim}
+% the line marked with |skipped| is not expanded because the result
+% of \cs{bool_lazy_any_p:n} is known once the second boolean expression is
+% found to be logically \texttt{true}. On the other hand, the last
+% line is expanded because its logical value is needed to determine the
+% result of \cs{bool_lazy_and_p:nn}.
+%
+% \begin{function}[EXP, pTF, updated = 2017-07-15]{\bool_if:n}
+% \begin{syntax}
+% \cs{bool_if_p:n} \Arg{boolean expression}
+% \cs{bool_if:nTF} \Arg{boolean expression} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests the current truth of \meta{boolean expression}, and
+% continues expansion based on this result. The
+% \meta{boolean expression} should consist of a series of predicates
+% or boolean variables with the logical relationship between these
+% defined using |&&| (\enquote{And}), \verb"||" (\enquote{Or}),
+% |!| (\enquote{Not}) and parentheses. The logical Not applies to
+% the next predicate or group.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added = 2015-11-15, updated = 2017-07-15]{\bool_lazy_all:n}
+% \begin{syntax}
+% \cs{bool_lazy_all_p:n} \{ \Arg{boolexpr_1} \Arg{boolexpr_2} $\cdots$ \Arg{boolexpr_N} \}
+% \cs{bool_lazy_all:nTF} \{ \Arg{boolexpr_1} \Arg{boolexpr_2} $\cdots$ \Arg{boolexpr_N} \} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Implements the \enquote{And} operation on the \meta{boolean
+% expressions}, hence is \texttt{true} if all of them are
+% \texttt{true} and \texttt{false} if any of them is \texttt{false}.
+% Contrarily to the infix operator |&&|, only the \meta{boolean
+% expressions} which are needed to determine the result of
+% \cs{bool_lazy_all:nTF} are evaluated. See also \cs{bool_lazy_and:nnTF}
+% when there are only two \meta{boolean expressions}.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added = 2015-11-15, updated = 2017-07-15]{\bool_lazy_and:nn}
+% \begin{syntax}
+% \cs{bool_lazy_and_p:nn} \Arg{boolexpr_1} \Arg{boolexpr_2}
+% \cs{bool_lazy_and:nnTF} \Arg{boolexpr_1} \Arg{boolexpr_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Implements the \enquote{And} operation between two boolean
+% expressions, hence is \texttt{true} if both are \texttt{true}.
+% Contrarily to the infix operator |&&|, the \meta{boolexpr_2} is only
+% evaluated if it is needed to determine the result of
+% \cs{bool_lazy_and:nnTF}. See also \cs{bool_lazy_all:nTF} when there are more
+% than two \meta{boolean expressions}.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added = 2015-11-15, updated = 2017-07-15]{\bool_lazy_any:n}
+% \begin{syntax}
+% \cs{bool_lazy_any_p:n} \{ \Arg{boolexpr_1} \Arg{boolexpr_2} $\cdots$ \Arg{boolexpr_N} \}
+% \cs{bool_lazy_any:nTF} \{ \Arg{boolexpr_1} \Arg{boolexpr_2} $\cdots$ \Arg{boolexpr_N} \} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Implements the \enquote{Or} operation on the \meta{boolean
+% expressions}, hence is \texttt{true} if any of them is
+% \texttt{true} and \texttt{false} if all of them are \texttt{false}.
+% Contrarily to the infix operator \verb"||", only the \meta{boolean
+% expressions} which are needed to determine the result of
+% \cs{bool_lazy_any:nTF} are evaluated. See also \cs{bool_lazy_or:nnTF}
+% when there are only two \meta{boolean expressions}.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added = 2015-11-15, updated = 2017-07-15]{\bool_lazy_or:nn}
+% \begin{syntax}
+% \cs{bool_lazy_or_p:nn} \Arg{boolexpr_1} \Arg{boolexpr_2}
+% \cs{bool_lazy_or:nnTF} \Arg{boolexpr_1} \Arg{boolexpr_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Implements the \enquote{Or} operation between two boolean
+% expressions, hence is \texttt{true} if either one is \texttt{true}.
+% Contrarily to the infix operator \verb"||", the \meta{boolexpr_2}
+% is only evaluated if it is needed to determine the result of
+% \cs{bool_lazy_or:nnTF}. See also \cs{bool_lazy_any:nTF} when there are more
+% than two \meta{boolean expressions}.
+% \end{function}
+%
+% \begin{function}[EXP, updated = 2017-07-15]{\bool_not_p:n}
+% \begin{syntax}
+% \cs{bool_not_p:n} \Arg{boolean expression}
+% \end{syntax}
+% Function version of |!(|\meta{boolean expression}|)| within a boolean
+% expression.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added = 2018-05-09]{\bool_xor:nn}
+% \begin{syntax}
+% \cs{bool_xor_p:nn} \Arg{boolexpr_1} \Arg{boolexpr_2}
+% \cs{bool_xor:nnTF} \Arg{boolexpr_1} \Arg{boolexpr_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Implements an \enquote{exclusive or} operation between two boolean
+% expressions. There is no infix operation for this logical operation.
+% \end{function}
+%
+% \section{Logical loops}
+%
+% Loops using either boolean expressions or stored boolean values.
+%
+% \begin{function}[rEXP, updated = 2017-07-15]{\bool_do_until:Nn, \bool_do_until:cn}
+% \begin{syntax}
+% \cs{bool_do_until:Nn} \meta{boolean} \Arg{code}
+% \end{syntax}
+% Places the \meta{code} in the input stream for \TeX{} to process,
+% and then checks the logical value of the \meta{boolean}. If it is
+% \texttt{false} then the \meta{code} is inserted into the input
+% stream again and the process loops until the \meta{boolean} is
+% \texttt{true}.
+% \end{function}
+%
+% \begin{function}[rEXP, updated = 2017-07-15]{\bool_do_while:Nn, \bool_do_while:cn}
+% \begin{syntax}
+% \cs{bool_do_while:Nn} \meta{boolean} \Arg{code}
+% \end{syntax}
+% Places the \meta{code} in the input stream for \TeX{} to process,
+% and then checks the logical value of the \meta{boolean}. If it is
+% \texttt{true} then the \meta{code} is inserted into the input
+% stream again and the process loops until the \meta{boolean} is
+% \texttt{false}.
+% \end{function}
+%
+% \begin{function}[rEXP, updated = 2017-07-15]{\bool_until_do:Nn, \bool_until_do:cn}
+% \begin{syntax}
+% \cs{bool_until_do:Nn} \meta{boolean} \Arg{code}
+% \end{syntax}
+% This function first checks the logical value of the \meta{boolean}.
+% If it is \texttt{false} the \meta{code} is placed in the input stream
+% and expanded. After the completion of the \meta{code} the truth
+% of the \meta{boolean} is re-evaluated. The process then loops
+% until the \meta{boolean} is \texttt{true}.
+% \end{function}
+%
+% \begin{function}[rEXP, updated = 2017-07-15]{\bool_while_do:Nn, \bool_while_do:cn}
+% \begin{syntax}
+% \cs{bool_while_do:Nn} \meta{boolean} \Arg{code}
+% \end{syntax}
+% This function first checks the logical value of the \meta{boolean}.
+% If it is \texttt{true} the \meta{code} is placed in the input stream
+% and expanded. After the completion of the \meta{code} the truth
+% of the \meta{boolean} is re-evaluated. The process then loops
+% until the \meta{boolean} is \texttt{false}.
+% \end{function}
+%
+% \begin{function}[rEXP, updated = 2017-07-15]{\bool_do_until:nn}
+% \begin{syntax}
+% \cs{bool_do_until:nn} \Arg{boolean expression} \Arg{code}
+% \end{syntax}
+% Places the \meta{code} in the input stream for \TeX{} to process,
+% and then checks the logical value of the \meta{boolean expression}
+% as described for \cs{bool_if:nTF}. If it is \texttt{false} then the
+% \meta{code} is inserted into the input stream again and the
+% process loops until the \meta{boolean expression} evaluates to
+% \texttt{true}.
+% \end{function}
+%
+% \begin{function}[rEXP, updated = 2017-07-15]{\bool_do_while:nn}
+% \begin{syntax}
+% \cs{bool_do_while:nn} \Arg{boolean expression} \Arg{code}
+% \end{syntax}
+% Places the \meta{code} in the input stream for \TeX{} to process,
+% and then checks the logical value of the \meta{boolean expression}
+% as described for \cs{bool_if:nTF}. If it is \texttt{true} then the
+% \meta{code} is inserted into the input stream again and the
+% process loops until the \meta{boolean expression} evaluates to
+% \texttt{false}.
+% \end{function}
+%
+% \begin{function}[rEXP, updated = 2017-07-15]{\bool_until_do:nn}
+% \begin{syntax}
+% \cs{bool_until_do:nn} \Arg{boolean expression} \Arg{code}
+% \end{syntax}
+% This function first checks the logical value of the
+% \meta{boolean expression} (as described for \cs{bool_if:nTF}).
+% If it is \texttt{false} the \meta{code} is placed in the input stream
+% and expanded. After the completion of the \meta{code} the truth
+% of the \meta{boolean expression} is re-evaluated. The process
+% then loops until the \meta{boolean expression} is \texttt{true}.
+% \end{function}
+%
+% \begin{function}[rEXP, updated = 2017-07-15]{\bool_while_do:nn}
+% \begin{syntax}
+% \cs{bool_while_do:nn} \Arg{boolean expression} \Arg{code}
+% \end{syntax}
+% This function first checks the logical value of the
+% \meta{boolean expression} (as described for \cs{bool_if:nTF}).
+% If it is \texttt{true} the \meta{code} is placed in the input stream
+% and expanded. After the completion of the \meta{code} the truth
+% of the \meta{boolean expression} is re-evaluated. The process
+% then loops until the \meta{boolean expression} is \texttt{false}.
+% \end{function}
+%
+% \begin{function}[added = 2023-05-03, EXP, noTF]{\bool_case:n}
+% \begin{syntax}
+% \cs{bool_case:nTF} \\
+% ~~|{| \\
+% ~~~~\Arg{boolexpr case_1} \Arg{code case_1} \\
+% ~~~~\Arg{boolexpr case_2} \Arg{code case_2} \\
+% ~~~~\ldots \\
+% ~~~~\Arg{boolexpr case_n} \Arg{code case_n} \\
+% ~~|}| \\
+% ~~\Arg{true code}
+% ~~\Arg{false code}
+% \end{syntax}
+% Evaluates in turn each of the \meta{boolean expression cases} until
+% the first one that evaluates to \texttt{true}.
+% The \meta{code} associated to this first case is left in the input
+% stream, followed by the \meta{true code}, and other cases are
+% discarded. If none of the cases match then only the \meta{false
+% code} is inserted. The function \cs{bool_case:n},
+% which does nothing if there is no match, is
+% also available. For example
+% \begin{verbatim}
+% \bool_case:nF
+% {
+% { \dim_compare_p:n { \l__mypkg_wd_dim <= 10pt } }
+% { Fits }
+% { \int_compare_p:n { \l__mypkg_total_int >= 10 } }
+% { Many }
+% { \l__mypkg_special_bool }
+% { Special }
+% }
+% { No idea! }
+% \end{verbatim}
+% leaves \enquote{\texttt{Fits}} or \enquote{\texttt{Many}} or
+% \enquote{\texttt{Special}} or \enquote{\texttt{No idea!}} in the
+% input stream, in a way similar to some other language's
+% \enquote{\texttt{if} \ldots\ \texttt{elseif} \ldots\ \texttt{elseif} \ldots\
+% \texttt{else} \ldots}.
+% \end{function}
+%
+% \section{Producing multiple copies}
+%
+% \begin{function}[updated = 2011-07-04, EXP]{\prg_replicate:nn}
+% \begin{syntax}
+% \cs{prg_replicate:nn} \Arg{integer expression} \Arg{tokens}
+% \end{syntax}
+% Evaluates the \meta{integer expression} (which should be
+% zero or positive) and creates the resulting number of copies
+% of the \meta{tokens}. The function is both expandable and safe for
+% nesting. It yields its result after two expansion steps.
+% \end{function}
+%
+% \section{Detecting \TeX{}'s mode}
+%
+% \begin{function}[EXP,pTF]{\mode_if_horizontal:}
+% \begin{syntax}
+% \cs{mode_if_horizontal_p:}
+% \cs{mode_if_horizontal:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Detects if \TeX{} is currently in horizontal mode.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\mode_if_inner:}
+% \begin{syntax}
+% \cs{mode_if_inner_p:}
+% \cs{mode_if_inner:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Detects if \TeX{} is currently in inner mode.
+% \end{function}
+%
+% \begin{function}[updated = 2011-09-05, EXP,pTF]{\mode_if_math:}
+% \begin{syntax}
+% \cs{mode_if_math_p:}
+% \cs{mode_if_math:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Detects if \TeX{} is currently in maths mode.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\mode_if_vertical:}
+% \begin{syntax}
+% \cs{mode_if_vertical_p:}
+% \cs{mode_if_vertical:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Detects if \TeX{} is currently in vertical mode.
+% \end{function}
+%
+% \section{Primitive conditionals}
+%
+% \begin{function}[EXP]{\if_predicate:w}
+% \begin{syntax}
+% \cs{if_predicate:w} \meta{predicate} \meta{true code} \cs{else:} \meta{false code} \cs{fi:}
+% \end{syntax}
+% This function takes a predicate function and
+% branches according to the result. (In practice this function would also
+% accept a single boolean variable in place of the \meta{predicate} but to make the
+% coding clearer this should be done through \cs{if_bool:N}.)
+% \end{function}
+%
+% \begin{function}[EXP]{\if_bool:N}
+% \begin{syntax}
+% \cs{if_bool:N} \meta{boolean} \meta{true code} \cs{else:} \meta{false code} \cs{fi:}
+% \end{syntax}
+% This function takes a boolean variable and
+% branches according to the result.
+% \end{function}
+%
+% \section{Nestable recursions and mappings}
+%
+% There are a number of places where recursion or mapping constructs are used in
+% \pkg{expl3}. At a low-level, these typically require insertion of tokens
+% at the end of the content to allow \enquote{clean up}. To support such
+% mappings in a nestable form, the following functions are provided.
+%
+% \begin{function}[EXP, added = 2018-03-26]{\prg_break_point:Nn}
+% \begin{syntax}
+% \cs{prg_break_point:Nn} \cs[no-index]{\meta{type}_map_break:} \Arg{code}
+% \end{syntax}
+% Used to mark the end of a recursion or mapping: the functions
+% \cs[no-index]{\meta{type}_map_break:} and
+% \cs[no-index]{\meta{type}_map_break:n} use this to break out of the loop
+% (see \cs{prg_map_break:Nn} for how to set these up).
+% After the loop ends, the \meta{code} is inserted into the input stream. This
+% occurs even if the break functions are \emph{not} applied:
+% \cs{prg_break_point:Nn} is functionally-equivalent in these cases
+% to \cs{use_ii:nn}.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-03-26]{\prg_map_break:Nn}
+% \begin{syntax}
+% \cs{prg_map_break:Nn} \cs[no-index]{\meta{type}_map_break:} \Arg{user code}
+% \ldots{}
+% \cs{prg_break_point:Nn} \cs[no-index]{\meta{type}_map_break:} \Arg{ending code}
+% \end{syntax}
+% Breaks a recursion in mapping contexts, inserting in the input
+% stream the \meta{user code} after the \meta{ending code} for the
+% loop. The function breaks loops, inserting their \meta{ending
+% code}, until reaching a loop with the same \meta{type} as its
+% first argument. This \cs[no-index]{\meta{type}_map_break:} argument
+% must be defined; it is simply used as a recognizable marker for the \meta{type}.
+%
+% For types with mappings defined in the kernel,
+% \cs[no-index]{\meta{type}_map_break:} and \cs[no-index]{\meta{type}_map_break:n}
+% are defined as \cs{prg_map_break:Nn} \cs[no-index]{\meta{type}_map_break:} |{}|
+% and the same with |{}| omitted.
+% \end{function}
+%
+% \subsection{Simple mappings}
+%
+% In addition to the more complex mappings above, non-nestable mappings are
+% used in a number of locations and support is provided for these.
+%
+% \begin{function}[EXP, added = 2018-03-27]{\prg_break_point:}
+% This copy of \cs{prg_do_nothing:} is used to mark the end of a fast
+% short-term recursion: the function \cs{prg_break:n} uses this to
+% break out of the loop.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-03-27]{\prg_break:, \prg_break:n}
+% \begin{syntax}
+% \cs{prg_break:n} \Arg{code} \ldots{} \cs{prg_break_point:}
+% \end{syntax}
+% Breaks a recursion which has no \meta{ending code} and which is not
+% a user-breakable mapping (see for instance
+% implementation of \cs{int_step_function:nnnN}), and
+% inserts the \meta{code} in the input stream.
+% \end{function}
+%
+% \section{Internal programming functions}
+%
+% \begin{function}[updated = 2011-08-11, EXP]
+% {\group_align_safe_begin:, \group_align_safe_end:}
+% \begin{syntax}
+% \cs{group_align_safe_begin:}
+% \ldots
+% \cs{group_align_safe_end:}
+% \end{syntax}
+% These functions are used to enclose material in a \TeX{} alignment
+% environment within a specially-constructed group. This group is
+% designed in such a way that it does not add brace groups to the
+% output but does act as a group for the |&| token inside
+% \tn{halign}. This is necessary to allow grabbing of tokens
+% for testing purposes, as \TeX{} uses group level to determine the
+% effect of alignment tokens. Without the special grouping, the use of
+% a function such as \cs{peek_after:Nw} would result in a forbidden
+% comparison of the internal \tn{endtemplate} token, yielding a
+% fatal error. Each \cs{group_align_safe_begin:} must be matched by a
+% \cs{group_align_safe_end:}, although this does not have to occur
+% within the same function.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3prg} implementation}
+%
+% \TestFiles{m3prg001.lvt,m3prg002.lvt,m3prg003.lvt}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \subsection{Primitive conditionals}
+%
+% \begin{macro}{\if_bool:N}
+% \begin{macro}{\if_predicate:w}
+% Those two primitive \TeX{} conditionals are synonyms.
+% \cs{if_bool:N} is defined in \pkg{l3basics}, as it's needed
+% earlier to define quark test functions.
+% \begin{macrocode}
+\cs_new_eq:NN \if_predicate:w \tex_ifodd:D
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Defining a set of conditional functions}
+%
+% \begin{macro}
+% {
+% \prg_set_conditional:Npnn,
+% \prg_new_conditional:Npnn,
+% \prg_set_protected_conditional:Npnn,
+% \prg_new_protected_conditional:Npnn
+% }
+% \begin{macro}
+% {
+% \prg_set_conditional:Nnn,
+% \prg_new_conditional:Nnn,
+% \prg_set_protected_conditional:Nnn,
+% \prg_new_protected_conditional:Nnn
+% }
+% \begin{macro}{\prg_set_eq_conditional:NNn, \prg_new_eq_conditional:NNn}
+% \begin{macro}{\prg_return_true:}
+% \TestMissing
+% {This function is implicitly tested with all other conditionals!}
+% \begin{macro}{\prg_return_false:}
+% \TestMissing
+% {This function is also implicitly tested with all other conditionals!}
+% These are all defined in \pkg{l3basics}, as they are needed
+% \enquote{early}. This is just a reminder!
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{The boolean data type}
+%
+% \begin{macrocode}
+%<@@=bool>
+% \end{macrocode}
+%
+% \begin{macro}{\bool_new:N, \bool_new:c}
+% \UnitTested
+% Boolean variables have to be initiated when they are created. Other
+% than that there is not much to say here.
+% \begin{macrocode}
+\cs_new_protected:Npn \bool_new:N #1 { \cs_new_eq:NN #1 \c_false_bool }
+\cs_generate_variant:Nn \bool_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[added = 2017-11-28]{\bool_const:Nn, \bool_const:cn}
+% A merger between \cs{tl_const:Nn} and \cs{bool_set:Nn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \bool_const:Nn #1#2
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \tex_global:D \tex_chardef:D #1 = \bool_if_p:n {#2}
+ }
+\cs_generate_variant:Nn \bool_const:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \bool_set_true:N, \bool_set_true:c,
+% \bool_gset_true:N, \bool_gset_true:c,
+% \bool_set_false:N, \bool_set_false:c,
+% \bool_gset_false:N, \bool_gset_false:c
+% }
+% \UnitTested
+% Setting is already pretty easy.
+% When \texttt{check-declarations} is active, the definitions are patched to
+% make sure the boolean exists. This is needed because booleans are
+% not based on token lists nor on \TeX{} registers.
+% \begin{macrocode}
+\cs_new_protected:Npn \bool_set_true:N #1
+ { \cs_set_eq:NN #1 \c_true_bool }
+\cs_new_protected:Npn \bool_set_false:N #1
+ { \cs_set_eq:NN #1 \c_false_bool }
+\cs_new_protected:Npn \bool_gset_true:N #1
+ { \cs_gset_eq:NN #1 \c_true_bool }
+\cs_new_protected:Npn \bool_gset_false:N #1
+ { \cs_gset_eq:NN #1 \c_false_bool }
+\cs_generate_variant:Nn \bool_set_true:N { c }
+\cs_generate_variant:Nn \bool_set_false:N { c }
+\cs_generate_variant:Nn \bool_gset_true:N { c }
+\cs_generate_variant:Nn \bool_gset_false:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \bool_set_eq:NN, \bool_set_eq:cN,
+% \bool_set_eq:Nc, \bool_set_eq:cc,
+% \bool_gset_eq:NN, \bool_gset_eq:cN,
+% \bool_gset_eq:Nc, \bool_gset_eq:cc
+% }
+% \UnitTested
+% The usual copy code. While it would be cleaner semantically to copy
+% the \cs{cs_set_eq:NN} family of functions, we copy \cs{tl_set_eq:NN}
+% because that has the correct checking code.
+% \begin{macrocode}
+\cs_new_eq:NN \bool_set_eq:NN \tl_set_eq:NN
+\cs_new_eq:NN \bool_gset_eq:NN \tl_gset_eq:NN
+\cs_generate_variant:Nn \bool_set_eq:NN { Nc, cN, cc }
+\cs_generate_variant:Nn \bool_gset_eq:NN { Nc, cN, cc }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\bool_set:Nn, \bool_set:cn}
+% \begin{macro}{\bool_gset:Nn, \bool_gset:cn}
+% This function evaluates a boolean expression and assigns the first
+% argument the meaning \cs{c_true_bool} or \cs{c_false_bool}. Again,
+% we include some checking code. It is important to evaluate the
+% expression before applying the \tn{chardef} primitive, because that
+% primitive sets the left-hand side to \cs{scan_stop:} before looking
+% for the right-hand side.
+% \begin{macrocode}
+\cs_new_protected:Npn \bool_set:Nn #1#2
+ {
+ \exp_last_unbraced:NNNf
+ \tex_chardef:D #1 = { \bool_if_p:n {#2} }
+ }
+\cs_new_protected:Npn \bool_gset:Nn #1#2
+ {
+ \exp_last_unbraced:NNNNf
+ \tex_global:D \tex_chardef:D #1 = { \bool_if_p:n {#2} }
+ }
+\cs_generate_variant:Nn \bool_set:Nn { c }
+\cs_generate_variant:Nn \bool_gset:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[added = 2018-05-10]
+% {\bool_set_inverse:N, \bool_set_inverse:c, \bool_gset_inverse:N, \bool_gset_inverse:c}
+% Set to \texttt{false} or \texttt{true} locally or globally.
+% \begin{macrocode}
+\cs_new_protected:Npn \bool_set_inverse:N #1
+ { \bool_if:NTF #1 { \bool_set_false:N } { \bool_set_true:N } #1 }
+\cs_generate_variant:Nn \bool_set_inverse:N { c }
+\cs_new_protected:Npn \bool_gset_inverse:N #1
+ { \bool_if:NTF #1 { \bool_gset_false:N } { \bool_gset_true:N } #1 }
+\cs_generate_variant:Nn \bool_gset_inverse:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Internal auxiliaries}
+%
+% \begin{variable}{\q_@@_recursion_tail,\q_@@_recursion_stop}
+% Internal recursion quarks.
+% \begin{macrocode}
+\quark_new:N \q_@@_recursion_tail
+\quark_new:N \q_@@_recursion_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_use_i_delimit_by_q_recursion_stop:nw}
+% Functions to gobble up to a quark.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_i_delimit_by_q_recursion_stop:nw
+ #1 #2 \q_@@_recursion_stop {#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_if_recursion_tail_stop_do:nn}
+% Functions to query recursion quarks.
+% \begin{macrocode}
+\__kernel_quark_new_test:N \@@_if_recursion_tail_stop_do:nn
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\bool_if:N, \bool_if:c}
+% \UnitTested
+% Straight forward here. We could optimize here if we wanted to as
+% the boolean can just be input directly.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \bool_if:N #1 { p , T , F , TF }
+ {
+ \if_bool:N #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \bool_if:N { c } { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\bool_to_str:N, \bool_to_str:c, \bool_to_str:n}
+% Expands to string literal \texttt{true} or \texttt{false}.
+% \begin{macrocode}
+\cs_new:Npe \bool_to_str:N #1
+ {
+ \exp_not:N \bool_if:NTF #1
+ { \tl_to_str:n { true } } { \tl_to_str:n { false } }
+ }
+\cs_generate_variant:Nn \bool_to_str:N { c }
+\cs_new:Npe \bool_to_str:n #1
+ {
+ \exp_not:N \bool_if:nTF {#1}
+ { \tl_to_str:n { true } } { \tl_to_str:n { false } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\bool_show:n, \bool_log:n}
+% Show the truth value of the boolean.
+% \begin{macrocode}
+\cs_new_protected:Npn \bool_show:n
+ { \__kernel_msg_show_eval:Nn \bool_to_str:n }
+\cs_new_protected:Npn \bool_log:n
+ { \__kernel_msg_log_eval:Nn \bool_to_str:n }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\bool_show:N, \bool_show:c, \bool_log:N, \bool_log:c, \@@_show:NN}
+% Show the truth value of the boolean, as \texttt{true} or
+% \texttt{false}.
+% \begin{macrocode}
+\cs_new_protected:Npn \bool_show:N { \@@_show:NN \tl_show:n }
+\cs_generate_variant:Nn \bool_show:N { c }
+\cs_new_protected:Npn \bool_log:N { \@@_show:NN \tl_log:n }
+\cs_generate_variant:Nn \bool_log:N { c }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ \token_case_meaning:NnF #2
+ {
+ \c_true_bool { \exp_args:Ne #1 { \token_to_str:N #2 = true } }
+ \c_false_bool { \exp_args:Ne #1 { \token_to_str:N #2 = false } }
+ }
+ {
+ \msg_error:nneee { kernel } { bad-type }
+ { \token_to_str:N #2 } { \token_to_meaning:N #2 } { bool }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\l_tmpa_bool, \l_tmpb_bool, \g_tmpa_bool, \g_tmpb_bool}
+% A few booleans just if you need them.
+% \begin{macrocode}
+\bool_new:N \l_tmpa_bool
+\bool_new:N \l_tmpb_bool
+\bool_new:N \g_tmpa_bool
+\bool_new:N \g_tmpb_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[pTF]{\bool_if_exist:N, \bool_if_exist:c}
+% Copies of the \texttt{cs} functions defined in \pkg{l3basics}.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \bool_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \bool_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Boolean expressions}
+%
+% \begin{macro}[pTF]{\bool_if:n}
+% \UnitTested
+% Evaluating the truth value of a list of predicates is done using an
+% input syntax somewhat similar to the one found in other programming
+% languages with |(| and |)| for grouping, |!| for logical
+% \enquote{Not}, |&&| for logical \enquote{And} and \verb"||" for
+% logical \enquote{Or}. However, they perform eager evaluation.
+% We shall use the terms Not, And, Or, Open and
+% Close for these operations.
+%
+% Any expression is terminated by a Close operation. Evaluation
+% happens from left to right in the following manner using a GetNext
+% function:
+% \begin{itemize}
+% \item If an Open is seen, start evaluating a new expression using
+% the Eval function and call GetNext again.
+% \item If a Not is seen, remove the |!| and call a GetNext
+% function with the logic reversed.
+% \item If none of the above, reinsert the token found (this is
+% supposed to be a predicate function) in front of an Eval
+% function, which evaluates it to the boolean value \meta{true} or
+% \meta{false}.
+% \end{itemize}
+% The Eval function then contains a post-processing operation which
+% grabs the instruction following the predicate. This is either And,
+% Or or Close. In each case the truth value is used to determine
+% where to go next. The following situations can arise:
+% \begin{description}
+% \item[\meta{true}And] Current truth value is true, logical And
+% seen, continue with GetNext to examine truth value of next
+% boolean (sub-)expression.
+% \item[\meta{false}And] Current truth value is false, logical And
+% seen, stop using the values of predicates within this
+% sub-expression until the next Close. Then return \meta{false}.
+% \item[\meta{true}Or] Current truth value is true, logical Or seen,
+% stop using the values of predicates within this sub-expression
+% until the nearest Close. Then return \meta{true}.
+% \item[\meta{false}Or] Current truth value is false, logical Or
+% seen, continue with GetNext to examine truth value of next
+% boolean (sub-)expression.
+% \item[\meta{true}Close] Current truth value is true, Close
+% seen, return \meta{true}.
+% \item[\meta{false}Close] Current truth value is false, Close
+% seen, return \meta{false}.
+% \end{description}
+% \begin{macrocode}
+\prg_new_conditional:Npnn \bool_if:n #1 { T , F , TF }
+ {
+ \if_predicate:w \bool_if_p:n {#1}
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\bool_if_p:n, \@@_if_p:n, \@@_if_p_aux:w}
+% To speed up the case of a single predicate, \texttt{f}-expand and
+% check whether the result is one token (possibly surrounded by
+% spaces), which must be \cs{c_true_bool} or \cs{c_false_bool}. We
+% use a version of \cs{tl_if_single:nTF} optimized for speed since we
+% know that an empty~|#1| is an error. The auxiliary
+% \cs{@@_if_p_aux:w} removes the trailing parenthesis and gets rid of
+% any space, then returns \cs{c_true_bool} or \cs{c_false_bool} as
+% appropriate. This extra work around is because in a
+% \cs{bool_set:Nn}, the underlying \tn{chardef} turns the bool being
+% set temporarily equal to \tn{relax}, thus assigning a boolean to
+% itself would fail (gh/1055). For the general case, first issue a
+% \cs{group_align_safe_begin:} as we are using |&&| as
+% syntax shorthand for the And operation and we need to hide it for
+% \TeX{}. This group is closed after \cs{@@_get_next:NN} returns
+% \cs{c_true_bool} or \cs{c_false_bool}. That function requires the
+% trailing parenthesis to know where the expression ends.
+% \begin{macrocode}
+\cs_new:Npn \bool_if_p:n { \exp_args:Nf \@@_if_p:n }
+\cs_new:Npn \@@_if_p:n #1
+ {
+ \tl_if_empty:oT { \use_none:nn #1 . } { \@@_if_p_aux:w }
+ \group_align_safe_begin:
+ \exp_after:wN
+ \group_align_safe_end:
+ \exp:w \exp_end_continue_f:w % (
+ \@@_get_next:NN \use_i:nnnn #1 )
+ }
+\cs_new:Npn \@@_if_p_aux:w #1 \use_i:nnnn #2#3
+ { \bool_if:NTF #2 \c_true_bool \c_false_bool }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_get_next:NN}
+% The GetNext operation. Its first argument is \cs{use_i:nnnn},
+% \cs{use_ii:nnnn}, \cs{use_iii:nnnn}, or \cs{use_iv:nnnn} (we call
+% these \enquote{states}). In the first state, this function
+% eventually expand to the truth value \cs{c_true_bool} or
+% \cs{c_false_bool} of the expression which follows until the next
+% unmatched closing parenthesis. For instance
+% \enquote{\cs{@@_get_next:NN} \cs{use_i:nnnn} \cs{c_true_bool}
+% \texttt{\&\&} \cs{c_true_bool} \texttt{)}} (including the closing
+% parenthesis) expands to \cs{c_true_bool}. In the second state
+% (after a |!|) the logic is reversed. We call these two states
+% \enquote{normal} and the next two \enquote{skipping}. In the third
+% state (after \cs{c_true_bool}\verb"||") it always returns
+% \cs{c_true_bool}. In the fourth state (after \cs{c_false_bool}|&&|)
+% it always returns \cs{c_false_bool} and also stops when encountering
+% \verb"||", not only parentheses. This code itself is a switch: if
+% what follows is neither |!| nor |(|, we assume it is a predicate.
+% \begin{macrocode}
+\cs_new:Npn \@@_get_next:NN #1#2
+ {
+ \use:c
+ {
+ @@_
+ \if_meaning:w !#2 ! \else: \if_meaning:w (#2 ( \else: p \fi: \fi:
+ :Nw
+ }
+ #1 #2
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_!:Nw}
+% The Not operation reverses the logic: it discards the |!|~token and
+% calls the GetNext operation with the appropriate first argument.
+% Namely the first and second states are interchanged, but after
+% \cs{c_true_bool}\verb"||" or \cs{c_false_bool}|&&| the |!|~is
+% ignored.
+% \begin{macrocode}
+\cs_new:cpn { @@_!:Nw } #1#2
+ {
+ \exp_after:wN \@@_get_next:NN
+ #1 \use_ii:nnnn \use_i:nnnn \use_iii:nnnn \use_iv:nnnn
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_(:Nw}
+% The Open operation starts a sub-expression after discarding the open
+% parenthesis. This is done by calling GetNext (which eventually
+% discards the corresponding closing parenthesis), with a
+% post-processing step which looks for And, Or or Close after the
+% group.
+% \begin{macrocode}
+\cs_new:cpn { @@_(:Nw } #1#2
+ {
+ \exp_after:wN \@@_choose:NNN \exp_after:wN #1
+ \int_value:w \@@_get_next:NN \use_i:nnnn
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_p:Nw}
+% If what follows GetNext is neither |!| nor |(|, evaluate the
+% predicate using the primitive \cs{int_value:w}. The canonical
+% \texttt{true} and \texttt{false} values have numerical values $1$
+% and $0$ respectively. Look for And, Or or Close afterwards.
+% \begin{macrocode}
+\cs_new:cpn { @@_p:Nw } #1
+ { \exp_after:wN \@@_choose:NNN \exp_after:wN #1 \int_value:w }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_choose:NNN}
+% \begin{macro}+\@@_)_0:+
+% \begin{macro}+\@@_)_1:+
+% \begin{macro}+\@@_)_2:+
+% \begin{macro}+\@@_&_0:+
+% \begin{macro}+\@@_&_1:+
+% \begin{macro}+\@@_&_2:+
+% \begin{macro}+\@@_|_0:+
+% \begin{macro}+\@@_|_1:+
+% \begin{macro}+\@@_|_2:+
+% The arguments are |#1|: a function such as \cs{use_i:nnnn}, |#2|:
+% $0$ or $1$ encoding the current truth value, |#3|: the next
+% operation, And, Or or Close. We distinguish three cases according
+% to a combination of |#1| and |#2|. Case~2 is when |#1| is
+% \cs{use_iii:nnnn} (state~3), namely after
+% \cs{c_true_bool}~\verb"||". Case~1 is when |#1| is \cs{use_i:nnnn}
+% and |#2| is \texttt{true} or when |#1| is \cs{use_ii:nnnn} and |#2|
+% is \texttt{false}, for instance for |!|\cs{c_false_bool}. Case~0
+% includes the same with \texttt{true}/\texttt{false} interchanged and
+% the case where |#1| is \cs{use_iv:nnnn} namely after
+% \cs{c_false_bool}~|&&|.
+%
+% When seeing~|)| the current subexpression is done, leave the
+% appropriate boolean. When seeing~|&| in case~0 go into state~4,
+% equivalent to having seen \cs{c_false_bool}~|&&|. In case~1, namely
+% when the argument is \texttt{true} and we are in a normal state
+% continue in the normal state~1. In case~2, namely when skipping
+% alternatives in an Or, continue in the same state. When
+% seeing~\verb"|" in case~0, continue in a normal state; in particular
+% stop skipping for \cs{c_false_bool}~|&&| because that binds more
+% tightly than~\verb"||". In the other two cases start skipping for
+% \cs{c_true_bool}~\verb"||".
+% \begin{macrocode}
+\cs_new:Npn \@@_choose:NNN #1#2#3
+ {
+ \use:c
+ {
+ @@_ \token_to_str:N #3 _
+ #1 #2 { \if_meaning:w 0 #2 1 \else: 0 \fi: } 2 0 :
+ }
+ }
+\cs_new:cpn { @@_)_0: } { \c_false_bool }
+\cs_new:cpn { @@_)_1: } { \c_true_bool }
+\cs_new:cpn { @@_)_2: } { \c_true_bool }
+\cs_new:cpn { @@_&_0: } & { \@@_get_next:NN \use_iv:nnnn }
+\cs_new:cpn { @@_&_1: } & { \@@_get_next:NN \use_i:nnnn }
+\cs_new:cpn { @@_&_2: } & { \@@_get_next:NN \use_iii:nnnn }
+\cs_new:cpn { @@_|_0: } | { \@@_get_next:NN \use_i:nnnn }
+\cs_new:cpn { @@_|_1: } | { \@@_get_next:NN \use_iii:nnnn }
+\cs_new:cpn { @@_|_2: } | { \@@_get_next:NN \use_iii:nnnn }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\bool_lazy_all:n}
+% \begin{macro}{\@@_lazy_all:n}
+% \UnitTested
+% Go through the list of expressions, stopping whenever an expression
+% is \texttt{false}. If the end is reached without finding any
+% \texttt{false} expression, then the result is \texttt{true}.
+% \begin{macrocode}
+\cs_new:Npn \bool_lazy_all_p:n #1
+ { \@@_lazy_all:n #1 \q_@@_recursion_tail \q_@@_recursion_stop }
+\prg_new_conditional:Npnn \bool_lazy_all:n #1 { T , F , TF }
+ {
+ \if_predicate:w \bool_lazy_all_p:n {#1}
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \@@_lazy_all:n #1
+ {
+ \@@_if_recursion_tail_stop_do:nn {#1} { \c_true_bool }
+ \bool_if:nF {#1}
+ { \@@_use_i_delimit_by_q_recursion_stop:nw { \c_false_bool } }
+ \@@_lazy_all:n
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\bool_lazy_and:nn}
+% \UnitTested
+% Only evaluate the second expression if the first is \texttt{true}.
+% Note that |#2| must be removed as an argument, not just by skipping
+% to the \cs{else:} branch of the conditional since |#2| may contain
+% unbalanced \TeX{} conditionals.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \bool_lazy_and:nn #1#2 { p , T , F , TF }
+ {
+ \if_predicate:w
+ \bool_if:nTF {#1} { \bool_if_p:n {#2} } { \c_false_bool }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\bool_lazy_any:n}
+% \begin{macro}{\@@_lazy_any:n}
+% \UnitTested
+% Go through the list of expressions, stopping whenever an expression
+% is \texttt{true}. If the end is reached without finding any
+% \texttt{true} expression, then the result is \texttt{false}.
+% \begin{macrocode}
+\cs_new:Npn \bool_lazy_any_p:n #1
+ { \@@_lazy_any:n #1 \q_@@_recursion_tail \q_@@_recursion_stop }
+\prg_new_conditional:Npnn \bool_lazy_any:n #1 { T , F , TF }
+ {
+ \if_predicate:w \bool_lazy_any_p:n {#1}
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \@@_lazy_any:n #1
+ {
+ \@@_if_recursion_tail_stop_do:nn {#1} { \c_false_bool }
+ \bool_if:nT {#1}
+ { \@@_use_i_delimit_by_q_recursion_stop:nw { \c_true_bool } }
+ \@@_lazy_any:n
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\bool_lazy_or:nn}
+% \UnitTested
+% Only evaluate the second expression if the first is \texttt{false}.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \bool_lazy_or:nn #1#2 { p , T , F , TF }
+ {
+ \if_predicate:w
+ \bool_if:nTF {#1} { \c_true_bool } { \bool_if_p:n {#2} }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\bool_not_p:n}
+% \UnitTested
+% The Not variant just reverses the outcome of \cs{bool_if_p:n}. Can
+% be optimized but this is nice and simple and according to the
+% implementation plan. Not even particularly useful to have it when
+% the infix notation is easier to use.
+% \begin{macrocode}
+\cs_new:Npn \bool_not_p:n #1 { \bool_if_p:n { ! ( #1 ) } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\bool_xor:nn}
+% \UnitTested
+% Exclusive or. If the boolean expressions have same truth value,
+% return \texttt{false}, otherwise return \texttt{true}.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \bool_xor:nn #1#2 { p , T , F , TF }
+ {
+ \bool_if:nT {#1} \reverse_if:N
+ \if_predicate:w \bool_if_p:n {#2}
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Logical loops}
+%
+% \begin{macro}{\bool_while_do:Nn, \bool_while_do:cn}
+% \UnitTested
+% \begin{macro}{\bool_until_do:Nn, \bool_until_do:cn}
+% \UnitTested
+% A |while| loop where the boolean is tested before executing the
+% statement. The \enquote{while} version executes the code as long as the
+% boolean is true; the \enquote{until} version executes the code as
+% long as the boolean is false.
+% \begin{macrocode}
+\cs_new:Npn \bool_while_do:Nn #1#2
+ { \bool_if:NT #1 { #2 \bool_while_do:Nn #1 {#2} } }
+\cs_new:Npn \bool_until_do:Nn #1#2
+ { \bool_if:NF #1 { #2 \bool_until_do:Nn #1 {#2} } }
+\cs_generate_variant:Nn \bool_while_do:Nn { c }
+\cs_generate_variant:Nn \bool_until_do:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\bool_do_while:Nn, \bool_do_while:cn}
+% \UnitTested
+% \begin{macro}{\bool_do_until:Nn, \bool_do_until:cn}
+% \UnitTested
+% A |do-while| loop where the body is performed at least once and the
+% boolean is tested after executing the body. Otherwise identical to
+% the above functions.
+% \begin{macrocode}
+\cs_new:Npn \bool_do_while:Nn #1#2
+ { #2 \bool_if:NT #1 { \bool_do_while:Nn #1 {#2} } }
+\cs_new:Npn \bool_do_until:Nn #1#2
+ { #2 \bool_if:NF #1 { \bool_do_until:Nn #1 {#2} } }
+\cs_generate_variant:Nn \bool_do_while:Nn { c }
+\cs_generate_variant:Nn \bool_do_until:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \bool_while_do:nn, \bool_do_while:nn ,
+% \bool_until_do:nn, \bool_do_until:nn
+% }
+% \UnitTested
+% Loop functions with the test either before or after the first body
+% expansion.
+% \begin{macrocode}
+\cs_new:Npn \bool_while_do:nn #1#2
+ {
+ \bool_if:nT {#1}
+ {
+ #2
+ \bool_while_do:nn {#1} {#2}
+ }
+ }
+\cs_new:Npn \bool_do_while:nn #1#2
+ {
+ #2
+ \bool_if:nT {#1} { \bool_do_while:nn {#1} {#2} }
+ }
+\cs_new:Npn \bool_until_do:nn #1#2
+ {
+ \bool_if:nF {#1}
+ {
+ #2
+ \bool_until_do:nn {#1} {#2}
+ }
+ }
+\cs_new:Npn \bool_do_until:nn #1#2
+ {
+ #2
+ \bool_if:nF {#1} { \bool_do_until:nn {#1} {#2} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\s_@@_mark,\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP, noTF]{\bool_case:n}
+% \begin{macro}{\@@_case:NnTF}
+% \begin{macro}{\@@_case:w,a\@@_case_end:nw}
+% For boolean cases the overall idea is the same as for
+% \cs{str_case:nnTF} as described in \pkg{l3str}.
+% \begin{macrocode}
+\cs_new:Npn \bool_case:nTF
+ { \exp:w \@@_case:nTF }
+\cs_new:Npn \bool_case:nT #1#2
+ { \exp:w \@@_case:nTF {#1} {#2} { } }
+\cs_new:Npn \bool_case:nF #1
+ { \exp:w \@@_case:nTF {#1} { } }
+\cs_new:Npn \bool_case:n #1
+ { \exp:w \@@_case:nTF {#1} { } { } }
+\cs_new:Npn \@@_case:nTF #1#2#3
+ {
+ \@@_case:w
+ #1 \c_true_bool { } \s_@@_mark {#2} \s_@@_mark {#3} \s_@@_stop
+ }
+\cs_new:Npn \@@_case:w #1#2
+ {
+ \bool_if:nTF {#1}
+ { \@@_case_end:nw {#2} }
+ { \@@_case:w }
+ }
+\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}
+% \end{macro}
+%
+% \subsection{Producing multiple copies}
+%
+% \begin{macrocode}
+%<@@=prg>
+% \end{macrocode}
+%
+% \begin{macro}{\prg_replicate:nn}
+% \UnitTested
+% \begin{macro}{\@@_replicate:N, \@@_replicate_first:N}
+% \begin{macro}{\@@_replicate_}
+% \begin{macro}
+% {
+% \@@_replicate_0:n,
+% \@@_replicate_1:n,
+% \@@_replicate_2:n,
+% \@@_replicate_3:n,
+% \@@_replicate_4:n,
+% \@@_replicate_5:n,
+% \@@_replicate_6:n,
+% \@@_replicate_7:n,
+% \@@_replicate_8:n,
+% \@@_replicate_9:n
+% }
+% \begin{macro}
+% {
+% \@@_replicate_first_-:n,
+% \@@_replicate_first_0:n,
+% \@@_replicate_first_1:n,
+% \@@_replicate_first_2:n,
+% \@@_replicate_first_3:n,
+% \@@_replicate_first_4:n,
+% \@@_replicate_first_5:n,
+% \@@_replicate_first_6:n,
+% \@@_replicate_first_7:n,
+% \@@_replicate_first_8:n,
+% \@@_replicate_first_9:n
+% }
+% This function uses a cascading csname technique by David Kastrup
+% (who else :-)
+%
+% The idea is to make the input |25| result in first adding five, and
+% then 20 copies of the code to be replicated. The technique uses
+% cascading csnames which means that we start building several csnames
+% so we end up with a list of functions to be called in reverse
+% order. This is important here (and other places) because it means
+% that we can for instance make the function that inserts five copies
+% of something to also hand down ten to the next function in
+% line. This is exactly what happens here: in the example with |25|
+% then the next function is the one that inserts two copies but it
+% sees the ten copies handed down by the previous function. In order
+% to avoid the last function to insert say, $100$ copies of the original
+% argument just to gobble them again we define separate functions to
+% be inserted first. These functions also close the expansion of
+% \cs{exp:w}, which ensures that \cs{prg_replicate:nn} only
+% requires two steps of expansion.
+%
+% This function has one flaw though: Since it constantly passes down
+% ten copies of its previous argument it severely affects the main
+% memory once you start demanding hundreds of thousands of copies. Now
+% I don't think this is a real limitation for any ordinary use, and if
+% necessary, it is possible to write \cs{prg_replicate:nn} |{1000}|
+% |{| \cs{prg_replicate:nn} |{1000}| \Arg{code}~|}|. An
+% alternative approach is to create a string of |m|'s with
+% \cs{exp:w} which can be done with just four macros but that
+% method has its own problems since it can exhaust the string
+% pool. Also, it is considerably slower than what we use here so the
+% few extra csnames are well spent I would say.
+% \begin{macrocode}
+\cs_new:Npn \prg_replicate:nn #1
+ {
+ \exp:w
+ \exp_after:wN \@@_replicate_first:N
+ \int_value:w \int_eval:n {#1}
+ \cs_end:
+ }
+\cs_new:Npn \@@_replicate:N #1
+ { \cs:w @@_replicate_#1 :n \@@_replicate:N }
+\cs_new:Npn \@@_replicate_first:N #1
+ { \cs:w @@_replicate_first_ #1 :n \@@_replicate:N }
+% \end{macrocode}
+% Then comes all the functions that do the hard work of inserting all
+% the copies. The first function takes |:n| as a parameter.
+% \begin{macrocode}
+\cs_new:Npn \@@_replicate_ :n #1 { \cs_end: }
+\cs_new:cpn { @@_replicate_0:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} }
+\cs_new:cpn { @@_replicate_1:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1 }
+\cs_new:cpn { @@_replicate_2:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1 }
+\cs_new:cpn { @@_replicate_3:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1 }
+\cs_new:cpn { @@_replicate_4:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1 }
+\cs_new:cpn { @@_replicate_5:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1#1 }
+\cs_new:cpn { @@_replicate_6:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1#1#1 }
+\cs_new:cpn { @@_replicate_7:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1#1#1#1 }
+\cs_new:cpn { @@_replicate_8:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1#1#1#1#1 }
+\cs_new:cpn { @@_replicate_9:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1#1#1#1#1#1 }
+% \end{macrocode}
+% Users shouldn't ask for something to be replicated once or even
+% not at all but\dots
+% \begin{macrocode}
+\cs_new:cpn { @@_replicate_first_-:n } #1
+ {
+ \exp_end:
+ \msg_expandable_error:nn { prg } { negative-replication }
+ }
+\cs_new:cpn { @@_replicate_first_0:n } #1 { \exp_end: }
+\cs_new:cpn { @@_replicate_first_1:n } #1 { \exp_end: #1 }
+\cs_new:cpn { @@_replicate_first_2:n } #1 { \exp_end: #1#1 }
+\cs_new:cpn { @@_replicate_first_3:n } #1 { \exp_end: #1#1#1 }
+\cs_new:cpn { @@_replicate_first_4:n } #1 { \exp_end: #1#1#1#1 }
+\cs_new:cpn { @@_replicate_first_5:n } #1 { \exp_end: #1#1#1#1#1 }
+\cs_new:cpn { @@_replicate_first_6:n } #1 { \exp_end: #1#1#1#1#1#1 }
+\cs_new:cpn { @@_replicate_first_7:n } #1 { \exp_end: #1#1#1#1#1#1#1 }
+\cs_new:cpn { @@_replicate_first_8:n } #1 { \exp_end: #1#1#1#1#1#1#1#1 }
+\cs_new:cpn { @@_replicate_first_9:n } #1
+ { \exp_end: #1#1#1#1#1#1#1#1#1 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Detecting \TeX{}'s mode}
+%
+% \begin{macro}[pTF]{\mode_if_vertical:}
+% \UnitTested
+% For testing vertical mode. Strikes me here on the bus with David,
+% that as long as we are just talking about returning true and
+% false states, we can just use the primitive conditionals for this
+% and gobbling the \cs{exp_end:} in the input stream. However this
+% requires knowledge of the implementation so we keep things nice
+% and clean and use the return statements.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \mode_if_vertical: { p , T , F , TF }
+ { \if_mode_vertical: \prg_return_true: \else: \prg_return_false: \fi: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\mode_if_horizontal:}
+% \UnitTested
+% For testing horizontal mode.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \mode_if_horizontal: { p , T , F , TF }
+ { \if_mode_horizontal: \prg_return_true: \else: \prg_return_false: \fi: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\mode_if_inner:}
+% \UnitTested
+% For testing inner mode.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \mode_if_inner: { p , T , F , TF }
+ { \if_mode_inner: \prg_return_true: \else: \prg_return_false: \fi: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\mode_if_math:}
+% \UnitTested
+% For testing math mode. At the beginning of an alignment cell,
+% this should be used only inside a non-expandable function.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \mode_if_math: { p , T , F , TF }
+ { \if_mode_math: \prg_return_true: \else: \prg_return_false: \fi: }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Internal programming functions}
+%
+% \begin{macro}{\group_align_safe_begin:, \group_align_safe_end:}
+% \TeX{}'s alignment structures present many problems. As Knuth says
+% himself in \emph{\TeX : The Program}: \enquote{It's sort of a miracle
+% whenever \tn{halign} or \tn{valign} work, [\ldots]} One problem relates
+% to commands that internally issue a \tn{cr} but also peek ahead for
+% the next character for use in, say, an optional argument. If the
+% next token happens to be a |&| with category code~4 we get some
+% sort of weird error message because the underlying
+% \tn{futurelet} stores the token at the end of the alignment
+% template. This could be a |&|$_4$ giving a message like
+% |! Misplaced \cr.| or even worse: it could be the \tn{endtemplate}
+% token causing even more trouble! To solve this we have to open a
+% special group so that \TeX{} still thinks it's on safe ground but at
+% the same time we don't want to introduce any brace group that may
+% find its way to the output. The following functions help with this
+% by using behaviour documented only in Appendix~D of
+% \emph{The \TeX{}book}\dots
+% In short evaluating |`{| and |`}| as numbers will not change the counter
+% \TeX{} uses to keep track of its state in an alignment, whereas gobbling a
+% brace using \cs{if_false:} will affect \TeX's state without producing any
+% real group.
+% We place the \cs{if_false:} |{| \cs{fi:} part at that place so
+% that the successive expansions of \cs{group_align_safe_begin/end:}
+% are always brace balanced.
+% \begin{macrocode}
+\group_begin:
+\tex_catcode:D `\^^@ = 2 \exp_stop_f:
+\cs_new:Npn \group_align_safe_begin:
+ { \exp:w \if_false: { \fi: `^^@ \exp_stop_f: }
+\tex_catcode:D `\^^@ = 1 \exp_stop_f:
+\cs_new:Npn \group_align_safe_end:
+ { \exp:w `^^@ \if_false: } \fi: \exp_stop_f: }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\g__kernel_prg_map_int}
+% A nesting counter for mapping.
+% \begin{macrocode}
+\int_new:N \g__kernel_prg_map_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\prg_break_point:Nn}
+% \begin{macro}{\prg_map_break:Nn}
+% These are defined in \pkg{l3basics}, as they are needed
+% \enquote{early}. This is just a reminder that is the case!
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\prg_break_point:}
+% \begin{macro}{\prg_break:, \prg_break:n}
+% Also done in \pkg{l3basics}.
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3prop.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3prop.dtx
new file mode 100644
index 00000000000..6d8054e956d
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3prop.dtx
@@ -0,0 +1,2602 @@
+% \iffalse meta-comment
+%
+%% File: l3prop.dtx
+%
+% Copyright (C) 1990-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3prop} module\\ Property lists^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \pkg{expl3} implements a \enquote{property list} data type, which contain
+% an unordered list of entries each of which consists of a \meta{key} (string)
+% and an associated \meta{value} (token list). The \meta{key} and \meta{value}
+% may both be given as any balanced text, and the \meta{key} is processed using
+% \cs{tl_to_str:n}, meaning that category codes are ignored. Entries can be
+% manipulated individually, as well as collectively by applying a function to
+% every key--value pair within the list.
+%
+% Each entry in a property list must have a unique \meta{key}: if an entry is
+% added to a property list which already contains the \meta{key} then the new
+% entry overwrites the existing one. The \meta{keys} are compared on a
+% string basis, using the same method as \cs{str_if_eq:nnTF}.
+%
+% Property lists are intended for storing key-based information for use within
+% code. They can be converted from and to key--value lists, which are a form of
+% \emph{input} parsed by the \pkg{l3keys} module. If a key--value list contains
+% a \meta{key} multiple times, only the last \meta{value} associated to it will
+% be kept in the conversion to a property list.
+%
+% Internally, property lists can use two distinct implementations with different
+% data storage, which are decided when declaring the property list variable
+% using \cs{prop_new:N} (\enquote{flat} storage) or \cs{prop_new_linked:N}
+% (\enquote{linked} storage). After a property list is declared with
+% \cs{prop_new:N} or \cs{prop_new_linked:N}, the type of internal data storage
+% can be changed by \cs{prop_make_flat:N} or \cs{prop_make_linked:N}, but only
+% at the outermost group level. All other \pkg{l3prop} functions transparently
+% manipulate either storage method and convert as needed.
+% \begin{itemize}
+% \item
+% The (default) \enquote{flat} storage method is suited for a relatively
+% small number of entries, or when the property list is likely to be
+% manipulated (copied, mapped) as a whole rather than entry-wise. It is
+% significantly faster for \cs{prop_set_eq:NN}, and only slightly faster for
+% \cs{prop_clear:N}, \cs{prop_concat:NNN}, and mapping functions
+% \cs[no-index]{prop_map_\ldots{}}.
+%
+% \item
+% The \enquote{linked} storage method is meant for property lists with a
+% large numbers of entries. It takes up more of \TeX{}'s memory during a run, but is
+% significantly faster (for long lists) when accessing or modifying
+% individual entries using functions such as \cs{prop_if_in:Nn},
+% \cs{prop_item:Nn}, \cs{prop_put:Nnn}, \cs{prop_get:NnN},
+% \cs{prop_pop:NnN}, \cs{prop_remove:Nn}, as it takes a constant
+% time for these operations (rather than the number of items for a
+% \enquote{flat} property list). A technical drawback is that
+% memory is permanently used\footnote{Until the end of the run, that
+% is.} by \meta{keys} stored in a \enquote{linked} property list,
+% even after they are removed and the property list is deleted.
+% \end{itemize}
+%
+% \section{Creating and initialising property lists}
+%
+% \begin{function}{\prop_new:N, \prop_new:c}
+% \begin{syntax}
+% \cs{prop_new:N} \meta{property list}
+% \end{syntax}
+% Creates a new \enquote{flat} \meta{property list} or raises an error if the
+% name is already taken. The declaration is global. The \meta{property list}
+% initially contains no entries. See also \cs{prop_new_linked:N}.
+% \end{function}
+%
+% \begin{function}[added = 2024-02-12]{\prop_new_linked:N, \prop_new_linked:c}
+% \begin{syntax}
+% \cs{prop_new_linked:N} \meta{property list}
+% \end{syntax}
+% Creates a new \enquote{linked} \meta{property list} or raises an error if
+% the name is already taken. The declaration is global. The \meta{property
+% list} initially contains no entries. The internal data storage differs from
+% that produced by \cs{prop_new:N} and it is optimized for property lists with
+% a large number of entries.
+% \end{function}
+%
+% \begin{function}
+% {\prop_clear:N, \prop_clear:c, \prop_gclear:N, \prop_gclear:c}
+% \begin{syntax}
+% \cs{prop_clear:N} \meta{property list}
+% \end{syntax}
+% Clears all entries from the \meta{property list}.
+% \end{function}
+%
+% \begin{function}
+% {
+% \prop_clear_new:N, \prop_clear_new:c,
+% \prop_gclear_new:N, \prop_gclear_new:c
+% }
+% \begin{syntax}
+% \cs{prop_clear_new:N} \meta{property list}
+% \end{syntax}
+% Ensures that the \meta{property list} exists globally by applying
+% \cs{prop_new:N} if necessary, then applies
+% \cs[index=prop_clear:N]{prop_(g)clear:N} to leave the list empty.
+% \begin{texnote}
+% If the property list exists and is of \enquote{linked} type, it
+% is cleared but not made into a flat property list.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2024-02-12]
+% {
+% \prop_clear_new_linked:N, \prop_clear_new_linked:c,
+% \prop_gclear_new_linked:N, \prop_gclear_new_linked:c
+% }
+% \begin{syntax}
+% \cs{prop_clear_new_linked:N} \meta{property list}
+% \end{syntax}
+% Ensures that the \meta{property list} exists globally by applying
+% \cs{prop_new_linked:N} if necessary, then applies
+% \cs[index=prop_clear:N]{prop_(g)clear:N} to leave the list empty.
+% \begin{texnote}
+% If the property list exists and is of \enquote{flat} type, it
+% is cleared but not made into a linked property list.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}
+% {
+% \prop_set_eq:NN, \prop_set_eq:cN, \prop_set_eq:Nc, \prop_set_eq:cc,
+% \prop_gset_eq:NN, \prop_gset_eq:cN, \prop_gset_eq:Nc, \prop_gset_eq:cc
+% }
+% \begin{syntax}
+% \cs{prop_set_eq:NN} \meta{property list_1} \meta{property list_2}
+% \end{syntax}
+% Sets the content of \meta{property list_1} equal to that of \meta{property
+% list_2}. This converts as needed between the two storage types.
+% \end{function}
+%
+% \begin{function}[added = 2017-11-28, updated = 2021-11-07]
+% {
+% \prop_set_from_keyval:Nn, \prop_set_from_keyval:cn,
+% \prop_gset_from_keyval:Nn, \prop_gset_from_keyval:cn,
+% }
+% \begin{syntax}
+% \cs{prop_set_from_keyval:Nn} \meta{property list}
+% \{
+% \meta{key1} |=| \meta{value1} |,|
+% \meta{key2} |=| \meta{value2} |,| \ldots{}
+% \}
+% \end{syntax}
+% Sets \meta{property list} to contain key--value pairs given in the second
+% argument. If duplicate keys appear only the last of the values is kept.
+% In contrast to most keyval lists (\emph{e.g.}~those in \pkg{l3keys}), each
+% key here \emph{must} be followed with an \texttt{=} sign even to specify an
+% empty \meta{value}.
+%
+% Spaces are trimmed around every \meta{key} and every \meta{value},
+% and if the result of trimming spaces consists of a single brace
+% group then a set of outer braces is removed. This enables both the
+% \meta{key} and the \meta{value} to contain spaces, commas or equal
+% signs. The \meta{key} is then processed by \cs{tl_to_str:n}.
+% This function correctly detects the |=| and |,| signs provided they
+% have the standard category code~$12$ or they are active.
+% \end{function}
+%
+% \begin{function}[added = 2017-11-28, updated = 2021-11-07]
+% {\prop_const_from_keyval:Nn, \prop_const_from_keyval:cn}
+% \begin{syntax}
+% \cs{prop_const_from_keyval:Nn} \meta{property list}
+% \{
+% \meta{key1} |=| \meta{value1} |,|
+% \meta{key2} |=| \meta{value2} |,| \ldots{}
+% \}
+% \end{syntax}
+% Creates a new constant \enquote{flat} \meta{property list} or raises
+% an error if the
+% name is already taken. The \meta{property list} is set globally to
+% contain key--value pairs given in the second argument, processed in
+% the way described for \cs{prop_set_from_keyval:Nn}. If duplicate
+% keys appear only the last of the values is kept.
+% This function correctly detects the |=| and |,| signs provided they
+% have the standard category code~$12$ or they are active.
+% \end{function}
+%
+% \begin{function}[added = 2024-02-12]
+% {\prop_const_linked_from_keyval:Nn, \prop_const_linked_from_keyval:cn}
+% \begin{syntax}
+% \cs{prop_const_linked_from_keyval:Nn} \meta{prop~var}
+% \{
+% \meta{key1} |=| \meta{value1} |,|
+% \meta{key2} |=| \meta{value2} |,| \ldots{}
+% \}
+% \end{syntax}
+% Creates a new constant \enquote{linked} \meta{prop~var} or raises an
+% error if the
+% name is already taken. The \meta{prop~var} is set globally to
+% contain key--value pairs given in the second argument, processed in
+% the way described for \cs{prop_set_from_keyval:Nn}. If duplicate
+% keys appear only the last of the values is kept.
+% This function correctly detects the |=| and |,| signs provided they
+% have the standard category code~$12$ or they are active.
+% \end{function}
+%
+% \begin{function}[added = 2024-02-12]{\prop_make_flat:N, \prop_make_flat:c}
+% \begin{syntax}
+% \cs{prop_make_flat:N} \meta{property list}
+% \end{syntax}
+% Changes the internal storage type of the \meta{property list} to be
+% the same \enquote{flat} storage as \cs{prop_new:N}. The key--value
+% pairs of the \meta{property list} are preserved by the change. If
+% the property list was already flat then nothing is done. This
+% function can only be used at the outermost group level.
+% \end{function}
+%
+% \begin{function}[added = 2024-02-12]{\prop_make_linked:N, \prop_make_linked:c}
+% \begin{syntax}
+% \cs{prop_make_linked:N} \meta{property list}
+% \end{syntax}
+% Changes the internal storage type of the \meta{property list} to be
+% the same \enquote{linked} storage as \cs{prop_new_linked:N}. The
+% key--value pairs of the \meta{property list} are preserved by the
+% change. If the property list was already linked then nothing is
+% done. This function can only be used at the outermost group level.
+% \end{function}
+%
+% \section{Adding and updating property list entries}
+%
+% \begin{function}[updated = 2012-07-09]
+% {
+% \prop_put:Nnn, \prop_put:NnV, \prop_put:Nnv, \prop_put:Nne,
+% \prop_put:NVn, \prop_put:NVV, \prop_put:NVv, \prop_put:NVe,
+% \prop_put:Nvn, \prop_put:NvV, \prop_put:Nvv, \prop_put:Nve,
+% \prop_put:Nen, \prop_put:NeV, \prop_put:Nev, \prop_put:Nee,
+% \prop_put:Nno, \prop_put:Non, \prop_put:Noo,
+% \prop_put:cnn, \prop_put:cnV, \prop_put:cnv, \prop_put:cne,
+% \prop_put:cVn, \prop_put:cVV, \prop_put:cVv, \prop_put:cVe,
+% \prop_put:cvn, \prop_put:cvV, \prop_put:cvv, \prop_put:cve,
+% \prop_put:cen, \prop_put:ceV, \prop_put:cev, \prop_put:cee,
+% \prop_put:cno, \prop_put:con, \prop_put:coo,
+% \prop_gput:Nnn, \prop_gput:NnV, \prop_gput:Nnv, \prop_gput:Nne,
+% \prop_gput:NVn, \prop_gput:NVV, \prop_gput:NVv, \prop_gput:NVe,
+% \prop_gput:Nvn, \prop_gput:NvV, \prop_gput:Nvv, \prop_gput:Nve,
+% \prop_gput:Nen, \prop_gput:NeV, \prop_gput:Nev, \prop_gput:Nee,
+% \prop_gput:Nno, \prop_gput:Non, \prop_gput:Noo,
+% \prop_gput:cnn, \prop_gput:cnV, \prop_gput:cnv, \prop_gput:cne,
+% \prop_gput:cVn, \prop_gput:cVV, \prop_gput:cVv, \prop_gput:cVe,
+% \prop_gput:cvn, \prop_gput:cvV, \prop_gput:cvv, \prop_gput:cve,
+% \prop_gput:cen, \prop_gput:ceV, \prop_gput:cev, \prop_gput:cee,
+% \prop_gput:cno, \prop_gput:con, \prop_gput:coo
+% }
+% \begin{syntax}
+% \cs{prop_put:Nnn} \meta{property list} \Arg{key} \Arg{value}
+% \end{syntax}
+% Adds an entry to the \meta{property list} which may be accessed
+% using the \meta{key} and which has \meta{value}. If the \meta{key}
+% is already present in the \meta{property list}, the existing entry
+% is overwritten by the new \meta{value}. Both the \meta{key} and
+% \meta{value} may contain any \meta{balanced text}. The \meta{key} is
+% stored after processing with \cs{tl_to_str:n}, meaning that category
+% codes are ignored.
+% \end{function}
+%
+% \begin{function}[added = 2024-03-30]
+% {
+% \prop_put_if_not_in:Nnn, \prop_put_if_not_in:NVn, \prop_put_if_not_in:NnV,
+% \prop_put_if_not_in:cnn, \prop_put_if_not_in:cVn, \prop_put_if_not_in:cnV,
+% \prop_gput_if_not_in:Nnn, \prop_gput_if_not_in:NVn, \prop_gput_if_not_in:NnV,
+% \prop_gput_if_not_in:cnn, \prop_gput_if_not_in:cVn, \prop_gput_if_not_in:cnV
+% }
+% \begin{syntax}
+% \cs{prop_put_if_not_in:Nnn} \meta{property list} \Arg{key} \Arg{value}
+% \end{syntax}
+% If the \meta{key} is present in the \meta{property list} then no
+% action is taken. Otherwise, a new entry is added as described for
+% \cs{prop_put:Nnn}.
+% \end{function}
+%
+% \begin{function}[added = 2021-05-16]
+% {
+% \prop_concat:NNN, \prop_concat:ccc,
+% \prop_gconcat:NNN, \prop_gconcat:ccc
+% }
+% \begin{syntax}
+% \cs{prop_concat:NNN} \meta{property list_1} \meta{property list_2} \meta{property list3}
+% \end{syntax}
+% Combines the key--value pairs of \meta{property list_2} and
+% \meta{property list_3}, and saves the result in \meta{property list_1}. If a
+% key appears in both \meta{property list_2} and \meta{property list_3} then the
+% last value, namely the value in \meta{property list_3} is kept.
+% This converts as needed between the two storage types.
+% \end{function}
+%
+% \begin{function}[added = 2021-05-16, updated = 2021-11-07]
+% {
+% \prop_put_from_keyval:Nn, \prop_put_from_keyval:cn,
+% \prop_gput_from_keyval:Nn, \prop_gput_from_keyval:cn,
+% }
+% \begin{syntax}
+% \cs{prop_put_from_keyval:Nn} \meta{property list}
+% \{
+% \meta{key1} |=| \meta{value1} |,|
+% \meta{key2} |=| \meta{value2} |,| \ldots{}
+% \}
+% \end{syntax}
+% Updates the \meta{property list} by adding entries for each key--value
+% pair given in the second argument. The addition is done through
+% \cs{prop_put:Nnn}, hence if the \meta{property list} already contains
+% some of the keys, the corresponding values are discarded and
+% replaced by those given in the key--value list. If duplicate keys
+% appear in the key--value list then only the last of the values is kept.
+%
+% The function is equivalent to storing the key--value pairs in a
+% temporary property list using \cs{prop_set_from_keyval:Nn}, then
+% combining \meta{property list} with the temporary variable using
+% \cs{prop_concat:NNN}. In particular, the \meta{keys} and
+% \meta{values} are space-trimmed and unbraced as described in
+% \cs{prop_set_from_keyval:Nn}.
+% This function correctly detects the |=| and |,| signs provided they
+% have the standard category code~$12$ or they are active.
+% \end{function}
+%
+% \section{Recovering values from property lists}
+%
+% \begin{function}[updated = 2011-08-28]
+% {
+% \prop_get:NnN, \prop_get:NVN, \prop_get:NvN, \prop_get:NeN,
+% \prop_get:NoN,
+% \prop_get:cnN, \prop_get:cVN, \prop_get:cvN, \prop_get:ceN,
+% \prop_get:coN,
+% \prop_get:cnc
+% }
+% \begin{syntax}
+% \cs{prop_get:NnN} \meta{property list} \Arg{key} \meta{tl var}
+% \end{syntax}
+% Recovers the \meta{value} stored with \meta{key} from the
+% \meta{property list}, and places this in the \meta{token list
+% variable}. If the \meta{key} is not found in the
+% \meta{property list} then the \meta{token list variable} is set
+% to the special marker \cs{q_no_value}. The \meta{token list
+% variable} is set within the current \TeX{} group. See also
+% \cs{prop_get:NnNTF}.
+% \end{function}
+%
+% \begin{function}[updated = 2011-08-18]
+% {
+% \prop_pop:NnN, \prop_pop:NVN,
+% \prop_pop:NoN,
+% \prop_pop:cnN, \prop_pop:cVN,
+% \prop_pop:coN
+% }
+% \begin{syntax}
+% \cs{prop_pop:NnN} \meta{property list} \Arg{key} \meta{tl var}
+% \end{syntax}
+% Recovers the \meta{value} stored with \meta{key} from the
+% \meta{property list}, and places this in the \meta{token list
+% variable}. If the \meta{key} is not found in the
+% \meta{property list} then the \meta{token list variable} is set
+% to the special marker \cs{q_no_value}. The \meta{key} and
+% \meta{value} are then deleted from the property list. Both
+% assignments are local. See also \cs{prop_pop:NnNTF}.
+% \end{function}
+%
+% \begin{function}[updated = 2011-08-18]
+% {
+% \prop_gpop:NnN, \prop_gpop:NVN,
+% \prop_gpop:NoN,
+% \prop_gpop:cnN, \prop_gpop:cVN,
+% \prop_gpop:coN
+% }
+% \begin{syntax}
+% \cs{prop_gpop:NnN} \meta{property list} \Arg{key} \meta{tl var}
+% \end{syntax}
+% Recovers the \meta{value} stored with \meta{key} from the
+% \meta{property list}, and places this in the \meta{token list
+% variable}. If the \meta{key} is not found in the
+% \meta{property list} then the \meta{token list variable} is set
+% to the special marker \cs{q_no_value}. The \meta{key} and
+% \meta{value} are then deleted from the property list.
+% The \meta{property list} is modified globally, while the assignment of
+% the \meta{token list variable} is local. See also \cs{prop_gpop:NnNTF}.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2014-07-17]
+% {
+% \prop_item:Nn, \prop_item:NV, \prop_item:Ne, \prop_item:No,
+% \prop_item:cn, \prop_item:cV, \prop_item:ce, \prop_item:co
+% }
+% \begin{syntax}
+% \cs{prop_item:Nn} \meta{property list} \Arg{key}
+% \end{syntax}
+% Expands to the \meta{value} corresponding to the \meta{key} in
+% the \meta{property list}. If the \meta{key} is missing, this has
+% an empty expansion.
+% \begin{texnote}
+% For \enquote{flat} property lists, this expandable function iterates
+% through every key--value pair and is therefore slower than a
+% non-expandable approach based on \cs{prop_get:NnN}.
+% (For \enquote{linked} property lists both functions are fast.)
+%
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{value}
+% does not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\prop_count:N, \prop_count:c}
+% \begin{syntax}
+% \cs{prop_count:N} \meta{property list}
+% \end{syntax}
+% Leaves the number of key--value pairs in the \meta{property list} in
+% the input stream as an \meta{integer denotation}.
+% \end{function}
+%
+% \begin{function}[EXP]{\prop_to_keyval:N}
+% \begin{syntax}
+% \cs{prop_to_keyval:N} \meta{property list}
+% \end{syntax}
+% Expands to the \meta{property list} in a key--value notation. Keep in mind
+% that a \meta{property list} is \emph{unordered}, while key--value interfaces
+% are not necessarily, so this cannot be used for arbitrary interfaces.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded} primitive
+% (\cs{exp_not:n}), which means that the key--value list does not expand
+% further when appearing in an \texttt{e}-type or \texttt{x}-type argument expansion.
+% It also needs exactly two steps of expansion.
+% \end{texnote}
+% \end{function}
+%
+% \section{Modifying property lists}
+%
+% \begin{function}[added = 2012-05-12]
+% {
+% \prop_remove:Nn, \prop_remove:NV, \prop_remove:Ne,
+% \prop_remove:cn, \prop_remove:cV, \prop_remove:ce,
+% \prop_gremove:Nn, \prop_gremove:NV, \prop_gremove:Ne,
+% \prop_gremove:cn, \prop_gremove:cV, \prop_gremove:ce,
+% }
+% \begin{syntax}
+% \cs{prop_remove:Nn} \meta{property list} \Arg{key}
+% \end{syntax}
+% Removes the entry listed under \meta{key} from the
+% \meta{property list}. If the \meta{key} is
+% not found in the \meta{property list} no change occurs,
+% \emph{i.e}~there is no need to test for the existence of a key before
+% deleting it.
+% \end{function}
+%
+% \section{Property list conditionals}
+%
+% \begin{function}[EXP, pTF, added = 2012-03-03]
+% {\prop_if_exist:N, \prop_if_exist:c}
+% \begin{syntax}
+% \cs{prop_if_exist_p:N} \meta{property list}
+% \cs{prop_if_exist:NTF} \meta{property list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{property list} is currently defined. This does not
+% check that the \meta{property list} really is a property list variable.
+% \end{function}
+%
+% \begin{function}[EXP, pTF]{\prop_if_empty:N, \prop_if_empty:c}
+% \begin{syntax}
+% \cs{prop_if_empty_p:N} \meta{property list}
+% \cs{prop_if_empty:NTF} \meta{property list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{property list} is empty (containing no entries).
+% \end{function}
+%
+% \begin{function}[EXP, pTF, updated = 2011-09-15]
+% {
+% \prop_if_in:Nn, \prop_if_in:NV, \prop_if_in:Ne, \prop_if_in:No,
+% \prop_if_in:cn, \prop_if_in:cV, \prop_if_in:ce, \prop_if_in:co
+% }
+% \begin{syntax}
+% \cs{prop_if_in_p:Nn} \meta{property list} \Arg{key}
+% \cs{prop_if_in:NnTF} \meta{property list} \Arg{key} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{key} is present in the \meta{property list},
+% making the comparison using the method described by \cs{str_if_eq:nnTF}.
+% \begin{texnote}
+% For \enquote{flat} property lists, this expandable function iterates
+% through every key--value pair and is therefore slower than a
+% non-expandable approach based on \cs{prop_get:NnNTF}.
+% (For \enquote{linked} property lists both functions are fast.)
+% \end{texnote}
+% \end{function}
+%
+% \section{Recovering values from property lists with branching}
+%
+% The functions in this section combine tests for the presence of a key
+% in a property list with recovery of the associated valued. This makes them
+% useful for cases where different code follows depending on the presence
+% or absence of a key in a property list. They offer increased readability
+% and performance over separate testing and recovery phases.
+%
+% \begin{function}[TF, updated = 2012-05-19]
+% {
+% \prop_get:NnN, \prop_get:NVN, \prop_get:NvN, \prop_get:NeN,
+% \prop_get:NoN,
+% \prop_get:cnN, \prop_get:cVN, \prop_get:cvN, \prop_get:ceN,
+% \prop_get:coN,
+% \prop_get:cnc
+% }
+% \begin{syntax}
+% \cs{prop_get:NnNTF} \meta{property list} \Arg{key} \meta{token list variable} \\
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{key} is not present in the \meta{property list}, leaves
+% the \meta{false code} in the input stream. The value of the
+% \meta{token list variable} is not defined in this case and should
+% not be relied upon. If the \meta{key} is present in the
+% \meta{property list}, stores the corresponding \meta{value} in the
+% \meta{token list variable} without removing it from the
+% \meta{property list}, then leaves the \meta{true code} in the input
+% stream. The \meta{token list variable} is assigned locally.
+% \end{function}
+%
+% \begin{function}[TF, added = 2011-08-18, updated = 2012-05-19]
+% {
+% \prop_pop:NnN, \prop_pop:NVN,
+% \prop_pop:NoN,
+% \prop_pop:cnN, \prop_pop:cVN,
+% \prop_pop:coN
+% }
+% \begin{syntax}
+% \cs{prop_pop:NnNTF} \meta{property list} \Arg{key} \meta{token list variable} \\
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{key} is not present in the \meta{property list}, leaves
+% the \meta{false code} in the input stream. The value of the
+% \meta{token list variable} is not defined in this case and should
+% not be relied upon. If the \meta{key} is present in
+% the \meta{property list}, pops the corresponding \meta{value}
+% in the \meta{token list variable}, \emph{i.e.}~removes the item from
+% the \meta{property list}.
+% Both the \meta{property list} and the \meta{token list variable}
+% are assigned locally.
+% \end{function}
+%
+% \begin{function}[TF, added = 2011-08-18, updated = 2012-05-19]
+% {
+% \prop_gpop:NnN, \prop_gpop:NVN,
+% \prop_gpop:NoN,
+% \prop_gpop:cnN, \prop_gpop:cVN,
+% \prop_gpop:coN
+% }
+% \begin{syntax}
+% \cs{prop_gpop:NnNTF} \meta{property list} \Arg{key} \meta{token list variable} \\
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{key} is not present in the \meta{property list}, leaves
+% the \meta{false code} in the input stream. The value of the
+% \meta{token list variable} is not defined in this case and should
+% not be relied upon. If the \meta{key} is present in
+% the \meta{property list}, pops the corresponding \meta{value}
+% in the \meta{token list variable}, \emph{i.e.}~removes the item from
+% the \meta{property list}.
+% The \meta{property list} is modified globally, while the
+% \meta{token list variable} is assigned locally.
+% \end{function}
+%
+% \section{Mapping over property lists}
+%
+% All mappings are done at the current group level, \emph{i.e.}~any
+% local assignments made by the \meta{function} or \meta{code} discussed
+% below remain in effect after the loop.
+%
+% \begin{function}[rEXP, updated = 2013-01-08]
+% {\prop_map_function:NN, \prop_map_function:cN}
+% \begin{syntax}
+% \cs{prop_map_function:NN} \meta{property list} \meta{function}
+% \end{syntax}
+% Applies \meta{function} to every \meta{entry} stored in the
+% \meta{property list}. The \meta{function} receives two arguments for
+% each iteration: the \meta{key} and associated \meta{value}.
+% The order in which \meta{entries} are returned is not defined and
+% should not be relied upon.
+% To pass further arguments to the \meta{function}, see
+% \cs{prop_map_inline:Nn} (non-expandable) or \cs{prop_map_tokens:Nn}.
+% \end{function}
+%
+% \begin{function}[updated = 2013-01-08]
+% {\prop_map_inline:Nn, \prop_map_inline:cn}
+% \begin{syntax}
+% \cs{prop_map_inline:Nn} \meta{property list} \Arg{inline function}
+% \end{syntax}
+% Applies \meta{inline function} to every \meta{entry} stored
+% within the \meta{property list}. The \meta{inline function} should
+% consist of code which receives the \meta{key} as |#1| and the
+% \meta{value} as |#2|.
+% The order in which \meta{entries} are returned is not defined and
+% should not be relied upon.
+% \end{function}
+%
+% \begin{function}[rEXP]
+% {\prop_map_tokens:Nn, \prop_map_tokens:cn}
+% \begin{syntax}
+% \cs{prop_map_tokens:Nn} \meta{property list} \Arg{code}
+% \end{syntax}
+% Analogue of \cs{prop_map_function:NN} which maps several tokens
+% instead of a single function. The \meta{code} receives each
+% key--value pair in the \meta{property list} as two trailing brace
+% groups. For instance,
+% \begin{verbatim}
+% \prop_map_tokens:Nn \l_my_prop { \str_if_eq:nnT { mykey } }
+% \end{verbatim}
+% expands to the value corresponding to \texttt{mykey}: for each
+% pair in |\l_my_prop| the function \cs{str_if_eq:nnT} receives
+% \texttt{mykey}, the \meta{key} and the \meta{value} as its three
+% arguments. For that specific task, \cs{prop_item:Nn} is faster.
+% \end{function}
+%
+% \begin{function}[rEXP, updated = 2012-06-29]{\prop_map_break:}
+% \begin{syntax}
+% \cs{prop_map_break:}
+% \end{syntax}
+% Used to terminate a \cs[no-index]{prop_map_\ldots} function before all
+% entries in the \meta{property list} have been processed. This
+% normally takes place within a conditional statement, for example
+% \begin{verbatim}
+% \prop_map_inline:Nn \l_my_prop
+% {
+% \str_if_eq:nnTF { #1 } { bingo }
+% { \prop_map_break: }
+% {
+% % Do something useful
+% }
+% }
+% \end{verbatim}
+% Use outside of a \cs[no-index]{prop_map_\ldots} scenario leads to low
+% level \TeX{} errors.
+% \begin{texnote}
+% When the mapping is broken, additional tokens may be inserted
+% before further items are taken
+% from the input stream. This depends on the design of the mapping
+% function.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[rEXP, updated = 2012-06-29]{\prop_map_break:n}
+% \begin{syntax}
+% \cs{prop_map_break:n} \Arg{code}
+% \end{syntax}
+% Used to terminate a \cs[no-index]{prop_map_\ldots} function before all
+% entries in the \meta{property list} have been processed, inserting
+% the \meta{code} after the mapping has ended. This
+% normally takes place within a conditional statement, for example
+% \begin{verbatim}
+% \prop_map_inline:Nn \l_my_prop
+% {
+% \str_if_eq:nnTF { #1 } { bingo }
+% { \prop_map_break:n { <code> } }
+% {
+% % Do something useful
+% }
+% }
+% \end{verbatim}
+% Use outside of a \cs[no-index]{prop_map_\ldots} scenario leads to low
+% level \TeX{} errors.
+% \begin{texnote}
+% When the mapping is broken, additional tokens may be inserted
+% before the \meta{code} is
+% inserted into the input stream.
+% This depends on the design of the mapping function.
+% \end{texnote}
+% \end{function}
+%
+% \section{Viewing property lists}
+%
+% \begin{function}[updated = 2021-04-29]{\prop_show:N, \prop_show:c}
+% \begin{syntax}
+% \cs{prop_show:N} \meta{property list}
+% \end{syntax}
+% Displays the entries in the \meta{property list} in the terminal,
+% and specifies its storage type.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-12, updated = 2021-04-29]{\prop_log:N, \prop_log:c}
+% \begin{syntax}
+% \cs{prop_log:N} \meta{property list}
+% \end{syntax}
+% Writes the entries in the \meta{property list} in the log file,
+% and specifies its storage type.
+% \end{function}
+%
+% \section{Scratch property lists}
+%
+% There is no need to include both flat and linked property lists as
+% scratch variables. We arbitrarily pick the older implementation.
+%
+% \begin{variable}[added = 2012-06-23]{\l_tmpa_prop, \l_tmpb_prop}
+% Scratch \enquote{flat} property lists for local assignment.
+% These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \begin{variable}[added = 2012-06-23]{\g_tmpa_prop, \g_tmpb_prop}
+% Scratch \enquote{flat} property lists for global assignment.
+% These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \section{Constants}
+%
+% \begin{variable}{\c_empty_prop}
+% A permanently-empty property list used for internal comparisons.
+% \end{variable}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3prop} implementation}
+%
+% \TestFiles{m3prop001, m3prop002, m3prop003, m3prop004, m3show001}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=prop>
+% \end{macrocode}
+%
+% With the (default) flat data storage, a property list is a macro whose
+% top-level expansion is of the form
+% \begin{quote}
+% \cs{s_@@} \cs{@@_chk:w}
+% \cs{@@_pair:wn} \meta{key_1} \cs{s_@@} \Arg{value_1} \\
+% \ldots{} \\
+% \cs{@@_pair:wn} \meta{key_n} \cs{s_@@} \Arg{value_n} \\
+% \end{quote}
+% where \cs{s_@@} is a scan mark (equal to \cs{scan_stop:}), \cs{@@_chk:w}
+% produces a suitable error if the property list is used directly in the input
+% stream, and \cs{@@_pair:wn} can be used to map through the property list.
+%
+% With the linked data storage, each property list entry
+% \meta{key_i}--\meta{value_i} is stored into a token list
+% \cs[no-index]{@@~\meta{prefix}~\meta{key_i}}. The \meta{prefix} is one or
+% more characters (no spaces), constructed automatically only once, when the
+% property list is initially declared. The control sequence name does not
+% conform to standard naming for variables because (1) this is an internal
+% control sequence, not really a \pkg{expl3} variable; (2) keeping track of the
+% scope |l| or~|g| throughout all functions would be a pretty big mess,
+% especially if users accidentally mix local and global use (we would have to
+% always check for such mistakes, rather than only checking when suitable debug
+% options are set); (3) shorter control sequence names use less memory and are
+% quicker in case of hash collisions, which may matter since we are using many
+% control sequences.
+%
+% We need to enable mapping through such a property list, but without storing a
+% list of all entries anywhere: this is achieved by making each of these token
+% lists also store a pointer to the next entry. To enable efficient deletion,
+% the token lists also store a pointer to the previous entry. This means we
+% have a doubly-linked list. To avoid having to special-case the two ends of
+% the doubly-linked list when deleting entries, we include as a zeroth entry in
+% the doubly-linked list the property list variable itself, and we include as an
+% $(n+1)$-th entry in the doubly-linked list an end-pointer
+% \cs{@@~\meta{prefix}} (no trailing space, so it differs from an empty key).
+% The space before \meta{prefix} ensures there is no collision with
+% other \pkg{l3prop} internal functions, even if we have very many
+% linked property lists being defined.
+%
+% The property list variable itself is a token list of the form
+% \begin{quote}
+% \cs{@@_flatten:w} \cs{@@~\meta{prefix}} \cs{s_@@} \Arg{prefix}
+% \cs[no-index]{@@~\meta{prefix}~\meta{key_1}}
+% \end{quote}
+% Here, \cs{@@_flatten:w} serves as an efficiently recognized marker, and when
+% \texttt{f}-expanded it is tasked with fully unpacking the property list into
+% the same form as the default data storage so as to ease conversion. The
+% \meta{prefix} is used when looking up an entry. The token list
+% \cs{@@~\meta{prefix}} (see below) contains a pointer to the last key to help
+% insert a new entry. The pointer to \meta{key_1} is needed to start a mapping.
+% The token list labeled by \meta{key_i} is of the form
+% \begin{quote}
+% \cs{use_none:n} \cs[no-index]{@@~\meta{prefix}~\meta{key$_{i-1}$}}
+% \cs{@@_pair:wn} \meta{key_i} \cs{s_@@} \Arg{value_i}
+% \cs[no-index]{@@~\meta{prefix}~\meta{key$_{i+1}$}}
+% \end{quote}
+% where the pointer to \meta{key$_{i-1}$} is needed when deleting the
+% \meta{key_i}. Expanding this will run \cs{@@_pair:wn} on the
+% \meta{key_i}--\meta{value_i} pair (for speed, \meta{key_i} is kept as explicit
+% tokens rather than slowly extracting it from a control sequence name), then
+% move on to the next key, thus mapping through the whole list. The mapping is
+% ended upon expanding \cs{@@~\meta{prefix}}, which is the token list
+% \begin{quote}
+% \cs{use_none:n} \cs[no-index]{@@~\meta{prefix}~\meta{key_n}}
+% \end{quote}
+% Let us think about deleting the \meta{key_i}. We need to update the
+% \meta{key$_{i-1}$} and \meta{key$_{i+1}$} to point to each other
+% instead of \meta{key_i}. To edit the corresponding token lists, it is
+% important that \cs[no-index]{@@~\meta{prefix}~\meta{key_i}} be at the
+% \enquote{same place} in the token lists also in the boundary cases
+% $i=1$ or $i=n$, namely as the second token, or as the second argument
+% after \cs{s_@@}.
+%
+% \subsection{Internal auxiliaries}
+%
+% \begin{macro}{\@@_tmp:w}
+% Scratch macro, defined as needed, for instance to save \cs{@@_pair:wn} when
+% concatenating.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_tmp:w ?
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_internal_tl}
+% Token list used in various places: for the prefix; when converting from flat
+% to linked props; and to store the new key--value pair inserted by
+% \cs{prop_put:Nnn}.
+% \begin{macrocode}
+\tl_new:N \l_@@_internal_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\s_@@_mark,\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\q_@@_recursion_tail,\q_@@_recursion_stop}
+% Internal recursion quarks.
+% \begin{macrocode}
+\quark_new:N \q_@@_recursion_tail
+\quark_new:N \q_@@_recursion_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_if_recursion_tail_stop:n}
+% \begin{macro}[EXP]{\@@_if_recursion_tail_stop:o}
+% Functions to query recursion quarks.
+% \begin{macrocode}
+\__kernel_quark_new_test:N \@@_if_recursion_tail_stop:n
+\cs_generate_variant:Nn \@@_if_recursion_tail_stop:n { o }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Structure of a property list}
+%
+% \begin{macro}{\s_@@}
+% A private scan mark is used as a marker after each key, and at the
+% very beginning of the property list.
+% \begin{macrocode}
+\scan_new:N \s_@@
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_chk:w, \@@_chk_loop:nw, \@@_chk_get:nw}
+% This removes the flat property list from the input stream and complains
+% about a bad use of a property list. Since property lists do not have an
+% end-marker, we slowly peek ahead in a loop. Speed does not matter since
+% this is for an error situation. While \cs{@@_pair:wn} does not keep a fixed
+% definition, it always includes the internal \cs{s_@@} in its argument
+% specification, so that there is no risk of accidentally picking up a public
+% token instead of \cs{@@_pair:wn} when doing a meaning test. We collect the
+% keys and values to produce a more useful error message.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_chk:w { \@@_chk_loop:nw { } }
+\cs_new_protected:Npn \@@_chk_loop:nw #1
+ {
+ \peek_meaning:NTF \@@_pair:wn
+ { \@@_chk_get:nw {#1} }
+ { \msg_error:nne { prop } { misused } {#1} }
+ }
+\cs_new_protected:Npn \@@_chk_get:nw #1 \@@_pair:wn #2 \s_@@ #3
+ { \@@_chk_loop:nw { #1 , ~ {#2} = { \tl_to_str:n {#3} } } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_pair:wn}
+% Used as \cs{@@_pair:wn} \meta{key} \cs{s_@@} \Arg{item} for both storage
+% types, this internal token starts each key--value pair in the property list.
+% This default definition is changed globally by any mapping function, so
+% there is not much point trying to make it an error. Instead, the error is
+% produced by \cs{@@_chk:w}.
+% \begin{macrocode}
+\cs_new:Npn \@@_pair:wn #1 \s_@@ #2 { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_flatten:w}
+% We implement here the fact that \texttt{f}-expanding a linked property list
+% gives a flat property list.
+% Leaving a linked property list in the input stream will turn it into a flat
+% property list so that the error implemented by \cs{@@_chk:w} will correctly
+% be triggered.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_flatten:w #1 \s_@@ #2#3
+ { \use:e { \@@_flatten_aux:N #3 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {\@@_flatten:N, \@@_flatten_aux:w, \@@_flatten_aux:N, \@@_flatten_loop:w}
+% The main function \cs{@@_flatten:N} receives a linked property list and
+% flattens it. The auxiliary \cs{@@_flatten_aux:N} receives a pointer to the
+% first key and flattens the linked property list into a flat property list.
+% This is only restricted-expandable as it involves mapping through all of the
+% property list's entries starting from \meta{key_1}. The looping function
+% \cs{@@_flatten_loop:w} removes \cs{use_none:n} and a backwards pointer~|#2|,
+% leaves the key--value pair for \cs{use:e} to receive, and calls itself
+% again after expanding the next key's token list. Its argument |#3| is
+% empty, except at the end where it is the \cs{use_none:nnnn} appearing in the
+% definition of~\cs{@@_flatten_aux:N}, which ends the loop.
+% \begin{macrocode}
+\cs_new:Npn \@@_flatten:N #1
+ { \exp_after:wN \@@_flatten_aux:w #1 }
+\cs_new:Npn \@@_flatten_aux:w #1 \s_@@ #2 { \@@_flatten_aux:N }
+\cs_new:Npn \@@_flatten_aux:N #1
+ {
+ \s_@@ \@@_chk:w
+ \exp_after:wN \@@_flatten_loop:w #1
+ \use_none:nnnn \@@_pair:wn \s_@@ { }
+ }
+\cs_new:Npn \@@_flatten_loop:w #1#2#3 \@@_pair:wn #4 \s_@@ #5
+ {
+ #3
+ \exp_not:n { \@@_pair:wn #4 \s_@@ {#5} }
+ \exp_after:wN \@@_flatten_loop:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\g_@@_prefix_int, \c_@@_basis_int}
+% Used to assign prefixes for each linked property list. It is converted to
+% base \cs{c_@@_basis_int}, then each digit is converted to a character,
+% starting at |!| (the character after space).
+% \begin{macrocode}
+\int_new:N \g_@@_prefix_int
+\int_const:Nn \c_@@_basis_int { \c_max_char_int - `\! }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_next_prefix:, \@@_to_prefix:n}
+% Store in \cs{l_@@_internal_tl} the conversion of \cs{g_@@_prefix_int} to
+% characters, and increment this integer for use in the next linked property
+% list. No need to optimize since this is only used when declaring the
+% property list the first time.
+% The aim here is to make this string as short as we can, given the
+% range of distinct characters available. This speeds up the work of
+% \cs{cs:w} \ldots{} \cs{cs_end:} that looks up keys in the hash table.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_next_prefix:
+ {
+ \tl_set:Ne \l_@@_internal_tl
+ { \@@_to_prefix:n { \g_@@_prefix_int } }
+ \int_gincr:N \g_@@_prefix_int
+ }
+\cs_new:Npn \@@_to_prefix:n #1
+ {
+ \int_compare:nNnTF {#1} > \c_@@_basis_int
+ {
+ \exp_args:Nf \@@_to_prefix:n
+ { \int_div_truncate:nn {#1} \c_@@_basis_int }
+ \exp_args:Nf \@@_to_prefix:n
+ { \int_mod:nn {#1} \c_@@_basis_int }
+ }
+ { \char_generate:nn { `\! + #1 } { 12 } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_if_flat:NTF, \@@_if_flat_aux:w}
+% We could either test for the presence of \cs{@@_chk:w} (flat
+% property list) or of \cs{@@_flatten:w} (linked property list). We
+% make the second choice; this way props that are accidentally
+% \tn{relax} are treated as they were before. The auxiliary receives
+% \cs{use_i:nn} or \cs{use_ii:nn} as~|#3|.
+% As a transitional fix we avoid erroring in case the prop is undefined (the \cs{exp_after:wN} is omitted in that case, taking the flat branch).
+% \begin{macrocode}
+\cs_new:Npn \@@_if_flat:NTF #1
+ {
+ \prop_if_exist:NT #1
+ \exp_after:wN \@@_if_flat_aux:w #1
+ \s_@@_mark \use_ii:nn
+ \@@_flatten:w \s_@@_mark \use_i:nn \s_@@_stop
+ }
+\cs_new:Npn \@@_if_flat_aux:w
+ #1 \@@_flatten:w #2 \s_@@_mark #3 #4 \s_@@_stop {#3}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Allocation and initialisation}
+%
+% \begin{variable}[tested = m3prop004]{\c_empty_prop}
+% An empty flat prop.
+% \begin{macrocode}
+\tl_const:Nn \c_empty_prop { \s_@@ \@@_chk:w }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[tested = m3prop001]{\prop_new:N, \prop_new:c}
+% Flat property lists are initialized with the value \cs{c_empty_prop}.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_eq:NN #1 \c_empty_prop
+ }
+\cs_generate_variant:Nn \prop_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[tested = m3prop001]{\prop_new_linked:N, \prop_new_linked:c}
+% \begin{macro}{\@@_new_linked:N}
+% The auxiliary is used in \cs{prop_make_linked:N}.
+% For linked property lists, get a new prefix in \cs{l_@@_internal_tl}, then
+% use it to set up the internal structure: the last token in~|#1| is usually a
+% pointer to the first key, which is here the end-pointer. That end-pointer
+% has a pointer to the previous key (usually the last key), which is the
+% variable~|#1| itself that begins the doubly-linked list.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_new_linked:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \@@_new_linked:N #1
+ }
+\cs_new_protected:Npn \@@_new_linked:N #1
+ {
+ \@@_next_prefix:
+ \cs_gset_nopar:Npe #1
+ {
+ \@@_flatten:w
+ \exp_not:c { @@ ~ \l_@@_internal_tl }
+ \s_@@ { \l_@@_internal_tl }
+ \exp_not:c { @@ ~ \l_@@_internal_tl }
+ }
+ \cs_gset_nopar:cpe { @@ ~ \l_@@_internal_tl }
+ {
+ \exp_not:N \use_none:n
+ \exp_not:N #1
+ }
+ }
+\cs_generate_variant:Nn \prop_new_linked:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[tested = m3prop001]{\prop_clear:N, \prop_clear:c}
+% \begin{macro}[tested = m3prop001]{\prop_gclear:N, \prop_gclear:c}
+% \begin{macro}{\@@_clear:NNN, \@@_clear:wNNN, \@@_clear_loop:Nw}
+% Clearing a flat property list is like declaring it anew, simply setting it
+% equal to \cs{c_empty_prop}. For linked property lists we must clear all of
+% the variables storing individual keys, which requires a loop. At each step
+% of the loop, \cs{@@_clear_loop:Nw} receives
+% \cs[index=cs_set_eq:NN]{cs_(g)set_eq:NN}, \cs{use_none:n}, the backwards
+% pointer, an empty~|#4| (except at the end of the loop), and the key--value
+% pair |#5=#6| which we disregard. The looping auxiliary undefines the
+% previous key's token list (this includes the main token list, but that is
+% fine because it is restored at the end) and calls itself after expanding the
+% next key's token list. The loop ends when |#4| is \cs{use_none:nnnn}.
+% After the loop, \cs{@@_clear:wNNN} correctly sets up the main variable~|#6|
+% and the end-pointer~|#1|. Importantly, this is done using
+% \cs[index=cs_set_nopar:Npe]{cs_(g)set_nopar:Npe} and \cs{exp_not:n} because
+% the almost-equivalent \cs{tl_set:Nn} would complain in debug mode about the
+% fact that the main variable is undefined at this stage. Importantly,
+% \cs{@@_clear_entries:NN} is used in the implementation of
+% \cs{prop_set_eq:NN}.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_clear:N
+ { \@@_clear:NNN \cs_set_eq:NN \cs_set_nopar:Npe }
+\cs_generate_variant:Nn \prop_clear:N { c }
+\cs_new_protected:Npn \prop_gclear:N
+ { \@@_clear:NNN \cs_gset_eq:NN \cs_gset_nopar:Npe }
+\cs_generate_variant:Nn \prop_gclear:N { c }
+\cs_new_protected:Npn \@@_clear:NNN #1#2#3
+ {
+ \@@_if_flat:NTF #3
+ { #1 #3 \c_empty_prop }
+ { \exp_after:wN \@@_clear:wNNN #3 #1 #2 #3 }
+ }
+\cs_new_protected:Npn \@@_clear:wNNN
+ \@@_flatten:w #1 \s_@@ #2#3#4#5#6
+ {
+ \@@_clear_entries:NN #4 #3
+ #5 #6 { \exp_not:n { \@@_flatten:w #1 \s_@@ {#2} #1 } }
+ #5 #1 { \exp_not:n { \use_none:n #6 } }
+ }
+\cs_new_protected:Npn \@@_clear_entries:NN #1#2
+ {
+ \exp_after:wN \@@_clear_loop:Nw \exp_after:wN #1 #2
+ \use_none:nnnn \@@_pair:wn \s_@@ { }
+ }
+\cs_new_protected:Npn \@@_clear_loop:Nw
+ #1#2#3#4 \@@_pair:wn #5 \s_@@ #6
+ {
+ #1 #3 \tex_undefined:D
+ #4
+ \exp_after:wN \@@_clear_loop:Nw
+ \exp_after:wN #1
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[tested = m3prop001]
+% {
+% \prop_clear_new:N, \prop_clear_new:c,
+% \prop_gclear_new:N, \prop_gclear_new:c,
+% \prop_clear_new_linked:N, \prop_clear_new_linked:c,
+% \prop_gclear_new_linked:N, \prop_gclear_new_linked:c
+% }
+% A simple variation of the token list functions.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_clear_new:N #1
+ { \prop_if_exist:NTF #1 { \prop_clear:N #1 } { \prop_new:N #1 } }
+\cs_generate_variant:Nn \prop_clear_new:N { c }
+\cs_new_protected:Npn \prop_gclear_new:N #1
+ { \prop_if_exist:NTF #1 { \prop_gclear:N #1 } { \prop_new:N #1 } }
+\cs_generate_variant:Nn \prop_gclear_new:N { c }
+\cs_new_protected:Npn \prop_clear_new_linked:N #1
+ { \prop_if_exist:NTF #1 { \prop_clear:N #1 } { \prop_new_linked:N #1 } }
+\cs_generate_variant:Nn \prop_clear_new_linked:N { c }
+\cs_new_protected:Npn \prop_gclear_new_linked:N #1
+ { \prop_if_exist:NTF #1 { \prop_gclear:N #1 } { \prop_new_linked:N #1 } }
+\cs_generate_variant:Nn \prop_gclear_new_linked:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[tested = m3prop001]
+% {\prop_set_eq:NN, \prop_set_eq:cN, \prop_set_eq:Nc, \prop_set_eq:cc}
+% \begin{macro}[tested = m3prop001]
+% {\prop_gset_eq:NN, \prop_gset_eq:cN, \prop_gset_eq:Nc, \prop_gset_eq:cc}
+% \begin{macro}
+% {
+% \@@_set_eq:NNNN, \@@_set_eq:wNNNN, \@@_set_eq:nNnNN,
+% \@@_set_eq_loop:NNnw, \@@_set_eq_end:w
+% }
+% If both variables are accidentally the same variable (or equal flat property
+% lists, as it turns out) we do nothing, otherwise the following code would
+% lose all entries. If the target variable~|#3| is a flat prop, either copy
+% directly or flatten before copying. If it is a linked prop, we must clear
+% it, then go through the entries in~|#4| to add them to~|#3|.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_set_eq:NN
+ { \@@_set_eq:NNNN \cs_set_eq:NN \cs_set_nopar:Npe }
+\cs_generate_variant:Nn \prop_set_eq:NN { Nc , cN , cc }
+\cs_new_protected:Npn \prop_gset_eq:NN
+ { \@@_set_eq:NNNN \cs_gset_eq:NN \cs_gset_nopar:Npe }
+\cs_generate_variant:Nn \prop_gset_eq:NN { Nc , cN , cc }
+\cs_new_protected:Npn \@@_set_eq:NNNN #1#2#3#4
+ {
+ \cs_if_eq:NNF #3#4
+ {
+ \@@_if_flat:NTF #3
+ {
+ \@@_if_flat:NTF #4
+ { #1 #3 #4 }
+ { #2 #3 { \@@_flatten:N #4 } }
+ }
+ { \exp_after:wN \@@_set_eq:wNNNN #3 #1#2#3#4 }
+ }
+ }
+\cs_new_protected:Npn \@@_set_eq:wNNNN
+ \@@_flatten:w #1 \s_@@ #2#3#4#5#6#7
+ {
+ \@@_clear_entries:NN #4 #3
+ \exp_args:Nf \@@_set_eq:nNnNN {#7} #1 {#2} #5 #6
+ }
+% \end{macrocode}
+% We have used that |f|-expanding either type of prop gives a flat prop. At
+% this stage \cs{@@_set_eq:nNnNN} receives the second variable as a flat prop,
+% the end-pointer, the prefix, the suitable
+% \cs[index=cs_set_nopar:Npe]{cs_(g)set_nopar:Npe} assignment, and the first
+% variable itself. Remove the leading \cs{s_@@} and \cs{@@_chk:w} with
+% \cs{use_i:nnn}, then start the loop.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_set_eq:nNnNN #1#2#3#4#5
+ {
+ \use_i:nnn
+ {
+ \@@_set_eq_loop:NNnw #5 #4 {#3}
+ \@@_flatten:w #2 \s_@@ {#3}
+ }
+ #1
+ \use_none:n \@@_pair:wn ? \s_@@
+ }
+% \end{macrocode}
+% The looping function receives the current pointer~|#1| (initially the
+% variable itself), the defining function~|#2| and the prefix~|#3|, then a
+% partial definition~|#4| (which in later stages includes the backwards
+% pointer), followed by the current value as \cs{s_@@} |{#5}|. It seeks the
+% next key~|#7| to construct in \cs{l_@@_internal_tl} the next pointer
+% \cs[no-index]{@@~\meta{prefix}~\meta{next key}} (the
+% argument~|#6| is empty, except at the end of the loop, where it is
+% \cs{use_none:n} in such a way as to delete the \meta{space} and \meta{next
+% key}). Then the token list (current pointer)~|#1| is set-up to contain the
+% partial definition and current value, as well as the newly constructed next
+% pointer. After a line responsible for correctly ending the loop with
+% \cs{@@_set_eq_end:w}, we loop, setting up the next definition, which starts
+% with \cs{use_none:n} and a backwards pointer to~|#1| followed by the
+% \meta{next key}~|#7| and so on.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_set_eq_loop:NNnw
+ #1#2#3#4 \s_@@ #5#6 \@@_pair:wn #7 \s_@@
+ {
+ \tl_set:Ne \l_@@_internal_tl { \exp_not:c { @@ ~ #3 #6 ~ #7 } }
+ #2 #1 { \exp_not:n { #4 \s_@@ {#5} } \exp_not:o \l_@@_internal_tl }
+ \use_none:n #6 \@@_set_eq_end:w
+ \exp_after:wN \@@_set_eq_loop:NNnw \l_@@_internal_tl #2 {#3}
+ \use_none:n #1 \@@_pair:wn #7 \s_@@
+ }
+% \end{macrocode}
+% The end-code picks up what is needed to correctly assign the last token
+% list (the end pointer), which is simply \cs{use_none:n}
+% \cs[no-index]{@@_\meta{prefix}\meta{space}\meta{key_n}}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_set_eq_end:w
+ \exp_after:wN \@@_set_eq_loop:NNnw #1#2#3
+ \use_none:n #4#5 \s_@@
+ {
+ \exp_after:wN #2 \l_@@_internal_tl { \exp_not:n { \use_none:n #4 } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\prop_make_flat:N, \prop_make_flat:c \@@_make_flat:Nn}
+% The only interesting case is when given a linked prop. Clear the
+% linked property list using \cs{@@_clear:wNNN} with local assignments
+% (it does not matter since we are at the outermost group level, and
+% \cs{cs_set_eq:NN} is very slightly faster than its global version.
+% Then store the contents (expanded preventively by \cs{exp_args:NNf})
+% with an assignment \cs{cs_set_nopar:Npe} that does not perform
+% \pkg{l3debug} checks.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_make_flat:N #1
+ {
+ \int_compare:nNnTF { \tex_currentgrouplevel:D } = 0
+ {
+ \@@_if_flat:NTF #1 { }
+ { \exp_args:NNf \@@_make_flat:Nn #1 {#1} }
+ }
+ {
+ \msg_error:nnee { prop } { inner-make }
+ { \token_to_str:N \prop_make_flat:N } { \token_to_str:N #1 }
+ }
+ }
+\cs_generate_variant:Nn \prop_make_flat:N { c }
+\cs_new_protected:Npn \@@_make_flat:Nn #1#2
+ {
+ \exp_after:wN \@@_clear:wNNN #1 \cs_set_eq:NN \cs_set_nopar:Npe #1
+ \cs_set_nopar:Npe #1 { \exp_not:n {#2} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\prop_make_linked:N, \prop_make_linked:c, \@@_make_linked:Nn}
+% The only interesting case is when given a flat prop. We expand the
+% contents for later use. Then \cs{@@_new_linked:N} disregards that
+% previous value of~|#1| and initializes the linked prop. We can then
+% use an auxiliary \cs{@@_set_eq:wNNNN} underlying
+% \cs{prop_set_eq:NN}, with the prop contents saved as
+% \cs{l_@@_internal_tl}. That step is a bit unsafe, as
+% \cs{l_@@_internal_tl} (really, a flat prop here) is used within
+% \cs{@@_set_eq:wNNNN} itself, but it is in fact expanded early enough
+% to be ok.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_make_linked:N #1
+ {
+ \int_compare:nNnTF { \tex_currentgrouplevel:D } = 0
+ {
+ \@@_if_flat:NTF #1
+ { \exp_args:NNo \@@_make_linked:Nn #1 {#1} } { }
+ }
+ {
+ \msg_error:nnee { prop } { inner-make }
+ { \token_to_str:N \prop_make_linked:N } { \token_to_str:N #1 }
+ }
+ }
+\cs_generate_variant:Nn \prop_make_linked:N { c }
+\cs_new_protected:Npn \@@_make_linked:Nn #1#2
+ {
+ \@@_new_linked:N #1
+ \tl_set:Nn \l_@@_internal_tl {#2}
+ \exp_after:wN \@@_set_eq:wNNNN #1
+ \cs_set_eq:NN \cs_set_nopar:Npe #1 \l_@@_internal_tl
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}[tested = m3prop004]{\l_tmpa_prop, \l_tmpb_prop}
+% \begin{variable}[tested = m3prop004]{\g_tmpa_prop, \g_tmpb_prop}
+% We can now initialize the scratch variables.
+% \begin{macrocode}
+\prop_new:N \l_tmpa_prop
+\prop_new:N \l_tmpb_prop
+\prop_new:N \g_tmpa_prop
+\prop_new:N \g_tmpb_prop
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{macro}
+% {
+% \prop_concat:NNN, \prop_concat:ccc,
+% \prop_gconcat:NNN, \prop_gconcat:ccc,
+% \@@_concat:NNNNN, \@@_concat:nNNN
+% }
+% The basic strategy is to copy the first variable into the target,
+% then loop through the second variable, calling
+% \cs[index=prop_put:Nnn]{prop_(g)put:Nnn} on each item. To avoid
+% running the \pkg{l3debug} scope checks on each of these steps, we
+% use the auxiliaries that underly \cs{prop_set_eq:NN} and
+% \cs{prop_put:Nnn}, whose syntax is a bit unwieldy.
+% We work directly with the target prop |#3| as a scratch space,
+% because copying over from a temporary variable to |#3| would be slow
+% in the linked case. If |#5| is |#3| itself we have to be careful
+% not to lose the data, and we even take the opportunity to skip the
+% copying step completely. To keep the correct version of the
+% duplicate keys we use the code underlying \cs{prop_put_if_not_in:Nnn},
+% which involves passing \cs{use_none:nnn} to the auxiliary instead of
+% nothing.
+% There is no need to check for the case where |#3| is equal to~|#4|
+% because in that case \cs[index=prop_set_eq:NN]{prop_(g)set_eq:NN}
+% |#3| |#4| (or rather the underlying auxiliary) is correctly set up
+% to do no needless work.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_concat:NNN
+ { \@@_concat:NNNNN \cs_set_eq:NN \cs_set_nopar:Npe }
+\cs_generate_variant:Nn \prop_concat:NNN { ccc }
+\cs_new_protected:Npn \prop_gconcat:NNN
+ { \@@_concat:NNNNN \cs_gset_eq:NN \cs_gset_nopar:Npe }
+\cs_generate_variant:Nn \prop_gconcat:NNN { ccc }
+\cs_new_protected:Npn \@@_concat:NNNNN #1#2#3#4#5
+ {
+ \cs_if_eq:NNTF #3 #5
+ { \@@_concat:nNNN \use_none:nnn #2 #3 #4 }
+ {
+ \@@_set_eq:NNNN #1 #2 #3 #4
+ \@@_concat:nNNN { } #2 #3 #5
+ }
+ }
+\cs_new_protected:Npn \@@_concat:nNNN #1#2#3#4
+ {
+ \cs_gset_eq:NN \@@_tmp:w \@@_pair:wn
+ \cs_gset_protected:Npn \@@_pair:wn ##1 \s_@@
+ { \@@_put:nNNnn {#1} #2 #3 {##1} }
+ \exp_last_unbraced:Nf \use_none:nn #4
+ \cs_gset_eq:NN \@@_pair:wn \@@_tmp:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \prop_put_from_keyval:Nn, \prop_put_from_keyval:cn,
+% \prop_gput_from_keyval:Nn, \prop_gput_from_keyval:cn,
+% \@@_from_keyval:nn, \@@_from_keyval:Nnn,
+% \@@_missing_eq:n
+% }
+% The core is a call to \cs{keyval_parse:nnn}, with an error message
+% \cs{@@_missing_eq:n} for entries without~|=|, and a call to (essentially)
+% \cs[index=prop_put:Nnn]{prop_(g)put:Nnn} for valid key--value pairs.
+% To avoid repeated scope checks (and errors) when \pkg{l3debug} is
+% active, we instead use the auxiliary underlying \cs{prop_put:Nnn}.
+% Because blank keys are valid here, in contrast to \pkg{l3keys}, we set and
+% restore \cs{l__kernel_keyval_allow_blank_keys_bool}.
+% The key--value argument may be quite large so we avoid reading it until it
+% is really necessary.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_put_from_keyval:Nn #1
+ { \@@_from_keyval:nn { \@@_put:nNNnn { } \cs_set_nopar:Npe #1 } }
+\cs_generate_variant:Nn \prop_put_from_keyval:Nn { c }
+\cs_new_protected:Npn \prop_gput_from_keyval:Nn #1
+ { \@@_from_keyval:nn { \@@_put:nNNnn { } \cs_gset_nopar:Npe #1 } }
+\cs_generate_variant:Nn \prop_gput_from_keyval:Nn { c }
+\cs_new_protected:Npn \@@_from_keyval:nn
+ {
+ \bool_if:NTF \l__kernel_keyval_allow_blank_keys_bool
+ { \@@_from_keyval:Nnn \c_true_bool }
+ { \@@_from_keyval:Nnn \c_false_bool }
+ }
+\cs_new_protected:Npn \@@_from_keyval:Nnn #1#2#3
+ {
+ \bool_set_eq:NN \l__kernel_keyval_allow_blank_keys_bool \c_true_bool
+ \keyval_parse:nnn \@@_missing_eq:n {#2} {#3}
+ \bool_set_eq:NN \l__kernel_keyval_allow_blank_keys_bool #1
+ }
+\cs_new_protected:Npn \@@_missing_eq:n
+ { \msg_error:nnn { prop } { prop-keyval } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \prop_set_from_keyval:Nn, \prop_set_from_keyval:cn,
+% \prop_gset_from_keyval:Nn, \prop_gset_from_keyval:cn,
+% }
+% Just empty the prop (with the auxiliary underlying
+% \cs{prop_clear:N} to avoid \pkg{l3debug} problems) and push
+% key--value entries using
+% \cs[index=prop_put_from_keyval:Nn]{prop_(g)put_from_keyval:Nn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_set_from_keyval:Nn #1
+ {
+ \@@_clear:NNN \cs_set_eq:NN \cs_set_nopar:Npe #1
+ \prop_put_from_keyval:Nn #1
+ }
+\cs_generate_variant:Nn \prop_set_from_keyval:Nn { c }
+\cs_new_protected:Npn \prop_gset_from_keyval:Nn #1
+ {
+ \@@_clear:NNN \cs_gset_eq:NN \cs_gset_nopar:Npe #1
+ \prop_gput_from_keyval:Nn #1
+ }
+\cs_generate_variant:Nn \prop_gset_from_keyval:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \prop_const_from_keyval:Nn, \prop_const_from_keyval:cn,
+% \prop_const_linked_from_keyval:Nn, \prop_const_linked_from_keyval:cn
+% }
+% For both flat and linked constant props, we create |#1| then use the
+% same auxiliary as for \cs{prop_gput_from_keyval:Nn}. It is most
+% natural to use the already packaged \cs{prop_gput:Nnn}, but that
+% would mean doing an assignment on a supposedly constant property
+% list. To avoid errors when \pkg{l3debug} is activated, we use the
+% auxiliary underlying \cs{prop_gput:Nnn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_const_from_keyval:Nn #1
+ {
+ \prop_new:N #1
+ \@@_from_keyval:nn { \@@_put:nNNnn { } \cs_gset_nopar:Npe #1 }
+ }
+\cs_generate_variant:Nn \prop_const_from_keyval:Nn { c }
+\cs_new_protected:Npn \prop_const_linked_from_keyval:Nn #1
+ {
+ \prop_new_linked:N #1
+ \@@_from_keyval:nn { \@@_put:nNNnn { } \cs_gset_nopar:Npe #1 }
+ }
+\cs_generate_variant:Nn \prop_const_linked_from_keyval:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Accessing data in property lists}
+%
+% Accessing/deleting/adding entries is mostly done by \cs{@@_split:NnTFn}, which
+% must be fast because it is used in many \pkg{l3prop} functions. Its syntax is
+% as follows.
+% \begin{quote}
+% \cs{@@_split:NnTFn} \meta{property list} \Arg{key} \\
+% ~~\Arg{true code} \Arg{false code} \Arg{link code}
+% \end{quote}
+% If the \meta{property list} uses the linked data storage, then it runs
+% the \meta{link code}, otherwise it does as follows.
+%
+% It splits the \meta{property list} at the \meta{key}, giving three
+% token lists: the \meta{entries before} the \meta{key}, the
+% \meta{value} associated with the \meta{key} and the \meta{entries
+% after} the \meta{key}. Both the \meta{entries before} and the
+% \meta{entries after} can be empty or consist of some number of
+% consecutive entries \cs{@@_pair:wn} \meta{key_i} \cs{s_@@}
+% \Arg{value_i}. If the \meta{key} is present in the \meta{property
+% list} then the \meta{true code} is left in the input stream, with
+% |#1|, |#2|, and |#3| replaced by the \meta{entries before},
+% \meta{value}, and \meta{entries after}. If the \meta{key} is not
+% present in the \meta{property list} then the \meta{false code} is left
+% in the input stream. Only the \meta{true code} is used in the
+% replacement text of a macro defined internally, which requires
+% |##|~doubling.
+%
+% \begin{macro}
+% {
+% \@@_split:NnTFn, \@@_split_aux:nNTFn, \@@_split_test:wn,
+% \@@_split_flat:w, \@@_split_linked:w, \@@_split_wrong:Nw
+% }
+% The aim is to distinguish four cases: a flat prop that contains the
+% given \meta{key}, a flat prop that does not contain it, a linked
+% prop, and an invalid prop. The last case includes those that are
+% set to \tn{relax} by \texttt{c}-expansion, as well as unrelated
+% token list variables since these unfortunately used to
+% \enquote{work} in earlier implementations.
+% In the first three cases we run the \texttt{T}, \texttt{F}, and
+% \texttt{n} arguments, and in the last case we raise an error, set
+% the variable to a known state (empty prop), and run the \texttt{F}
+% code (some conditionals such as \cs{prop_pop:NnNTF} otherwise blow
+% up pretty badly).
+%
+% The first distinction between these cases is done by
+% \cs{@@_split_test:wn}, which looks for the argument after \cs{s_@@}.
+% For a flat prop it will be \cs{@@_chk:w}, which leads to running
+% \cs{@@_split_flat:w}, explained below. For a linked prop it is the
+% prefix, consisting of characters, so we end up running
+% \cs{@@_split_linked:w}, which cleans up and selects the
+% aforementioned \texttt{n} argument. For invalid props, or rather,
+% variables that do not contain \cs{s_@@}, the argument includes
+% \cs{fi:}, and we end up calling \cs{@@_split_wrong:Nw}, which calls
+% \cs{prop_show:N} to raise a detailed error stating how the variable
+% is wrong.
+%
+% Let us return to \cs{@@_split_flat:w}. This function is defined
+% dynamically as
+% \begin{quote}
+% \cs{cs_set:Npn} \cs{@@_split_flat:w} \cs{@@_split_linked:w} |#1| \\
+% \quad \cs{@@_pair:wn} \meta{key} \cs{s_@@} |#2| \\
+% \quad |#3| \cs{s_@@_mark} |#4| |#5| \cs{s_@@_stop} \\
+% \quad |{| |#4| \Arg{true code} |}|
+% \end{quote}
+% Its job is to seek the \meta{key} in the property list (known to be
+% flat at this stage) by using an argument |#1| delimited essentially
+% by that key. If indeed the variable contained the \meta{key}, then
+% |#1|~is the \meta{extract_1} before the key--value pair, |#2|~is the
+% \meta{value} associated with the \meta{key}, |#3|~is the
+% \meta{extract_2} after the key--value pair, |#4|~is \cs{use_i:nnn},
+% and we run \cs{use_i:nnn} \Arg{true code} \Arg{false code} \Arg{link
+% code}, selecting the \meta{true code}. Otherwise, the whole
+% property list together with \cs{s_@@_mark} \cs{use_i:nnn} is taken
+% in as |#1|, then |#2| is some tokens |?| \cs{fi:}
+% \cs{@@_split_wrong:Nw} \meta{variable} that were only useful in the
+% case of invalid props, |#3|~is empty, and most importantly |#4| is
+% \cs{use_ii:nnn}. This command selects the \meta{false code}.
+%
+% Note that we define \cs{@@_split_flat:w} in all cases even though it
+% is only used in the flat case. Indeed, to avoid taking in the whole
+% property list (which may be large) as an argument more than strictly
+% necessary, we would have to keep the \meta{true code} positioned
+% before the expansion of the prop variable in order to use it in the
+% definition. The only way to do that is to store it using an
+% assignment so we might as well just perform the assignment that we
+% can actually use in the flat case.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_split:NnTFn #1#2
+ {
+ \exp_after:wN \@@_split_aux:nNTFn
+ \exp_after:wN { \tl_to_str:n {#2} } #1
+ }
+\cs_new_protected:Npn \@@_split_aux:nNTFn #1#2#3
+ {
+ \cs_set:Npn \@@_split_flat:w \@@_split_linked:w ##1
+ \@@_pair:wn #1 \s_@@ ##2 ##3 \s_@@_mark ##4 ##5 \s_@@_stop
+ { ##4 {#3} }
+ \exp_after:wN \@@_split_test:wn #2 \s_@@_mark \use_i:nnn
+ \@@_pair:wn #1 \s_@@ { ? \fi: \@@_split_wrong:Nw #2 }
+ \s_@@_mark \use_ii:nnn
+ \s_@@_stop
+ }
+\cs_new:Npn \@@_split_flat:w { }
+\cs_new_protected:Npn \@@_split_test:wn #1 \s_@@ #2
+ {
+ \if_meaning:w \@@_chk:w #2 \exp_after:wN \@@_split_flat:w \fi:
+ \@@_split_linked:w
+ }
+\cs_new_protected:Npn \@@_split_linked:w #1 \s_@@_stop #2#3 {#3}
+\cs_new_protected:Npn \@@_split_wrong:Nw #1#2 \s_@@_stop #3#4
+ {
+ \prop_show:N #1
+ \cs_gset_eq:NN #1 \c_empty_prop
+ #3
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[tested = m3prop002]
+% {
+% \prop_get:NnN, \prop_get:NVN, \prop_get:NvN, \prop_get:NeN,
+% \prop_get:NoN, \prop_get:NxN,
+% \prop_get:cnN, \prop_get:cVN, \prop_get:cvN, \prop_get:ceN,
+% \prop_get:coN, \prop_get:cxN,
+% \prop_get:cnc
+% }
+% \begin{macro}[TF, tested = m3prop004]
+% {
+% \prop_get:NnN, \prop_get:NVN, \prop_get:NvN, \prop_get:NeN,
+% \prop_get:NoN, \prop_get:NxN,
+% \prop_get:cnN, \prop_get:cVN, \prop_get:cvN, \prop_get:ceN,
+% \prop_get:coN, \prop_get:cxN,
+% \prop_get:cnc
+% }
+% \begin{macro}{\@@_get:NnnTF}
+% \begin{macro}[EXP]{\@@_get_linked:w, \@@_get_linked_aux:w}
+% Here we implement both \cs{prop_get:NnN} and its branching version through
+% \cs{@@_get:NnnTF}. It receives the prop and key, followed by an assignment
+% used when the value is found, \meta{true code} to run after the assignment,
+% and some fall-back \meta{false code} for absent values. It relies on
+% \cs{@@_split:NnTFn}. For a flat prop, the first four arguments of
+% \cs{@@_split:NnTFn} are used, and run either the assignment~|#3{##3}| and
+% \meta{true code}~|#4|, or the \meta{false code}~|#5|.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_get:NnN #1#2#3
+ {
+ \@@_get:NnnTF #1 {#2}
+ { \tl_set:Nn #3 } { } { \tl_set:Nn #3 { \q_no_value } }
+ }
+\cs_generate_variant:Nn \prop_get:NnN { NV , Nv , Ne , c , cV , cv , ce }
+\cs_generate_variant:Nn \prop_get:NnN { No , Nx , co , cx }
+\cs_generate_variant:Nn \prop_get:NnN { cnc }
+\prg_new_protected_conditional:Npnn \prop_get:NnN #1#2#3 { T , F , TF }
+ {
+ \@@_get:NnnTF #1 {#2}
+ { \tl_set:Nn #3 } \prg_return_true: \prg_return_false:
+ }
+\prg_generate_conditional_variant:Nnn \prop_get:NnN
+ { NV , Nv , Ne , c , cV , cv , ce } { T , F , TF }
+\prg_generate_conditional_variant:Nnn \prop_get:NnN
+ { No , Nx , co , cx } { T , F , TF }
+\prg_generate_conditional_variant:Nnn \prop_get:NnN
+ { cnc } { T , F , TF }
+\cs_new_protected:Npn \@@_get:NnnTF #1#2#3#4#5
+ {
+ \@@_split:NnTFn #1 {#2}
+ { #3 {##2} #4 }
+ {#5}
+ { \exp_after:wN \@@_get_linked:w #1 {#2} {#3} {#4} {#5} }
+ }
+% \end{macrocode}
+% For a linked prop we must work a bit: \cs{@@_get_linked:w} is followed by
+% the expansion of the prop, then by four brace groups: the key~|#4|, the
+% assignment code~|#5|, \meta{true code}~|#6|, and \meta{false code}~|#7|. If
+% the key is present, its value is stored in the token list
+% \cs[no-index]{@@_}|#2~#4|. If that token list exists,
+% \cs{@@_get_linked_aux:w} gets called followed by the expansion of that token
+% list and we grab as~|#2| the value associated to that key, which we feed to
+% the assignment code and follow-up code. If the key is absent the token list
+% can be \tn{undefined} or \tn{relax}. In both cases \cs{@@_get_linked_aux:w}
+% finds an empty brace group as~|#2|, \cs{use_none:n} as~|#4| and the
+% \meta{false code} as~|#5|.
+% Note that we made \cs{@@_get_linked:w} and subsequent auxiliaries
+% expandable, because they are also used in \cs{prop_item:Nn}.
+% \begin{macrocode}
+\cs_new:Npn \@@_get_linked:w
+ \@@_flatten:w #1 \s_@@ #2#3#4#5#6#7
+ {
+ \if_cs_exist:w @@ ~ #2 ~ \tl_to_str:n {#4} \cs_end:
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_get_linked_aux:w
+ \cs:w @@ ~ #2 ~ \tl_to_str:n {#4} \exp_after:wN \cs_end:
+ \else:
+ \exp_after:wN \@@_get_linked_aux:w
+ \fi:
+ \s_@@_mark {#5} {#6}
+ \s_@@ { } \s_@@_mark \use_none:n {#7}
+ \s_@@_stop
+ }
+\cs_new:Npn \@@_get_linked_aux:w
+ #1 \s_@@ #2 #3 \s_@@_mark #4 #5 #6 \s_@@_stop { #4 {#2} #5 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \prop_item:Nn, \prop_item:NV, \prop_item:No, \prop_item:Ne,
+% \prop_item:cn, \prop_item:cV, \prop_item:co, \prop_item:ce
+% }
+% \begin{macro}[EXP]{\@@_item:nnn}
+% Getting the value corresponding to a key in a flat property list in an
+% expandable fashion simply uses \cs{prop_map_tokens:Nn} to go through
+% the property list. The auxiliary \cs{@@_item:nnn} receives the
+% search string~|#1|, the key~|#2| and the value~|#3| and returns as
+% appropriate.
+% \begin{macrocode}
+\cs_new:Npn \prop_item:Nn #1#2
+ {
+ \@@_if_flat:NTF #1
+ {
+ \exp_args:NNo \prop_map_tokens:Nn #1
+ {
+ \exp_after:wN \@@_item:nnn
+ \exp_after:wN { \tl_to_str:n {#2} }
+ }
+ }
+ { \exp_after:wN \@@_get_linked:w #1 {#2} \use:n { } { } }
+ }
+\cs_new:Npn \@@_item:nnn #1#2#3
+ {
+ \str_if_eq:eeT {#1} {#2}
+ { \prop_map_break:n { \exp_not:n {#3} } }
+ }
+\cs_generate_variant:Nn \prop_item:Nn { NV , No , Ne , c , cV , co , ce }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Removing data from property lists}
+%
+% \begin{macro}
+% {
+% \@@_pop:NnNNnTF,
+% \@@_pop_linked:wnNNnTF, \@@_pop_linked:NNNn,
+% \@@_pop_linked:w,
+% \@@_pop_linked_prev:w, \@@_pop_linked_next:w
+% }
+% This auxiliary is used by both the \cs[no-index]{prop_pop} family and
+% the \cs[no-index]{prop_remove} family of functions.
+% It receives a \meta{prop} and a \Arg{key}, three assignment functions
+% (\cs{tl_set:Nn} \cs{cs_set_eq:NN} \cs{cs_set_nopar:Npe} or their global
+% versions), then \Arg{code} \Arg{true code} \Arg{false code}.
+%
+% For a flat prop, split it. If the \meta{key} is there, reconstruct the rest
+% of the prop from the two extracts |##2| |##4| and assign using
+% \cs[index=tl_set:Nn]{tl_(g)set:Nn}, then run \meta{code} \Arg{value} with
+% the \meta{value} found, and run the \meta{true code}. If the \meta{key} is
+% absent, run the \meta{false code}.
+%
+% For a linked prop, the removal is done by \cs{@@_pop_linked:wnNNnTF}, which
+% removes the key--value pair from the doubly-linked list and runs its last
+% three arguments \Arg{code} \Arg{true code} \Arg{false code} depending on
+% whether the key--value is found, in the same way as for flat props.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_pop:NnNNnTF #1#2#3#4#5#6#7
+ {
+ \@@_split:NnTFn #1 {#2}
+ {
+ #4 #1 { \exp_not:n { \s_@@ \@@_chk:w ##1 ##3 } }
+ #5 {##2}
+ #6
+ }
+ {#7}
+ {
+ \exp_after:wN \@@_pop_linked:wnNNnTF #1 {#2}
+ #3 #4 {#5} {#6} {#7}
+ }
+ }
+% \end{macrocode}
+% The next auxiliary \cs{@@_pop_linked:wnNNnTF}, together with the |NNNn|
+% auxiliary, checks if the key is present in the \meta{linked prop}, then the
+% corresponding value (if present) is passed as a braced argument to the
+% \meta{code} and the \meta{true code} or \meta{false code} is run as
+% appropriate. Before that, there are also three assignments: the token lists
+% for the previous key and next key are made to point to each other, cf.\
+% \cs{@@_pop_linked:w}, and the token list for the given key is made
+% undefined.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_pop_linked:wnNNnTF
+ \@@_flatten:w #1 \s_@@ #2#3#4#5#6#7
+ {
+ \if_cs_exist:w @@ ~ #2 ~ \tl_to_str:n {#4} \cs_end:
+ \exp_after:wN \@@_pop_linked:NNNn
+ \cs:w @@ ~ #2 ~ \tl_to_str:n {#4} \cs_end:
+ #5 #6 {#7}
+ \else:
+ \exp_after:wN \use_iii:nnn
+ \fi:
+ \use_i:nn
+ }
+\cs_new_protected:Npn \@@_pop_linked:NNNn #1#2#3#4
+ {
+ \if_meaning:w \scan_stop: #1
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_iii:nnn
+ \else:
+ \exp_after:wN \@@_pop_linked:w #1 #1 #2 #3 {#4}
+ \fi:
+ }
+\cs_new_protected:Npn \@@_pop_linked:w
+ \use_none:n #1#2 \s_@@ #3#4#5#6#7#8
+ {
+ #6 #5 \tex_undefined:D
+ #7 #1
+ {
+ \exp_after:wN \@@_pop_linked_prev:w #1
+ \exp_not:N #4
+ }
+ #7 #4
+ {
+ \exp_not:n { \use_none:n #1 }
+ \exp_not:f { \exp_after:wN \@@_pop_linked_next:w #4 }
+ }
+ #8 {#3}
+ }
+\cs_new:Npn \@@_pop_linked_prev:w #1 \s_@@ #2#3
+ { \exp_not:n { #1 \s_@@ {#2} } }
+\cs_new:Npn \@@_pop_linked_next:w \use_none:n #1 { \exp_stop_f: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[tested = m3prop002]
+% {
+% \prop_remove:Nn, \prop_remove:NV, \prop_remove:Ne,
+% \prop_remove:cn, \prop_remove:cV, \prop_remove:ce
+% }
+% \begin{macro}[tested = m3prop002]
+% {
+% \prop_gremove:Nn, \prop_gremove:NV, \prop_gremove:Ne,
+% \prop_gremove:cn, \prop_gremove:cV, \prop_gremove:ce
+% }
+% Deleting from a property relies on \cs{@@_pop:NnNNnTF}. The three
+% assignment functions are suitably local or global. The last three arguments
+% are \cs{use_none:n} and two empty brace groups: if the key is found we get
+% \cs{use_none:n} \Arg{key} \meta{empty}, which expands to nothing, and
+% otherwise we just get \meta{empty}. The auxiliary takes care of actually
+% removing the entry from the prop.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_remove:Nn #1#2
+ {
+ \@@_pop:NnNNnTF #1 {#2}
+ \cs_set_eq:NN \cs_set_nopar:Npe
+ \use_none:n { } { }
+ }
+\cs_new_protected:Npn \prop_gremove:Nn #1#2
+ {
+ \@@_pop:NnNNnTF #1 {#2}
+ \cs_gset_eq:NN \cs_gset_nopar:Npe
+ \use_none:n { } { }
+ }
+\cs_generate_variant:Nn \prop_remove:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \prop_gremove:Nn { NV , Ne , c , cV , ce }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[tested = m3prop002]
+% {
+% \prop_pop:NnN, \prop_pop:NVN, \prop_pop:NoN,
+% \prop_pop:cnN, \prop_pop:cVN, \prop_pop:coN,
+% \prop_gpop:NnN, \prop_gpop:NVN, \prop_gpop:NoN,
+% \prop_gpop:cnN, \prop_gpop:cVN, \prop_gpop:coN,
+% }
+% \begin{macro}[TF, tested = m3prop004]
+% {
+% \prop_pop:NnN, \prop_pop:NVN, \prop_pop:NoN,
+% \prop_pop:cnN, \prop_pop:cVN, \prop_pop:coN,
+% \prop_gpop:NnN, \prop_gpop:NVN, \prop_gpop:NoN,
+% \prop_gpop:cnN, \prop_gpop:cVN, \prop_gpop:coN,
+% }
+% Popping a value is almost the same, but the value found is kept.
+% For the non-branching version, we additionally set the target token list to
+% \cs{q_no_value}, while for the branching version we must produce
+% \cs{prg_return_true:} or \cs{prg_return_false:}.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_pop:NnN #1#2#3
+ {
+ \@@_pop:NnNNnTF #1 {#2}
+ \cs_set_eq:NN \cs_set_nopar:Npe
+ { \tl_set:Nn #3 } { } { \tl_set:Nn #3 { \q_no_value } }
+ }
+\cs_new_protected:Npn \prop_gpop:NnN #1#2#3
+ {
+ \@@_pop:NnNNnTF #1 {#2}
+ \cs_gset_eq:NN \cs_gset_nopar:Npe
+ { \tl_set:Nn #3 } { } { \tl_set:Nn #3 { \q_no_value } }
+ }
+\cs_generate_variant:Nn \prop_pop:NnN { NV , No }
+\cs_generate_variant:Nn \prop_pop:NnN { c , cV , co }
+\cs_generate_variant:Nn \prop_gpop:NnN { NV , No }
+\cs_generate_variant:Nn \prop_gpop:NnN { c , cV , co }
+\prg_new_protected_conditional:Npnn \prop_pop:NnN #1#2#3 { T , F , TF }
+ {
+ \@@_pop:NnNNnTF #1 {#2}
+ \cs_set_eq:NN \cs_set_nopar:Npe
+ { \tl_set:Nn #3 } \prg_return_true: \prg_return_false:
+ }
+\prg_new_protected_conditional:Npnn \prop_gpop:NnN #1#2#3 { T , F , TF }
+ {
+ \@@_pop:NnNNnTF #1 {#2}
+ \cs_gset_eq:NN \cs_gset_nopar:Npe
+ { \tl_set:Nn #3 } \prg_return_true: \prg_return_false:
+ }
+\prg_generate_conditional_variant:Nnn \prop_pop:NnN
+ { NV , No , c , cV , co } { T , F , TF }
+\prg_generate_conditional_variant:Nnn \prop_gpop:NnN
+ { NV , No , c , cV , co } { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Adding data to property lists}
+%
+% \begin{macro}[tested = m3prop002]
+% {
+% \prop_put:Nnn, \prop_put:NnV, \prop_put:Nnv, \prop_put:Nne,
+% \prop_put:NVn, \prop_put:NVV, \prop_put:NVv, \prop_put:NVe,
+% \prop_put:Nvn, \prop_put:NvV, \prop_put:Nvv, \prop_put:Nve,
+% \prop_put:Nen, \prop_put:NeV, \prop_put:Nev, \prop_put:Nee,
+% \prop_put:Nno, \prop_put:Non, \prop_put:Noo,
+% \prop_put:Nnx, \prop_put:NVx, \prop_put:NxV, \prop_put:Nxx,
+% \prop_put:cnn, \prop_put:cnV, \prop_put:cnv, \prop_put:cne,
+% \prop_put:cVn, \prop_put:cVV, \prop_put:cVv, \prop_put:cVe,
+% \prop_put:cvn, \prop_put:cvV, \prop_put:cvv, \prop_put:cve,
+% \prop_put:cen, \prop_put:ceV, \prop_put:cev, \prop_put:cee,
+% \prop_put:cno, \prop_put:con, \prop_put:coo,
+% \prop_put:cnx, \prop_put:cVx, \prop_put:cxV, \prop_put:cxx
+% }
+% \begin{macro}[tested = m3prop002]
+% {
+% \prop_gput:Nnn, \prop_gput:NnV, \prop_gput:Nnv, \prop_gput:Nne,
+% \prop_gput:NVn, \prop_gput:NVV, \prop_gput:NVv, \prop_gput:NVe,
+% \prop_gput:Nvn, \prop_gput:NvV, \prop_gput:Nvv, \prop_gput:Nve,
+% \prop_gput:Nen, \prop_gput:NeV, \prop_gput:Nev, \prop_gput:Nee,
+% \prop_gput:Nno, \prop_gput:Non, \prop_gput:Noo,
+% \prop_gput:Nnx, \prop_gput:NVx, \prop_gput:NxV, \prop_gput:Nxx,
+% \prop_gput:cnn, \prop_gput:cnV, \prop_gput:cnv, \prop_gput:cne,
+% \prop_gput:cVn, \prop_gput:cVV, \prop_gput:cVv, \prop_gput:cVe,
+% \prop_gput:cvn, \prop_gput:cvV, \prop_gput:cvv, \prop_gput:cve,
+% \prop_gput:cen, \prop_gput:ceV, \prop_gput:cev, \prop_gput:cee,
+% \prop_gput:cno, \prop_gput:con, \prop_gput:coo,
+% \prop_gput:cnx, \prop_gput:cVx, \prop_gput:cxV, \prop_gput:cxx
+% }
+% \begin{macro}[tested = m3prop002]
+% {
+% \prop_put_if_not_in:Nnn, \prop_put_if_not_in:NVn, \prop_put_if_not_in:NnV,
+% \prop_put_if_not_in:cnn, \prop_put_if_not_in:cVn, \prop_put_if_not_in:cnV,
+% \prop_gput_if_not_in:Nnn, \prop_gput_if_not_in:NVn, \prop_gput_if_not_in:NnV,
+% \prop_gput_if_not_in:cnn, \prop_gput_if_not_in:cVn, \prop_gput_if_not_in:cnV
+% }
+% \begin{macro}[tested = m3prop002]
+% {
+% \@@_put:NNNnn, \@@_put_linked:wnNN,
+% \@@_put_linked:NNNN, \@@_put_linked_old:w,
+% \@@_put_linked_new:w
+% }
+% All of the \cs[no-index]{prop_(g)put(_if_new):Nnn} functions are
+% based on the same auxiliary, which receives \meta{code} and an
+% \enquote{assignment}, followed by \meta{prop} \Arg{key} \Arg{new
+% value}. The
+% assignment \cs[index=cs_set_nopar:Npe]{cs_(g)set_nopar:Npe} is the
+% primitive assignment without any checking: in the case of linked
+% props it is applied to individual
+% pieces of the linked prop, which are typically not yet defined.
+% Debugging the scope of the variable is done at a higher level by
+% letting \pkg{l3debug} change \cs{prop_put:Nnn} and friends. This
+% allows other \pkg{l3prop} commands to directly call the underlying
+% auxiliary to skip this checking step and avoid getting multiple
+% error messages for the same error.
+% The \meta{code} (empty for |put| and \cs{use_none:nnn} for
+% |put_if_not_in|) is placed before the assignment in cases where the key
+% is already present, in order to suppress the assignment in the
+% |put_if_not_in| case.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_put:Nnn
+ { \@@_put:nNNnn { } \cs_set_nopar:Npe }
+\cs_new_protected:Npn \prop_gput:Nnn
+ { \@@_put:nNNnn { } \cs_gset_nopar:Npe }
+\cs_new_protected:Npn \prop_put_if_not_in:Nnn
+ { \@@_put:nNNnn \use_none:nnn \cs_set_nopar:Npe }
+\cs_new_protected:Npn \prop_gput_if_not_in:Nnn
+ { \@@_put:nNNnn \use_none:nnn \cs_gset_nopar:Npe }
+\cs_generate_variant:Nn \prop_put:Nnn
+ {
+ NnV , Nnv , Nne , NV , NVV , NVv , NVe ,
+ Nv , NvV , Nvv , Nve , Ne , NeV , Nev , Nee
+ }
+\cs_generate_variant:Nn \prop_put:Nnn
+ { Nno , No , Noo , Nnx , NVx , NxV , Nxx }
+\cs_generate_variant:Nn \prop_put:Nnn
+ {
+ c , cnV , cnv , cne , cV , cVV , cVv , cVe ,
+ cv , cvV , cvv , cve , ce , ceV , cev , cee
+ }
+\cs_generate_variant:Nn \prop_put:Nnn
+ { cno , co , coo , cnx , cVx , cxV , cxx }
+\cs_generate_variant:Nn \prop_gput:Nnn
+ {
+ NnV , Nnv , Nne , NV , NVV , NVv , NVe ,
+ Nv , NvV , Nvv , Nve , Ne , NeV , Nev , Nee
+ }
+\cs_generate_variant:Nn \prop_gput:Nnn
+ { Nno , No , Noo , Nnx , NVx , NxV , Nxx }
+\cs_generate_variant:Nn \prop_gput:Nnn
+ {
+ c , cnV , cnv , cne , cV , cVV , cVv , cVe ,
+ cv , cvV , cvv , cve , ce , ceV , cev , cee
+ }
+\cs_generate_variant:Nn \prop_gput:Nnn
+ { cno , co , coo , cnx , cVx , cxV , cxx }
+\cs_generate_variant:Nn \prop_put_if_not_in:Nnn
+ { NnV , NV , c , cnV , cV }
+\cs_generate_variant:Nn \prop_gput_if_not_in:Nnn
+ { NnV , NV , c , cnV , cV }
+% \end{macrocode}
+% Since the true branch of \cs{@@_split:NnTFn} is used as the
+% replacement text of an internal macro, and since the \meta{key} and
+% new \meta{value} may contain arbitrary tokens, it is not safe to
+% include them in the argument of \cs{@@_split:NnTFn}. We thus start
+% by storing in \cs{l_@@_internal_tl} tokens which (after
+% \texttt{x}-expansion) encode the key--value pair. This variable can
+% safely be used in \cs{@@_split:NnTFn}. For a flat prop, if the
+% \meta{key} was absent, append the new key--value to the list;
+% otherwise concatenate the extracts |##2| and |##4| with the new
+% key--value pair \cs{l_@@_internal_tl}. The updated entry is placed
+% at the same spot as the original \meta{key} in the property list,
+% preserving the order of entries. For a linked prop, call
+% \cs{@@_put_linked:wnNN}, which constructs the control sequence in
+% which we will place the new value. If it matches \cs{scan_stop:}
+% then the key was not yet there and we add it using
+% \cs{@@_put_linked_new:w}, otherwise it was already there and we use
+% \cs{@@_put_linked_old:w}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_put:nNNnn #1#2#3#4#5
+ {
+ \tl_set:Nn \l_@@_internal_tl
+ {
+ \exp_not:N \@@_pair:wn \tl_to_str:n {#4}
+ \s_@@ { \exp_not:n {#5} }
+ }
+ \@@_split:NnTFn #3 {#4}
+ {
+ #1 #2 #3
+ {
+ \s_@@ \@@_chk:w \exp_not:n {##1}
+ \l_@@_internal_tl \exp_not:n {##3}
+ }
+ }
+ { #2 #3 { \exp_not:o {#3} \l_@@_internal_tl } }
+ { \exp_after:wN \@@_put_linked:wnnN #3 {#4} {#1} #2 }
+ }
+\cs_new_protected:Npn \@@_put_linked:wnnN
+ \@@_flatten:w #1 \s_@@ #2#3#4
+ {
+ \exp_after:wN \@@_put_linked:NNnN
+ \cs:w @@ ~ #2 ~ \tl_to_str:n {#4} \cs_end:
+ #1
+ }
+\cs_new_protected:Npn \@@_put_linked:NNnN #1#2#3#4
+ {
+ \if_meaning:w \scan_stop: #1
+ \exp_after:wN \@@_put_linked_new:w #2 #1 #2 #4
+ \else:
+ \exp_after:wN \@@_put_linked_old:w #1 { #3 #4 #1 }
+ \fi:
+ }
+% \end{macrocode}
+% To add a new entry, \cs{@@_put_linked_new:w} receives the expansion
+% of the end-pointer, namely \cs{use_none:n} \meta{last key pointer},
+% followed by the new key pointer~|#2|, the end pointer~|#3|, and an
+% assignment function~|#4|. Set up the doubly-linked list in the
+% order |#1|, |#2|, |#3|, placing the key--value pair
+% \cs{l_@@_internal_tl} in~|#2|. To replace an old entry,
+% \cs{@@_put_linked_old:w} receives the expansion of that entry, and
+% it reassigns it (|#5|) using the assignment~|#6|, by simply
+% replacing the payload |#2| \cs{s_@@} |#3| by \cs{l_@@_internal_tl}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_put_linked_new:w
+ \use_none:n #1#2#3#4
+ {
+ #4 #1
+ {
+ \exp_after:wN \@@_pop_linked_prev:w #1
+ \exp_not:N #2
+ }
+ #4 #2
+ {
+ \exp_not:n { \use_none:n #1 }
+ \l_@@_internal_tl
+ \exp_not:N #3
+ }
+ #4 #3 { \exp_not:n { \use_none:n #2 } }
+ }
+\cs_new_protected:Npn \@@_put_linked_old:w
+ \use_none:n #1#2 \s_@@ #3#4#5
+ {
+ #5
+ {
+ \exp_not:n { \use_none:n #1 }
+ \l_@@_internal_tl
+ \exp_not:N #4
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Property list conditionals}
+%
+% \begin{macro}[pTF, tested = m3prop004]{\prop_if_exist:N, \prop_if_exist:c}
+% Copies of the \texttt{cs} functions defined in \pkg{l3basics}.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \prop_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \prop_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF, tested = m3prop003]{\prop_if_empty:N, \prop_if_empty:c}
+% \begin{macro}{\@@_if_empty_return:w}
+% A flat property list is empty if it matches \cs{c_empty_prop}. A linked
+% property list is empty if its second token (the end pointer) and last token
+% (the first key pointer) are equal. There cannot be false positives because
+% the end pointer takes the form \cs{use_none:n} \meta{pointer} while the
+% other pointers have more elaborate structure. The subtle code branch here
+% is when a non-empty flat property list is given: then \cs{@@_if_empty:w}
+% reads the whole property list as~|#1| and |#2|, |#3|, |#4| are |2|, |3|,
+% |4|, respectively.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \prop_if_empty:N #1 { p , T , F , TF }
+ {
+ \if_meaning:w #1 \c_empty_prop
+ \prg_return_true:
+ \else:
+ \exp_after:wN \@@_if_empty_return:w #1
+ \@@_flatten:w 2 \s_@@ 34 \s_@@_stop
+ \fi:
+ }
+\cs_new:Npn \@@_if_empty_return:w
+ #1 \@@_flatten:w #2 \s_@@ #3#4#5 \s_@@_stop
+ {
+ \if_meaning:w #2 #4
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \prop_if_empty:N
+ { c } { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF, tested = m3prop003]
+% {
+% \prop_if_in:Nn, \prop_if_in:NV, \prop_if_in:Ne, \prop_if_in:No,
+% \prop_if_in:cn, \prop_if_in:cV, \prop_if_in:ce, \prop_if_in:co
+% }
+% \begin{macro}[EXP]{\@@_if_in_flat:nnn}
+% For a linked prop, use \cs{@@_get_linked:w} to look up whether the control
+% sequence constructed from the prefix and the sought-after key exists; this
+% auxiliary calls \cs{use_none:n} \Arg{value} \cs{prg_return_true:} if the key
+% is found, and otherwise \cs{prg_return_false:}. For a flat prop, testing
+% expandably if a key is there requires to go through the key--value pairs one
+% by one. This is rather slow, and a faster test would be
+% \begin{verbatim}
+% \prg_new_protected_conditional:Npnn \prop_if_in:Nn #1 #2
+% {
+% \@@_split:NnTFn #1 {#2}
+% { \prg_return_true: }
+% { \prg_return_false: }
+% { ... }
+% }
+% \end{verbatim}
+% but \cs{@@_split:NnTFn} is non-expandable.
+% Instead, we use \cs{prop_map_tokens:Nn} to compare the search key to
+% each key in turn using \cs{str_if_eq:ee}, which is expandable.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \prop_if_in:Nn #1#2 { p , T , F , TF }
+ {
+ \@@_if_flat:NTF #1
+ {
+ \exp_after:wN \prop_map_tokens:Nn \exp_after:wN #1
+ {
+ \exp_after:wN \@@_if_in_flat:nnn
+ \exp_after:wN { \tl_to_str:n {#2} }
+ }
+ \prg_return_false:
+ }
+ {
+ \exp_after:wN \@@_get_linked:w #1 {#2}
+ \use_none:n \prg_return_true: \prg_return_false:
+ }
+ }
+\cs_new:Npn \@@_if_in_flat:nnn #1#2#3
+ {
+ \str_if_eq:eeT {#1} {#2}
+ { \prop_map_break:n { \use_i:nn \prg_return_true: } }
+ }
+\prg_generate_conditional_variant:Nnn \prop_if_in:Nn
+ { NV , Ne , No , c , cV , ce , co } { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Mapping over property lists}
+%
+% \begin{macro}[tested = m3prop003]
+% {
+% \prop_map_function:NN, \prop_map_function:Nc,
+% \prop_map_function:cN, \prop_map_function:cc
+% }
+% \begin{macro}{\@@_map_function:Nw}
+% We first |f|-expand to flatten~|#1| in case it was a linked list.
+% The \cs{use_i:nnn} removes the leading \cs{s_@@} \cs{@@_chk:w} of
+% the flattened prop.
+% The even-numbered arguments of \cs{@@_map_function:Nw} are keys,
+% hence have string catcodes, except at the end where they are
+% \cs{fi:} \cs{prop_map_break:}. The \cs{fi:} ends the \cs{if_false:}
+% |#|\meta{even} \cs{fi:} construction and we jump out of the loop.
+% No need for any quark test.
+% \begin{macrocode}
+\cs_new:Npn \prop_map_function:NN #1#2
+ {
+ \exp_last_unbraced:Nnf
+ \use_i:nnn { \@@_map_function:Nw #2 } #1
+ \@@_pair:wn \fi: \prop_map_break: \s_@@ { }
+ \@@_pair:wn \fi: \prop_map_break: \s_@@ { }
+ \@@_pair:wn \fi: \prop_map_break: \s_@@ { }
+ \@@_pair:wn \fi: \prop_map_break: \s_@@ { }
+ \prg_break_point:Nn \prop_map_break: { }
+ }
+\cs_new:Npn \@@_map_function:Nw #1
+ \@@_pair:wn #2 \s_@@ #3
+ \@@_pair:wn #4 \s_@@ #5
+ \@@_pair:wn #6 \s_@@ #7
+ \@@_pair:wn #8 \s_@@ #9
+ {
+ \if_false: #2 \fi: #1 {#2} {#3}
+ \if_false: #4 \fi: #1 {#4} {#5}
+ \if_false: #6 \fi: #1 {#6} {#7}
+ \if_false: #8 \fi: #1 {#8} {#9}
+ \@@_map_function:Nw #1
+ }
+\cs_generate_variant:Nn \prop_map_function:NN { Nc , c , cc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[tested = m3prop003]{\prop_map_inline:Nn, \prop_map_inline:cn}
+% Mapping in line requires a nesting level counter. Store the current
+% definition of \cs{@@_pair:wn}, and define it anew. At the end of
+% the loop, revert to the earlier definition. Note that besides pairs
+% of the form \cs{@@_pair:wn} \meta{key} \cs{s_@@} \Arg{value}, there
+% are a leading and a trailing tokens, but both are equal to
+% \cs{scan_stop:}, hence have no effect in such inline mapping.
+% Such \cs{scan_stop:} could have affected ligatures if they appeared
+% during the mapping.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_map_inline:Nn #1#2
+ {
+ \cs_gset_eq:cN
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :wn } \@@_pair:wn
+ \int_gincr:N \g__kernel_prg_map_int
+ \cs_gset_protected:Npn \@@_pair:wn ##1 \s_@@ ##2 {#2}
+ \exp_last_unbraced:Nf \use_none:nn #1
+ \prg_break_point:Nn \prop_map_break:
+ {
+ \int_gdecr:N \g__kernel_prg_map_int
+ \cs_gset_eq:Nc \@@_pair:wn
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :wn }
+ }
+ }
+\cs_generate_variant:Nn \prop_map_inline:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\prop_map_tokens:Nn, \prop_map_tokens:cn}
+% \begin{macro}{\@@_map_tokens:nw}
+% The mapping is very similar to \cs{prop_map_function:NN}.
+% The odd construction
+% |\use:n {#1}| allows |#1| to contain any token without interfering
+% with \cs{prop_map_break:}. The loop stops when the \meta{key}
+% between \cs{@@_pair:wn} and \cs{s_@@} is \cs{fi:}
+% \cs{prop_map_break:} instead of being a string.
+% \begin{macrocode}
+\cs_new:Npn \prop_map_tokens:Nn #1#2
+ {
+ \exp_last_unbraced:Nnf
+ \use_i:nnn { \@@_map_tokens:nw {#2} } #1
+ \@@_pair:wn \fi: \prop_map_break: \s_@@ { }
+ \@@_pair:wn \fi: \prop_map_break: \s_@@ { }
+ \@@_pair:wn \fi: \prop_map_break: \s_@@ { }
+ \@@_pair:wn \fi: \prop_map_break: \s_@@ { }
+ \prg_break_point:Nn \prop_map_break: { }
+ }
+\cs_new:Npn \@@_map_tokens:nw #1
+ \@@_pair:wn #2 \s_@@ #3
+ \@@_pair:wn #4 \s_@@ #5
+ \@@_pair:wn #6 \s_@@ #7
+ \@@_pair:wn #8 \s_@@ #9
+ {
+ \if_false: #2 \fi: \use:n {#1} {#2} {#3}
+ \if_false: #4 \fi: \use:n {#1} {#4} {#5}
+ \if_false: #6 \fi: \use:n {#1} {#6} {#7}
+ \if_false: #8 \fi: \use:n {#1} {#8} {#9}
+ \@@_map_tokens:nw {#1}
+ }
+\cs_generate_variant:Nn \prop_map_tokens:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+%
+% \begin{macro}[tested = m3prop003]{\prop_map_break:}
+% \begin{macro}[tested = m3prop003]{\prop_map_break:n}
+% The break statements are based on the general \cs{prg_map_break:Nn}.
+% \begin{macrocode}
+\cs_new:Npn \prop_map_break:
+ { \prg_map_break:Nn \prop_map_break: { } }
+\cs_new:Npn \prop_map_break:n
+ { \prg_map_break:Nn \prop_map_break: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Uses of mapping over property lists}
+%
+% \begin{macro}[EXP]{\prop_count:N, \prop_count:c}
+% \begin{macro}[EXP]{\@@_count:nn}
+% Counting the key--value pairs in a property list is done using the
+% same approach as for other count functions: turn each entry into a
+% \texttt{+1} then use integer evaluation to actually do the
+% mathematics.
+% \begin{macrocode}
+\cs_new:Npn \prop_count:N #1
+ {
+ \int_eval:n
+ {
+ 0
+ \prop_map_function:NN #1 \@@_count:nn
+ }
+ }
+\cs_new:Npn \@@_count:nn #1#2 { + 1 }
+\cs_generate_variant:Nn \prop_count:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\prop_to_keyval:N}
+% \begin{macro}[EXP]
+% {\@@_to_keyval_exp_after:wN, \@@_to_keyval:nn, \@@_to_keyval:nnw}
+% Each property name and value pair will be returned in the form
+% \verb*| |\marg{name}\verb*|= |\marg{value}. As one of the main use cases for
+% this macro is to pass the \meta{property list} on to a key--value parser, we
+% have to make sure that the behaviour is as good as possible. Using a space
+% before the opening brace we get the correct brace stripping behaviour for
+% most of the key--value parsers available in \LaTeX{}.
+% Iterate over the
+% \meta{property list} and remove the leading comma afterwards. Only the value
+% has to be protected in \cs{__kernel_exp_not:w} as the property name is
+% always a string. After the loop the leading comma is removed by
+% \cs{use_none:n} and afterwards \cs{__kernel_exp_not:w} eventually finds the
+% opening brace of its argument.
+% \begin{macrocode}
+\cs_new:Npn \prop_to_keyval:N #1
+ {
+ \__kernel_exp_not:w
+ \prop_if_empty:NTF #1
+ { {} }
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ {
+ \tex_expanded:D
+ {
+ \exp_not:N \use_none:n
+ \prop_map_function:NN #1 \@@_to_keyval:nn
+ }
+ }
+ }
+ }
+\cs_new:Npn \@@_to_keyval:nn #1#2
+ { , ~ {#1} =~ { \__kernel_exp_not:w {#2} } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Viewing property lists}
+%
+% \begin{macro}[tested = m3show001]
+% {\prop_show:N, \prop_show:c, \prop_log:N, \prop_log:c}
+% \begin{macro}{\@@_show:NN, \@@_show_finally:NNn, \@@_show_prepare:w, \@@_show_loop:NNw, \@@_show_bad_name:NNN, \@@_show_end:NNN, \@@_show_loop_key:wNNN}
+% \begin{macro}[rEXP]{\@@_show_flat:w, \@@_show_linked:w}
+% Experience shows one source of problems is very hard to debug: when
+% a data structure such as a |seq| or~|prop| gets corrupted. In the
+% past, \cs{prop_show:N} would in some cases happily show items of
+% such a |prop|, even though other more demanding \pkg{l3prop}
+% functions would choke. It is thus best to make \cs{prop_show:N}
+% check very thoroughly the structure and flag issues, even though
+% that is very painful for linked props. Throughout the code below,
+% we strive to remain as safe as possible, but in the explanations we
+% only state what the arguments are when the prop is correctly formed,
+% rather than saying at every step that various arguments can be
+% arbitrary junk, made safe by using \cs{tl_to_str:n} generously.
+%
+% The general \cs{__kernel_chk_tl_type:NnnT} checks that its first
+% argument is a token list, and if it is, then it \texttt{e}-expands
+% its second argument and compares with the contents of its first
+% argument. Thus, within this \texttt{e}-expansion it is safe to use
+% \cs{@@_if_flat:NTF} to check if the prop is flat or linked. In the
+% flat case we simply reconstruct the expected structure using
+% \cs{@@_show_flat:w}, which loops through the prop and correctly
+% turns all keys to strings for instance. In the linked case, we use
+% \cs{@@_show_linked:w}, which ensures the form \cs{@@_flatten:w}
+% \cs[no-index]{@@~\meta{prefix}} \cs{s_@@} \Arg{prefix} \meta{rest},
+% where \meta{prefix} is made into a string and \meta{rest} cannot be
+% a brace group or multiple tokens since \cs{@@_show_linked:w} would
+% in such cases give a different result from the original token list.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_show:N { \@@_show:NN \msg_show:nneeee }
+\cs_generate_variant:Nn \prop_show:N { c }
+\cs_new_protected:Npn \prop_log:N { \@@_show:NN \msg_log:nneeee }
+\cs_generate_variant:Nn \prop_log:N { c }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \__kernel_chk_tl_type:NnnT #2 { prop }
+ {
+ \@@_if_flat:NTF #2
+ {
+ \s_@@ \@@_chk:w
+ \exp_after:wN \@@_show_flat:w #2
+ \s_@@ { }
+ \@@_pair:wn \q_@@_recursion_tail \s_@@ { }
+ \q_@@_recursion_stop
+ }
+ { \exp_after:wN \@@_show_linked:w #2 \s_@@ ! ? \s_@@_stop }
+ }
+ {
+ \@@_if_flat:NTF #2
+ { \@@_show_finally:NNn #1 #2 { flat } }
+ {
+ \tl_set:Nn \l_@@_internal_tl { #1 #2 }
+ \exp_after:wN \@@_show_prepare:w #2 #2
+ }
+ }
+ }
+\cs_new:Npn \@@_show_flat:w #1 \@@_pair:wn #2 \s_@@ #3
+ {
+ \@@_if_recursion_tail_stop:n {#2}
+ \exp_not:N \@@_pair:wn \tl_to_str:n {#2} \s_@@ \exp_not:n { {#3} }
+ \@@_show_flat:w
+ }
+\cs_new:Npn \@@_show_linked:w #1 \s_@@ #2#3#4 \s_@@_stop
+ {
+ \exp_not:N \@@_flatten:w
+ \exp_not:c { @@ ~ \tl_to_str:n {#2} }
+ \s_@@ { \tl_to_str:n {#2} }
+ \exp_not:n {#3}
+ }
+% \end{macrocode}
+% For flat props we are done by using \cs{msg_show:nneeee} or
+% \cs{msg_log:nneeee}. The auxiliary \cs{@@_show_finally:NNn} is
+% eventually also used in the linked case after some more tests. To
+% avoid having to bring along the message function and the property
+% list, we store them into \cs{l_@@_internal_tl}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_show_finally:NNn #1#2#3
+ {
+ #1 { prop } { show }
+ { \token_to_str:N #2 }
+ { \prop_map_function:NN #2 \msg_show_item:nn }
+ {#3} { }
+ }
+% \end{macrocode}
+% For linked props, we now know they have a reasonable form so that we
+% are calling \cs{@@_show_prepare:w} \cs{@@_flatten:w}
+% \cs[no-index]{@@~\meta{prefix}} \cs{s_@@} \Arg{prefix} \meta{token}
+% \meta{property list}, and the task is to loop through the linked
+% list and check integrity. We first set things up: the auxiliary
+% \cs{@@_tmp:w} will be in charge of checking that various tokens
+% start with \cs[no-index]{@@~\meta{prefix}} (in the sense of string
+% representations), and calling one of \cs{@@_show_loop_key:wNNN},
+% \cs{@@_show_end:NNN}, \cs{@@_show_bad_name:NNN}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_show_prepare:w
+ \@@_flatten:w #1 \s_@@ #2#3#4
+ {
+ \use:e
+ {
+ \cs_set_nopar:Npn \exp_not:N \@@_tmp:w
+ ##1 \token_to_str:N #1 ##2 \s_@@_mark ##3 \s_@@_stop
+ {
+ \exp_not:N \tl_if_empty:nTF {##1}
+ {
+ \exp_not:N \tl_if_head_is_space:nTF {##2}
+ { \exp_not:N \exp_args:Nf \@@_show_loop_key:wNNN }
+ { \exp_not:N \tl_if_empty:nTF }
+ {##2}
+ }
+ { \exp_not:N \use_ii:nn }
+ \@@_show_end:NNN
+ \@@_show_bad_name:NNN
+ }
+ }
+ \exp_last_unbraced:NNNo \@@_show_loop:NNw #1 #4 #4
+ }
+% \end{macrocode}
+% The loop will consist of calls to \cs{@@_show_loop:NNw}
+% \cs[no-index]{@@~\meta{prefix}} \meta{token} \meta{expansion}, where
+% \meta{token} is one of the items in the list, specifically the key
+% container for \meta{key$_{i-1}$} (starting at $i=1$ with the
+% property list variable itself), and \meta{expansion} stands for the
+% expansion of that token, which has already been checked, and takes
+% the form \meta{junk} \cs{s_@@} \Arg{value}
+% \cs[no-index]{@@~\meta{prefix}~\meta{key_i}}. Thus, the loop
+% auxiliary receives the prefix command as |#1|, and the $(i-1)$-th
+% and $i$-th key containers as |#2| and~|#5|. Then \cs{@@_tmp:w}
+% checks that the name of the $i$-th key container is valid.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_show_loop:NNw #1#2 #3 \s_@@ #4#5
+ {
+ \exp_last_two_unbraced:Noo \@@_tmp:w
+ { \token_to_str:N #5 \s_@@_mark }
+ { \token_to_str:N #1 \s_@@_mark \s_@@_stop }
+ #1 #2 #5
+ }
+% \end{macrocode}
+% If the $i$-th key container has the wrong name we get
+% \cs{@@_show_bad_name:NNN} \cs[no-index]{@@~\meta{prefix}}
+% \meta{previous container} \meta{current container with bad name}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_show_bad_name:NNN #1#2#3
+ {
+ \msg_error:nneeee { prop } { bad-link }
+ { \tl_tail:N \l_@@_internal_tl }
+ { \token_to_str:N #2 }
+ { \token_to_str:N #3 }
+ { \token_to_str:N #1 }
+ }
+% \end{macrocode}
+% If the $i$-th key container has the name
+% \cs[no-index]{@@~\meta{prefix}} (without space), it is the trailing
+% one. We check that it is the right kind of macro to be a token
+% list, and that it has the right contents \cs{use_none:n}
+% \meta{previous container}. If so, we are done checking everything,
+% and we display the property list using the message function and
+% property list name stored in \cs{l_@@_internal_tl}. Note that we
+% also use this \cs{l_@@_internal_tl} in the type argument of
+% \cs{__kernel_chk_tl_type:NnnT}, to build up the name
+% \enquote{\meta{property list} prop entry} used in error messages.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_show_end:NNN #1#2#3
+ {
+ \__kernel_chk_tl_type:NnnT #3
+ { \tl_tail:N \l_@@_internal_tl prop~entry }
+ { \exp_not:n { \use_none:n #2 } }
+ {
+ \exp_after:wN \@@_show_finally:NNn
+ \l_@@_internal_tl { linked }
+ }
+ }
+% \end{macrocode}
+% If the $i$-th container has a name \cs[no-index]{@@~\meta{prefix}
+% \meta{key}} (with a space before the key), then we have a call to
+% \cs{@@_show_loop_key:wNNN} \Arg{key} \meta{junk_1} \meta{junk_2}
+% \cs[no-index]{@@~\meta{prefix}} \meta{previous container}
+% \meta{current container}. (with an \texttt{f}-expansion to eliminate
+% the space). The first argument is the \meta{key} without a leading
+% space, thanks to a judicious \texttt{f}-expansion earlier on. We
+% check that the \meta{current container} is a token list with the
+% expected structure \cs{use_none:n} \meta{previous container}
+% \cs{@@_pair:wn} \meta{string} \cs{s_@@} \Arg{anything} \meta{single
+% token}. The auxiliary \cs{@@_show_flat:w} is reused to produce the
+% \cs{@@_pair:wn} part, and the last token is produced by
+% \cs{tl_item:Nn} (we don't waste a specialized auxiliary to speed
+% that up). If the check succeed, move on to the next item.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_show_loop_key:wNNN #1#2#3#4#5#6
+ {
+ \__kernel_chk_tl_type:NnnT #6
+ { \tl_tail:N \l_@@_internal_tl prop~entry }
+ {
+ \exp_not:n { \use_none:n #5 }
+ \exp_after:wN \@@_show_flat:w #6 \s_@@ { }
+ \@@_pair:wn \q_@@_recursion_tail \s_@@ { }
+ \q_@@_recursion_stop
+ \tl_item:Nn #6 { -1 }
+ }
+ { \exp_last_unbraced:NNNo \@@_show_loop:NNw #4 #6 #6 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3quark.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3quark.dtx
new file mode 100644
index 00000000000..6dba950462c
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3quark.dtx
@@ -0,0 +1,1008 @@
+% \iffalse meta-comment
+%
+%% File: l3quark.dtx
+%
+% Copyright (C) 1990-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3quark} module\\ Quarks and scan marks^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% Two special types of constants in \LaTeX3 are \enquote{quarks} and
+% \enquote{scan marks}. By convention all constants of type quark
+% start out with |\q_|, and scan marks start with |\s_|.
+%
+% \section{Quarks}
+%
+% Quarks are control sequences (and in fact, token lists) that expand
+% to themselves and should
+% therefore \emph{never} be executed directly in the code.
+% This would result in an endless loop!
+%
+% They are meant to be used as delimiter in weird functions, the most
+% common use case being the `stop token' (\emph{i.e.}~\cs{q_stop}).
+% For example, when writing a macro to parse a user-defined date
+% \begin{verbatim}
+% \date_parse:n {19/June/1981}
+% \end{verbatim}
+% one might write a command such as
+% \begin{verbatim}
+% \cs_new:Npn \date_parse:n #1 { \date_parse_aux:w #1 \q_stop }
+% \cs_new:Npn \date_parse_aux:w #1 / #2 / #3 \q_stop
+% { <do something with the date> }
+% \end{verbatim}
+%
+% Quarks are sometimes also used as error return values for functions
+% that receive erroneous input.
+% For example, in the function \cs{prop_get:NnN} to retrieve a value stored
+% in some key of a property list, if the key does not exist then the return value
+% is the quark \cs{q_no_value}.
+% As mentioned above, such quarks are extremely fragile and it is imperative
+% when using such functions that code is carefully written to check for
+% pathological cases to avoid leakage of a quark into an uncontrolled
+% environment.
+%
+% Quarks also permit the
+% following ingenious trick when parsing tokens:
+% when you pick up a token in a temporary variable
+% and you want to know whether you have picked up a particular quark,
+% all you have to do is compare the temporary variable to the quark using
+% \cs{tl_if_eq:NNTF}. A set of special quark testing functions is set up
+% below. All the quark testing functions are expandable although the
+% ones testing only single tokens are much faster.
+%
+% \section{Defining quarks}
+%
+% \begin{function}{\quark_new:N}
+% \begin{syntax}
+% \cs{quark_new:N} \meta{quark}
+% \end{syntax}
+% Creates a new \meta{quark} which expands only to \meta{quark}.
+% The \meta{quark} is defined globally, and an error message
+% is raised if the name was already taken.
+% \end{function}
+%
+% \begin{variable}{\q_stop}
+% Used as a marker for delimited arguments, such as
+% \begin{verbatim}
+% \cs_set:Npn \tmp:w #1#2 \q_stop {#1}
+% \end{verbatim}
+% \end{variable}
+%
+% \begin{variable}{\q_mark}
+% Used as a marker for delimited arguments when \cs{q_stop} is
+% already in use.
+% \end{variable}
+%
+% \begin{variable}{\q_nil}
+% Quark to mark a null value in structured variables or functions. Used
+% as an end delimiter when this may itself need to be tested
+% (in contrast to \cs{q_stop}, which is only ever used as a delimiter).
+% \end{variable}
+%
+% \begin{variable}{\q_no_value}
+% A canonical value for a missing value, when one is requested from
+% a data structure. This is therefore used as a \enquote{return} value
+% by functions such as \cs{prop_get:NnN} if there is no data to
+% return.
+% \end{variable}
+%
+% \section{Quark tests}
+%
+% The method used to define quarks means that the single token (\texttt{N})
+% tests are faster than the multi-token (\texttt{n}) tests. The latter
+% should therefore only be used when the argument can definitely take
+% more than a single token.
+%
+% \begin{function}[EXP,pTF]{\quark_if_nil:N}
+% \begin{syntax}
+% \cs{quark_if_nil_p:N} \meta{token}
+% \cs{quark_if_nil:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is equal to \cs{q_nil}.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\quark_if_nil:n, \quark_if_nil:o, \quark_if_nil:V}
+% \begin{syntax}
+% \cs{quark_if_nil_p:n} \Arg{token list} \\
+% \cs{quark_if_nil:nTF} \Arg{token list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token list} contains only \cs{q_nil} (distinct
+% from \meta{token list} being empty or containing \cs{q_nil} plus one
+% or more other tokens).
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\quark_if_no_value:N, \quark_if_no_value:c}
+% \begin{syntax}
+% \cs{quark_if_no_value_p:N} \meta{token} \\
+% \cs{quark_if_no_value:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is equal to \cs{q_no_value}.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\quark_if_no_value:n}
+% \begin{syntax}
+% \cs{quark_if_no_value_p:n} \Arg{token list} \\
+% \cs{quark_if_no_value:nTF} \Arg{token list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token list} contains only \cs{q_no_value}
+% (distinct from \meta{token list} being empty or containing
+% \cs{q_no_value} plus one or more other tokens).
+% \end{function}
+%
+% \section{Recursion}
+%
+% This module provides a uniform interface to intercepting and
+% terminating loops as when one is doing tail recursion. The building
+% blocks follow below and an example is shown in
+% Section~\ref{sec:l3quark:quark-example}.
+%
+% \begin{variable}{\q_recursion_tail}
+% This quark is appended to the data structure in question and
+% appears as a real element there. This means it gets any list
+% separators around it.
+% \end{variable}
+%
+% \begin{variable}{\q_recursion_stop}
+% This quark is added \emph{after} the data structure. Its purpose
+% is to make it possible to terminate the recursion at any point
+% easily.
+% \end{variable}
+%
+% \begin{function}[EXP]{\quark_if_recursion_tail_stop:N}
+% \begin{syntax}
+% \cs{quark_if_recursion_tail_stop:N} \meta{token}
+% \end{syntax}
+% Tests if \meta{token} contains only the marker
+% \cs{q_recursion_tail}, and if so uses
+% \cs{use_none_delimit_by_q_recursion_stop:w} to terminate the
+% recursion that this belongs to. The
+% recursion input must include the marker tokens \cs{q_recursion_tail}
+% and \cs{q_recursion_stop} as the last two items.
+% \end{function}
+%
+% \begin{function}[EXP,updated = 2011-09-06]
+% {\quark_if_recursion_tail_stop:n, \quark_if_recursion_tail_stop:o}
+% \begin{syntax}
+% \cs{quark_if_recursion_tail_stop:n} \Arg{token list}
+% \end{syntax}
+% Tests if the \meta{token list} contains only
+% \cs{q_recursion_tail}, and if so uses
+% \cs{use_none_delimit_by_q_recursion_stop:w} to terminate the
+% recursion that this belongs to. The
+% recursion input must include the marker tokens \cs{q_recursion_tail}
+% and \cs{q_recursion_stop} as the last two items.
+% \end{function}
+%
+% \begin{function}[EXP]{\quark_if_recursion_tail_stop_do:Nn}
+% \begin{syntax}
+% \cs{quark_if_recursion_tail_stop_do:Nn} \meta{token} \Arg{insertion}
+% \end{syntax}
+% Tests if \meta{token} contains only the marker
+% \cs{q_recursion_tail}, and if so uses
+% \cs{use_i_delimit_by_q_recursion_stop:w} to terminate the
+% recursion that this belongs to. The
+% recursion input must include the marker tokens \cs{q_recursion_tail}
+% and \cs{q_recursion_stop} as the last two items. The \meta{insertion}
+% code is then added to the input stream after the recursion has
+% ended.
+% \end{function}
+%
+% \begin{function}[EXP,updated = 2011-09-06]
+% {\quark_if_recursion_tail_stop_do:nn, \quark_if_recursion_tail_stop_do:on}
+% \begin{syntax}
+% \cs{quark_if_recursion_tail_stop_do:nn} \Arg{token list} \Arg{insertion}
+% \end{syntax}
+% Tests if the \meta{token list} contains only
+% \cs{q_recursion_tail}, and if so uses
+% \cs{use_i_delimit_by_q_recursion_stop:w} to terminate the
+% recursion that this belongs to. The
+% recursion input must include the marker tokens \cs{q_recursion_tail}
+% and \cs{q_recursion_stop} as the last two items. The \meta{insertion}
+% code is then added to the input stream after the recursion has
+% ended.
+% \end{function}
+%
+%
+% \begin{function}[EXP,added = 2018-04-10]
+% {
+% \quark_if_recursion_tail_break:NN,
+% \quark_if_recursion_tail_break:nN
+% }
+% \begin{syntax}
+% \cs{quark_if_recursion_tail_break:nN} \Arg{token list} \cs[no-index]{\meta{type}_map_break:}
+% \end{syntax}
+% Tests if \meta{token list} contains only \cs{q_recursion_tail}, and
+% if so terminates the recursion using \cs[no-index]{\meta{type}_map_break:}.
+% The recursion end should be marked by \cs{prg_break_point:Nn}
+% \cs[no-index]{\meta{type}_map_break:}.
+% \end{function}
+%
+% \subsection{An example of recursion with quarks}
+% \label{sec:l3quark:quark-example}
+%
+% Quarks are mainly used internally in the \pkg{expl3} code to define
+% recursion functions such as \cs{tl_map_inline:nn} and so on.
+% Here is a small example to demonstrate how to use quarks in this fashion.
+% We shall define a command called |\my_map_dbl:nn| which takes a token list
+% and applies an operation to every \emph{pair} of tokens.
+% For example, |\my_map_dbl:nn {abcd} {[--#1--#2--]~}| would produce
+% \enquote{\ttfamily [--a--b--]~[--c--d--]~}.
+% Using quarks to define such functions simplifies their logic and ensures
+% robustness in many cases.
+%
+%
+% Here's the definition of |\my_map_dbl:nn|.
+% First of all, define the function that does the processing based on the
+% inline function argument |#2|.
+% Then initiate the recursion using an internal function.
+% The token list |#1| is terminated using \cs{q_recursion_tail}, with
+% delimiters according to the type of recursion (here a pair of
+% \cs{q_recursion_tail}), concluding with \cs{q_recursion_stop}.
+% These quarks are used to mark the end of the token list being operated upon.
+%\begin{verbatim}
+% \cs_new:Npn \my_map_dbl:nn #1#2
+% {
+% \cs_set:Npn \__my_map_dbl_fn:nn ##1 ##2 {#2}
+% \__my_map_dbl:nn #1 \q_recursion_tail \q_recursion_tail
+% \q_recursion_stop
+% }
+%\end{verbatim}
+%
+% The definition of the internal recursion function follows.
+% First check if either of the input tokens are the termination quarks.
+% Then, if not, apply the inline function to the two arguments.
+%\begin{verbatim}
+% \cs_new:Nn \__my_map_dbl:nn
+% {
+% \quark_if_recursion_tail_stop:n {#1}
+% \quark_if_recursion_tail_stop:n {#2}
+% \__my_map_dbl_fn:nn {#1} {#2}
+%\end{verbatim}
+% Finally, recurse:
+%\begin{verbatim}
+% \__my_map_dbl:nn
+% }
+%\end{verbatim}
+% Note that contrarily to \LaTeX3 built-in mapping functions, this
+% mapping function cannot be nested, since the second map would overwrite
+% the definition of |\__my_map_dbl_fn:nn|.
+%
+% \section{Scan marks}
+%
+% Scan marks are control sequences set equal to \cs{scan_stop:},
+% hence never expand in an expansion context and are (largely)
+% invisible if they are encountered in a typesetting context.
+%
+% Like quarks, they can be used as delimiters in weird functions
+% and are often safer to use for this purpose.
+% Since they are harmless when executed by \TeX{} in non-expandable
+% contexts, they can be used to mark the end of a set of instructions.
+% This allows to skip to that point if the end of the instructions
+% should not be performed (see \pkg{l3regex}).
+%
+% \begin{function}[added = 2018-04-01]{\scan_new:N}
+% \begin{syntax}
+% \cs{scan_new:N} \meta{scan mark}
+% \end{syntax}
+% Creates a new \meta{scan mark} which is set equal to \cs{scan_stop:}.
+% The \meta{scan mark} is defined globally, and an error message
+% is raised if the name was already taken by another scan mark.
+% \end{function}
+%
+% \begin{variable}[added = 2018-04-01]{\s_stop}
+% Used at the end of a set of instructions, as a marker
+% that can be jumped to using \cs{use_none_delimit_by_s_stop:w}.
+% \end{variable}
+%
+% \begin{function}[EXP,added = 2018-04-01]{\use_none_delimit_by_s_stop:w}
+% \begin{syntax}
+% \cs{use_none_delimit_by_s_stop:w} \meta{tokens} \cs{s_stop}
+% \end{syntax}
+% Removes the \meta{tokens} and \cs{s_stop} from the input stream.
+% This leads to a low-level \TeX{} error if \cs{s_stop} is absent.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3quark} implementation}
+%
+% \TestFiles{m3quark001.lvt}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \subsection{Quarks}
+%
+% \begin{macrocode}
+%<@@=quark>
+% \end{macrocode}
+%
+% \begin{macro}{\quark_new:N}
+% \UnitTested
+% Allocate a new quark.
+% \begin{macrocode}
+\cs_new_protected:Npn \quark_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_nopar:Npn #1 {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\q_nil, \q_mark, \q_no_value, \q_stop}
+% Some \enquote{public} quarks. \cs{q_stop} is an \enquote{end of
+% argument} marker, \cs{q_nil} is a empty value and \cs{q_no_value}
+% marks an empty argument.
+% \begin{macrocode}
+\quark_new:N \q_nil
+\quark_new:N \q_mark
+\quark_new:N \q_no_value
+\quark_new:N \q_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\q_recursion_tail, \q_recursion_stop}
+% Quarks for ending recursions. Only ever used there!
+% \cs{q_recursion_tail} is appended to whatever list structure we are
+% doing recursion on, meaning it is added as a proper list item with
+% whatever list separator is in use. \cs{q_recursion_stop} is placed
+% directly after the list.
+% \begin{macrocode}
+\quark_new:N \q_recursion_tail
+\quark_new:N \q_recursion_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\s_@@}
+% Private scan mark used in \pkg{l3quark}. We don't have \pkg{l3scan}
+% yet, so we declare the scan mark here and add it to the scan mark pool
+% later.
+% \begin{macrocode}
+\cs_new_eq:NN \s_@@ \scan_stop:
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\q_@@_nil}
+% Private quark use for some tests.
+% \begin{macrocode}
+\quark_new:N \q_@@_nil
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\quark_if_recursion_tail_stop:N}
+% \UnitTested
+% \begin{macro}[EXP]{\quark_if_recursion_tail_stop_do:Nn}
+% \UnitTested
+% When doing recursions, it is easy to spend a lot of time testing if the
+% end marker has been found. To avoid this, a dedicated end marker is used
+% each time a recursion is set up. Thus if the marker is found everything
+% can be wrapper up and finished off. The simple case is when the test
+% can guarantee that only a single token is being tested. In this case,
+% there is just a dedicated copy of the standard quark test. Both a gobbling
+% version and one inserting end code are provided.
+% \begin{macrocode}
+\cs_new:Npn \quark_if_recursion_tail_stop:N #1
+ {
+ \if_meaning:w \q_recursion_tail #1
+ \exp_after:wN \use_none_delimit_by_q_recursion_stop:w
+ \fi:
+ }
+\cs_new:Npn \quark_if_recursion_tail_stop_do:Nn #1
+ {
+ \if_meaning:w \q_recursion_tail #1
+ \exp_after:wN \use_i_delimit_by_q_recursion_stop:nw
+ \else:
+ \exp_after:wN \use_none:n
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\quark_if_recursion_tail_stop:n, \quark_if_recursion_tail_stop:o}
+% \UnitTested
+% \begin{macro}[EXP]
+% {\quark_if_recursion_tail_stop_do:nn, \quark_if_recursion_tail_stop_do:on}
+% \UnitTested
+% \begin{macro}[EXP]{\@@_if_recursion_tail:w}
+% See \cs{quark_if_nil:nTF} for the details. Expanding
+% \cs{@@_if_recursion_tail:w} once in front of the tokens chosen here
+% gives an empty result if and only if |#1|~is exactly
+% \cs{q_recursion_tail}.
+% \begin{macrocode}
+\cs_new:Npn \quark_if_recursion_tail_stop:n #1
+ {
+ \tl_if_empty:oTF
+ { \@@_if_recursion_tail:w {} #1 {} ?! \q_recursion_tail ??! }
+ { \use_none_delimit_by_q_recursion_stop:w }
+ { }
+ }
+\cs_new:Npn \quark_if_recursion_tail_stop_do:nn #1
+ {
+ \tl_if_empty:oTF
+ { \@@_if_recursion_tail:w {} #1 {} ?! \q_recursion_tail ??! }
+ { \use_i_delimit_by_q_recursion_stop:nw }
+ { \use_none:n }
+ }
+\cs_new:Npn \@@_if_recursion_tail:w
+ #1 \q_recursion_tail #2 ? #3 ?! { #1 #2 }
+\cs_generate_variant:Nn \quark_if_recursion_tail_stop:n { o }
+\cs_generate_variant:Nn \quark_if_recursion_tail_stop_do:nn { o }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\quark_if_recursion_tail_break:NN}
+% \begin{macro}[EXP]{\quark_if_recursion_tail_break:nN}
+% Analogues of the \cs[index=quark_if_recursion_tail_stop:n]
+% {quark_if_recursion_tail_stop\ldots{}} functions.
+% Break the mapping using |#2|.
+% \begin{macrocode}
+\cs_new:Npn \quark_if_recursion_tail_break:NN #1#2
+ {
+ \if_meaning:w \q_recursion_tail #1
+ \exp_after:wN #2
+ \fi:
+ }
+\cs_new:Npn \quark_if_recursion_tail_break:nN #1#2
+ {
+ \tl_if_empty:oT
+ { \@@_if_recursion_tail:w {} #1 {} ?! \q_recursion_tail ??! }
+ {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\quark_if_nil:N}
+% \UnitTested
+% \begin{macro}[pTF]{\quark_if_no_value:N, \quark_if_no_value:c}
+% \UnitTested
+% Here we test if we found a special quark as the first argument.
+% We better start with \cs{q_no_value} as the first argument since
+% the whole thing may otherwise loop if |#1| is wrongly given
+% a string like |aabc| instead of a single token.\footnote{It may
+% still loop in special circumstances however!}
+% \begin{macrocode}
+\prg_new_conditional:Npnn \quark_if_nil:N #1 { p, T , F , TF }
+ {
+ \if_meaning:w \q_nil #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_new_conditional:Npnn \quark_if_no_value:N #1 { p, T , F , TF }
+ {
+ \if_meaning:w \q_no_value #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \quark_if_no_value:N
+ { c } { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\quark_if_nil:n, \quark_if_nil:V, \quark_if_nil:o}
+% \UnitTested
+% \begin{macro}[pTF]{\quark_if_no_value:n}
+% \UnitTested
+% \begin{macro}{\@@_if_nil:w, \@@_if_no_value:w}
+% \begin{macro}[EXP]{\@@_if_empty_if:o}
+% Let us explain \cs{quark_if_nil:nTF}. Expanding \cs{@@_if_nil:w}
+% once is safe thanks to the trailing \cs{q_nil} |??!|. The result of
+% expanding once is empty if and only if both delimited arguments |#1|
+% and~|#2| are empty and |#3|~is delimited by the last tokens~|?!|.
+% Thanks to the leading~|{}|, the argument~|#1| is empty if and only
+% if the argument of \cs{quark_if_nil:n} starts with \cs{q_nil}. The
+% argument~|#2| is empty if and only if this \cs{q_nil} is followed
+% immediately by~|?| or by~|{}?|, coming either from the trailing
+% tokens in the definition of \cs{quark_if_nil:n}, or from its
+% argument. In the first case, \cs{@@_if_nil:w} is followed by
+% |{}\q_nil| |{}?| |!\q_nil|~|??!|, hence |#3|~is delimited by the
+% final~|?!|, and the test returns \texttt{true} as wanted. In the
+% second case, the result is not empty since the first~|?!| in the
+% definition of \cs{quark_if_nil:n} stop~|#3|. The auxiliary here
+% is the same as \cs{__tl_if_empty_if:o}, with the same comments
+% applying.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \quark_if_nil:n #1 { p, T , F , TF }
+ {
+ \@@_if_empty_if:o
+ { \@@_if_nil:w {} #1 {} ? ! \q_nil ? ? ! }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \@@_if_nil:w #1 \q_nil #2 ? #3 ? ! { #1 #2 }
+\prg_new_conditional:Npnn \quark_if_no_value:n #1 { p, T , F , TF }
+ {
+ \@@_if_empty_if:o
+ { \@@_if_no_value:w {} #1 {} ? ! \q_no_value ? ? ! }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \@@_if_no_value:w #1 \q_no_value #2 ? #3 ? ! { #1 #2 }
+\prg_generate_conditional_variant:Nnn \quark_if_nil:n
+ { V , o } { p , TF , T , F }
+\cs_new:Npn \@@_if_empty_if:o #1
+ {
+ \exp_after:wN \if_meaning:w \exp_after:wN \q_nil
+ \__kernel_tl_to_str:w \exp_after:wN {#1} \q_nil
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\__kernel_quark_new_test:N}
+% The function \cs{__kernel_quark_new_test:N} defines |#1| in a
+% similar way as \cs[no-index]{quark_if_recursion_tail_...} functions
+% (as described below), using
+% \cs[no-index]{q__\meta{namespace}_recursion_tail} as the test quark
+% and \cs[no-index]{q__\meta{namespace}_recursion_stop} as the
+% delimiter quark, where the \meta{namespace} is determined as the
+% first |_|-delimited part in~|#1|.
+%
+% There are six possible function types which this function can define,
+% and which is defined depends on the signature of the function being
+% defined:
+% \begin{description}
+% \def\makelabel#1{\texttt{:#1}~}
+% \item[n] gives an analogue of \cs{quark_if_recursion_tail_stop:n}
+% \item[nn] gives an analogue of \cs{quark_if_recursion_tail_stop_do:nn}
+% \item[nN] gives an analogue of \cs{quark_if_recursion_tail_break:nN}
+% \item[N] gives an analogue of \cs{quark_if_recursion_tail_stop:N}
+% \item[Nn] gives an analogue of \cs{quark_if_recursion_tail_stop_do:Nn}
+% \item[NN] gives an analogue of \cs{quark_if_recursion_tail_break:NN}
+% \end{description}
+% Any other signature causes an error, as does a function without signature.
+%
+% \begin{macro}{\__kernel_quark_new_conditional:Nn}
+% Similar to \cs{__kernel_quark_new_test:N}, but defines quark
+% branching conditionals like \cs{quark_if_nil:nTF} that test for the
+% quark \cs[no-index]{q__\meta{namespace}_\meta{name}}.
+% The \meta{namespace} and \meta{name} are determined from the
+% conditional~|#1|, which must take the rather rigid form
+% \cs[no-index]{__\meta{namespace}_quark_if_\meta{name}:\meta{arg spec}}.
+% There are only two cases for the \meta{arg spec} here:
+% \begin{description}
+% \def\makelabel#1{\texttt{:#1}~}
+% \item[n] gives an analogue of \cs{quark_if_nil:nTF}
+% \item[N] gives an analogue of \cs{quark_if_nil:NTF}
+% \end{description}
+% Any other signature causes an error, as does a function without signature.
+% We use low-level emptiness tests as \pkg{l3tl} is not available yet when these
+% functions are used; thankfully we only care about whether strings
+% are empty so a simple \cs{if_meaning:w} \cs{q_nil} \meta{string}
+% \cs{q_nil} suffices.
+%
+% \begin{macro}{\@@_new_test:NNNn, \@@_new_test:Nccn,
+% \@@_new_test_aux:nnNNnnnn}
+% \begin{macro}{\@@_new_conditional:Nnnn, \@@_new_conditional:Neen}
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_quark_new_test:N #1
+ { \@@_new_test_aux:Ne #1 { \@@_module_name:N #1 } }
+\cs_new_protected:Npn \@@_new_test_aux:Nn #1 #2
+ {
+ \if_meaning:w \q_nil #2 \q_nil
+ \msg_error:nne { quark } { invalid-function }
+ { \token_to_str:N #1 }
+ \else:
+ \@@_new_test:Nccn #1
+ { q__#2_recursion_tail } { q__#2_recursion_stop } { __#2 }
+ \fi:
+ }
+\cs_generate_variant:Nn \@@_new_test_aux:Nn { Ne }
+\cs_new_protected:Npn \@@_new_test:NNNn #1
+ {
+ \exp_last_unbraced:Nf \@@_new_test_aux:nnNNnnnn
+ { \cs_split_function:N #1 }
+ #1 { test }
+ }
+\cs_generate_variant:Nn \@@_new_test:NNNn { Ncc }
+\cs_new_protected:Npn \__kernel_quark_new_conditional:Nn #1
+ {
+ \@@_new_conditional:Neen #1
+ { \@@_quark_conditional_name:N #1 }
+ { \@@_module_name:N #1 }
+ }
+\cs_new_protected:Npn \@@_new_conditional:Nnnn #1#2#3#4
+ {
+ \if_meaning:w \q_nil #2 \q_nil
+ \msg_error:nne { quark } { invalid-function }
+ { \token_to_str:N #1 }
+ \else:
+ \if_meaning:w \q_nil #3 \q_nil
+ \msg_error:nne { quark } { invalid-function }
+ { \token_to_str:N #1 }
+ \else:
+ \exp_last_unbraced:Nf \@@_new_test_aux:nnNNnnnn
+ { \cs_split_function:N #1 }
+ #1 { conditional }
+ {#2} {#3} {#4}
+ \fi:
+ \fi:
+ }
+\cs_generate_variant:Nn \@@_new_conditional:Nnnn { Nee }
+\cs_new_protected:Npn \@@_new_test_aux:nnNNnnnn #1 #2 #3 #4 #5
+ {
+ \cs_if_exist_use:cTF { @@_new_#5_#2:Nnnn } { #4 }
+ {
+ \msg_error:nnee { quark } { invalid-function }
+ { \token_to_str:N #4 } {#2}
+ \use_none:nnn
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{
+% \@@_new_test_n:Nnnn, \@@_new_test_nn:Nnnn,
+% \@@_new_test_N:Nnnn, \@@_new_test_Nn:Nnnn,
+% \@@_new_test_NN:Nnnn, \@@_new_test_NN:Nnnn,
+% }
+% These macros implement the six possibilities mentioned above, passing
+% the right arguments to \cs{@@_new_test_aux_do:nNNnnnnNNn},
+% which defines some auxiliaries, and then to
+% \cs{@@_new_test_define_tl:nNnNNn} (|:n(n)| variants) or to
+% \cs{@@_new_test_define_ifx:nNnNNn} (|:N(n)|) which define the
+% main conditionals.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_new_test_n:Nnnn #1 #2 #3 #4
+ {
+ \@@_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { none } { } { } { }
+ \@@_new_test_define_tl:nNnNNn #1 { }
+ }
+\cs_new_protected:Npn \@@_new_test_nn:Nnnn #1 #2 #3 #4
+ {
+ \@@_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { i } { n } {##1} {##2}
+ \@@_new_test_define_tl:nNnNNn #1 { \use_none:n }
+ }
+\cs_new_protected:Npn \@@_new_test_nN:Nnnn #1 #2 #3 #4
+ {
+ \@@_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { i } { n } {##1} {##2}
+ \@@_new_test_define_break_tl:nNNNNn #1 { }
+ }
+\cs_new_protected:Npn \@@_new_test_N:Nnnn #1 #2 #3 #4
+ {
+ \@@_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { none } { } { } { }
+ \@@_new_test_define_ifx:nNnNNn #1 { }
+ }
+\cs_new_protected:Npn \@@_new_test_Nn:Nnnn #1 #2 #3 #4
+ {
+ \@@_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { i } { n } {##1} {##2}
+ \@@_new_test_define_ifx:nNnNNn #1
+ { \else: \exp_after:wN \use_none:n }
+ }
+\cs_new_protected:Npn \@@_new_test_NN:Nnnn #1 #2 #3 #4
+ {
+ \@@_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { i } { n } {##1} {##2}
+ \@@_new_test_define_break_ifx:nNNNNn #1 { }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+% \@@_new_test_aux_do:nNNnnnnNNn,
+% \@@_test_define_aux:NNNNnnNNn
+% }
+% \cs{@@_new_test_aux_do:nNNnnnnNNn} makes the control sequence names
+% which will be used by \cs{@@_test_define_aux:NNNNnnNNn}, and then later
+% by \cs{@@_new_test_define_tl:nNnNNn} or
+% \cs{@@_new_test_define_ifx:nNnNNn}. The control sequences defined
+% here are analogous to \cs{@@_if_recursion_tail:w} and to
+% \cs[no-index]{use_(none|i)_delimit_by_q_recursion_stop:(|n)w}.
+%
+% The name is composed by the name-space and the name of the quarks.
+% Suppose \cs{__kernel_quark_new_test:N} was used with:
+% \begin{verbatim}
+% \__kernel_quark_new_test:N \__test_quark_tail:n
+% \end{verbatim}
+% then the first auxiliary will be \cs[no-index]{__test_quark_recursion_tail:w},
+% and the second one will be
+% \cs[no-index]{__test_use_none_delimit_by_q_recursion_stop:w}.
+%
+% Note that the actual quarks are \emph{not} defined here. They should
+% be defined separately using \cs{quark_new:N}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_new_test_aux_do:nNNnnnnNNn #1 #2 #3 #4 #5
+ {
+ \exp_args:Ncc \@@_test_define_aux:NNNNnnNNn
+ { #1 _quark_recursion_tail:w }
+ { #1 _use_ #4 _delimit_by_q_recursion_stop: #5 w }
+ #2 #3
+ }
+\cs_new_protected:Npn \@@_test_define_aux:NNNNnnNNn #1 #2 #3 #4 #5 #6 #7
+ {
+ \cs_gset:Npn #1 ##1 #3 ##2 ? ##3 ?! { ##1 ##2 }
+ \cs_gset:Npn #2 ##1 #6 #4 {#5}
+ #7 {##1} #1 #2 #3
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+% \@@_new_test_define_tl:nNnNNn,
+% \@@_new_test_define_ifx:nNnNNn
+% }
+% \begin{macro}{
+% \@@_new_test_define_break_tl:nNNNNn,
+% \@@_new_test_define_break_ifx:nNNNNn
+% }
+% Finally, these two macros define the main conditional function using
+% what's been set up before.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_new_test_define_tl:nNnNNn #1 #2 #3 #4 #5 #6
+ {
+ \cs_new:Npn #5 #1
+ {
+ \tl_if_empty:oTF
+ { #2 {} ##1 {} ?! #4 ??! }
+ {#3} {#6}
+ }
+ }
+\cs_new_protected:Npn \@@_new_test_define_ifx:nNnNNn #1 #2 #3 #4 #5 #6
+ {
+ \cs_new:Npn #5 #1
+ {
+ \if_meaning:w #4 ##1
+ \exp_after:wN #3
+ #6
+ \fi:
+ }
+ }
+\cs_new_protected:Npn \@@_new_test_define_break_tl:nNNNNn #1 #2 #3
+ { \@@_new_test_define_tl:nNnNNn {##1##2} #2 {##2} }
+\cs_new_protected:Npn \@@_new_test_define_break_ifx:nNNNNn #1 #2 #3
+ { \@@_new_test_define_ifx:nNnNNn {##1##2} #2 {##2} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{
+% \@@_new_conditional_n:Nnnn,
+% \@@_new_conditional_N:Nnnn,
+% \@@_new_conditional_n_aux:NNNn,
+% \@@_new_conditional_N_aux:NNNn
+% }
+% These macros implement the two possibilities for branching quark
+% conditionals. To avoid constructing without defining the
+% \cs[no-index]{__\meta{type}_if_quark_\meta{name}:w} helper,
+% \texttt{N}-type function accepts a \cs{prg_do_nothing:} as
+% a placeholder.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_new_conditional_n:Nnnn #1 #2 #3
+ {
+ \exp_args:Ncc \@@_new_conditional_n_aux:NNNn
+ { __ #3 _if_quark_ #2 :w } { q__ #3 _ #2 } #1
+ }
+\cs_new_protected:Npn \@@_new_conditional_N:Nnnn #1 #2 #3
+ {
+ \exp_args:NNc \@@_new_conditional_N_aux:NNNn
+ \prg_do_nothing: { q__ #3 _ #2 } #1
+ }
+\cs_new_protected:Npn \@@_new_conditional_n_aux:NNNn #1 #2 #3 #4
+ {
+ \cs_gset:Npn #1 ##1 #2 ##2 ? ##3 ?! { ##1##2 }
+ \prg_new_conditional:Npnn #3 ##1 {#4}
+ {
+ \@@_if_empty_if:o { #1 {} ##1 {} ?! #2 ??! }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ }
+\cs_new_protected:Npn \@@_new_conditional_N_aux:NNNn #1 #2 #3 #4
+ {
+ \prg_new_conditional:Npnn #3 ##1 {#4}
+ {
+ \if_meaning:w #2 ##1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_module_name:N}
+% \begin{macro}[EXP]{
+% \@@_module_name:w,
+% \@@_module_name_loop:w,
+% \@@_module_name_end:w
+% }
+% \cs{@@_module_name:N} takes a control sequence and returns its
+% \meta{module} name, determined as the first non-empty
+% non-single-character word, separated by |_| or~|:|. These rules
+% give the correct result for public functions
+% \cs[no-index]{\meta{module}_\ldots{}}, private functions
+% \cs[no-index]{__\meta{module}_\ldots{}}, and variables such as
+% \cs[no-index]{l_\meta{module}_\ldots{}}. If no valid module is
+% found the result is an empty string. The approach is to first cut
+% off everything after the (first) |:| if any is present, then
+% repeatedly grab |_|-delimited words until finding one of length at
+% least~$2$ (we use low-level tests as \pkg{l3tl} is not fully
+% available when \cs{__kernel_quark_new_test:N} is first used.
+% If no \meta{module} is found (such as in \cs{::n}) we
+% get the trailing marker \cs{use_none:n} |{}|, which expands to
+% nothing.
+% \begin{macrocode}
+\cs_set:Npn \@@_tmp:w #1#2
+ {
+ \cs_new:Npn \@@_module_name:N ##1
+ {
+ \exp_last_unbraced:Nf \@@_module_name:w
+ { \cs_to_str:N ##1 } #1 \s_@@
+ }
+ \cs_new:Npn \@@_module_name:w ##1 #1 ##2 \s_@@
+ { \@@_module_name_loop:w ##1 #2 \use_none:n { } #2 \s_@@ }
+ \cs_new:Npn \@@_module_name_loop:w ##1 #2
+ {
+ \use_i_ii:nnn \if_meaning:w \prg_do_nothing:
+ ##1 \prg_do_nothing: \prg_do_nothing:
+ \exp_after:wN \@@_module_name_loop:w
+ \else:
+ \@@_module_name_end:w ##1
+ \fi:
+ }
+ \cs_new:Npn \@@_module_name_end:w
+ ##1 \fi: ##2 \s_@@ { \fi: ##1 }
+ }
+\exp_after:wN \@@_tmp:w \tl_to_str:n { : _ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_quark_conditional_name:N, \@@_quark_conditional_name:w}
+% \cs{@@_quark_conditional_name:N} determines the quark name that the quark
+% conditional function~|##1| queries, as the part of the function name
+% between |_quark_if_| and the trailing~|:|. Again we define it
+% through \cs{@@_tmp:w}, which receives |:| as |#1| and |_quark_if_|
+% as |#2|. The auxiliary \cs{@@_quark_conditional_name:w} returns the part
+% between the first |_quark_if_| and the next~|:|, and we apply this
+% auxiliary to the function name followed by |:| (in case the function
+% name is lacking a signature), and |_quark_if_:| so that
+% \cs{@@_quark_conditional_name:N} returns an empty string if |_quark_if_| is
+% not present.
+% \begin{macrocode}
+\cs_set:Npn \@@_tmp:w #1 #2 \s_@@
+ {
+ \cs_new:Npn \@@_quark_conditional_name:N ##1
+ {
+ \exp_last_unbraced:Nf \@@_quark_conditional_name:w
+ { \cs_to_str:N ##1 } #1 #2 #1 \s_@@
+ }
+ \cs_new:Npn \@@_quark_conditional_name:w
+ ##1 #2 ##2 #1 ##3 \s_@@ {##2}
+ }
+\exp_after:wN \@@_tmp:w \tl_to_str:n { : _quark_if_ } \s_@@
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Scan marks}
+%
+% \begin{macrocode}
+%<@@=scan>
+% \end{macrocode}
+%
+% \begin{macro}{\scan_new:N}
+% \UnitTested
+% Check whether the variable is already a scan mark,
+% then declare it to be equal to \cs{scan_stop:} globally.
+% \begin{macrocode}
+\cs_new_protected:Npn \scan_new:N #1
+ {
+ \tl_if_in:NnTF \g_@@_marks_tl { #1 }
+ {
+ \msg_error:nne { scanmark } { already-defined }
+ { \token_to_str:N #1 }
+ }
+ {
+ \tl_gput_right:Nn \g_@@_marks_tl {#1}
+ \cs_new_eq:NN #1 \scan_stop:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\s_stop}
+% \begin{variable}{\g_@@_marks_tl}
+% \UnitTested
+% We only declare one scan mark here, more can be defined
+% by specific modules.
+% Can't use \cs{scan_new:N} yet because \pkg{l3tl} isn't loaded,
+% so define \cs{s_stop} by hand and add it to \cs{g_@@_marks_tl}.
+% We also add the scan marks declared earlier to the pool here.
+% Since they lives in a different namespace, a little \pkg{DocStrip}
+% cheating is necessary.
+% \begin{macrocode}
+\cs_new_eq:NN \s_stop \scan_stop:
+\cs_gset_nopar:Npn \g_@@_marks_tl
+ {
+ \s_stop
+%<@@=quark>
+ \s_@@
+%<@@=cs>
+ \s_@@_mark
+ \s_@@_stop
+%<@@=scan>
+ }
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\use_none_delimit_by_s_stop:w}
+% \UnitTested
+% Similar to \cs{use_none_delimit_by_q_stop:w}.
+% \begin{macrocode}
+\cs_new:Npn \use_none_delimit_by_s_stop:w #1 \s_stop { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3regex.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3regex.dtx
new file mode 100644
index 00000000000..ce85b562627
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3regex.dtx
@@ -0,0 +1,8205 @@
+% \iffalse meta-comment
+%
+%% File: l3regex.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3regex} module\\ Regular expressions in \TeX{}^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+% \newenvironment{l3regex-syntax}
+% {\begin{itemize}\def\\{\char`\\}\def\makelabel##1{\hss\llap{\ttfamily##1}}}
+% {\end{itemize}}
+%
+% The \pkg{l3regex} module provides regular expression testing,
+% extraction of submatches, splitting, and replacement, all acting
+% on token lists. The syntax of regular expressions is mostly a subset
+% of the \textsc{pcre} syntax (and very close to \textsc{posix}),
+% with some additions
+% due to the fact that \TeX{} manipulates tokens rather than characters.
+% For performance reasons, only a limited set of features are implemented.
+% Notably, back-references are not supported.
+%
+% Let us give a few examples. After
+% \begin{verbatim}
+% \tl_set:Nn \l_my_tl { That~cat. }
+% \regex_replace_once:nnN { at } { is } \l_my_tl
+% \end{verbatim}
+% the token list variable \cs[no-index]{l_my_tl} holds the text
+% \enquote{\texttt{This cat.}}, where the first
+% occurrence of \enquote{\texttt{at}} was replaced
+% by \enquote{\texttt{is}}. A more complicated example is
+% a pattern to emphasize each word and add a comma after it:
+% \begin{verbatim}
+% \regex_replace_all:nnN { \w+ } { \c{emph}\cB\{ \0 \cE\} , } \l_my_tl
+% \end{verbatim}
+% The |\w| sequence represents any \enquote{word} character, and |+|
+% indicates that the |\w| sequence should be repeated as many times as
+% possible (at least once), hence matching a word in the input token
+% list. In the replacement text, |\0| denotes the full match (here, a
+% word). The command |\emph| is inserted using |\c{emph}|, and its
+% argument |\0| is put between braces |\cB\{| and |\cE\}|.
+%
+% If a regular expression is to be used several times,
+% it can be compiled once, and stored in a regex
+% variable using \cs{regex_set:Nn}. For example,
+% \begin{verbatim}
+% \regex_new:N \l_foo_regex
+% \regex_set:Nn \l_foo_regex { \c{begin} \cB. (\c[^BE].*) \cE. }
+% \end{verbatim}
+% stores in \cs[no-index]{l_foo_regex} a regular expression which matches the
+% starting marker for an environment: \cs[no-index]{begin}, followed by a
+% begin-group token (|\cB.|), then any number of tokens which are
+% neither begin-group nor end-group character tokens (|\c[^BE].*|),
+% ending with an end-group token (|\cE.|). As explained in the next
+% section, the parentheses \enquote{capture} the result of |\c[^BE].*|,
+% giving us access to the name of the environment when doing
+% replacements.
+%
+% \section{Syntax of regular expressions}
+%
+% \subsection{Regular expression examples}
+%
+% We start with a few examples, and encourage the reader to apply
+% \cs{regex_show:n} to these regular expressions.
+% \begin{itemize}
+% \item |Cat| matches the word \enquote{Cat} capitalized in this way,
+% but also matches the beginning of the word \enquote{Cattle}: use
+% |\bCat\b| to match a complete word only.
+% \item |[abc]| matches one letter among \enquote{a}, \enquote{b},
+% \enquote{c}; the pattern \verb"(a|b|c)" matches the same three
+% possible letters (but see the discussion of submatches below).
+% \item |[A-Za-z]*| matches any number (due to the quantifier
+% \verb"*") of Latin letters (not accented).
+% \item |\c{[A-Za-z]*}| matches a control sequence made of Latin
+% letters.
+% \item |\_[^\_]*\_| matches an underscore, any number of characters
+% other than underscore, and another underscore; it is equivalent to
+% |\_.*?\_| where |.| matches arbitrary characters and the
+% lazy quantifier |*?| means to match as few characters as
+% possible, thus avoiding matching underscores.
+% \item |[\+\-]?\d+| matches an explicit integer with at most one
+% sign.
+% \item \verb*"[\+\-\ ]*\d+\ *" matches an explicit integer with any
+% number of $+$ and $-$ signs, with spaces allowed except within the
+% mantissa, and surrounded by spaces.
+% \item \verb*"[\+\-\ ]*(\d+|\d*\.\d+)\ *" matches an explicit integer or
+% decimal number; using \verb*"[.,]" instead of \verb*"\." would allow
+% the comma as a decimal marker.
+% \item
+% \verb*"[\+\-\ ]*(\d+|\d*\.\d+)\ *((?i)pt|in|[cem]m|ex|[bs]p|[dn]d|[pcn]c)\ *"
+% \allowbreak matches an explicit dimension with any unit that \TeX{} knows, where
+% \verb*"(?i)" means to treat lowercase and uppercase letters
+% identically.
+% \item \verb*"[\+\-\ ]*((?i)nan|inf|(\d+|\d*\.\d+)(\ *e[\+\-\ ]*\d+)?)\ *"
+% matches an explicit floating point number or the special values
+% \verb*"nan" and \verb*"inf" (with signs and spaces allowed).
+% \item \verb*"[\+\-\ ]*(\d+|\cC.)\ *" matches an explicit integer or
+% control sequence (without checking whether it is an integer
+% variable).
+% \item |\G.*?\K| at the beginning of a regular expression matches and
+% discards (due to |\K|) everything between the end of the previous
+% match (|\G|) and what is matched by the rest of the regular
+% expression; this is useful in \cs{regex_replace_all:nnN} when the
+% goal is to extract matches or submatches in a finer way than with
+% \cs{regex_extract_all:nnN}.
+% \end{itemize}
+% While it is impossible for a regular expression to match only integer
+% expressions, \verb*"[\+\-\(]*\d+\)*([\+\-*/][\+\-\(]*\d+\)*)*" matches among
+% other things all valid integer expressions (made only with explicit
+% integers). One should follow it with further testing.
+%
+% \subsection{Characters in regular expressions}
+%
+% Most characters match exactly themselves,
+% with an arbitrary category code. Some characters are
+% special and must be escaped with a backslash (\emph{e.g.}, |\*|
+% matches a star character). Some escape sequences of
+% the form backslash--letter also have a special meaning
+% (for instance |\d| matches any digit). As a rule,
+% \begin{itemize}
+% \item every alphanumeric character (\texttt{A}--\texttt{Z},
+% \texttt{a}--\texttt{z}, \texttt{0}--\texttt{9}) matches
+% exactly itself, and should not be escaped, because
+% |\A|, |\B|, \ldots{} have special meanings;
+% \item non-alphanumeric printable ascii characters can (and should)
+% always be escaped: many of them have special meanings (\emph{e.g.},
+% use |\(|, |\)|, |\?|, |\.|, |\^|);
+% \item spaces should always be escaped (even in character
+% classes);
+% \item any other character may be escaped or not, without any
+% effect: both versions match exactly that character.
+% \end{itemize}
+% Note that these rules play nicely with the fact that many
+% non-alphanumeric characters are difficult to input into \TeX{}
+% under normal category codes. For instance, |\\abc\%|
+% matches the characters |\abc%| (with arbitrary category codes),
+% but does not match the control sequence |\abc| followed by a
+% percent character. Matching control sequences can be done
+% using the |\c|\Arg{regex} syntax (see below).
+%
+% Any special character which appears at a place where its special
+% behaviour cannot apply matches itself instead (for instance, a
+% quantifier appearing at the beginning of a string), after raising a
+% warning.
+%
+% Characters.
+% \begin{l3regex-syntax}
+% \item[\\x\{hh\ldots{}\}] Character with hex code \texttt{hh\ldots{}}
+% \item[\\xhh] Character with hex code \texttt{hh}.
+% \item[\\a] Alarm (hex 07).
+% \item[\\e] Escape (hex 1B).
+% \item[\\f] Form-feed (hex 0C).
+% \item[\\n] New line (hex 0A).
+% \item[\\r] Carriage return (hex 0D).
+% \item[\\t] Horizontal tab (hex 09).
+% \end{l3regex-syntax}
+%
+% \subsection{Characters classes}
+%
+% Character properties.
+% \begin{l3regex-syntax}
+% \item[.] A single period matches any token.
+% \item[\\d] Any decimal digit.
+% \item[\\h] Any horizontal space character,
+% equivalent to |[\ \^^I]|: space and tab.
+% \item[\\s] Any space character,
+% equivalent to |[\ \^^I\^^J\^^L\^^M]|.
+% \item[\\v] Any vertical space character,
+% equivalent to |[\^^J\^^K\^^L\^^M]|. Note that |\^^K| is a vertical space,
+% but not a space, for compatibility with Perl.
+% \item[\\w] Any word character, \emph{i.e.},
+% alphanumerics and underscore, equivalent to the explicit
+% class |[A-Za-z0-9\_]|.
+% \item[\\D] Any token not matched by |\d|.
+% \item[\\H] Any token not matched by |\h|.
+% \item[\\N] Any token other than the |\n| character (hex 0A).
+% \item[\\S] Any token not matched by |\s|.
+% \item[\\V] Any token not matched by |\v|.
+% \item[\\W] Any token not matched by |\w|.
+% \end{l3regex-syntax}
+% Of those, |.|, |\D|, |\H|, |\N|, |\S|, |\V|, and |\W| match arbitrary
+% control sequences.
+%
+% Character classes match exactly one token in the subject.
+% \begin{l3regex-syntax}
+% \item[{[\ldots{}]}] Positive character class.
+% Matches any of the specified tokens.
+% \item[{[\char`\^\ldots{}]}] Negative character class.
+% Matches any token other than the specified characters.
+% \item[{[x-y]}] Within a character class, this denotes a range (can be
+% used with escaped characters).
+% \item[{[:\meta{name}:]}] Within a character class (one more set of
+% brackets), this denotes the \textsc{posix} character class
+% \meta{name}, which can be \texttt{alnum}, \texttt{alpha},
+% \texttt{ascii}, \texttt{blank}, \texttt{cntrl}, \texttt{digit},
+% \texttt{graph}, \texttt{lower}, \texttt{print}, \texttt{punct},
+% \texttt{space}, \texttt{upper}, \texttt{word}, or \texttt{xdigit}.
+% \item[{[:\char`\^\meta{name}:]}] Negative \textsc{posix} character class.
+% \end{l3regex-syntax}
+% For instance, |[a-oq-z\cC.]| matches any lowercase latin letter
+% except |p|, as well as control sequences (see below for a description
+% of |\c|).
+%
+% In character classes, only |[|, |^|, |-|, |]|, |\| and spaces are
+% special, and should be escaped. Other non-alphanumeric characters can
+% still be escaped without harm. Any escape sequence which matches a
+% single character (|\d|, |\D|, \emph{etc.}) is supported in character
+% classes. If the first character is |^|, then
+% the meaning of the character class is inverted; |^| appearing anywhere
+% else in the range is not special. If the first character (possibly
+% following a leading |^|) is |]| then it does not need to be escaped
+% since ending the range there would make it empty.
+% Ranges of characters
+% can be expressed using |-|, for instance, |[\D 0-5]| and |[^6-9]| are
+% equivalent.
+%
+% \subsection{Structure: alternatives, groups, repetitions}
+%
+% Quantifiers (repetition).
+% \begin{l3regex-syntax}
+% \item[?] $0$ or $1$, greedy.
+% \item[??] $0$ or $1$, lazy.
+% \item[*] $0$ or more, greedy.
+% \item[*?] $0$ or more, lazy.
+% \item[+] $1$ or more, greedy.
+% \item[+?] $1$ or more, lazy.
+% \item[\{$n$\}] Exactly $n$.
+% \item[\{$n,$\}] $n$ or more, greedy.
+% \item[\{$n,$\}?] $n$ or more, lazy.
+% \item[\{$n,m$\}] At least $n$, no more than $m$, greedy.
+% \item[\{$n,m$\}?] At least $n$, no more than $m$, lazy.
+% \end{l3regex-syntax}
+% For greedy quantifiers the regex code will first investigate matches
+% that involve as many repetitions as possible, while for lazy
+% quantifiers it investigates matches with as few repetitions as
+% possible first.
+%
+% Alternation and capturing groups.
+% \begin{l3regex-syntax}
+% \item[A\char`|B\char`|C] Either one of \texttt{A}, \texttt{B},
+% or \texttt{C}, investigating \texttt{A} first.
+% \item[(\ldots{})] Capturing group.
+% \item[(?:\ldots{})] Non-capturing group.
+% \item[(?\char`|\ldots{})] Non-capturing group which resets
+% the group number for capturing groups in each alternative.
+% The following group is numbered with the first unused
+% group number.
+% \end{l3regex-syntax}
+%
+% Capturing groups are a means of extracting information about the
+% match. Parenthesized groups are labelled in the order of their
+% opening parenthesis, starting at $1$. The contents of those groups
+% corresponding to the \enquote{best} match (leftmost longest)
+% can be extracted and stored in a sequence of token lists using for
+% instance \cs{regex_extract_once:nnNTF}.
+%
+% The |\K| escape sequence resets the beginning of the match to the
+% current position in the token list. This only affects what is reported
+% as the full match. For instance,
+% \begin{verbatim}
+% \regex_extract_all:nnN { a \K . } { a123aaxyz } \l_foo_seq
+% \end{verbatim}
+% results in \cs[no-index]{l_foo_seq} containing the items |{1}| and |{a}|: the
+% true matches are |{a1}| and |{aa}|, but they are trimmed by the use of
+% |\K|. The |\K| command does not affect capturing groups: for instance,
+% \begin{verbatim}
+% \regex_extract_once:nnN { (. \K c)+ \d } { acbc3 } \l_foo_seq
+% \end{verbatim}
+% results in \cs[no-index]{l_foo_seq} containing the items |{c3}| and |{bc}|: the
+% true match is |{acbc3}|, with first submatch |{bc}|, but |\K| resets
+% the beginning of the match to the last position where it appears.
+%
+% \subsection{Matching exact tokens}
+%
+% The |\c| escape sequence allows to test the category code of tokens,
+% and match control sequences. Each character category is represented
+% by a single uppercase letter:
+% \begin{itemize}
+% \item |C| for control sequences;
+% \item |B| for begin-group tokens;
+% \item |E| for end-group tokens;
+% \item |M| for math shift;
+% \item |T| for alignment tab tokens;
+% \item |P| for macro parameter tokens;
+% \item |U| for superscript tokens (up);
+% \item |D| for subscript tokens (down);
+% \item |S| for spaces;
+% \item |L| for letters;
+% \item |O| for others; and
+% \item |A| for active characters.
+% \end{itemize}
+% The |\c| escape sequence is used as follows.
+% \begin{l3regex-syntax}
+% \item[\\c\Arg{regex}] A control sequence whose csname matches the
+% \meta{regex}, anchored at the beginning and end, so that |\c{begin}|
+% matches exactly \cs[no-index]{begin}, and nothing else.
+% \item[\\cX] Applies to the next object, which can be a character,
+% escape character sequence such as |\x{0A}|, character class, or
+% group, and forces this object to only match tokens with category
+% |X| (any of |CBEMTPUDSLOA|. For instance, |\cL[A-Z\d]| matches
+% uppercase letters and digits of category code letter, |\cC.|
+% matches any control sequence, and |\cO(abc)| matches |abc| where
+% each character has category other.\footnote{This last example also
+% captures \enquote{\texttt{abc}} as a regex group; to avoid this
+% use a non-capturing group \texttt{\textbackslash cO(?:abc)}.}
+% \item[{\\c[XYZ]}] Applies to the next object, and forces it to only
+% match tokens with category |X|, |Y|, or |Z| (each being any of
+% |CBEMTPUDSLOA|). For instance, |\c[LSO](..)| matches two tokens of
+% category letter, space, or other.
+% \item[{\\c[\char`\^XYZ]}] Applies to the next object and prevents it
+% from matching any token with category |X|, |Y|, or |Z| (each being
+% any of |CBEMTPUDSLOA|). For instance, |\c[^O]\d| matches digits
+% which have any category different from other.
+% \end{l3regex-syntax}
+% The category code tests can be used inside classes; for instance,
+% |[\cO\d \c[LO][A-F]]| matches what \TeX{} considers as hexadecimal
+% digits, namely digits with category other, or uppercase letters from
+% |A| to |F| with category either letter or other. Within a group
+% affected by a category code test, the outer test can be overridden by
+% a nested test: for instance, |\cL(ab\cO\*cd)| matches |ab*cd| where
+% all characters are of category letter, except |*| which has category
+% other.
+%
+% The |\u| escape sequence allows to insert the contents of a token list
+% directly into a regular expression or a replacement, avoiding the need
+% to escape special characters. Namely, |\u|\Arg{var~name} matches
+% the exact contents (both character codes and category codes) of the
+% variable \cs[no-index]{\meta{var~name}},
+% which are obtained by applying \cs{exp_not:v} \Arg{var~name} at the
+% time the regular expression is compiled. Within a |\c{...}|
+% control sequence matching, the |\u| escape sequence only expands its
+% argument once, in effect performing \cs{tl_to_str:v}.
+% Quantifiers are supported.
+%
+% The |\ur| escape sequence allows to insert the contents of a |regex|
+% variable into a larger regular expression. For instance,
+% |A\ur{l_tmpa_regex}D| matches the tokens |A| and |D| separated by
+% something that matches the regular expression
+% \cs[no-index]{l_tmpa_regex}. This behaves as if a non-capturing group
+% were surrounding \cs[no-index]{l_tmpa_regex}, and any group contained
+% in \cs[no-index]{l_tmpa_regex} is converted to a non-capturing group.
+% Quantifiers are supported.
+%
+% For instance, if \cs[no-index]{l_tmpa_regex} has value \verb"B|C",
+% then |A\ur{l_tmpa_regex}D| is equivalent to \verb"A(?:B|C)D" (matching
+% |ABD| or |ACD|) and not to \verb"AB|CD" (matching |AB| or |CD|). To
+% get the latter effect, it is simplest to use \TeX{}'s expansion
+% machinery directly: if \cs[no-index]{l_mymodule_BC_tl} contains
+% \verb"B|C" then the following two lines show the same result:
+% \begin{quote}
+% \cs{regex_show:n} |{ A \u{l_mymodule_BC_tl} D }| \\
+% \cs{regex_show:n} \verb"{ A B | C D }"
+% \end{quote}
+%
+% \subsection{Miscellaneous}
+%
+% Anchors and simple assertions.
+% \begin{l3regex-syntax}
+% \item[\\b] Word boundary: either the previous token is matched by
+% |\w| and the next by |\W|, or the opposite. For this purpose,
+% the ends of the token list are considered as |\W|.
+% \item[\\B] Not a word boundary: between two |\w| tokens
+% or two |\W| tokens (including the boundary).
+% \item[\char`^ \textrm{or} \\A]
+% Start of the subject token list.
+% \item[\char`$\textrm{,} \\Z \textrm{or} \\z] ^^A $
+% End of the subject token list.
+% \item[\\G] Start of the current match. This is only different from |^|
+% in the case of multiple matches: for instance
+% |\regex_count:nnN { \G a } { aaba } \l_tmpa_int| yields $2$, but
+% replacing |\G| by |^| would result in \cs{l_tmpa_int} holding the
+% value $1$.
+% \end{l3regex-syntax}
+%
+% The option |(?i)| makes the match case insensitive (treating
+% \texttt{A}--\texttt{Z} and \texttt{a}--\texttt{z} as equivalent, with
+% no support yet for Unicode case changing). This
+% applies until the end of the group in which it appears, and
+% can be reverted using |(?-i)|. For instance, in
+% \verb"(?i)(a(?-i)b|c)d", the letters |a| and |d| are affected by the
+% |i| option. Characters within ranges and classes are affected
+% individually: |(?i)[\?-B]| is equivalent to |[\?@ABab]|
+% (and differs from the much larger class |[\?-b]|), and
+% |(?i)[^aeiou]| matches any character which is not a vowel.
+% The |i|~option has no effect on |\c{...}|, on |\u{...}|, on character
+% properties, or on character classes, for instance it has no effect at
+% all in |(?i)\u{l_foo_tl}\d\d[[:lower:]]|.
+%
+% \section{Syntax of the replacement text}
+%
+% Most of the features described in regular expressions do not make
+% sense within the replacement text. Backslash introduces various
+% special constructions, described further below:
+% \begin{itemize}
+% \item |\0| is the whole match;
+% \item |\1| is the submatch that was matched by the first (capturing)
+% group |(...)|; similarly for |\2|, \ldots{}, |\9| and
+% |\g{|\meta{number}|}|;
+% \item \verb*|\ | inserts a space (spaces are ignored when not
+% escaped);
+% \item |\a|, |\e|, |\f|, |\n|, |\r|, |\t|, |\xhh|, |\x{hhh}|
+% correspond to single characters as in regular expressions;
+% \item |\c|\Arg{cs~name} inserts a control sequence;
+% \item |\c|\meta{category}\meta{character} (see below);
+% \item |\u|\Arg{tl~var~name} inserts the contents of the
+% \meta{tl~var} (see below).
+% \end{itemize}
+% Characters other than backslash and space are simply inserted in the
+% result (but since the replacement text is first converted to a string,
+% one should also escape characters that are special for \TeX{}, for
+% instance use~|\#|). Non-alphanumeric characters can always be safely
+% escaped with a backslash.
+%
+% For instance,
+% \begin{verbatim}
+% \tl_set:Nn \l_my_tl { Hello,~world! }
+% \regex_replace_all:nnN { ([er]?l|o) . } { (\0--\1) } \l_my_tl
+% \end{verbatim}
+% results in \cs[no-index]{l_my_tl} holding |H(ell--el)(o,--o) w(or--o)(ld--l)!|
+%
+% The submatches are numbered according to the order in which the
+% opening parenthesis of capturing groups appear in the regular
+% expression to match. The $n$-th submatch is empty if there are fewer
+% than $n$ capturing groups or for capturing groups that appear in
+% alternatives that were not used for the match. In case a capturing
+% group matches several times during a match (due to quantifiers) only
+% the last match is used in the replacement text. Submatches always keep
+% the same category codes as in the original token list.
+%
+% By default, the category code of characters inserted by the
+% replacement are determined by the prevailing category code regime at
+% the time where the replacement is made, with two exceptions:
+% \begin{itemize}
+% \item space characters (with character code $32$) inserted with
+% \verb*|\ | or |\x20| or |\x{20}| have category code~$10$ regardless
+% of the prevailing category code regime;
+% \item if the category code would be $0$~(escape), $5$~(newline),
+% $9$~(ignore), $14$~(comment) or $15$~(invalid), it is replaced by
+% $12$~(other) instead.
+% \end{itemize}
+% The escape sequence |\c| allows to insert characters
+% with arbitrary category codes, as well as control sequences.
+% \begin{l3regex-syntax}
+% \item[\\cX(\ldots{})] Produces the characters \enquote{\ldots{}} with
+% category~|X|, which must be one of |CBEMTPUDSLOA| as in regular
+% expressions. Parentheses are optional for a single character (which
+% can be an escape sequence). When nested, the innermost category
+% code applies, for instance |\cL(Hello\cS\ world)!| gives this text
+% with standard category codes.
+% \item[\\c\Arg{text}] Produces the control sequence with csname
+% \meta{text}. The \meta{text} may contain references to the
+% submatches |\0|, |\1|, and so on, as in the example for |\u| below.
+% \end{l3regex-syntax}
+%
+% The escape sequence |\u|\Arg{var~name} allows to insert the
+% contents of the variable with name \meta{var~name} directly into
+% the replacement, giving an easier control of category codes. When
+% nested in |\c{|\ldots{}|}| and |\u{|\ldots{}|}| constructions, the
+% |\u| and |\c|~escape sequences perform \cs{tl_to_str:v}, namely
+% extract the value of the control sequence and turn it into a string.
+% Matches can also be used within the arguments of |\c| and |\u|. For
+% instance,
+% \begin{verbatim}
+% \tl_set:Nn \l_my_one_tl { first }
+% \tl_set:Nn \l_my_two_tl { \emph{second} }
+% \tl_set:Nn \l_my_tl { one , two , one , one }
+% \regex_replace_all:nnN { [^,]+ } { \u{l_my_\0_tl} } \l_my_tl
+% \end{verbatim}
+% results in \cs[no-index]{l_my_tl} holding |first,\emph{second},first,first|.
+%
+% Regex replacement is also a convenient way to produce token lists
+% with arbitrary category codes. For instance
+% \begin{verbatim}
+% \tl_clear:N \l_tmpa_tl
+% \regex_replace_all:nnN { } { \cU\% \cA\~ } \l_tmpa_tl
+% \end{verbatim}
+% results in \cs[no-index]{l_tmpa_tl} containing the percent character
+% with category code~$7$ (superscript) and an active tilde character.
+%
+% \section{Pre-compiling regular expressions}
+%
+% If a regular expression is to be used several times,
+% it is better to compile it once rather than doing it
+% each time the regular expression is used. The compiled
+% regular expression is stored in a variable. All
+% of the \pkg{l3regex} module's functions can be given their
+% regular expression argument either as an explicit string
+% or as a compiled regular expression.
+%
+% \begin{function}[added = 2017-05-26]{\regex_new:N}
+% \begin{syntax}
+% \cs{regex_new:N} \meta{regex~var}
+% \end{syntax}
+% Creates a new \meta{regex~var} or raises an error if the
+% name is already taken. The declaration is global. The
+% \meta{regex~var} is initially such that it never matches.
+% \end{function}
+%
+% \begin{function}[added = 2017-05-26]{\regex_set:Nn, \regex_gset:Nn}
+% \begin{syntax}
+% \cs{regex_set:Nn} \meta{regex~var} \Arg{regex}
+% \end{syntax}
+% Stores a compiled version of the \meta{regular expression} in the
+% \meta{regex~var}. The assignment is local for \cs{regex_set:Nn} and
+% global for \cs{regex_gset:Nn}. For instance, this function can be
+% used as
+% \begin{verbatim}
+% \regex_new:N \l_my_regex
+% \regex_set:Nn \l_my_regex { my\ (simple\ )? reg(ex|ular\ expression) }
+% \end{verbatim}
+% \end{function}
+%
+% \begin{function}[added = 2017-05-26]{\regex_const:Nn}
+% \begin{syntax}
+% \cs{regex_const:Nn} \meta{regex~var} \Arg{regex}
+% \end{syntax}
+% Creates a new constant \meta{regex~var} or raises an error if the name
+% is already taken. The value of the \meta{regex~var} is set
+% globally to the compiled version of the \meta{regular expression}.
+% \end{function}
+%
+% \begin{function}[added = 2021-04-26, updated = 2021-04-29]
+% {\regex_show:N, \regex_show:n, \regex_log:N, \regex_log:n}
+% \begin{syntax}
+% \cs{regex_show:n} \Arg{regex}
+% \cs{regex_log:n} \Arg{regex}
+% \end{syntax}
+% Displays in the terminal or writes in the log file (respectively)
+% how \pkg{l3regex} interprets the \meta{regex}. For instance,
+% \cs{regex_show:n} \verb+{\A X|Y}+ shows
+% \begin{verbatim}
+% +-branch
+% anchor at start (\A)
+% char code 88 (X)
+% +-branch
+% char code 89 (Y)
+% \end{verbatim}
+% indicating that the anchor |\A| only applies to the first branch:
+% the second branch is not anchored to the beginning of the match.
+% \end{function}
+%
+% \section{Matching}
+%
+% All regular expression functions are available in both |:n| and |:N|
+% variants. The former require a \enquote{standard} regular expression,
+% while the later require a compiled expression as generated by
+% \cs{regex_set:Nn}.
+%
+% \begin{function}[TF, added = 2017-05-26]
+% {
+% \regex_match:nn, \regex_match:nV,
+% \regex_match:Nn, \regex_match:NV
+% }
+% \begin{syntax}
+% \cs{regex_match:nnTF} \Arg{regex} \Arg{token list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{regular expression} matches any part
+% of the \meta{token list}. For instance,
+% \begin{verbatim}
+% \regex_match:nnTF { b [cde]* } { abecdcx } { TRUE } { FALSE }
+% \regex_match:nnTF { [b-dq-w] } { example } { TRUE } { FALSE }
+% \end{verbatim}
+% leaves \texttt{TRUE} then \texttt{FALSE} in the input stream.
+% \end{function}
+%
+% \begin{function}[added = 2017-05-26]
+% {
+% \regex_count:nnN, \regex_count:nVN,
+% \regex_count:NnN, \regex_count:NVN
+% }
+% \begin{syntax}
+% \cs{regex_count:nnN} \Arg{regex} \Arg{token list} \meta{int var}
+% \end{syntax}
+% Sets \meta{int var} within the current \TeX{} group level
+% equal to the number of times
+% \meta{regular expression} appears in \meta{token list}.
+% The search starts by finding the left-most longest match,
+% respecting greedy and lazy (non-greedy) operators. Then the search
+% starts again from the character following the last character
+% of the previous match, until reaching the end of the token list.
+% Infinite loops are prevented in the case where the regular expression
+% can match an empty token list: then we count one match between each
+% pair of characters.
+% For instance,
+% \begin{verbatim}
+% \int_new:N \l_foo_int
+% \regex_count:nnN { (b+|c) } { abbababcbb } \l_foo_int
+% \end{verbatim}
+% results in \cs[no-index]{l_foo_int} taking the value $5$.
+% \end{function}
+%
+% \begin{function}[noTF, added = 2022-01-10]{\regex_match_case:nn}
+% \begin{syntax}
+% \cs{regex_match_case:nnTF}
+% ~~|{| \\
+% ~~~~\Arg{regex_1} \Arg{code case_1} \\
+% ~~~~\Arg{regex_2} \Arg{code case_2} \\
+% ~~~~\ldots \\
+% ~~~~\Arg{regex_n} \Arg{code case_n} \\
+% ~~|}| \Arg{token list}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Determines which of the \meta{regular expressions} matches at the
+% earliest point in the \meta{token list}, and leaves the
+% corresponding \meta{code_i} followed by the \meta{true code} in the
+% input stream. If several \meta{regex} match starting at the same
+% point, then the first one in the list is selected and the others are
+% discarded. If none of the \meta{regex} match, the \meta{false code}
+% is left in the input stream. Each \meta{regex} can either be given
+% as a regex variable or as an explicit regular expression.
+%
+% In detail, for each starting position in the \meta{token list}, each
+% of the \meta{regex} is searched in turn. If one of them matches
+% then the corresponding \meta{code} is used and everything else is
+% discarded, while if none of the \meta{regex} match at a given
+% position then the next starting position is attempted. If none of
+% the \meta{regex} match anywhere in the \meta{token list} then
+% nothing is left in the input stream. Note that this differs from
+% nested \cs{regex_match:nnTF} statements since all \meta{regex} are
+% attempted at each position rather than attempting to match
+% \meta{regex_1} at every position before moving on to \meta{regex_2}.
+% \end{function}
+%
+% \section{Submatch extraction}
+%
+% \begin{function}[noTF, added = 2017-05-26]
+% {
+% \regex_extract_once:nnN, \regex_extract_once:nVN,
+% \regex_extract_once:NnN, \regex_extract_once:NVN
+% }
+% \begin{syntax}
+% \cs{regex_extract_once:nnN} \Arg{regex} \Arg{token list} \meta{seq~var}
+% \cs{regex_extract_once:nnNTF} \Arg{regex} \Arg{token list} \meta{seq~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Finds the first match of the \meta{regular expression} in the
+% \meta{token list}. If it exists, the match is stored as the first
+% item of the \meta{seq~var}, and further items are the contents of
+% capturing groups, in the order of their opening parenthesis. The
+% \meta{seq~var} is assigned locally. If there is no match, the
+% \meta{seq~var} is cleared. The testing versions insert the
+% \meta{true code} into the input stream if a match was found, and the
+% \meta{false code} otherwise.
+%
+% For instance, assume that you type
+% \begin{verbatim}
+% \regex_extract_once:nnNTF { \A(La)?TeX(!*)\Z } { LaTeX!!! } \l_foo_seq
+% { true } { false }
+% \end{verbatim}
+% Then the regular expression (anchored at the start with |\A| and
+% at the end with |\Z|) must match the whole token list. The first
+% capturing group, |(La)?|, matches |La|, and the second capturing
+% group, |(!*)|, matches |!!!|. Thus, \cs[no-index]{l_foo_seq} contains as a result
+% the items |{LaTeX!!!}|, |{La}|, and |{!!!}|, and the \texttt{true}
+% branch is left in the input stream.
+% Note that the $n$-th item of \cs[no-index]{l_foo_seq}, as obtained using
+% \cs{seq_item:Nn}, correspond to the submatch numbered $(n-1)$ in
+% functions such as \cs{regex_replace_once:nnN}.
+% \end{function}
+%
+% \begin{function}[noTF, added = 2017-05-26]
+% {
+% \regex_extract_all:nnN, \regex_extract_all:nVN,
+% \regex_extract_all:NnN, \regex_extract_all:NVN
+% }
+% \begin{syntax}
+% \cs{regex_extract_all:nnN} \Arg{regex} \Arg{token list} \meta{seq~var}
+% \cs{regex_extract_all:nnNTF} \Arg{regex} \Arg{token list} \meta{seq~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Finds all matches of the \meta{regular expression}
+% in the \meta{token list}, and stores all the submatch information
+% in a single sequence (concatenating the results of
+% multiple \cs{regex_extract_once:nnN} calls).
+% The \meta{seq~var} is assigned locally. If there is no match,
+% the \meta{seq~var} is cleared.
+% The testing versions insert the \meta{true code} into the input
+% stream if a match was found, and the \meta{false code} otherwise.
+% For instance, assume that you type
+% \begin{verbatim}
+% \regex_extract_all:nnNTF { \w+ } { Hello,~world! } \l_foo_seq
+% { true } { false }
+% \end{verbatim}
+% Then the regular expression matches twice, the resulting
+% sequence contains the two items |{Hello}| and |{world}|,
+% and the \texttt{true} branch is left in the input stream.
+% \end{function}
+%
+% \begin{function}[noTF, added = 2017-05-26]
+% {
+% \regex_split:nnN, \regex_split:nVN,
+% \regex_split:NnN, \regex_split:NVN,
+% }
+% \begin{syntax}
+% \cs{regex_split:nnN} \Arg{regular expression} \Arg{token list} \meta{seq~var}
+% \cs{regex_split:nnNTF} \Arg{regular expression} \Arg{token list} \meta{seq~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Splits the \meta{token list} into a sequence of parts, delimited by
+% matches of the \meta{regular expression}. If the \meta{regular expression}
+% has capturing groups, then the token lists that they match are stored as
+% items of the sequence as well. The assignment to \meta{seq~var} is local.
+% If no match is found the resulting \meta{seq~var} has the
+% \meta{token list} as its sole item. If the \meta{regular expression}
+% matches the empty token list, then the \meta{token list} is split
+% into single tokens.
+% The testing versions insert the \meta{true code} into the input
+% stream if a match was found, and the \meta{false code} otherwise.
+% For example, after
+% \begin{verbatim}
+% \seq_new:N \l_path_seq
+% \regex_split:nnNTF { / } { the/path/for/this/file.tex } \l_path_seq
+% { true } { false }
+% \end{verbatim}
+% the sequence |\l_path_seq| contains the items |{the}|, |{path}|,
+% |{for}|, |{this}|, and |{file.tex}|, and the \texttt{true} branch
+% is left in the input stream.
+% \end{function}
+%
+% \section{Replacement}
+%
+% \begin{function}[noTF, added = 2017-05-26]
+% {
+% \regex_replace_once:nnN,\regex_replace_once:nVN,
+% \regex_replace_once:NnN,\regex_replace_once:NVN
+% }
+% \begin{syntax}
+% \cs{regex_replace_once:nnN} \Arg{regular expression} \Arg{replacement} \meta{tl~var}
+% \cs{regex_replace_once:nnNTF} \Arg{regular expression} \Arg{replacement} \meta{tl~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Searches for the \meta{regular expression} in the contents of the
+% \meta{tl~var} and replaces the first match with the
+% \meta{replacement}. In the \meta{replacement},
+% |\0| represents the full match, |\1| represent the contents of the
+% first capturing group, |\2| of the second, \emph{etc.}
+% The result is assigned locally to \meta{tl~var}.
+% \end{function}
+%
+% \begin{function}[noTF, added = 2017-05-26]
+% {
+% \regex_replace_all:nnN,\regex_replace_all:nVN,
+% \regex_replace_all:NnN,\regex_replace_all:NVN
+% }
+% \begin{syntax}
+% \cs{regex_replace_all:nnN} \Arg{regular expression} \Arg{replacement} \meta{tl~var}
+% \cs{regex_replace_all:nnNTF} \Arg{regular expression} \Arg{replacement} \meta{tl~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Replaces all occurrences of the \meta{regular expression} in the
+% contents of the \meta{tl~var}
+% by the \meta{replacement}, where |\0| represents
+% the full match, |\1| represent the contents of the first capturing
+% group, |\2| of the second, \emph{etc.} Every match is treated
+% independently, and matches cannot overlap. The result is assigned
+% locally to \meta{tl~var}.
+% \end{function}
+%
+% \begin{function}[noTF, added = 2022-01-10]{\regex_replace_case_once:nN}
+% \begin{syntax}
+% \cs{regex_replace_case_once:nNTF}
+% ~~|{| \\
+% ~~~~\Arg{regex_1} \Arg{replacement_1} \\
+% ~~~~\Arg{regex_2} \Arg{replacement_2} \\
+% ~~~~\ldots \\
+% ~~~~\Arg{regex_n} \Arg{replacement_n} \\
+% ~~|}| \meta{tl~var}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Replaces the earliest match of the regular expression
+% "(?|"\meta{regex_1}"|"\dots"|"\meta{regex_n}")" in the \meta{token
+% list variable} by the \meta{replacement} corresponding to which
+% \meta{regex_i} matched, then leaves the \meta{true code} in the
+% input stream. If none of the \meta{regex} match, then the
+% \meta{tl~var} is not modified, and the \meta{false code} is left in
+% the input stream. Each \meta{regex} can either be given as a regex
+% variable or as an explicit regular expression.
+%
+% In detail, for each starting position in the \meta{token list}, each
+% of the \meta{regex} is searched in turn. If one of them matches
+% then it is replaced by the corresponding \meta{replacement} as
+% described for \cs{regex_replace_once:nnN}. This is equivalent to
+% checking with \cs{regex_match_case:nn} which \meta{regex} matches,
+% then performing the replacement with \cs{regex_replace_once:nnN}.
+% \end{function}
+%
+% \begin{function}[noTF, added = 2022-01-10]{\regex_replace_case_all:nN}
+% \begin{syntax}
+% \cs{regex_replace_case_all:nNTF}
+% ~~|{| \\
+% ~~~~\Arg{regex_1} \Arg{replacement_1} \\
+% ~~~~\Arg{regex_2} \Arg{replacement_2} \\
+% ~~~~\ldots \\
+% ~~~~\Arg{regex_n} \Arg{replacement_n} \\
+% ~~|}| \meta{tl~var}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Replaces all occurrences of all \meta{regex} in the \meta{token
+% list} by the corresponding \meta{replacement}. Every match is
+% treated independently, and matches cannot overlap. The result is
+% assigned locally to \meta{tl~var}, and the \meta{true code} or
+% \meta{false code} is left in the input stream depending on whether
+% any replacement was made or not.
+%
+% In detail, for each starting position in the \meta{token list}, each
+% of the \meta{regex} is searched in turn. If one of them matches
+% then it is replaced by the corresponding \meta{replacement}, and the
+% search resumes at the position that follows this match (and
+% replacement). For instance
+% \begin{verbatim}
+% \tl_set:Nn \l_tmpa_tl { Hello,~world! }
+% \regex_replace_case_all:nN
+% {
+% { [A-Za-z]+ } { ``\0'' }
+% { \b } { --- }
+% { . } { [\0] }
+% } \l_tmpa_tl
+% \end{verbatim}
+% results in \cs{l_tmpa_tl} having the contents
+% \verb*|``Hello''---[,][ ]``world''---[!]|. Note in particular that
+% the word-boundary assertion |\b| did not match at the start of words
+% because the case |[A-Za-z]+| matched at these positions. To change
+% this, one could simply swap the order of the two cases in the
+% argument of \cs{regex_replace_case_all:nN}.
+% \end{function}
+%
+% \section{Scratch regular expressions}
+%
+% \begin{variable}[added = 2017-12-11]{\l_tmpa_regex, \l_tmpb_regex}
+% Scratch regex for local assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \begin{variable}[added = 2017-12-11]{\g_tmpa_regex, \g_tmpb_regex}
+% Scratch regex for global assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \section{Bugs, misfeatures, future work, and other possibilities}
+%
+% The following need to be done now.
+% \begin{itemize}
+% \item Rewrite the documentation in a more ordered way, perhaps add a
+% \textsc{bnf}?
+% \end{itemize}
+%
+% Additional error-checking to come.
+% \begin{itemize}
+% \item Clean up the use of messages.
+% \item Cleaner error reporting in the replacement phase.
+% \item Add tracing information.
+% \item Detect attempts to use back-references and other
+% non-implemented syntax.
+% \item Test for the maximum register \cs[no-index]{c_max_register_int}.
+% \item Find out whether the fact that |\W| and friends match the
+% end-marker leads to bugs. Possibly update \cs[no-index]{__regex_item_reverse:n}.
+% \item The empty cs should be matched by |\c{}|, not by
+% |\c{csname.?endcsname\s?}|.
+% \end{itemize}
+%
+% Code improvements to come.
+% \begin{itemize}
+% \item Shift arrays so that the useful information starts at
+% position~$1$.
+% \item Only build |\c{...}| once.
+% \item Use arrays for the left and right state stacks when
+% compiling a regex.
+% \item Should \cs[no-index]{__regex_action_free_group:n} only be used for greedy
+% |{n,}| quantifier? (I think not.)
+% \item Quantifiers for |\u| and assertions.
+% \item When matching, keep track of an explicit stack of
+% \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]{g__regex_balance_tl}
+% to build the function \cs[no-index]{__regex_replacement_balance_one_match:n}.
+% \item Reduce the number of epsilon-transitions in alternatives.
+% \item Optimize simple strings: use less states (|abcade| should give
+% two states, for |abc| and |ade|). [Does that really make sense?]
+% \item Optimize groups with no alternative.
+% \item Optimize states with a single \cs[no-index]{__regex_action_free:n}.
+% \item Optimize the use of \cs[no-index]{__regex_action_success:} by inserting it
+% in state $2$ directly instead of having an extra transition.
+% \item Optimize the use of \cs[no-index]{int_step_...} functions.
+% \item Groups don't capture within regexes for csnames; optimize and
+% document.
+% \item Better \enquote{show} for anchors, properties, and catcode tests.
+% \item Does |\K| really need a new state for itself?
+% \item When compiling, use a boolean \texttt{in_cs} and less magic
+% numbers.
+% \end{itemize}
+%
+% The following features are likely to be implemented at some point
+% in the future.
+% \begin{itemize}
+% \item General look-ahead/behind assertions.
+% \item Regex matching on external files.
+% \item Conditional subpatterns with look ahead/behind: \enquote{if
+% what follows is [\ldots{}], then [\ldots{}]}.
+% \item |(*..)| and |(?..)| sequences to set some options.
+% \item UTF-8 mode for \pdfTeX{}.
+% \item Newline conventions are not done.
+% In particular, we should have an option for |.| not to match newlines.
+% Also, |\A| should differ from |^|, and |\Z|, |\z| and |$| should
+% differ.
+% \item Unicode properties: |\p{..}| and |\P{..}|;
+% |\X| which should match any \enquote{extended} Unicode sequence.
+% This requires to manipulate a lot of data, probably using tree-boxes.
+% \end{itemize}
+%
+% The following features of \textsc{pcre} or Perl may or may not be
+% implemented.
+% \begin{itemize}
+% \item Callout with |(?C...)| or other syntax: some internal code
+% changes make that possible, and it can be useful for instance in
+% the replacement code to stop a regex replacement when some marker
+% has been found; this raises the question of a potential
+% |\regex_break:| and then of playing well with \cs{tl_map_break:}
+% called from within the code in a regex. It also raises the
+% question of nested calls to the regex machinery, which is a
+% problem since \tn{fontdimen} are global.
+% \item Conditional subpatterns (other than with a look-ahead or
+% look-behind condition): this is non-regular, isn't it?
+% \item Named subpatterns: \TeX{} programmers have lived so far
+% without any need for named macro parameters.
+% \end{itemize}
+%
+% The following features of \textsc{pcre} or Perl will definitely not be
+% implemented.
+% \begin{itemize}
+% \item Back-references: non-regular feature, this requires
+% backtracking, which is prohibitively slow.
+% \item Recursion: this is a non-regular feature.
+% \item Atomic grouping, possessive quantifiers: those tools, mostly
+% meant to fix catastrophic backtracking, are unnecessary in a
+% non-backtracking algorithm, and difficult to implement.
+% \item Subroutine calls: this syntactic sugar is difficult to include
+% in a non-backtracking algorithm, in particular because the
+% corresponding group should be treated as atomic.
+% \item Backtracking control verbs: intrinsically tied to
+% backtracking.
+% \item |\ddd|, matching the character with octal code \texttt{ddd}:
+% we already have |\x{...}| and the syntax is confusingly close to
+% what we could have used for backreferences (|\1|, |\2|, \ldots{}),
+% making it harder to produce useful error message.
+% \item |\cx|, similar to \TeX{}'s own |\^^x|.
+% \item Comments: \TeX{} already has its own system for comments.
+% \item |\Q...\E| escaping: this would require to read the argument
+% verbatim, which is not in the scope of this module.
+% \item |\C| single byte in UTF-8 mode: \XeTeX{} and \LuaTeX{} serve
+% us characters directly, and splitting those into bytes is tricky,
+% encoding dependent, and most likely not useful anyways.
+% \end{itemize}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3regex} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=regex>
+% \end{macrocode}
+%
+% \subsection{Plan of attack}
+%
+% Most regex engines use backtracking. This allows to provide very
+% powerful features (back-references come to mind first), but it is
+% costly, and raises the problem of catastrophic backtracking. Since
+% \TeX{} is not first and foremost a programming language, complicated
+% code tends to run slowly, and we must use faster, albeit slightly more
+% restrictive, techniques, coming from automata theory.
+%
+% Given a regular expression of $n$ characters, we do the following:
+% \begin{itemize}
+% \item (Compiling.) Analyse the regex, finding invalid input, and
+% convert it to an internal representation.
+% \item (Building.) Convert the compiled regex to a non-deterministic
+% finite automaton (\textsc{nfa}) with $O(n)$ states which
+% accepts precisely token lists matching that regex.
+% \item (Matching.) Loop through the query token list one token (one
+% \enquote{position}) at a time, exploring in parallel every
+% possible path (\enquote{active thread}) through the \textsc{nfa},
+% considering active threads in an order determined by the
+% quantifiers' greediness.
+% \end{itemize}
+%
+% We use the following vocabulary in the code comments (and in variable
+% names).
+% \begin{itemize}
+% \item \emph{Group}: index of the capturing group, $-1$ for
+% 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 $\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
+% integer \meta{state} with $\texttt{min_state} \leq \meta{state} <
+% \texttt{max_state}$.
+% \item \emph{Active thread}: state of the \textsc{nfa} that is reached
+% when reading the query token list for the matching. Those threads
+% are ordered according to the greediness of quantifiers.
+% \item \emph{Step}: used when matching, starts at $0$, incremented
+% every time a character is read, and is not reset when searching
+% for repeated matches. The integer \cs{l_@@_step_int} is a
+% unique id for all the steps of the matching algorithm.
+% \end{itemize}
+%
+% 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
+% follows. When building,
+% \tn{toks}\meta{state} holds the tests and actions to perform in the
+% \meta{state} of the \textsc{nfa}. When matching,
+% \begin{itemize}
+% \item \cs{g_@@_state_active_intarray} holds the last \meta{step} in
+% which each \meta{state} was active.
+% \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 \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
+% submatch started to be looked for and its two end-points. For
+% historical reasons, the minimum index is twice \texttt{max_state},
+% and the used registers go up to \cs{l_@@_submatch_int}. They are
+% organized in blocks of \cs{l_@@_capturing_group_int} entries, each
+% 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{e}-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
+% comes the code for compiling a regular expression, and for showing the
+% result of the compilation. The building phase converts a compiled
+% regex to \textsc{nfa} states, and the automaton is run by the code in
+% the following section. The only remaining brick is parsing the
+% replacement text and performing the replacement. We are then ready for
+% all the user functions. Finally, messages, and a little bit of tracing
+% code.
+%
+% \subsection{Helpers}
+%
+% \begin{macro}{\@@_int_eval:w}
+% Access the primitive: performance is key here, so we do not use
+% the slower route \emph{via} \cs{int_eval:n}.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_int_eval:w \tex_numexpr:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_standard_escapechar:}
+% Make the \tn{escapechar} into the standard backslash.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_standard_escapechar:
+ { \int_set:Nn \tex_escapechar:D { `\\ } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_toks_use:w}
+% Unpack a \tn{toks} given its number.
+% \begin{macrocode}
+\cs_new:Npn \@@_toks_use:w { \tex_the:D \tex_toks:D }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_toks_clear:N, \@@_toks_set:Nn, \@@_toks_set:No}
+% Empty a \tn{toks} or set it to a value, given its number.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_toks_clear:N #1
+ { \tex_toks:D #1 = { } }
+\cs_new_eq:NN \@@_toks_set:Nn \tex_toks:D
+\cs_new_protected:Npn \@@_toks_set:No #1
+ { \tex_toks:D #1 = \exp_after:wN }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_toks_memcpy:NNn}
+% Copy |#3| \tn{toks} registers from |#2| onwards to |#1| onwards,
+% like |C|'s |memcpy|.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_toks_memcpy:NNn #1#2#3
+ {
+ \prg_replicate:nn {#3}
+ {
+ \tex_toks:D #1 = \tex_toks:D #2
+ \int_incr:N #1
+ \int_incr:N #2
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_toks_put_left:Ne}
+% \begin{macro}{\@@_toks_put_right:Ne, \@@_toks_put_right:Nn}
+% During the building phase we wish to add \texttt{e}-expanded
+% material to \tn{toks}, either to the left or to the right. The
+% expansion is done \enquote{by hand} for optimization (these
+% operations are used quite a lot). The \texttt{Nn} version of
+% \cs{@@_toks_put_right:Ne} is provided because it is more
+% efficient than \texttt{e}-expanding with \cs{exp_not:n}.
+% \begin{macrocode}
+\cs_if_exist:NTF \tex_etokspre:D
+ { \cs_new_eq:NN \@@_toks_put_left:Ne \tex_etokspre:D }
+ {
+ \cs_new_protected:Npn \@@_toks_put_left:Ne #1#2
+ { \tex_toks:D #1 = \tex_expanded:D {{ #2 \tex_the:D \tex_toks:D #1 }} }
+ }
+\cs_if_exist:NTF \tex_etoksapp:D
+ { \cs_new_eq:NN \@@_toks_put_right:Ne \tex_etoksapp:D }
+ {
+ \cs_new_protected:Npn \@@_toks_put_right:Ne #1#2
+ { \tex_toks:D #1 = \tex_expanded:D {{ \tex_the:D \tex_toks:D #1 #2 }} }
+ }
+\cs_if_exist:NTF \tex_toksapp:D
+ { \cs_new_eq:NN \@@_toks_put_right:Nn \tex_toksapp:D }
+ {
+ \cs_new_protected:Npn \@@_toks_put_right:Nn #1#2
+ { \tex_toks:D #1 = \exp_after:wN { \tex_the:D \tex_toks:D #1 #2 } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_curr_cs_to_str:}
+% Expands to the string representation of the token (known to be a
+% control sequence) at the current position \cs{l_@@_curr_pos_int}.
+% It should only be used in \texttt{e}/\texttt{x}-expansion to avoid losing a
+% leading space.
+% \begin{macrocode}
+\cs_new:Npn \@@_curr_cs_to_str:
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \cs_to_str:N
+ \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:No \@@_intarray_item_aux:nNF { \tex_the:D \@@_int_eval:w #2 } #1 }
+\cs_new:Npn \@@_intarray_item_aux:nNF #1#2
+ {
+ \if_int_compare:w #1 > \c_zero_int
+ \exp_after:wN \use_ii:nnn
+ \fi:
+ \use_ii:nn { \__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}
+%
+% \begin{macro}{\@@_tl_odd_items:n, \@@_tl_even_items:n, \@@_tl_even_items_loop:nn}
+% Map through a token list one pair at a time, leaving the
+% odd-numbered or even-numbered items (the first item is
+% numbered~$1$).
+% \begin{macrocode}
+\cs_new:Npn \@@_tl_odd_items:n #1 { \@@_tl_even_items:n { ? #1 } }
+\cs_new:Npn \@@_tl_even_items:n #1
+ {
+ \@@_tl_even_items_loop:nn #1 \q_@@_nil \q_@@_nil
+ \prg_break_point:
+ }
+\cs_new:Npn \@@_tl_even_items_loop:nn #1#2
+ {
+ \@@_use_none_delimit_by_q_nil:w #2 \prg_break: \q_@@_nil
+ { \exp_not:n {#2} }
+ \@@_tl_even_items_loop:nn
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Constants and variables}
+%
+% \begin{macro}{\@@_tmp:w}
+% Temporary function used for various short-term purposes.
+% \begin{macrocode}
+\cs_new:Npn \@@_tmp:w { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}
+% {
+% \l_@@_internal_a_tl, \l_@@_internal_b_tl,
+% \l_@@_internal_a_int, \l_@@_internal_b_int,
+% \l_@@_internal_c_int, \l_@@_internal_bool,
+% \l_@@_internal_seq, \g_@@_internal_tl,
+% }
+% Temporary variables used for various purposes.
+% \begin{macrocode}
+\tl_new:N \l_@@_internal_a_tl
+\tl_new:N \l_@@_internal_b_tl
+\int_new:N \l_@@_internal_a_int
+\int_new:N \l_@@_internal_b_int
+\int_new:N \l_@@_internal_c_int
+\bool_new:N \l_@@_internal_bool
+\seq_new:N \l_@@_internal_seq
+\tl_new:N \g_@@_internal_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_build_tl}
+% This temporary variable is specifically for use with the |tl_build|
+% machinery.
+% \begin{macrocode}
+\tl_new:N \l_@@_build_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_no_match_regex}
+% This regular expression matches nothing, but is still a valid
+% regular expression. We could use a failing assertion, but I went for
+% an empty class. It is used as the initial value for regular
+% expressions declared using \cs{regex_new:N}.
+% \begin{macrocode}
+\tl_const:Nn \c_@@_no_match_regex
+ {
+ \@@_branch:n
+ { \@@_class:NnnnN \c_true_bool { } { 1 } { 0 } \c_true_bool }
+ }
+% \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
+% given point in the token list. This variable is also used to keep
+% track of the balance in the replacement text.
+% \begin{macrocode}
+\int_new:N \l_@@_balance_int
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Testing characters}
+%
+% \begin{macro}{\c_@@_ascii_min_int, \c_@@_ascii_max_control_int, \c_@@_ascii_max_int}
+% \begin{macrocode}
+\int_const:Nn \c_@@_ascii_min_int { 0 }
+\int_const:Nn \c_@@_ascii_max_control_int { 31 }
+\int_const:Nn \c_@@_ascii_max_int { 127 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\c_@@_ascii_lower_int}
+% \begin{macrocode}
+\int_const:Nn \c_@@_ascii_lower_int { `a - `A }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Internal auxiliaries}
+%
+% \begin{variable}{\q_@@_recursion_stop}
+% Internal recursion quarks.
+% \begin{macrocode}
+\quark_new:N \q_@@_recursion_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\q_@@_nil}
+% Internal quarks.
+% \begin{macrocode}
+\quark_new:N \q_@@_nil
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{
+% \@@_use_none_delimit_by_q_recursion_stop:w,
+% \@@_use_i_delimit_by_q_recursion_stop:nw,
+% \@@_use_none_delimit_by_q_nil:w,
+% }
+% Functions to gobble up to a quark.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_none_delimit_by_q_recursion_stop:w
+ #1 \q_@@_recursion_stop { }
+\cs_new:Npn \@@_use_i_delimit_by_q_recursion_stop:nw
+ #1 #2 \q_@@_recursion_stop {#1}
+\cs_new:Npn \@@_use_none_delimit_by_q_nil:w #1 \q_@@_nil { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\@@_quark_if_nil:n}
+% Branching quark conditional.
+% \begin{macrocode}
+\__kernel_quark_new_conditional:Nn \@@_quark_if_nil:N { F }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_break_point:TF}
+% \begin{macro}{\@@_break_true:w}
+% When testing whether a character of the query token list matches
+% a given character class in the regular expression, we often
+% have to test it against several ranges of characters, checking
+% if any one of those matches. This is done with a structure like
+% \begin{quote}
+% \meta{test1} \ldots{} \meta{test$\sb{n}$} \\
+% \cs{@@_break_point:TF} \Arg{true code} \Arg{false code}
+% \end{quote}
+% If any of the tests succeeds, it calls \cs{@@_break_true:w},
+% which cleans up and leaves \meta{true code} in the input stream.
+% Otherwise, \cs{@@_break_point:TF} leaves the \meta{false code}
+% in the input stream.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_break_true:w
+ #1 \@@_break_point:TF #2 #3 {#2}
+\cs_new_protected:Npn \@@_break_point:TF #1 #2 { #2 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_item_reverse:n}
+% This function makes showing regular expressions easier, and lets us
+% define |\D| in terms of |\d| for instance. There is a subtlety: the
+% end of the query is marked by $-2$, and thus matches |\D| and
+% other negated properties; this case is caught by another part of
+% the code.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_item_reverse:n #1
+ {
+ #1
+ \@@_break_point:TF { } \@@_break_true:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_item_caseful_equal:n, \@@_item_caseful_range:nn}
+% Simple comparisons triggering \cs{@@_break_true:w} when true.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_item_caseful_equal:n #1
+ {
+ \if_int_compare:w #1 = \l_@@_curr_char_int
+ \exp_after:wN \@@_break_true:w
+ \fi:
+ }
+\cs_new_protected:Npn \@@_item_caseful_range:nn #1 #2
+ {
+ \reverse_if:N \if_int_compare:w #1 > \l_@@_curr_char_int
+ \reverse_if:N \if_int_compare:w #2 < \l_@@_curr_char_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_break_true:w
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_item_caseless_equal:n, \@@_item_caseless_range:nn}
+% For caseless matching, we perform the test both on the
+% \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}
+\cs_new_protected:Npn \@@_item_caseless_equal:n #1
+ {
+ \if_int_compare:w #1 = \l_@@_curr_char_int
+ \exp_after:wN \@@_break_true:w
+ \fi:
+ \@@_maybe_compute_ccc:
+ \if_int_compare:w #1 = \l_@@_case_changed_char_int
+ \exp_after:wN \@@_break_true:w
+ \fi:
+ }
+\cs_new_protected:Npn \@@_item_caseless_range:nn #1 #2
+ {
+ \reverse_if:N \if_int_compare:w #1 > \l_@@_curr_char_int
+ \reverse_if:N \if_int_compare:w #2 < \l_@@_curr_char_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_break_true:w
+ \fi:
+ \fi:
+ \@@_maybe_compute_ccc:
+ \reverse_if:N \if_int_compare:w #1 > \l_@@_case_changed_char_int
+ \reverse_if:N \if_int_compare:w #2 < \l_@@_case_changed_char_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_break_true:w
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compute_case_changed_char:}
+% This function is called when \cs{l_@@_case_changed_char_int} has
+% not yet been computed. If the current character code is in the range
+% $[65,90]$ (upper-case), then add $32$, making it lowercase. If it is
+% in the lower-case letter range $[97,122]$, subtract $32$.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compute_case_changed_char:
+ {
+ \int_set_eq:NN \l_@@_case_changed_char_int \l_@@_curr_char_int
+ \if_int_compare:w \l_@@_curr_char_int > `Z \exp_stop_f:
+ \if_int_compare:w \l_@@_curr_char_int > `z \exp_stop_f: \else:
+ \if_int_compare:w \l_@@_curr_char_int < `a \exp_stop_f: \else:
+ \int_sub:Nn \l_@@_case_changed_char_int \c_@@_ascii_lower_int
+ \fi:
+ \fi:
+ \else:
+ \if_int_compare:w \l_@@_curr_char_int < `A \exp_stop_f: \else:
+ \int_add:Nn \l_@@_case_changed_char_int \c_@@_ascii_lower_int
+ \fi:
+ \fi:
+ \cs_set_eq:NN \@@_maybe_compute_ccc: \prg_do_nothing:
+ }
+\cs_new_eq:NN \@@_maybe_compute_ccc: \@@_compute_case_changed_char:
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_item_equal:n, \@@_item_range:nn}
+% Those must always be defined to expand to a \texttt{caseful}
+% (default) or \texttt{caseless} version, and not be protected: they
+% must expand when compiling, to hard-code which tests are caseless or
+% caseful.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_item_equal:n ?
+\cs_new_eq:NN \@@_item_range:nn ?
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_item_catcode:nT, \@@_item_catcode_reverse:nT}
+% \begin{macro}{\@@_item_catcode:}
+% The argument is a sum of powers of $4$ with exponents given by the
+% allowed category codes (between $0$ and $13$). Dividing by a given
+% power of $4$ gives an odd result if and only if that category code
+% is allowed. If the catcode does not match, then skip the character
+% code tests which follow.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_item_catcode:
+ {
+ "
+ \if_case:w \l_@@_curr_catcode_int
+ 1 \or: 4 \or: 10 \or: 40
+ \or: 100 \or: \or: 1000 \or: 4000
+ \or: 10000 \or: \or: 100000 \or: 400000
+ \or: 1000000 \or: 4000000 \else: 1*0
+ \fi:
+ }
+\prg_new_protected_conditional:Npnn \@@_item_catcode:n #1 { T }
+ {
+ \if_int_odd:w \@@_int_eval:w #1 / \@@_item_catcode: \scan_stop:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new_protected:Npn \@@_item_catcode_reverse:nT #1#2
+ { \@@_item_catcode:nT {#1} { \@@_item_reverse:n {#2} } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \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, separated by \cs{scan_stop:}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_item_exact:nn #1#2
+ {
+ \if_int_compare:w #1 = \l_@@_curr_catcode_int
+ \if_int_compare:w #2 = \l_@@_curr_char_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_break_true:w
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \@@_item_exact_cs:n #1
+ {
+ \int_compare:nNnTF \l_@@_curr_catcode_int = \c_zero_int
+ {
+ \__kernel_tl_set:Nx \l_@@_internal_a_tl
+ { \scan_stop: \@@_curr_cs_to_str: \scan_stop: }
+ \tl_if_in:noTF { \scan_stop: #1 \scan_stop: }
+ \l_@@_internal_a_tl
+ { \@@_break_true:w } { }
+ }
+ { }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_item_cs:n}
+% 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.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_item_cs:n #1
+ {
+ \int_compare:nNnT \l_@@_curr_catcode_int = \c_zero_int
+ {
+ \group_begin:
+ \@@_single_match:
+ \@@_disable_submatches:
+ \@@_build_for_cs:n {#1}
+ \bool_set_eq:NN \l_@@_saved_success_bool
+ \g_@@_success_bool
+ \exp_args:Ne \@@_match_cs:n { \@@_curr_cs_to_str: }
+ \if_meaning:w \c_true_bool \g_@@_success_bool
+ \group_insert_after:N \@@_break_true:w
+ \fi:
+ \bool_gset_eq:NN \g_@@_success_bool
+ \l_@@_saved_success_bool
+ \group_end:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Character property tests}
+%
+% \begin{macro}
+% {
+% \@@_prop_d:, \@@_prop_h:, \@@_prop_s:,
+% \@@_prop_v:, \@@_prop_w:, \@@_prop_N:
+% }
+% Character property tests for |\d|, |\W|, \emph{etc.} These character
+% properties are not affected by the |(?i)| option. The characters
+% recognized by each one are as follows: |\d=[0-9]|,
+% |\w=[0-9A-Z_a-z]|, \verb*+\s=[\ \^^I\^^J\^^L\^^M]+,
+% \verb*+\h=[\ \^^I]+, |\v=[\^^J-\^^M]|, and the upper case
+% counterparts match anything that the lower case does not match. The
+% order in which the various tests appear is optimized for usual
+% mostly lower case letter text.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_prop_d:
+ { \@@_item_caseful_range:nn { `0 } { `9 } }
+\cs_new_protected:Npn \@@_prop_h:
+ {
+ \@@_item_caseful_equal:n { `\ }
+ \@@_item_caseful_equal:n { `\^^I }
+ }
+\cs_new_protected:Npn \@@_prop_s:
+ {
+ \@@_item_caseful_equal:n { `\ }
+ \@@_item_caseful_equal:n { `\^^I }
+ \@@_item_caseful_equal:n { `\^^J }
+ \@@_item_caseful_equal:n { `\^^L }
+ \@@_item_caseful_equal:n { `\^^M }
+ }
+\cs_new_protected:Npn \@@_prop_v:
+ { \@@_item_caseful_range:nn { `\^^J } { `\^^M } } % lf, vtab, ff, cr
+\cs_new_protected:Npn \@@_prop_w:
+ {
+ \@@_item_caseful_range:nn { `a } { `z }
+ \@@_item_caseful_range:nn { `A } { `Z }
+ \@@_item_caseful_range:nn { `0 } { `9 }
+ \@@_item_caseful_equal:n { `_ }
+ }
+\cs_new_protected:Npn \@@_prop_N:
+ {
+ \@@_item_reverse:n
+ { \@@_item_caseful_equal:n { `\^^J } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_posix_alnum:, \@@_posix_alpha:, \@@_posix_ascii:,
+% \@@_posix_blank:, \@@_posix_cntrl:, \@@_posix_digit:,
+% \@@_posix_graph:, \@@_posix_lower:, \@@_posix_print:,
+% \@@_posix_punct:, \@@_posix_space:, \@@_posix_upper:,
+% \@@_posix_word: , \@@_posix_xdigit:
+% }
+% \textsc{posix} properties. No surprise.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_posix_alnum:
+ { \@@_posix_alpha: \@@_posix_digit: }
+\cs_new_protected:Npn \@@_posix_alpha:
+ { \@@_posix_lower: \@@_posix_upper: }
+\cs_new_protected:Npn \@@_posix_ascii:
+ {
+ \@@_item_caseful_range:nn
+ \c_@@_ascii_min_int
+ \c_@@_ascii_max_int
+ }
+\cs_new_eq:NN \@@_posix_blank: \@@_prop_h:
+\cs_new_protected:Npn \@@_posix_cntrl:
+ {
+ \@@_item_caseful_range:nn
+ \c_@@_ascii_min_int
+ \c_@@_ascii_max_control_int
+ \@@_item_caseful_equal:n \c_@@_ascii_max_int
+ }
+\cs_new_eq:NN \@@_posix_digit: \@@_prop_d:
+\cs_new_protected:Npn \@@_posix_graph:
+ { \@@_item_caseful_range:nn { `! } { `\~ } }
+\cs_new_protected:Npn \@@_posix_lower:
+ { \@@_item_caseful_range:nn { `a } { `z } }
+\cs_new_protected:Npn \@@_posix_print:
+ { \@@_item_caseful_range:nn { `\ } { `\~ } }
+\cs_new_protected:Npn \@@_posix_punct:
+ {
+ \@@_item_caseful_range:nn { `! } { `/ }
+ \@@_item_caseful_range:nn { `: } { `@ }
+ \@@_item_caseful_range:nn { `[ } { `` }
+ \@@_item_caseful_range:nn { `\{ } { `\~ }
+ }
+\cs_new_protected:Npn \@@_posix_space:
+ {
+ \@@_item_caseful_equal:n { `\ }
+ \@@_item_caseful_range:nn { `\^^I } { `\^^M }
+ }
+\cs_new_protected:Npn \@@_posix_upper:
+ { \@@_item_caseful_range:nn { `A } { `Z } }
+\cs_new_eq:NN \@@_posix_word: \@@_prop_w:
+\cs_new_protected:Npn \@@_posix_xdigit:
+ {
+ \@@_posix_digit:
+ \@@_item_caseful_range:nn { `A } { `F }
+ \@@_item_caseful_range:nn { `a } { `f }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Simple character escape}
+%
+% Before actually parsing the regular expression or the replacement
+% text, we go through them once, converting |\n| to the character $10$,
+% \emph{etc.} In this pass, we also convert any special character
+% (\texttt{*}, \texttt{?}, \texttt{\{}, etc.) or escaped alphanumeric
+% character into a marker indicating that this was a special sequence,
+% and replace escaped special characters and non-escaped alphanumeric
+% characters by markers indicating that those were \enquote{raw}
+% characters. The rest of the code can then avoid caring about escaping
+% issues (those can become quite complex to handle in combination with
+% ranges in character classes).
+%
+% Usage: \cs{@@_escape_use:nnnn} \meta{inline~1} \meta{inline~2}
+% \meta{inline~3} \Arg{token list} The \meta{token list} is converted to
+% a string, then read from left to right, interpreting backslashes as
+% escaping the next character. Unescaped characters are fed to the
+% function \meta{inline~1}, and escaped characters are fed to the function
+% \meta{inline~2} within an \texttt{e}-expansion context (typically those
+% functions perform some tests on their argument to decide how to output
+% them). The escape sequences |\a|, |\e|, |\f|, |\n|, |\r|, |\t| and
+% |\x| are recognized, and those are replaced by the corresponding
+% character, then fed to \meta{inline~3}. The result is then left in the
+% input stream. Spaces are ignored unless escaped.
+%
+% The conversion is done within an \texttt{e}-expanding assignment.
+%
+% \begin{macro}{\@@_escape_use:nnnn}
+% The result is built in \cs{l_@@_internal_a_tl}, which is then left
+% in the input stream. Tracing code is added as appropriate inside
+% this token list. Go through |#4| once, applying |#1|,
+% |#2|, or |#3| as relevant to each character (after de-escaping
+% it).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_escape_use:nnnn #1#2#3#4
+ {
+ \group_begin:
+ \tl_clear:N \l_@@_internal_a_tl
+ \cs_set:Npn \@@_escape_unescaped:N ##1 { #1 }
+ \cs_set:Npn \@@_escape_escaped:N ##1 { #2 }
+ \cs_set:Npn \@@_escape_raw:N ##1 { #3 }
+ \@@_standard_escapechar:
+ \__kernel_tl_gset:Nx \g_@@_internal_tl
+ { \__kernel_str_to_other_fast:n {#4} }
+ \tl_put_right:Ne \l_@@_internal_a_tl
+ {
+ \exp_after:wN \@@_escape_loop:N \g_@@_internal_tl
+ \scan_stop: \prg_break_point:
+ }
+ \exp_after:wN
+ \group_end:
+ \l_@@_internal_a_tl
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_escape_loop:N}
+% \begin{macro}+\@@_escape_\:w+
+% \cs{@@_escape_loop:N} reads one character: if it is special
+% (space, backslash, or end-marker), perform the associated action,
+% otherwise it is simply an unescaped character. After a backslash,
+% the same is done, but unknown characters are \enquote{escaped}.
+% \begin{macrocode}
+\cs_new:Npn \@@_escape_loop:N #1
+ {
+ \cs_if_exist_use:cF { @@_escape_\token_to_str:N #1:w }
+ { \@@_escape_unescaped:N #1 }
+ \@@_escape_loop:N
+ }
+\cs_new:cpn { @@_escape_ \c_backslash_str :w }
+ \@@_escape_loop:N #1
+ {
+ \cs_if_exist_use:cF { @@_escape_/\token_to_str:N #1:w }
+ { \@@_escape_escaped:N #1 }
+ \@@_escape_loop:N
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_escape_unescaped:N, \@@_escape_escaped:N, \@@_escape_raw:N}
+% Those functions are never called before being given a new meaning,
+% so their definitions here don't matter.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_escape_unescaped:N ?
+\cs_new_eq:NN \@@_escape_escaped:N ?
+\cs_new_eq:NN \@@_escape_raw:N ?
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_escape_\scan_stop::w, \@@_escape_/\scan_stop::w,
+% \@@_escape_/a:w, \@@_escape_/e:w, \@@_escape_/f:w,
+% \@@_escape_/n:w, \@@_escape_/r:w, \@@_escape_/t:w
+% }
+% \begin{macro}+\@@_escape_ :w+
+% The loop is ended upon seeing the end-marker
+% \enquote{\texttt{break}}, with an error if the string ended in a
+% backslash. Spaces are ignored, and |\a|, |\e|, |\f|, |\n|, |\r|,
+% |\t| take their meaning here.
+% \begin{macrocode}
+\cs_new_eq:cN { @@_escape_ \iow_char:N\\scan_stop: :w } \prg_break:
+\cs_new:cpn { @@_escape_/ \iow_char:N\\scan_stop: :w }
+ {
+ \msg_expandable_error:nn { regex } { trailing-backslash }
+ \prg_break:
+ }
+\cs_new:cpn { @@_escape_~:w } { }
+\cs_new:cpe { @@_escape_/a:w }
+ { \exp_not:N \@@_escape_raw:N \iow_char:N \^^G }
+\cs_new:cpe { @@_escape_/t:w }
+ { \exp_not:N \@@_escape_raw:N \iow_char:N \^^I }
+\cs_new:cpe { @@_escape_/n:w }
+ { \exp_not:N \@@_escape_raw:N \iow_char:N \^^J }
+\cs_new:cpe { @@_escape_/f:w }
+ { \exp_not:N \@@_escape_raw:N \iow_char:N \^^L }
+\cs_new:cpe { @@_escape_/r:w }
+ { \exp_not:N \@@_escape_raw:N \iow_char:N \^^M }
+\cs_new:cpe { @@_escape_/e:w }
+ { \exp_not:N \@@_escape_raw:N \iow_char:N \^^[ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_escape_/x:w}
+% \begin{macro}{\@@_escape_x_end:w, \@@_escape_x_large:n}
+% When |\x| is encountered, \cs{@@_escape_x_test:N} is responsible for
+% grabbing some hexadecimal digits, and feeding the result to
+% \cs{@@_escape_x_end:w}. If the number is too big interrupt the
+% assignment and produce an error, otherwise call \cs{@@_escape_raw:N}
+% on the corresponding character token.
+% \begin{macrocode}
+\cs_new:cpn { @@_escape_/x:w } \@@_escape_loop:N
+ {
+ \exp_after:wN \@@_escape_x_end:w
+ \int_value:w "0 \@@_escape_x_test:N
+ }
+\cs_new:Npn \@@_escape_x_end:w #1 ;
+ {
+ \int_compare:nNnTF {#1} > \c_max_char_int
+ {
+ \msg_expandable_error:nnff { regex } { x-overflow }
+ {#1} { \int_to_Hex:n {#1} }
+ }
+ {
+ \exp_last_unbraced:Nf \@@_escape_raw:N
+ { \char_generate:nn {#1} { 12 } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_escape_x_test:N, \@@_escape_x_testii:N}
+% Find out whether the first character is a left brace (allowing any
+% number of hexadecimal digits), or not (allowing up to two
+% hexadecimal digits). We need to check for the end-of-string marker.
+% Eventually, call either \cs{@@_escape_x_loop:N} or
+% \cs{@@_escape_x:N}.
+% \begin{macrocode}
+\cs_new:Npn \@@_escape_x_test:N #1
+ {
+ \if_meaning:w \scan_stop: #1
+ \exp_after:wN \use_i:nnn \exp_after:wN ;
+ \fi:
+ \use:n
+ {
+ \if_charcode:w \c_space_token #1
+ \exp_after:wN \@@_escape_x_test:N
+ \else:
+ \exp_after:wN \@@_escape_x_testii:N
+ \exp_after:wN #1
+ \fi:
+ }
+ }
+\cs_new:Npn \@@_escape_x_testii:N #1
+ {
+ \if_charcode:w \c_left_brace_str #1
+ \exp_after:wN \@@_escape_x_loop:N
+ \else:
+ \@@_hexadecimal_use:NTF #1
+ { \exp_after:wN \@@_escape_x:N }
+ { ; \exp_after:wN \@@_escape_loop:N \exp_after:wN #1 }
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_escape_x:N}
+% This looks for the second digit in the unbraced case.
+% \begin{macrocode}
+\cs_new:Npn \@@_escape_x:N #1
+ {
+ \if_meaning:w \scan_stop: #1
+ \exp_after:wN \use_i:nnn \exp_after:wN ;
+ \fi:
+ \use:n
+ {
+ \@@_hexadecimal_use:NTF #1
+ { ; \@@_escape_loop:N }
+ { ; \@@_escape_loop:N #1 }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_escape_x_loop:N, \@@_escape_x_loop_error:}
+% Grab hexadecimal digits, skip spaces, and at the end, check that
+% there is a right brace, otherwise raise an error outside the
+% assignment.
+% \begin{macrocode}
+\cs_new:Npn \@@_escape_x_loop:N #1
+ {
+ \if_meaning:w \scan_stop: #1
+ \exp_after:wN \use_ii:nnn
+ \fi:
+ \use_ii:nn
+ { ; \@@_escape_x_loop_error:n { } {#1} }
+ {
+ \@@_hexadecimal_use:NTF #1
+ { \@@_escape_x_loop:N }
+ {
+ \token_if_eq_charcode:NNTF \c_space_token #1
+ { \@@_escape_x_loop:N }
+ {
+ ;
+ \exp_after:wN
+ \token_if_eq_charcode:NNTF \c_right_brace_str #1
+ { \@@_escape_loop:N }
+ { \@@_escape_x_loop_error:n {#1} }
+ }
+ }
+ }
+ }
+\cs_new:Npn \@@_escape_x_loop_error:n #1
+ {
+ \msg_expandable_error:nnn { regex } { x-missing-rbrace } {#1}
+ \@@_escape_loop:N #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_hexadecimal_use:NTF}
+% \TeX{} detects uppercase hexadecimal digits for us but not the
+% lowercase letters, which we need to detect and replace by their
+% uppercase counterpart.
+% \begin{macrocode}
+\cs_new:Npn \@@_hexadecimal_use:NTF #1
+ {
+ \if_int_compare:w \c_one_int < "1 \token_to_str:N #1 \exp_stop_f:
+ #1
+ \else:
+ \if_case:w
+ \@@_int_eval:w \exp_after:wN ` \token_to_str:N #1 - `a \scan_stop:
+ A
+ \or: B
+ \or: C
+ \or: D
+ \or: E
+ \or: F
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_iii:nnn
+ \fi:
+ \fi:
+ \use_i:nn
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_char_if_alphanumeric:NTF, \@@_char_if_special:NTF}
+% These two tests are used in the first pass when parsing a regular
+% expression. That pass is responsible for finding escaped and
+% non-escaped characters, and recognizing which ones have special
+% meanings and which should be interpreted as \enquote{raw}
+% characters. Namely,
+% \begin{itemize}
+% \item alphanumerics are \enquote{raw} if they are not escaped, and
+% may have a special meaning when escaped;
+% \item non-alphanumeric printable ascii characters are
+% \enquote{raw} if they are escaped, and may have a special
+% meaning when not escaped;
+% \item characters other than printable ascii are always
+% \enquote{raw}.
+% \end{itemize}
+% The code is ugly, and highly based on magic numbers and the ascii
+% codes of characters. This is mostly unavoidable for performance
+% reasons. Maybe the tests can be optimized a little bit more.
+% Here, \enquote{alphanumeric} means \texttt{0}--\texttt{9},
+% \texttt{A}--\texttt{Z}, \texttt{a}--\texttt{z};
+% \enquote{special} character means non-alphanumeric
+% but printable ascii, from space (hex \texttt{20}) to
+% \texttt{del} (hex \texttt{7E}).
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_char_if_special:N #1 { TF }
+ {
+ \if:w
+ T
+ \if_int_compare:w `#1 > `Z \exp_stop_f:
+ \if_int_compare:w `#1 > `z \exp_stop_f:
+ \if_int_compare:w `#1 < \c_@@_ascii_max_int
+ \else: F \fi:
+ \else:
+ \if_int_compare:w `#1 < `a \exp_stop_f:
+ \else: F \fi:
+ \fi:
+ \else:
+ \if_int_compare:w `#1 > `9 \exp_stop_f:
+ \if_int_compare:w `#1 < `A \exp_stop_f:
+ \else: F \fi:
+ \else:
+ \if_int_compare:w `#1 < `0 \exp_stop_f:
+ \if_int_compare:w `#1 < `\ \exp_stop_f:
+ F \fi:
+ \else: F \fi:
+ \fi:
+ \fi:
+ T
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_new_conditional:Npnn \@@_char_if_alphanumeric:N #1 { TF }
+ {
+ \if:w
+ T
+ \if_int_compare:w `#1 > `Z \exp_stop_f:
+ \if_int_compare:w `#1 > `z \exp_stop_f:
+ F
+ \else:
+ \if_int_compare:w `#1 < `a \exp_stop_f:
+ F \fi:
+ \fi:
+ \else:
+ \if_int_compare:w `#1 > `9 \exp_stop_f:
+ \if_int_compare:w `#1 < `A \exp_stop_f:
+ F \fi:
+ \else:
+ \if_int_compare:w `#1 < `0 \exp_stop_f:
+ F \fi:
+ \fi:
+ \fi:
+ T
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Compiling}
+%
+% A regular expression starts its life as a string of characters. In
+% this section, we convert it to internal instructions, resulting in a
+% \enquote{compiled} regular expression. This compiled expression is
+% then turned into states of an automaton in the building
+% phase. Compiled regular expressions consist of the following:
+% \begin{itemize}
+% \item \cs{@@_class:NnnnN} \meta{boolean} \Arg{tests} \Arg{min}
+% \Arg{more} \meta{lazyness}
+% \item \cs{@@_group:nnnN} \Arg{branches} \Arg{min} \Arg{more}
+% \meta{lazyness}, also \cs{@@_group_no_capture:nnnN} and
+% \cs{@@_group_resetting:nnnN} with the same syntax.
+% \item \cs{@@_branch:n} \Arg{contents}
+% \item \cs{@@_command_K:}
+% \item \cs{@@_assertion:Nn} \meta{boolean} \Arg{assertion test},
+% where the \meta{assertion test} is \cs{@@_b_test:} or
+% \cs{@@_Z_test:} or \cs{@@_A_test:} or \cs{@@_G_test:}
+% \end{itemize}
+% Tests can be the following:
+% \begin{itemize}
+% \item \cs{@@_item_caseful_equal:n} \Arg{char code}
+% \item \cs{@@_item_caseless_equal:n} \Arg{char code}
+% \item \cs{@@_item_caseful_range:nn} \Arg{min} \Arg{max}
+% \item \cs{@@_item_caseless_range:nn} \Arg{min} \Arg{max}
+% \item \cs{@@_item_catcode:nT} \Arg{catcode bitmap} \Arg{tests}
+% \item \cs{@@_item_catcode_reverse:nT} \Arg{catcode bitmap} \Arg{tests}
+% \item \cs{@@_item_reverse:n} \Arg{tests}
+% \item \cs{@@_item_exact:nn} \Arg{catcode} \Arg{char code}
+% \item \cs{@@_item_exact_cs:n} \Arg{csnames}, more precisely given as
+% \meta{csname} \cs{scan_stop:} \meta{csname} \cs{scan_stop:}
+% \meta{csname} and so on in a brace group.
+% \item \cs{@@_item_cs:n} \Arg{compiled regex}
+% \end{itemize}
+%
+% \subsubsection{Variables used when compiling}
+%
+% \begin{variable}{\l_@@_group_level_int}
+% We make sure to open the same number of groups as we close.
+% \begin{macrocode}
+\int_new:N \l_@@_group_level_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_mode_int}
+% \begin{variable}
+% {
+% \c_@@_cs_in_class_mode_int,
+% \c_@@_cs_mode_int,
+% \c_@@_outer_mode_int,
+% \c_@@_catcode_mode_int,
+% \c_@@_class_mode_int,
+% \c_@@_catcode_in_class_mode_int
+% }
+% While compiling, ten modes are recognized, labelled $-63$, $-23$,
+% $-6$, $-2$, $0$, $2$, $3$, $6$, $23$, $63$. See
+% section~\ref{sec:regex-modes}. We only define some of these as
+% constants.
+% \begin{macrocode}
+\int_new:N \l_@@_mode_int
+\int_const:Nn \c_@@_cs_in_class_mode_int { -6 }
+\int_const:Nn \c_@@_cs_mode_int { -2 }
+\int_const:Nn \c_@@_outer_mode_int { 0 }
+\int_const:Nn \c_@@_catcode_mode_int { 2 }
+\int_const:Nn \c_@@_class_mode_int { 3 }
+\int_const:Nn \c_@@_catcode_in_class_mode_int { 6 }
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_catcodes_int, \l_@@_default_catcodes_int}
+% \begin{variable}{\l_@@_catcodes_bool}
+% We wish to allow constructions such as |\c[^BE](..\cL[a-z]..)|,
+% where the outer catcode test applies to the whole group, but is
+% superseded by the inner catcode test. For this to work, we need to
+% keep track of lists of allowed category codes:
+% \cs{l_@@_catcodes_int} and \cs{l_@@_default_catcodes_int} are
+% bitmaps, sums of $4^c$, for all allowed catcodes $c$. The latter is
+% local to each capturing group, and we reset
+% \cs{l_@@_catcodes_int} to that value after each character or
+% class, changing it only when encountering a |\c| escape. The boolean
+% records whether the list of categories of a catcode test has to be
+% inverted: compare |\c[^BE]| and |\c[BE]|.
+% \begin{macrocode}
+\int_new:N \l_@@_catcodes_int
+\int_new:N \l_@@_default_catcodes_int
+\bool_new:N \l_@@_catcodes_bool
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \c_@@_catcode_C_int, \c_@@_catcode_B_int, \c_@@_catcode_E_int,
+% \c_@@_catcode_M_int, \c_@@_catcode_T_int, \c_@@_catcode_P_int,
+% \c_@@_catcode_U_int, \c_@@_catcode_D_int, \c_@@_catcode_S_int,
+% \c_@@_catcode_L_int, \c_@@_catcode_O_int, \c_@@_catcode_A_int
+% }
+% \begin{variable}{\c_@@_all_catcodes_int}
+% Constants: $4^c$ for each category, and the sum of all powers of $4$.
+% \begin{macrocode}
+\int_const:Nn \c_@@_catcode_C_int { "1 }
+\int_const:Nn \c_@@_catcode_B_int { "4 }
+\int_const:Nn \c_@@_catcode_E_int { "10 }
+\int_const:Nn \c_@@_catcode_M_int { "40 }
+\int_const:Nn \c_@@_catcode_T_int { "100 }
+\int_const:Nn \c_@@_catcode_P_int { "1000 }
+\int_const:Nn \c_@@_catcode_U_int { "4000 }
+\int_const:Nn \c_@@_catcode_D_int { "10000 }
+\int_const:Nn \c_@@_catcode_S_int { "100000 }
+\int_const:Nn \c_@@_catcode_L_int { "400000 }
+\int_const:Nn \c_@@_catcode_O_int { "1000000 }
+\int_const:Nn \c_@@_catcode_A_int { "4000000 }
+\int_const:Nn \c_@@_all_catcodes_int { "5515155 }
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_internal_regex}
+% The compilation step stores its result in this variable.
+% \begin{macrocode}
+\cs_new_eq:NN \l_@@_internal_regex \c_@@_no_match_regex
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_show_prefix_seq}
+% This sequence holds the prefix that makes up the line displayed to
+% the user. The various items must be removed from the right, which is
+% tricky with a token list, hence we use a sequence.
+% \begin{macrocode}
+\seq_new:N \l_@@_show_prefix_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_show_lines_int}
+% A hack. To know whether a given class has a single item in it or
+% not, we count the number of lines when showing the class.
+% \begin{macrocode}
+\int_new:N \l_@@_show_lines_int
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Generic helpers used when compiling}
+%
+% \begin{macro}{\@@_two_if_eq:NNNNTF}
+% Used to compare pairs of things like \cs{@@_compile_special:N} |?|
+% together. It's often inconvenient to get the catcodes of the
+% character to match so we just compare the character code.
+% Besides, the expanding behaviour of \cs{if:w} is very useful as that
+% means we can use \cs{c_left_brace_str} and the like.
+% \begin{macrocode}
+\cs_new:Npn \@@_two_if_eq:NNNNTF #1#2#3#4
+ {
+ \if_meaning:w #1 #3
+ \if:w #2 #4
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_ii:nnn
+ \fi:
+ \fi:
+ \use_ii:nn
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_get_digits:NTFw}
+% \begin{macro}[rEXP]{\@@_get_digits_loop:w}
+% If followed by some raw digits, collect them one by one in the
+% integer variable |#1|, and take the \texttt{true} branch. Otherwise,
+% take the \texttt{false} branch.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_get_digits:NTFw #1#2#3#4#5
+ {
+ \@@_if_raw_digit:NNTF #4 #5
+ { #1 = #5 \@@_get_digits_loop:nw {#2} }
+ { #3 #4 #5 }
+ }
+\cs_new:Npn \@@_get_digits_loop:nw #1#2#3
+ {
+ \@@_if_raw_digit:NNTF #2 #3
+ { #3 \@@_get_digits_loop:nw {#1} }
+ { \scan_stop: #1 #2 #3 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_if_raw_digit:NNTF}
+% Test used when grabbing digits for the |{m,n}| quantifier.
+% It only accepts non-escaped digits.
+% \begin{macrocode}
+\cs_new:Npn \@@_if_raw_digit:NNTF #1#2
+ {
+ \if_meaning:w \@@_compile_raw:N #1
+ \if_int_compare:w \c_one_int < 1 #2 \exp_stop_f:
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_ii:nnn
+ \fi:
+ \fi:
+ \use_ii:nn
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Mode}
+% \label{sec:regex-modes}
+%
+% When compiling the \textsc{nfa} corresponding to a given regex string,
+% we can be in ten distinct modes, which we label by some magic numbers:
+% \begin{itemize}
+% \item[-6] |[\c{...}]| control sequence in a class,
+% \item[-2] |\c{...}| control sequence,
+% \item[0] |...| outer,
+% \item[2] |\c...| catcode test,
+% \item[6] |[\c...]| catcode test in a class,
+% \item[-63] |[\c{[...]}]| class inside mode $-6$,
+% \item[-23] |\c{[...]}| class inside mode $-2$,
+% \item[3] |[...]| class inside mode $0$,
+% \item[23] |\c[...]| class inside mode $2$,
+% \item[63] |[\c[...]]| class inside mode $6$.
+% \end{itemize}
+% This list is exhaustive, because |\c| escape sequences cannot be
+% nested, and character classes cannot be nested directly. The choice of
+% numbers is such as to optimize the most useful tests, and make
+% transitions from one mode to another as simple as possible.
+% \begin{itemize}
+% \item Even modes mean that we are not directly in a character class.
+% In this case, a left bracket appends $3$ to the mode. In a
+% character class, a right bracket changes the mode as $m\to
+% (m-15)/13$, truncated.
+% \item Grouping, assertion, and anchors are allowed in non-positive
+% even modes ($0$, $-2$, $-6$), and do not change the
+% mode. Otherwise, they trigger an error.
+% \item A left bracket is special in even modes, appending $3$ to the
+% mode; in those modes, quantifiers and the dot are recognized, and
+% the right bracket is normal. In odd modes (within classes), the
+% left bracket is normal, but the right bracket ends the class,
+% changing the mode from $m$ to $(m-15)/13$, truncated; also, ranges
+% are recognized.
+% \item In non-negative modes, left and right braces are normal. In
+% negative modes, however, left braces trigger a warning; right
+% braces end the control sequence, going from $-2$ to $0$ or $-6$ to
+% $3$, with error recovery for odd modes.
+% \item Properties (such as the |\d| character class) can appear in
+% any mode.
+% \end{itemize}
+%
+% \begin{macro}[EXP]{\@@_if_in_class:TF}
+% Test whether we are directly in a character class (at the innermost
+% level of nesting). There, many escape sequences are not recognized,
+% and special characters are normal. Also, for every raw character, we
+% must look ahead for a possible raw dash.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_if_in_class: { TF }
+ {
+ \if_int_odd:w \l_@@_mode_int
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_if_in_cs:TF}
+% Right braces are special only directly inside control sequences (at
+% the inner-most level of nesting, not counting groups).
+% \begin{macrocode}
+\cs_new:Npn \@@_if_in_cs:TF
+ {
+ \if_int_odd:w \l_@@_mode_int
+ \else:
+ \if_int_compare:w \l_@@_mode_int < \c_@@_outer_mode_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_ii:nnn
+ \fi:
+ \fi:
+ \use_ii:nn
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_if_in_class_or_catcode:TF}
+% Assertions are only allowed in modes $0$, $-2$, and $-6$,
+% \emph{i.e.}, even, non-positive modes.
+% \begin{macrocode}
+\cs_new:Npn \@@_if_in_class_or_catcode:TF
+ {
+ \if_int_odd:w \l_@@_mode_int
+ \else:
+ \if_int_compare:w \l_@@_mode_int > \c_@@_outer_mode_int
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_iii:nnn
+ \fi:
+ \fi:
+ \use_i:nn
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_if_within_catcode:TF}
+% This test takes the true branch if we are in a catcode test, either
+% immediately following it (modes $2$ and $6$) or in a class on which
+% it applies (modes $23$ and $63$). This is used to tweak how left
+% brackets behave in modes $2$ and $6$.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_if_within_catcode: { TF }
+ {
+ \if_int_compare:w \l_@@_mode_int > \c_@@_outer_mode_int
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_chk_c_allowed:T}
+% The |\c| escape sequence is only allowed in modes $0$ and $3$,
+% \emph{i.e.}, not within any other |\c| escape sequence.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_chk_c_allowed:T
+ {
+ \if_int_compare:w \l_@@_mode_int = \c_@@_outer_mode_int
+ \else:
+ \if_int_compare:w \l_@@_mode_int = \c_@@_class_mode_int
+ \else:
+ \msg_error:nn { regex } { c-bad-mode }
+ \exp_after:wN \use_i:nnn
+ \fi:
+ \fi:
+ \use:n
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_mode_quit_c:}
+% This function changes the mode as it is needed just after a catcode
+% test.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_mode_quit_c:
+ {
+ \if_int_compare:w \l_@@_mode_int = \c_@@_catcode_mode_int
+ \int_set_eq:NN \l_@@_mode_int \c_@@_outer_mode_int
+ \else:
+ \if_int_compare:w \l_@@_mode_int =
+ \c_@@_catcode_in_class_mode_int
+ \int_set_eq:NN \l_@@_mode_int \c_@@_class_mode_int
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Framework}
+%
+% \begin{macro}{\@@_compile:w, \@@_compile_end:}
+% Used when compiling a user regex or a regex for the |\c{...}| escape
+% sequence within another regex. Start building a token list within a
+% group (with \texttt{e}-expansion at the outset), and set a few
+% variables (group level, catcodes), then start the first branch. At
+% the end, make sure there are no dangling classes nor groups, close
+% the last branch: we are done building \cs{l_@@_internal_regex}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile:w
+ {
+ \group_begin:
+ \tl_build_begin:N \l_@@_build_tl
+ \int_zero:N \l_@@_group_level_int
+ \int_set_eq:NN \l_@@_default_catcodes_int
+ \c_@@_all_catcodes_int
+ \int_set_eq:NN \l_@@_catcodes_int \l_@@_default_catcodes_int
+ \cs_set:Npn \@@_item_equal:n { \@@_item_caseful_equal:n }
+ \cs_set:Npn \@@_item_range:nn { \@@_item_caseful_range:nn }
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \@@_branch:n { \if_false: } \fi: }
+ }
+\cs_new_protected:Npn \@@_compile_end:
+ {
+ \@@_if_in_class:TF
+ {
+ \msg_error:nn { regex } { missing-rbrack }
+ \use:c { @@_compile_]: }
+ \prg_do_nothing: \prg_do_nothing:
+ }
+ { }
+ \if_int_compare:w \l_@@_group_level_int > \c_zero_int
+ \msg_error:nne { regex } { missing-rparen }
+ { \int_use:N \l_@@_group_level_int }
+ \prg_replicate:nn
+ \l_@@_group_level_int
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl
+ {
+ \if_false: { \fi: }
+ \if_false: { \fi: } { 1 } { 0 } \c_true_bool
+ }
+ \tl_build_end:N \l_@@_build_tl
+ \exp_args:NNNo
+ \group_end:
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \l_@@_build_tl }
+ }
+ \fi:
+ \tl_build_put_right:Nn \l_@@_build_tl { \if_false: { \fi: } }
+ \tl_build_end:N \l_@@_build_tl
+ \exp_args:NNNe
+ \group_end:
+ \tl_set:Nn \l_@@_internal_regex { \l_@@_build_tl }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile:n}
+% The compilation is done between \cs{@@_compile:w} and
+% \cs{@@_compile_end:}, starting in mode~$0$. Then
+% \cs{@@_escape_use:nnnn} distinguishes special characters, escaped
+% alphanumerics, and raw characters, interpreting |\a|, |\x| and other
+% sequences. The $4$ trailing \cs{prg_do_nothing:} are needed because
+% some functions defined later look up to $4$ tokens ahead. Before
+% ending, make sure that any |\c{...}| is properly closed. No need to
+% check that brackets are closed properly since \cs{@@_compile_end:}
+% does that. However, catch the case of a trailing |\cL|
+% construction.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile:n #1
+ {
+ \@@_compile:w
+ \@@_standard_escapechar:
+ \int_set_eq:NN \l_@@_mode_int \c_@@_outer_mode_int
+ \@@_escape_use:nnnn
+ {
+ \@@_char_if_special:NTF ##1
+ \@@_compile_special:N \@@_compile_raw:N ##1
+ }
+ {
+ \@@_char_if_alphanumeric:NTF ##1
+ \@@_compile_escaped:N \@@_compile_raw:N ##1
+ }
+ { \@@_compile_raw:N ##1 }
+ { #1 }
+ \prg_do_nothing: \prg_do_nothing:
+ \prg_do_nothing: \prg_do_nothing:
+ \int_compare:nNnT \l_@@_mode_int = \c_@@_catcode_mode_int
+ { \msg_error:nn { regex } { c-trailing } }
+ \int_compare:nNnT \l_@@_mode_int < \c_@@_outer_mode_int
+ {
+ \msg_error:nn { regex } { c-missing-rbrace }
+ \@@_compile_end_cs:
+ \prg_do_nothing: \prg_do_nothing:
+ \prg_do_nothing: \prg_do_nothing:
+ }
+ \@@_compile_end:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_use:n}
+% Use a regex, regardless of whether it is given as a string (in which
+% case we need to compile) or as a regex variable. This is used for
+% \cs{regex_match_case:nn} and related functions to allow a mixture of
+% explicit regex and regex variables.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_use:n #1
+ {
+ \tl_if_single_token:nT {#1}
+ {
+ \exp_after:wN \@@_compile_use_aux:w
+ \token_to_meaning:N #1 ~ \q_@@_nil
+ }
+ \@@_compile:n {#1} \l_@@_internal_regex
+ }
+\cs_new_protected:Npn \@@_compile_use_aux:w #1 ~ #2 \q_@@_nil
+ {
+ \str_if_eq:nnT { #1 ~ } { macro:->\@@_branch:n }
+ { \use_ii:nnn }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_escaped:N, \@@_compile_special:N}
+% If the special character or escaped alphanumeric has a particular
+% meaning in regexes, the corresponding function is used. Otherwise,
+% it is interpreted as a raw character. We distinguish special
+% characters from escaped alphanumeric characters because they behave
+% differently when appearing as an end-point of a range.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_special:N #1
+ {
+ \cs_if_exist_use:cF { @@_compile_#1: }
+ { \@@_compile_raw:N #1 }
+ }
+\cs_new_protected:Npn \@@_compile_escaped:N #1
+ {
+ \cs_if_exist_use:cF { @@_compile_/#1: }
+ { \@@_compile_raw:N #1 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_one:n}
+% This is used after finding one \enquote{test}, such as |\d|, or a
+% raw character. If that followed a catcode test (\emph{e.g.}, |\cL|),
+% then restore the mode. If we are not in a class, then the test is
+% \enquote{standalone}, and we need to add \cs{@@_class:NnnnN} and
+% search for quantifiers. In any case, insert the test, possibly
+% together with a catcode test if appropriate.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_one:n #1
+ {
+ \@@_mode_quit_c:
+ \@@_if_in_class:TF { }
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \@@_class:NnnnN \c_true_bool { \if_false: } \fi: }
+ }
+ \tl_build_put_right:Ne \l_@@_build_tl
+ {
+ \if_int_compare:w \l_@@_catcodes_int <
+ \c_@@_all_catcodes_int
+ \@@_item_catcode:nT { \int_use:N \l_@@_catcodes_int }
+ { \exp_not:N \exp_not:n {#1} }
+ \else:
+ \exp_not:N \exp_not:n {#1}
+ \fi:
+ }
+ \int_set_eq:NN \l_@@_catcodes_int \l_@@_default_catcodes_int
+ \@@_if_in_class:TF { } { \@@_compile_quantifier:w }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_compile_abort_tokens:n, \@@_compile_abort_tokens:e}
+% This function places the collected tokens back in the input stream,
+% each as a raw character. Spaces are not preserved.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_abort_tokens:n #1
+ {
+ \use:e
+ {
+ \exp_args:No \tl_map_function:nN { \tl_to_str:n {#1} }
+ \@@_compile_raw:N
+ }
+ }
+\cs_generate_variant:Nn \@@_compile_abort_tokens:n { e }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Quantifiers}
+%
+% \begin{macro}{\@@_compile_if_quantifier:TFw}
+% This looks ahead and checks whether there are any quantifier
+% (special character equal to either of \texttt{?+*\{}). This is
+% useful for the |\u| and |\ur| escape sequences.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_if_quantifier:TFw #1#2#3#4
+ {
+ \token_if_eq_meaning:NNTF #3 \@@_compile_special:N
+ { \cs_if_exist:cTF { @@_compile_quantifier_#4:w } }
+ { \use_ii:nn }
+ {#1} {#2} #3 #4
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_quantifier:w}
+% This looks ahead and finds any quantifier (special character equal
+% to either of \texttt{?+*\{}).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_quantifier:w #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \@@_compile_special:N
+ {
+ \cs_if_exist_use:cF { @@_compile_quantifier_#2:w }
+ { \@@_compile_quantifier_none: #1 #2 }
+ }
+ { \@@_compile_quantifier_none: #1 #2 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_quantifier_none:}
+% \begin{macro}{\@@_compile_quantifier_abort:eNN}
+% Those functions are called whenever there is no quantifier, or a
+% braced construction is invalid (equivalent to no quantifier, and
+% whatever characters were grabbed are left raw).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_quantifier_none:
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \if_false: { \fi: } { 1 } { 0 } \c_false_bool }
+ }
+\cs_new_protected:Npn \@@_compile_quantifier_abort:eNN #1#2#3
+ {
+ \@@_compile_quantifier_none:
+ \msg_warning:nnee { regex } { invalid-quantifier } {#1} {#3}
+ \@@_compile_abort_tokens:e {#1}
+ #2 #3
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_quantifier_lazyness:nnNN}
+% Once the \enquote{main} quantifier (\texttt{?}, \texttt{*},
+% \texttt{+} or a braced construction) is found, we check whether it
+% is lazy (followed by a question mark). We then add to the compiled
+% regex a closing brace (ending \cs{@@_class:NnnnN} and friends),
+% the start-point of the range, its end-point, and a boolean,
+% \texttt{true} for lazy and \texttt{false} for greedy operators.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_quantifier_lazyness:nnNN #1#2#3#4
+ {
+ \@@_two_if_eq:NNNNTF #3 #4 \@@_compile_special:N ?
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \if_false: { \fi: } { #1 } { #2 } \c_true_bool }
+ }
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \if_false: { \fi: } { #1 } { #2 } \c_false_bool }
+ #3 #4
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_compile_quantifier_?:w,
+% \@@_compile_quantifier_*:w,
+% \@@_compile_quantifier_+:w
+% }
+% For each \enquote{basic} quantifier, |?|, |*|, |+|, feed the correct
+% arguments to \cs{@@_compile_quantifier_lazyness:nnNN}, $-1$ means
+% that there is no upper bound on the number of repetitions.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_compile_quantifier_?:w }
+ { \@@_compile_quantifier_lazyness:nnNN { 0 } { 1 } }
+\cs_new_protected:cpn { @@_compile_quantifier_*:w }
+ { \@@_compile_quantifier_lazyness:nnNN { 0 } { -1 } }
+\cs_new_protected:cpn { @@_compile_quantifier_+:w }
+ { \@@_compile_quantifier_lazyness:nnNN { 1 } { -1 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}+\@@_compile_quantifier_{:w+ ^^A}
+% \begin{macro}
+% {
+% \@@_compile_quantifier_braced_auxi:w,
+% \@@_compile_quantifier_braced_auxii:w,
+% \@@_compile_quantifier_braced_auxiii:w,
+% }
+% Three possible syntaxes: \texttt{\{\meta{int}\}},
+% \texttt{\{\meta{int},\}}, or \texttt{\{\meta{int},\meta{int}\}}. Any
+% other syntax causes us to abort and put whatever we collected back
+% in the input stream, as \texttt{raw} characters, including the
+% opening brace. Grab a number into \cs{l_@@_internal_a_int}. If
+% the number is followed by a right brace, the range is $[a,a]$. If
+% followed by a comma, grab one more number, and call the \texttt{_ii}
+% or \texttt{_iii} auxiliary. Those auxiliaries check for a closing
+% brace, leading to the range $[a,\infty]$ or $[a,b]$, encoded as
+% $\{a\}\{-1\}$ and $\{a\}\{b-a\}$.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_compile_quantifier_ \c_left_brace_str :w }
+ {
+ \@@_get_digits:NTFw \l_@@_internal_a_int
+ { \@@_compile_quantifier_braced_auxi:w }
+ { \@@_compile_quantifier_abort:eNN { \c_left_brace_str } }
+ }
+\cs_new_protected:Npn \@@_compile_quantifier_braced_auxi:w #1#2
+ {
+ \str_case_e:nnF { #1 #2 }
+ {
+ { \@@_compile_special:N \c_right_brace_str }
+ {
+ \exp_args:No \@@_compile_quantifier_lazyness:nnNN
+ { \int_use:N \l_@@_internal_a_int } 0
+ }
+ { \@@_compile_special:N , }
+ {
+ \@@_get_digits:NTFw \l_@@_internal_b_int
+ { \@@_compile_quantifier_braced_auxiii:w }
+ { \@@_compile_quantifier_braced_auxii:w }
+ }
+ }
+ {
+ \@@_compile_quantifier_abort:eNN
+ { \c_left_brace_str \int_use:N \l_@@_internal_a_int }
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \@@_compile_quantifier_braced_auxii:w #1#2
+ {
+ \@@_two_if_eq:NNNNTF #1 #2 \@@_compile_special:N \c_right_brace_str
+ {
+ \exp_args:No \@@_compile_quantifier_lazyness:nnNN
+ { \int_use:N \l_@@_internal_a_int } { -1 }
+ }
+ {
+ \@@_compile_quantifier_abort:eNN
+ { \c_left_brace_str \int_use:N \l_@@_internal_a_int , }
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \@@_compile_quantifier_braced_auxiii:w #1#2
+ {
+ \@@_two_if_eq:NNNNTF #1 #2 \@@_compile_special:N \c_right_brace_str
+ {
+ \if_int_compare:w \l_@@_internal_a_int >
+ \l_@@_internal_b_int
+ \msg_error:nnee { regex } { backwards-quantifier }
+ { \int_use:N \l_@@_internal_a_int }
+ { \int_use:N \l_@@_internal_b_int }
+ \int_zero:N \l_@@_internal_b_int
+ \else:
+ \int_sub:Nn \l_@@_internal_b_int \l_@@_internal_a_int
+ \fi:
+ \exp_args:Noo \@@_compile_quantifier_lazyness:nnNN
+ { \int_use:N \l_@@_internal_a_int }
+ { \int_use:N \l_@@_internal_b_int }
+ }
+ {
+ \@@_compile_quantifier_abort:eNN
+ {
+ \c_left_brace_str
+ \int_use:N \l_@@_internal_a_int ,
+ \int_use:N \l_@@_internal_b_int
+ }
+ #1 #2
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Raw characters}
+%
+% \begin{macro}{\@@_compile_raw_error:N}
+% Within character classes, and following catcode tests, some escaped
+% alphanumeric sequences such as |\b| do not have any meaning. They
+% are replaced by a raw character, after spitting out an error.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_raw_error:N #1
+ {
+ \msg_error:nne { regex } { bad-escape } {#1}
+ \@@_compile_raw:N #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_raw:N}
+% If we are in a character class and the next character is an
+% unescaped dash, this denotes a range. Otherwise, the current
+% character |#1| matches itself.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_raw:N #1#2#3
+ {
+ \@@_if_in_class:TF
+ {
+ \@@_two_if_eq:NNNNTF #2 #3 \@@_compile_special:N -
+ { \@@_compile_range:Nw #1 }
+ {
+ \@@_compile_one:n
+ { \@@_item_equal:n { \int_value:w `#1 } }
+ #2 #3
+ }
+ }
+ {
+ \@@_compile_one:n
+ { \@@_item_equal:n { \int_value:w `#1 } }
+ #2 #3
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_range:Nw, \@@_if_end_range:NNTF}
+% We have just read a raw character followed by a dash; this should be
+% followed by an end-point for the range. Valid end-points are: any
+% raw character; any special character, except a right bracket. In
+% particular, escaped characters are forbidden.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_if_end_range:NNTF #1#2
+ {
+ \if_meaning:w \@@_compile_raw:N #1
+ \else:
+ \if_meaning:w \@@_compile_special:N #1
+ \if_charcode:w ] #2
+ \use_i:nn
+ \fi:
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_iii:nnn
+ \fi:
+ \fi:
+ \use_i:nn
+ }
+\cs_new_protected:Npn \@@_compile_range:Nw #1#2#3
+ {
+ \@@_if_end_range:NNTF #2 #3
+ {
+ \if_int_compare:w `#1 > `#3 \exp_stop_f:
+ \msg_error:nnee { regex } { range-backwards } {#1} {#3}
+ \else:
+ \tl_build_put_right:Ne \l_@@_build_tl
+ {
+ \if_int_compare:w `#1 = `#3 \exp_stop_f:
+ \@@_item_equal:n
+ \else:
+ \@@_item_range:nn { \int_value:w `#1 }
+ \fi:
+ { \int_value:w `#3 }
+ }
+ \fi:
+ }
+ {
+ \msg_warning:nnee { regex } { range-missing-end }
+ {#1} { \c_backslash_str #3 }
+ \tl_build_put_right:Ne \l_@@_build_tl
+ {
+ \@@_item_equal:n { \int_value:w `#1 \exp_stop_f: }
+ \@@_item_equal:n { \int_value:w `- \exp_stop_f: }
+ }
+ #2#3
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Character properties}
+%
+% \begin{macro}{\@@_compile_.:, \@@_prop_.:}
+% In a class, the dot has no special meaning. Outside, insert
+% \cs{@@_prop_.:}, which matches any character or control
+% sequence, and refuses $-2$ (end-marker).
+% \begin{macrocode}
+\cs_new_protected:cpe { @@_compile_.: }
+ {
+ \exp_not:N \@@_if_in_class:TF
+ { \@@_compile_raw:N . }
+ { \@@_compile_one:n \exp_not:c { @@_prop_.: } }
+ }
+\cs_new_protected:cpn { @@_prop_.: }
+ {
+ \if_int_compare:w \l_@@_curr_char_int > - 2 \exp_stop_f:
+ \exp_after:wN \@@_break_true:w
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_compile_/d:, \@@_compile_/D:,
+% \@@_compile_/h:, \@@_compile_/H:,
+% \@@_compile_/s:, \@@_compile_/S:,
+% \@@_compile_/v:, \@@_compile_/V:,
+% \@@_compile_/w:, \@@_compile_/W:,
+% \@@_compile_/N:,
+% }
+% The constants \cs{@@_prop_d:}, \emph{etc.} hold
+% a list of tests which match the corresponding character
+% class, and jump to the \cs{@@_break_point:TF} marker.
+% As for a normal character, we check for quantifiers.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2
+ {
+ \cs_new_protected:cpe { @@_compile_/#1: }
+ { \@@_compile_one:n \exp_not:c { @@_prop_#1: } }
+ \cs_new_protected:cpe { @@_compile_/#2: }
+ {
+ \@@_compile_one:n
+ { \@@_item_reverse:n { \exp_not:c { @@_prop_#1: } } }
+ }
+ }
+\@@_tmp:w d D
+\@@_tmp:w h H
+\@@_tmp:w s S
+\@@_tmp:w v V
+\@@_tmp:w w W
+\cs_new_protected:cpn { @@_compile_/N: }
+ { \@@_compile_one:n \@@_prop_N: }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Anchoring and simple assertions}
+%
+% \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_$:+
+% 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_letter:NNN #1#2#3
+ {
+ \@@_if_in_class_or_catcode:TF { \@@_compile_raw_error:N #1 }
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \@@_assertion:Nn #2 {#3} }
+ }
+ }
+\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: }
+ {
+ \@@_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:Ne \@@_tmp:w { \iow_char:N \^ } { \@@_A_test: }
+\exp_args:Ne \@@_tmp:w { \iow_char:N \$ } { \@@_Z_test: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Character classes}
+%
+% \begin{macro}{\@@_compile_]:}
+% Outside a class, right brackets have no meaning. In a class, change
+% the mode ($m\to (m-15)/13$, truncated) to reflect the fact that we
+% are leaving the class. Look for quantifiers, unless we are still in
+% a class after leaving one (the case of |[...\cL[...]...]|).
+% quantifiers.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_compile_]: }
+ {
+ \@@_if_in_class:TF
+ {
+ \if_int_compare:w \l_@@_mode_int >
+ \c_@@_catcode_in_class_mode_int
+ \tl_build_put_right:Nn \l_@@_build_tl { \if_false: { \fi: } }
+ \fi:
+ \tex_advance:D \l_@@_mode_int - 15 \exp_stop_f:
+ \tex_divide:D \l_@@_mode_int 13 \exp_stop_f:
+ \if_int_odd:w \l_@@_mode_int \else:
+ \exp_after:wN \@@_compile_quantifier:w
+ \fi:
+ }
+ { \@@_compile_raw:N ] }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_[:}
+% In a class, left brackets might introduce a \textsc{posix} character
+% class, or mean nothing. Immediately following |\c|\meta{category},
+% we must insert the appropriate catcode test, then parse the class; we
+% pre-expand the catcode as an optimization. Otherwise (modes $0$,
+% $-2$ and $-6$) just parse the class. The mode is updated later.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_compile_[: }
+ {
+ \@@_if_in_class:TF
+ { \@@_compile_class_posix_test:w }
+ {
+ \@@_if_within_catcode:TF
+ {
+ \exp_after:wN \@@_compile_class_catcode:w
+ \int_use:N \l_@@_catcodes_int ;
+ }
+ { \@@_compile_class_normal:w }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_class_normal:w}
+% In the \enquote{normal} case, we insert \cs{@@_class:NnnnN}
+% \meta{boolean} in the compiled code. The \meta{boolean} is true for
+% positive classes, and false for negative classes, characterized by a
+% leading |^|. The auxiliary \cs{@@_compile_class:TFNN} also
+% checks for a leading |]| which has a special meaning.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_class_normal:w
+ {
+ \@@_compile_class:TFNN
+ { \@@_class:NnnnN \c_true_bool }
+ { \@@_class:NnnnN \c_false_bool }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_class_catcode:w}
+% This function is called for a left bracket in modes $2$ or $6$
+% (catcode test, and catcode test within a class). In mode $2$ the
+% whole construction needs to be put in a class (like single
+% character). Then determine if the class is positive or negative,
+% inserting \cs{@@_item_catcode:nT} or the \texttt{reverse} variant
+% as appropriate, each with the current catcodes bitmap |#1| as an
+% argument, and reset the catcodes.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_class_catcode:w #1;
+ {
+ \if_int_compare:w \l_@@_mode_int = \c_@@_catcode_mode_int
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \@@_class:NnnnN \c_true_bool { \if_false: } \fi: }
+ \fi:
+ \int_set_eq:NN \l_@@_catcodes_int \l_@@_default_catcodes_int
+ \@@_compile_class:TFNN
+ { \@@_item_catcode:nT {#1} }
+ { \@@_item_catcode_reverse:nT {#1} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_compile_class:TFNN, \@@_compile_class:NN}
+% If the first character is |^|, then the class is negative (use
+% |#2|), otherwise it is positive (use |#1|). If the next character
+% is a right bracket, then it should be changed to a raw one.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_class:TFNN #1#2#3#4
+ {
+ \l_@@_mode_int = \int_value:w \l_@@_mode_int 3 \exp_stop_f:
+ \@@_two_if_eq:NNNNTF #3 #4 \@@_compile_special:N ^
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl { #2 { \if_false: } \fi: }
+ \@@_compile_class:NN
+ }
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl { #1 { \if_false: } \fi: }
+ \@@_compile_class:NN #3 #4
+ }
+ }
+\cs_new_protected:Npn \@@_compile_class:NN #1#2
+ {
+ \token_if_eq_charcode:NNTF #2 ]
+ { \@@_compile_raw:N #2 }
+ { #1 #2 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_compile_class_posix_test:w,
+% \@@_compile_class_posix:NNNNw,
+% \@@_compile_class_posix_loop:w,
+% \@@_compile_class_posix_end:w
+% }
+% Here we check for a syntax such as |[:alpha:]|. We also detect |[=|
+% and |[.| which have a meaning in \textsc{posix} regular expressions,
+% but are not implemented in \pkg{l3regex}. In case we see |[:|, grab
+% raw characters until hopefully reaching |:]|. If that's missing, or
+% the \textsc{posix} class is unknown, abort. If all is right, add the
+% test to the current class, with an extra \cs{@@_item_reverse:n}
+% for negative classes (we make sure to wrap its argument in braces
+% otherwise \cs{regex_show:N} would not recognize the regex as valid).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_class_posix_test:w #1#2
+ {
+ \token_if_eq_meaning:NNT \@@_compile_special:N #1
+ {
+ \str_case:nn { #2 }
+ {
+ : { \@@_compile_class_posix:NNNNw }
+ = {
+ \msg_warning:nne { regex }
+ { posix-unsupported } { = }
+ }
+ . {
+ \msg_warning:nne { regex }
+ { posix-unsupported } { . }
+ }
+ }
+ }
+ \@@_compile_raw:N [ #1 #2
+ }
+\cs_new_protected:Npn \@@_compile_class_posix:NNNNw #1#2#3#4#5#6
+ {
+ \@@_two_if_eq:NNNNTF #5 #6 \@@_compile_special:N ^
+ {
+ \bool_set_false:N \l_@@_internal_bool
+ \__kernel_tl_set:Nx \l_@@_internal_a_tl { \if_false: } \fi:
+ \@@_compile_class_posix_loop:w
+ }
+ {
+ \bool_set_true:N \l_@@_internal_bool
+ \__kernel_tl_set:Nx \l_@@_internal_a_tl { \if_false: } \fi:
+ \@@_compile_class_posix_loop:w #5 #6
+ }
+ }
+\cs_new:Npn \@@_compile_class_posix_loop:w #1#2
+ {
+ \token_if_eq_meaning:NNTF \@@_compile_raw:N #1
+ { #2 \@@_compile_class_posix_loop:w }
+ { \if_false: { \fi: } \@@_compile_class_posix_end:w #1 #2 }
+ }
+\cs_new_protected:Npn \@@_compile_class_posix_end:w #1#2#3#4
+ {
+ \@@_two_if_eq:NNNNTF #1 #2 \@@_compile_special:N :
+ { \@@_two_if_eq:NNNNTF #3 #4 \@@_compile_special:N ] }
+ { \use_ii:nn }
+ {
+ \cs_if_exist:cTF { @@_posix_ \l_@@_internal_a_tl : }
+ {
+ \@@_compile_one:n
+ {
+ \bool_if:NTF \l_@@_internal_bool \use:n \@@_item_reverse:n
+ { \exp_not:c { @@_posix_ \l_@@_internal_a_tl : } }
+ }
+ }
+ {
+ \msg_warning:nne { regex } { posix-unknown }
+ { \l_@@_internal_a_tl }
+ \@@_compile_abort_tokens:e
+ {
+ [: \bool_if:NF \l_@@_internal_bool { ^ }
+ \l_@@_internal_a_tl :]
+ }
+ }
+ }
+ {
+ \msg_error:nnee { regex } { posix-missing-close }
+ { [: \l_@@_internal_a_tl } { #2 #4 }
+ \@@_compile_abort_tokens:e { [: \l_@@_internal_a_tl }
+ #1 #2 #3 #4
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Groups and alternations}
+%
+% \begin{macro}{\@@_compile_group_begin:N, \@@_compile_group_end:}
+% The contents of a regex group are turned into compiled code in
+% \cs{l_@@_build_tl}, which ends up with items of the form
+% \cs{@@_branch:n} \Arg{concatenation}. This construction is done
+% using \cs[no-index]{tl_build_\ldots{}} functions within a \TeX{} group, which automatically
+% makes sure that options (case-sensitivity and default catcode) are
+% reset at the end of the group. The argument |#1| is
+% \cs{@@_group:nnnN} or a variant thereof. A small subtlety to
+% support |\cL(abc)| as a shorthand for |(\cLa\cLb\cLc)|: exit any
+% pending catcode test, save the category code at the start of the
+% group as the default catcode for that group, and make sure that the
+% catcode is restored to the default outside the group.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_group_begin:N #1
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl { #1 { \if_false: } \fi: }
+ \@@_mode_quit_c:
+ \group_begin:
+ \tl_build_begin:N \l_@@_build_tl
+ \int_set_eq:NN \l_@@_default_catcodes_int \l_@@_catcodes_int
+ \int_incr:N \l_@@_group_level_int
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \@@_branch:n { \if_false: } \fi: }
+ }
+\cs_new_protected:Npn \@@_compile_group_end:
+ {
+ \if_int_compare:w \l_@@_group_level_int > \c_zero_int
+ \tl_build_put_right:Nn \l_@@_build_tl { \if_false: { \fi: } }
+ \tl_build_end:N \l_@@_build_tl
+ \exp_args:NNNe
+ \group_end:
+ \tl_build_put_right:Nn \l_@@_build_tl { \l_@@_build_tl }
+ \int_set_eq:NN \l_@@_catcodes_int \l_@@_default_catcodes_int
+ \exp_after:wN \@@_compile_quantifier:w
+ \else:
+ \msg_warning:nn { regex } { extra-rparen }
+ \exp_after:wN \@@_compile_raw:N \exp_after:wN )
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_(:}
+% In a class, parentheses are not special. In a catcode test inside a
+% class, a left parenthesis gives an error, to catch |[a\cL(bcd)e]|.
+% Otherwise check for a |?|, denoting special groups, and run the code
+% for the corresponding special group.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_compile_(: }
+ {
+ \@@_if_in_class:TF { \@@_compile_raw:N ( }
+ {
+ \if_int_compare:w \l_@@_mode_int =
+ \c_@@_catcode_in_class_mode_int
+ \msg_error:nn { regex } { c-lparen-in-class }
+ \exp_after:wN \@@_compile_raw:N \exp_after:wN (
+ \else:
+ \exp_after:wN \@@_compile_lparen:w
+ \fi:
+ }
+ }
+\cs_new_protected:Npn \@@_compile_lparen:w #1#2#3#4
+ {
+ \@@_two_if_eq:NNNNTF #1 #2 \@@_compile_special:N ?
+ {
+ \cs_if_exist_use:cF
+ { @@_compile_special_group_\token_to_str:N #4 :w }
+ {
+ \msg_warning:nne { regex } { special-group-unknown }
+ { (? #4 }
+ \@@_compile_group_begin:N \@@_group:nnnN
+ \@@_compile_raw:N ? #3 #4
+ }
+ }
+ {
+ \@@_compile_group_begin:N \@@_group:nnnN
+ #1 #2 #3 #4
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}+\@@_compile_|:+
+% In a class, the pipe is not special. Otherwise, end the current
+% branch and open another one.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_compile_|: }
+ {
+ \@@_if_in_class:TF { \@@_compile_raw:N | }
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \if_false: { \fi: } \@@_branch:n { \if_false: } \fi: }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_):}
+% Within a class, parentheses are not special. Outside, close a group.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_compile_): }
+ {
+ \@@_if_in_class:TF { \@@_compile_raw:N ) }
+ { \@@_compile_group_end: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_special_group_::w}
+% \begin{macro}+\@@_compile_special_group_|:w+
+% Non-capturing, and resetting groups are easy to take care of during
+% compilation; for those groups, the harder parts come when building.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_compile_special_group_::w }
+ { \@@_compile_group_begin:N \@@_group_no_capture:nnnN }
+\cs_new_protected:cpn { @@_compile_special_group_|:w }
+ { \@@_compile_group_begin:N \@@_group_resetting:nnnN }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_compile_special_group_i:w, \@@_compile_special_group_-:w}
+% The match can be made case-insensitive by setting the option with
+% \texttt{(?i)}; the original behaviour is restored by \texttt{(?-i)}.
+% This is the only supported option.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_special_group_i:w #1#2
+ {
+ \@@_two_if_eq:NNNNTF #1 #2 \@@_compile_special:N )
+ {
+ \cs_set:Npn \@@_item_equal:n
+ { \@@_item_caseless_equal:n }
+ \cs_set:Npn \@@_item_range:nn
+ { \@@_item_caseless_range:nn }
+ }
+ {
+ \msg_warning:nne { regex } { unknown-option } { (?i #2 }
+ \@@_compile_raw:N (
+ \@@_compile_raw:N ?
+ \@@_compile_raw:N i
+ #1 #2
+ }
+ }
+\cs_new_protected:cpn { @@_compile_special_group_-:w } #1#2#3#4
+ {
+ \@@_two_if_eq:NNNNTF #1 #2 \@@_compile_raw:N i
+ { \@@_two_if_eq:NNNNTF #3 #4 \@@_compile_special:N ) }
+ { \use_ii:nn }
+ {
+ \cs_set:Npn \@@_item_equal:n
+ { \@@_item_caseful_equal:n }
+ \cs_set:Npn \@@_item_range:nn
+ { \@@_item_caseful_range:nn }
+ }
+ {
+ \msg_warning:nne { regex } { unknown-option } { (?-#2#4 }
+ \@@_compile_raw:N (
+ \@@_compile_raw:N ?
+ \@@_compile_raw:N -
+ #1 #2 #3 #4
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Catcodes and csnames}
+%
+% \begin{macro}{\@@_compile_/c:, \@@_compile_c_test:NN}
+% The |\c| escape sequence can be followed by a capital letter
+% representing a character category, by a left bracket which starts a
+% list of categories, or by a brace group holding a regular expression
+% for a control sequence name. Otherwise, raise an error.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_compile_/c: }
+ { \@@_chk_c_allowed:T { \@@_compile_c_test:NN } }
+\cs_new_protected:Npn \@@_compile_c_test:NN #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \@@_compile_raw:N
+ {
+ \int_if_exist:cTF { c_@@_catcode_#2_int }
+ {
+ \int_set_eq:Nc \l_@@_catcodes_int
+ { c_@@_catcode_#2_int }
+ \l_@@_mode_int
+ = \if_case:w \l_@@_mode_int
+ \c_@@_catcode_mode_int
+ \else:
+ \c_@@_catcode_in_class_mode_int
+ \fi:
+ \token_if_eq_charcode:NNT C #2 { \@@_compile_c_C:NN }
+ }
+ }
+ { \cs_if_exist_use:cF { @@_compile_c_#2:w } }
+ {
+ \msg_error:nne { regex } { c-missing-category } {#2}
+ #1 #2
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_c_C:NN}
+% If |\cC| is not followed by |.| or |(...)| then complain because
+% that construction cannot match anything, except in cases like
+% |\cC[\c{...}]|, where it has no effect.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_c_C:NN #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \@@_compile_special:N
+ {
+ \token_if_eq_charcode:NNTF #2 .
+ { \use_none:n }
+ { \token_if_eq_charcode:NNF #2 ( } % )
+ }
+ { \use:n }
+ { \msg_error:nnn { regex } { c-C-invalid } {#2} }
+ #1 #2
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_compile_c_[:w,
+% \@@_compile_c_lbrack_loop:NN,
+% \@@_compile_c_lbrack_add:N,
+% \@@_compile_c_lbrack_end:,
+% }
+% When encountering |\c[|, the task is to collect uppercase letters
+% representing character categories. First check for |^| which negates
+% the list of category codes.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_compile_c_[:w } #1#2
+ {
+ \l_@@_mode_int
+ = \if_case:w \l_@@_mode_int
+ \c_@@_catcode_mode_int
+ \else:
+ \c_@@_catcode_in_class_mode_int
+ \fi:
+ \int_zero:N \l_@@_catcodes_int
+ \@@_two_if_eq:NNNNTF #1 #2 \@@_compile_special:N ^
+ {
+ \bool_set_false:N \l_@@_catcodes_bool
+ \@@_compile_c_lbrack_loop:NN
+ }
+ {
+ \bool_set_true:N \l_@@_catcodes_bool
+ \@@_compile_c_lbrack_loop:NN
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \@@_compile_c_lbrack_loop:NN #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \@@_compile_raw:N
+ {
+ \int_if_exist:cTF { c_@@_catcode_#2_int }
+ {
+ \exp_args:Nc \@@_compile_c_lbrack_add:N
+ { c_@@_catcode_#2_int }
+ \@@_compile_c_lbrack_loop:NN
+ }
+ }
+ {
+ \token_if_eq_charcode:NNTF #2 ]
+ { \@@_compile_c_lbrack_end: }
+ }
+ {
+ \msg_error:nne { regex } { c-missing-rbrack } {#2}
+ \@@_compile_c_lbrack_end:
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \@@_compile_c_lbrack_add:N #1
+ {
+ \if_int_odd:w \@@_int_eval:w \l_@@_catcodes_int / #1 \scan_stop:
+ \else:
+ \int_add:Nn \l_@@_catcodes_int {#1}
+ \fi:
+ }
+\cs_new_protected:Npn \@@_compile_c_lbrack_end:
+ {
+ \if_meaning:w \c_false_bool \l_@@_catcodes_bool
+ \int_set:Nn \l_@@_catcodes_int
+ { \c_@@_all_catcodes_int - \l_@@_catcodes_int }
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}+\@@_compile_c_{:+
+% The case of a left brace is easy, based on what we have done so far:
+% in a group, compile the regular expression, after changing the mode
+% to forbid nesting |\c|. Additionally, disable submatch tracking
+% since groups don't escape the scope of |\c{...}|.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_compile_c_ \c_left_brace_str :w }
+ {
+ \@@_compile:w
+ \@@_disable_submatches:
+ \l_@@_mode_int
+ = \if_case:w \l_@@_mode_int
+ \c_@@_cs_mode_int
+ \else:
+ \c_@@_cs_in_class_mode_int
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}+\@@_compile_{:+
+% We forbid unescaped left braces inside a |\c{...}| escape because
+% they otherwise lead to the confusing question of whether the first
+% right brace in |\c{{}x}| should end |\c| or whether one should
+% match braces.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_compile_ \c_left_brace_str : }
+ {
+ \@@_if_in_cs:TF
+ { \msg_error:nnn { regex } { cu-lbrace } { c } }
+ { \exp_after:wN \@@_compile_raw:N \c_left_brace_str }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_cs_flag}
+% \begin{macro}+\@@_compile_}:+
+% \begin{macro}{\@@_compile_end_cs:}
+% \begin{macro}[EXP]{\@@_compile_cs_aux:Nn, \@@_compile_cs_aux:NNnnnN}
+% Non-escaped right braces are only special if they appear when
+% compiling the regular expression for a csname, but not within a
+% class: |\c{[{}]}| matches the control sequences |\{| and |\}|. So,
+% end compiling the inner regex (this closes any dangling class or
+% group). Then insert the corresponding test in the outer regex. As
+% an optimization, if the control sequence test simply consists of
+% several explicit possibilities (branches) then use
+% \cs{@@_item_exact_cs:n} with an argument consisting of all
+% possibilities separated by \cs{scan_stop:}.
+% \begin{macrocode}
+\flag_new:N \l_@@_cs_flag
+\cs_new_protected:cpn { @@_compile_ \c_right_brace_str : }
+ {
+ \@@_if_in_cs:TF
+ { \@@_compile_end_cs: }
+ { \exp_after:wN \@@_compile_raw:N \c_right_brace_str }
+ }
+\cs_new_protected:Npn \@@_compile_end_cs:
+ {
+ \@@_compile_end:
+ \flag_clear:N \l_@@_cs_flag
+ \__kernel_tl_set:Nx \l_@@_internal_a_tl
+ {
+ \exp_after:wN \@@_compile_cs_aux:Nn \l_@@_internal_regex
+ \q_@@_nil \q_@@_nil \q_@@_recursion_stop
+ }
+ \exp_args:Ne \@@_compile_one:n
+ {
+ \flag_if_raised:NTF \l_@@_cs_flag
+ { \@@_item_cs:n { \exp_not:o \l_@@_internal_regex } }
+ {
+ \@@_item_exact_cs:n
+ { \tl_tail:N \l_@@_internal_a_tl }
+ }
+ }
+ }
+\cs_new:Npn \@@_compile_cs_aux:Nn #1#2
+ {
+ \cs_if_eq:NNTF #1 \@@_branch:n
+ {
+ \scan_stop:
+ \@@_compile_cs_aux:NNnnnN #2
+ \q_@@_nil \q_@@_nil \q_@@_nil
+ \q_@@_nil \q_@@_nil \q_@@_nil \q_@@_recursion_stop
+ \@@_compile_cs_aux:Nn
+ }
+ {
+ \@@_quark_if_nil:NF #1 { \flag_ensure_raised:N \l_@@_cs_flag }
+ \@@_use_none_delimit_by_q_recursion_stop:w
+ }
+ }
+\cs_new:Npn \@@_compile_cs_aux:NNnnnN #1#2#3#4#5#6
+ {
+ \bool_lazy_all:nTF
+ {
+ { \cs_if_eq_p:NN #1 \@@_class:NnnnN }
+ {#2}
+ { \tl_if_head_eq_meaning_p:nN {#3} \@@_item_caseful_equal:n }
+ { \int_compare_p:nNn { \tl_count:n {#3} } = { 2 } }
+ { \int_compare_p:nNn {#5} = \c_zero_int }
+ }
+ {
+ \prg_replicate:nn {#4}
+ { \char_generate:nn { \use_ii:nn #3 } {12} }
+ \@@_compile_cs_aux:NNnnnN
+ }
+ {
+ \@@_quark_if_nil:NF #1
+ {
+ \flag_ensure_raised:N \l_@@_cs_flag
+ \@@_use_i_delimit_by_q_recursion_stop:nw
+ }
+ \@@_use_none_delimit_by_q_recursion_stop:w
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{variable}
+%
+% \subsubsection{Raw token lists with \cs[no-index]{u}}
+%
+% \begin{macro}{\@@_compile_/u:}
+% The |\u| escape is invalid in classes and directly following a
+% catcode test. Otherwise test for a following |r| (for |\ur|), and
+% call an auxiliary responsible for finding the variable name.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_compile_/u: } #1#2
+ {
+ \@@_if_in_class_or_catcode:TF
+ { \@@_compile_raw_error:N u #1 #2 }
+ {
+ \@@_two_if_eq:NNNNTF #1 #2 \@@_compile_raw:N r
+ { \@@_compile_u_brace:NNN \@@_compile_ur_end: }
+ { \@@_compile_u_brace:NNN \@@_compile_u_end: #1 #2 }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_u_brace:NNN}
+% This enforces the presence of a left brace, then starts a loop to
+% find the variable name.
+% \begin{macrocode}
+\cs_new:Npn \@@_compile_u_brace:NNN #1#2#3
+ {
+ \@@_two_if_eq:NNNNTF #2 #3 \@@_compile_special:N \c_left_brace_str
+ {
+ \tl_set:Nn \l_@@_internal_b_tl {#1}
+ \__kernel_tl_set:Nx \l_@@_internal_a_tl { \if_false: } \fi:
+ \@@_compile_u_loop:NN
+ }
+ {
+ \msg_error:nn { regex } { u-missing-lbrace }
+ \token_if_eq_meaning:NNTF #1 \@@_compile_ur_end:
+ { \@@_compile_raw:N u \@@_compile_raw:N r }
+ { \@@_compile_raw:N u }
+ #2 #3
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_compile_u_loop:NN}
+% We collect the characters for the argument of |\u| within an
+% \texttt{e}-expanding assignment. In principle we could just wait to
+% encounter a right brace, but this is unsafe: if the right brace was
+% missing, then we would reach the end-markers of the regex, and
+% continue, leading to obscure fatal errors. Instead, we only allow
+% raw and special characters, and stop when encountering a special
+% right brace, any escaped character, or the end-marker.
+% \begin{macrocode}
+\cs_new:Npn \@@_compile_u_loop:NN #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \@@_compile_raw:N
+ { #2 \@@_compile_u_loop:NN }
+ {
+ \token_if_eq_meaning:NNTF #1 \@@_compile_special:N
+ {
+ \exp_after:wN \token_if_eq_charcode:NNTF \c_right_brace_str #2
+ { \if_false: { \fi: } \l_@@_internal_b_tl }
+ {
+ \if_charcode:w \c_left_brace_str #2
+ \msg_expandable_error:nnn { regex } { cu-lbrace } { u }
+ \else:
+ #2
+ \fi:
+ \@@_compile_u_loop:NN
+ }
+ }
+ {
+ \if_false: { \fi: }
+ \msg_error:nne { regex } { u-missing-rbrace } {#2}
+ \l_@@_internal_b_tl
+ #1 #2
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_ur_end:, \@@_compile_ur:n}
+% \begin{macro}[EXP]{\@@_compile_ur_aux:w}
+% For the |\ur{...}| construction, once we have extracted the
+% variable's name, we replace all groups by non-capturing groups in
+% the compiled regex (passed as the
+% argument of \cs{@@_compile_ur:n}). If that has a single branch
+% (namely \cs{tl_if_empty:oTF} is false) and there is no quantifier,
+% then simply insert the contents of this branch (obtained by
+% \cs{use_ii:nn}, which is expanded later). In all other cases,
+% insert a non-capturing group and look for quantifiers to determine
+% the number of repetition etc.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_ur_end:
+ {
+ \group_begin:
+ \cs_set:Npn \@@_group:nnnN { \@@_group_no_capture:nnnN }
+ \cs_set:Npn \@@_group_resetting:nnnN { \@@_group_no_capture:nnnN }
+ \exp_args:NNe
+ \group_end:
+ \@@_compile_ur:n { \use:c { \l_@@_internal_a_tl } }
+ }
+\cs_new_protected:Npn \@@_compile_ur:n #1
+ {
+ \tl_if_empty:oTF { \@@_compile_ur_aux:w #1 {} ? ? \q_@@_nil }
+ { \@@_compile_if_quantifier:TFw }
+ { \use_i:nn }
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \@@_group_no_capture:nnnN { \if_false: } \fi: #1 }
+ \@@_compile_quantifier:w
+ }
+ { \tl_build_put_right:Nn \l_@@_build_tl { \use_ii:nn #1 } }
+ }
+\cs_new:Npn \@@_compile_ur_aux:w \@@_branch:n #1#2#3 \q_@@_nil {#2}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_u_end:, \@@_compile_u_payload:}
+% Once we have extracted the variable's name, we check for
+% quantifiers, in which case we set up a non-capturing group with a
+% single branch. Inside this branch (we omit it and the group if
+% there is no quantifier), \cs{@@_compile_u_payload:} puts
+% the right tests corresponding to the contents of the variable, which
+% we store in \cs{l_@@_internal_a_tl}. The behaviour of |\u|
+% then depends on whether we are within a |\c{...}| escape (in this
+% case, the variable is turned to a string), or not.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_u_end:
+ {
+ \@@_compile_if_quantifier:TFw
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl
+ {
+ \@@_group_no_capture:nnnN { \if_false: } \fi:
+ \@@_branch:n { \if_false: } \fi:
+ }
+ \@@_compile_u_payload:
+ \tl_build_put_right:Nn \l_@@_build_tl { \if_false: { \fi: } }
+ \@@_compile_quantifier:w
+ }
+ { \@@_compile_u_payload: }
+ }
+\cs_new_protected:Npn \@@_compile_u_payload:
+ {
+ \tl_set:Nv \l_@@_internal_a_tl { \l_@@_internal_a_tl }
+ \if_int_compare:w \l_@@_mode_int = \c_@@_outer_mode_int
+ \@@_compile_u_not_cs:
+ \else:
+ \@@_compile_u_in_cs:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_u_in_cs:}
+% When |\u| appears within a control sequence, we convert the variable
+% to a string with escaped spaces. Then for each character insert a
+% class matching exactly that character, once.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_u_in_cs:
+ {
+ \__kernel_tl_gset:Nx \g_@@_internal_tl
+ {
+ \exp_args:No \__kernel_str_to_other_fast:n
+ { \l_@@_internal_a_tl }
+ }
+ \tl_build_put_right:Ne \l_@@_build_tl
+ {
+ \tl_map_function:NN \g_@@_internal_tl
+ \@@_compile_u_in_cs_aux:n
+ }
+ }
+\cs_new:Npn \@@_compile_u_in_cs_aux:n #1
+ {
+ \@@_class:NnnnN \c_true_bool
+ { \@@_item_caseful_equal:n { \int_value:w `#1 } }
+ { 1 } { 0 } \c_false_bool
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_u_not_cs:}
+% In mode $0$, the |\u| escape adds one state to the NFA for each
+% token in \cs{l_@@_internal_a_tl}. If a given \meta{token} is a
+% control sequence, then insert a string comparison test, otherwise,
+% \cs{@@_item_exact:nn} which compares catcode and character code.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_u_not_cs:
+ {
+ \tl_analysis_map_inline:Nn \l_@@_internal_a_tl
+ {
+ \tl_build_put_right:Ne \l_@@_build_tl
+ {
+ \@@_class:NnnnN \c_true_bool
+ {
+ \if_int_compare:w "##3 = \c_zero_int
+ \@@_item_exact_cs:n
+ { \exp_after:wN \cs_to_str:N ##1 }
+ \else:
+ \@@_item_exact:nn { \int_value:w "##3 } { ##2 }
+ \fi:
+ }
+ { 1 } { 0 } \c_false_bool
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Other}
+%
+% \begin{macro}{\@@_compile_/K:}
+% The |\K| control sequence is currently the only \enquote{command},
+% which performs some action, rather than matching something. It is
+% allowed in the same contexts as |\b|. At the compilation stage, we
+% leave it as a single control sequence, defined later.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_compile_/K: }
+ {
+ \int_compare:nNnTF \l_@@_mode_int = \c_@@_outer_mode_int
+ { \tl_build_put_right:Nn \l_@@_build_tl { \@@_command_K: } }
+ { \@@_compile_raw_error:N K }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Showing regexes}
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_clean_bool:n, \@@_clean_int:n, \@@_clean_int_aux:N,
+% \@@_clean_regex:n, \@@_clean_regex_loop:w, \@@_clean_branch:n,
+% \@@_clean_branch_loop:n, \@@_clean_assertion:Nn,
+% \@@_clean_class:NnnnN, \@@_clean_group:nnnN, \@@_clean_class:n,
+% \@@_clean_class_loop:nnn, \@@_clean_exact_cs:n,
+% \@@_clean_exact_cs:w
+% }
+% Before showing a regex we check that it is \enquote{clean} in the
+% sense that it has the correct internal structure. We do this (in
+% the implementation of \cs{regex_show:N} and \cs{regex_log:N}) by
+% comparing it with a cleaned-up version of the same regex. Along the
+% way we also need similar functions for other types: all
+% \cs[no-index]{@@_clean_\meta{type}:n} functions produce valid
+% \meta{type} tokens (bool, explicit integer, etc.\@) from arbitrary
+% input, and the output coincides with the input if that was valid.
+% \begin{macrocode}
+\cs_new:Npn \@@_clean_bool:n #1
+ {
+ \tl_if_single:nTF {#1}
+ { \bool_if:NTF #1 \c_true_bool \c_false_bool }
+ { \c_true_bool }
+ }
+\cs_new:Npn \@@_clean_int:n #1
+ {
+ \tl_if_head_eq_meaning:nNTF {#1} -
+ { - \exp_args:No \@@_clean_int:n { \use_none:n #1 } }
+ { \int_eval:n { 0 \str_map_function:nN {#1} \@@_clean_int_aux:N } }
+ }
+\cs_new:Npn \@@_clean_int_aux:N #1
+ {
+ \if_int_compare:w \c_one_int < 1 #1 ~
+ #1
+ \else:
+ \str_map_break:n
+ \fi:
+ }
+\cs_new:Npn \@@_clean_regex:n #1
+ {
+ \@@_clean_regex_loop:w #1
+ \@@_branch:n { \q_recursion_tail } \q_recursion_stop
+ }
+\cs_new:Npn \@@_clean_regex_loop:w #1 \@@_branch:n #2
+ {
+ \quark_if_recursion_tail_stop:n {#2}
+ \@@_branch:n { \@@_clean_branch:n {#2} }
+ \@@_clean_regex_loop:w
+ }
+\cs_new:Npn \@@_clean_branch:n #1
+ {
+ \@@_clean_branch_loop:n #1
+ ? ? ? ? ? ? \prg_break_point:
+ }
+\cs_new:Npn \@@_clean_branch_loop:n #1
+ {
+ \tl_if_single:nF {#1} \prg_break:
+ \token_case_meaning:NnF #1
+ {
+ \@@_command_K: { #1 \@@_clean_branch_loop:n }
+ \@@_assertion:Nn { #1 \@@_clean_assertion:Nn }
+ \@@_class:NnnnN { #1 \@@_clean_class:NnnnN }
+ \@@_group:nnnN { #1 \@@_clean_group:nnnN }
+ \@@_group_no_capture:nnnN { #1 \@@_clean_group:nnnN }
+ \@@_group_resetting:nnnN { #1 \@@_clean_group:nnnN }
+ }
+ \prg_break:
+ }
+\cs_new:Npn \@@_clean_assertion:Nn #1#2
+ {
+ \@@_clean_bool:n {#1}
+ \tl_if_single:nF {#2} { { \@@_A_test: } \prg_break: }
+ \token_case_meaning:NnTF #2
+ {
+ \@@_A_test: { }
+ \@@_G_test: { }
+ \@@_Z_test: { }
+ \@@_b_test: { }
+ }
+ { {#2} }
+ { { \@@_A_test: } \prg_break: }
+ \@@_clean_branch_loop:n
+ }
+\cs_new:Npn \@@_clean_class:NnnnN #1#2#3#4#5
+ {
+ \@@_clean_bool:n {#1}
+ { \@@_clean_class:n {#2} }
+ { \int_max:nn \c_zero_int { \@@_clean_int:n {#3} } }
+ { \int_max:nn { -\c_one_int } { \@@_clean_int:n {#4} } }
+ \@@_clean_bool:n {#5}
+ \@@_clean_branch_loop:n
+ }
+\cs_new:Npn \@@_clean_group:nnnN #1#2#3#4
+ {
+ { \@@_clean_regex:n {#1} }
+ { \int_max:nn \c_zero_int { \@@_clean_int:n {#2} } }
+ { \int_max:nn { -\c_one_int } { \@@_clean_int:n {#3} } }
+ \@@_clean_bool:n {#4}
+ \@@_clean_branch_loop:n
+ }
+\cs_new:Npn \@@_clean_class:n #1
+ { \@@_clean_class_loop:nnn #1 ????? \prg_break_point: }
+% \end{macrocode}
+% When cleaning a class there are many cases, including a dozen or so
+% like \cs{@@_prop_d:} or \cs{@@_posix_alpha:}. To avoid listing all of
+% them we allow any command that starts with the
+% $13$ characters |__regex_prop_| or |__regex_posix| (handily these have
+% the same length, except for the trailing underscore).
+% \begin{macrocode}
+\cs_new:Npn \@@_clean_class_loop:nnn #1#2#3
+ {
+ \tl_if_single:nF {#1} \prg_break:
+ \token_case_meaning:NnTF #1
+ {
+ \@@_item_cs:n { #1 { \@@_clean_regex:n {#2} } }
+ \@@_item_exact_cs:n { #1 { \@@_clean_exact_cs:n {#2} } }
+ \@@_item_caseful_equal:n { #1 { \@@_clean_int:n {#2} } }
+ \@@_item_caseless_equal:n { #1 { \@@_clean_int:n {#2} } }
+ \@@_item_reverse:n { #1 { \@@_clean_class:n {#2} } }
+ }
+ { \@@_clean_class_loop:nnn {#3} }
+ {
+ \token_case_meaning:NnTF #1
+ {
+ \@@_item_caseful_range:nn { }
+ \@@_item_caseless_range:nn { }
+ \@@_item_exact:nn { }
+ }
+ {
+ #1 { \@@_clean_int:n {#2} } { \@@_clean_int:n {#3} }
+ \@@_clean_class_loop:nnn
+ }
+ {
+ \token_case_meaning:NnTF #1
+ {
+ \@@_item_catcode:nT { }
+ \@@_item_catcode_reverse:nT { }
+ }
+ {
+ #1 { \@@_clean_int:n {#2} } { \@@_clean_class:n {#3} }
+ \@@_clean_class_loop:nnn
+ }
+ {
+ \exp_args:Ne \str_case:nnTF
+ {
+ \exp_args:Ne \str_range:nnn
+ { \cs_to_str:N #1 } \c_one_int { 13 }
+ }
+ {
+ { @@_prop_ } { }
+ { @@_posix } { }
+ }
+ {
+ #1
+ \@@_clean_class_loop:nnn {#2} {#3}
+ }
+ \prg_break:
+ }
+ }
+ }
+ }
+\cs_new:Npn \@@_clean_exact_cs:n #1
+ {
+ \exp_last_unbraced:Nf \use_none:n
+ {
+ \@@_clean_exact_cs:w #1
+ \scan_stop: \q_recursion_tail \scan_stop:
+ \q_recursion_stop
+ }
+ }
+\cs_new:Npn \@@_clean_exact_cs:w #1 \scan_stop:
+ {
+ \quark_if_recursion_tail_stop:n {#1}
+ \scan_stop: \tl_to_str:n {#1}
+ \@@_clean_exact_cs:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_show:N}
+% Within a group and within \cs{tl_build_begin:N} \ldots{} \cs{tl_build_end:N} we
+% redefine all the function that can appear in a compiled regex, then
+% run the regex. The result stored in \cs{l_@@_internal_a_tl} is then
+% meant to be shown.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_show:N #1
+ {
+ \group_begin:
+ \tl_build_begin:N \l_@@_build_tl
+ \cs_set_protected:Npn \@@_branch:n
+ {
+ \seq_pop_right:NN \l_@@_show_prefix_seq
+ \l_@@_internal_a_tl
+ \@@_show_one:n { +-branch }
+ \seq_put_right:No \l_@@_show_prefix_seq
+ \l_@@_internal_a_tl
+ \use:n
+ }
+ \cs_set_protected:Npn \@@_group:nnnN
+ { \@@_show_group_aux:nnnnN { } }
+ \cs_set_protected:Npn \@@_group_no_capture:nnnN
+ { \@@_show_group_aux:nnnnN { ~(no~capture) } }
+ \cs_set_protected:Npn \@@_group_resetting:nnnN
+ { \@@_show_group_aux:nnnnN { ~(resetting) } }
+ \cs_set_eq:NN \@@_class:NnnnN \@@_show_class:NnnnN
+ \cs_set_protected:Npn \@@_command_K:
+ { \@@_show_one:n { reset~match~start~(\iow_char:N\\K) } }
+ \cs_set_protected:Npn \@@_assertion:Nn ##1##2
+ {
+ \@@_show_one:n
+ { \bool_if:NF ##1 { negative~ } assertion:~##2 }
+ }
+ \cs_set:Npn \@@_b_test: { word~boundary }
+ \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~\@@_show_char:n{##1} } }
+ \cs_set_protected:Npn \@@_item_caseful_range:nn ##1##2
+ {
+ \@@_show_one:n
+ { range~[\@@_show_char:n{##1}, \@@_show_char:n{##2}] }
+ }
+ \cs_set_protected:Npn \@@_item_caseless_equal:n ##1
+ { \@@_show_one:n { char~code~\@@_show_char:n{##1}~(caseless) } }
+ \cs_set_protected:Npn \@@_item_caseless_range:nn ##1##2
+ {
+ \@@_show_one:n
+ { Range~[\@@_show_char:n{##1}, \@@_show_char:n{##2}]~(caseless) }
+ }
+ \cs_set_protected:Npn \@@_item_catcode:nT
+ { \@@_show_item_catcode:NnT \c_true_bool }
+ \cs_set_protected:Npn \@@_item_catcode_reverse:nT
+ { \@@_show_item_catcode:NnT \c_false_bool }
+ \cs_set_protected:Npn \@@_item_reverse:n
+ { \@@_show_scope:nn { Reversed~match } }
+ \cs_set_protected:Npn \@@_item_exact:nn ##1##2
+ { \@@_show_one:n { char~\@@_show_char:n{##2},~catcode~##1 } }
+ \cs_set_eq:NN \@@_item_exact_cs:n \@@_show_item_exact_cs:n
+ \cs_set_protected:Npn \@@_item_cs:n
+ { \@@_show_scope:nn { control~sequence } }
+ \cs_set:cpn { @@_prop_.: } { \@@_show_one:n { any~token } }
+ \seq_clear:N \l_@@_show_prefix_seq
+ \@@_show_push:n { ~ }
+ \cs_if_exist_use:N #1
+ \tl_build_end:N \l_@@_build_tl
+ \exp_args:NNNo
+ \group_end:
+ \tl_set:Nn \l_@@_internal_a_tl { \l_@@_build_tl }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_show_char:n}
+% Show a single character, together with its ascii representation if available.
+% This could be extended to beyond ascii. It is not ideal for parentheses themselves.
+% \begin{macrocode}
+\cs_new:Npn \@@_show_char:n #1
+ {
+ \int_eval:n {#1}
+ \int_compare:nT { 32 <= #1 <= 126 }
+ { ~ ( \char_generate:nn {#1} {12} ) }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_show_one:n}
+% Every part of the final message go through this function, which adds
+% one line to the output, with the appropriate prefix.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_show_one:n #1
+ {
+ \int_incr:N \l_@@_show_lines_int
+ \tl_build_put_right:Ne \l_@@_build_tl
+ {
+ \exp_not:N \iow_newline:
+ \seq_map_function:NN \l_@@_show_prefix_seq \use:n
+ #1
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_show_push:n, \@@_show_pop:, \@@_show_scope:nn}
+% Enter and exit levels of nesting. The \texttt{scope} function prints
+% its first argument as an \enquote{introduction}, then performs its
+% second argument in a deeper level of nesting.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_show_push:n #1
+ { \seq_put_right:Ne \l_@@_show_prefix_seq { #1 ~ } }
+\cs_new_protected:Npn \@@_show_pop:
+ { \seq_pop_right:NN \l_@@_show_prefix_seq \l_@@_internal_a_tl }
+\cs_new_protected:Npn \@@_show_scope:nn #1#2
+ {
+ \@@_show_one:n {#1}
+ \@@_show_push:n { ~ }
+ #2
+ \@@_show_pop:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_show_group_aux:nnnnN}
+% We display all groups in the same way, simply adding a message,
+% \texttt{(no capture)} or \texttt{(resetting)}, to special groups.
+% The odd \cs{use_ii:nn} avoids printing a spurious \texttt{+-branch}
+% for the first branch.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_show_group_aux:nnnnN #1#2#3#4#5
+ {
+ \@@_show_one:n { ,-group~begin #1 }
+ \@@_show_push:n { | }
+ \use_ii:nn #2
+ \@@_show_pop:
+ \@@_show_one:n
+ { `-group~end \@@_msg_repeated:nnN {#3} {#4} #5 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_show_class:NnnnN}
+% I'm entirely unhappy about this function: I couldn't find a way to
+% test if a class is a single test. Instead, collect the
+% representation of the tests in the class. If that had more than one
+% line, write \texttt{Match} or \texttt{Don't match} on its own line,
+% with the repeating information if any. Then the various tests on
+% lines of their own, and finally a line. Otherwise, we need to
+% evaluate the representation of the tests again (since the prefix is
+% incorrect). That's clunky, but not too expensive, since it's only
+% one test.
+% \begin{macrocode}
+\cs_new:Npn \@@_show_class:NnnnN #1#2#3#4#5
+ {
+ \group_begin:
+ \tl_build_begin:N \l_@@_build_tl
+ \int_zero:N \l_@@_show_lines_int
+ \@@_show_push:n {~}
+ #2
+ \int_compare:nTF { \l_@@_show_lines_int = \c_zero_int }
+ {
+ \group_end:
+ \@@_show_one:n { \bool_if:NTF #1 { Fail } { Pass } }
+ }
+ {
+ \bool_if:nTF
+ { #1 && \int_compare_p:n { \l_@@_show_lines_int = \c_one_int } }
+ {
+ \group_end:
+ #2
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \@@_msg_repeated:nnN {#3} {#4} #5 }
+ }
+ {
+ \tl_build_end:N \l_@@_build_tl
+ \exp_args:NNNo
+ \group_end:
+ \tl_set:Nn \l_@@_internal_a_tl \l_@@_build_tl
+ \@@_show_one:n
+ {
+ \bool_if:NTF #1 { Match } { Don't~match }
+ \@@_msg_repeated:nnN {#3} {#4} #5
+ }
+ \tl_build_put_right:Ne \l_@@_build_tl
+ { \exp_not:o \l_@@_internal_a_tl }
+ }
+ }
+ }
+% \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
+% constraint applies.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_show_item_catcode:NnT #1#2
+ {
+ \seq_set_split:Nnn \l_@@_internal_seq { } { CBEMTPUDSLOA }
+ \seq_set_filter:NNn \l_@@_internal_seq \l_@@_internal_seq
+ { \int_if_odd_p:n { #2 / \int_use:c { c_@@_catcode_##1_int } } }
+ \@@_show_scope:nn
+ {
+ categories~
+ \seq_map_function:NN \l_@@_internal_seq \use:n
+ , ~
+ \bool_if:NF #1 { negative~ } class
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_show_item_exact_cs:n}
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_show_item_exact_cs:n #1
+ {
+ \seq_set_split:Nnn \l_@@_internal_seq { \scan_stop: } {#1}
+ \seq_set_map_e:NNn \l_@@_internal_seq
+ \l_@@_internal_seq { \iow_char:N\\##1 }
+ \@@_show_one:n
+ { control~sequence~ \seq_use:Nn \l_@@_internal_seq { ~or~ } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Building}
+%
+% \subsubsection{Variables used while building}
+%
+% \begin{variable}{\l_@@_min_state_int, \l_@@_max_state_int}
+% The last state that was allocated is
+% $\cs{l_@@_max_state_int}-1$, so that \cs{l_@@_max_state_int} always
+% points to a free state. The \texttt{min_state} variable is
+% $1$ to begin with, but gets shifted in nested calls to the matching
+% code, namely in |\c{...}| constructions.
+% \begin{macrocode}
+\int_new:N \l_@@_min_state_int
+\int_set:Nn \l_@@_min_state_int { 1 }
+\int_new:N \l_@@_max_state_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_left_state_int, \l_@@_right_state_int}
+% \begin{variable}{\l_@@_left_state_seq, \l_@@_right_state_seq}
+% Alternatives are implemented by branching from a \texttt{left} state
+% into the various choices, then merging those into a \texttt{right}
+% state. We store information about those states in two sequences.
+% Those states are also used to implement group quantifiers. Most
+% often, the left and right pointers only differ by~$1$.
+% \begin{macrocode}
+\int_new:N \l_@@_left_state_int
+\int_new:N \l_@@_right_state_int
+\seq_new:N \l_@@_left_state_seq
+\seq_new:N \l_@@_right_state_seq
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_capturing_group_int}
+% \cs{l_@@_capturing_group_int} is the next \textsc{id} number to
+% be assigned to a capturing group. This starts
+% at $0$ for the group enclosing the full regular expression, and
+% groups are counted in the order of their left parenthesis, except
+% when encountering \texttt{resetting} groups.
+% \begin{macrocode}
+\int_new:N \l_@@_capturing_group_int
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Framework}
+%
+% This phase is about going from a compiled regex to an \textsc{nfa}.
+% 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: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
+% current \meta{state} to $\meta{state}+\meta{shift}$, which
+% consumes the current character: the target state is saved and will
+% be considered again when matching at the next position.
+% \item \cs{@@_action_free:n} \Arg{shift}, and
+% \cs{@@_action_free_group:n} \Arg{shift} are free transitions,
+% which immediately perform the actions for the state
+% $\meta{state}+\meta{shift}$ of the \textsc{nfa}. They differ in
+% 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: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.
+% \begin{itemize}
+% \item The current capturing group is
+% $\text{\texttt{capturing_group}}-1$, and if a group opened now
+% it would be labelled \texttt{capturing_group}.
+% \item The last allocated state is $\text{\texttt{max_state}}-1$, so
+% \texttt{max_state} is a free state.
+% \item The \texttt{left_state} points to a state to the left of the
+% current group or of the last class.
+% \item The \texttt{right_state} points to a newly created,
+% empty state, with some transitions leading to it.
+% \item The \texttt{left/right} sequences hold a list of the
+% corresponding end-points of nested groups.
+% \end{itemize}
+%
+% \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. 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
+ { \@@_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 {#2}
+ \@@_build_aux:NN #1 \l_@@_internal_regex
+ }
+\cs_new_protected:Npn \@@_build_aux:NN #1#2
+ {
+ \@@_standard_escapechar:
+ \int_zero:N \l_@@_capturing_group_int
+ \int_set_eq:NN \l_@@_max_state_int \l_@@_min_state_int
+ \@@_build_new_state:
+ \@@_build_new_state:
+ \@@_toks_put_right:Nn \l_@@_left_state_int
+ { \@@_action_start_wildcard:N #1 }
+ \@@_group:nnnN {#2} { 1 } { 0 } \c_false_bool
+ \@@_toks_put_right:Nn \l_@@_right_state_int
+ { \@@_action_success: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\g_@@_case_int}
+% Case number that was successfully matched in
+% \cs{regex_match_case:nn} and related functions.
+% \begin{macrocode}
+\int_new:N \g_@@_case_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_case_max_group_int}
+% The largest group number appearing in any of the \meta{regex} in the
+% argument of \cs{regex_match_case:nn} and related functions.
+% \begin{macrocode}
+\int_new:N \l_@@_case_max_group_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_case_build:n, \@@_case_build:e, \@@_case_build_aux:Nn, \@@_case_build_loop:n}
+% See \cs{@@_build:n}, but with a loop.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_case_build:n #1
+ {
+ \@@_case_build_aux:Nn \c_true_bool {#1}
+ \int_gzero:N \g_@@_case_int
+ }
+\cs_generate_variant:Nn \@@_case_build:n { e }
+\cs_new_protected:Npn \@@_case_build_aux:Nn #1#2
+ {
+ \@@_standard_escapechar:
+ \int_set_eq:NN \l_@@_max_state_int \l_@@_min_state_int
+ \@@_build_new_state:
+ \@@_build_new_state:
+ \@@_toks_put_right:Nn \l_@@_left_state_int
+ { \@@_action_start_wildcard:N #1 }
+ %
+ \@@_build_new_state:
+ \@@_toks_put_left:Ne \l_@@_left_state_int
+ { \@@_action_submatch:nN \c_zero_int < }
+ \@@_push_lr_states:
+ \int_zero:N \l_@@_case_max_group_int
+ \int_gzero:N \g_@@_case_int
+ \tl_map_inline:nn {#2}
+ {
+ \int_gincr:N \g_@@_case_int
+ \@@_case_build_loop:n {##1}
+ }
+ \int_set_eq:NN \l_@@_capturing_group_int \l_@@_case_max_group_int
+ \@@_pop_lr_states:
+ }
+\cs_new_protected:Npn \@@_case_build_loop:n #1
+ {
+ \int_set_eq:NN \l_@@_capturing_group_int \c_one_int
+ \@@_compile_use:n {#1}
+ \int_set:Nn \l_@@_case_max_group_int
+ { \int_max:nn \l_@@_case_max_group_int \l_@@_capturing_group_int }
+ \seq_pop:NN \l_@@_right_state_seq \l_@@_internal_a_tl
+ \int_set:Nn \l_@@_right_state_int \l_@@_internal_a_tl
+ \@@_toks_put_left:Ne \l_@@_right_state_int
+ {
+ \@@_action_submatch:nN \c_zero_int >
+ \int_gset:Nn \g_@@_case_int
+ { \int_use:N \g_@@_case_int }
+ \@@_action_success:
+ }
+ \@@_toks_clear:N \l_@@_max_state_int
+ \seq_push:No \l_@@_right_state_seq
+ { \int_use:N \l_@@_max_state_int }
+ \int_incr:N \l_@@_max_state_int
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_build_for_cs:n}
+% 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$;
+% \end{itemize}
+% 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_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
+% to surround the expression with a group. However, for branches to
+% work properly at the outer level, we need to put the appropriate
+% \texttt{left} and \texttt{right} states in their sequence.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_build_for_cs:n #1
+ {
+ \int_set_eq:NN \l_@@_min_state_int \l_@@_max_state_int
+ \@@_build_new_state:
+ \@@_build_new_state:
+ \@@_push_lr_states:
+ #1
+ \@@_pop_lr_states:
+ \@@_toks_put_right:Nn \l_@@_right_state_int
+ {
+ \if_int_compare:w -2 = \l_@@_curr_char_int
+ \exp_after:wN \@@_action_success:
+ \fi:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Helpers for building an \textsc{nfa}}
+%
+% \begin{macro}{\@@_push_lr_states:, \@@_pop_lr_states:}
+% When building the regular expression, we keep track of pointers to
+% the left-end and right-end of each group without help from \TeX{}'s
+% grouping.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_push_lr_states:
+ {
+ \seq_push:No \l_@@_left_state_seq
+ { \int_use:N \l_@@_left_state_int }
+ \seq_push:No \l_@@_right_state_seq
+ { \int_use:N \l_@@_right_state_int }
+ }
+\cs_new_protected:Npn \@@_pop_lr_states:
+ {
+ \seq_pop:NN \l_@@_left_state_seq \l_@@_internal_a_tl
+ \int_set:Nn \l_@@_left_state_int \l_@@_internal_a_tl
+ \seq_pop:NN \l_@@_right_state_seq \l_@@_internal_a_tl
+ \int_set:Nn \l_@@_right_state_int \l_@@_internal_a_tl
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_build_transition_left:NNN,
+% \@@_build_transition_right:nNn
+% }
+% Add a transition from |#2| to |#3| using the function |#1|. The
+% \texttt{left} function is used for higher priority transitions, and
+% the \texttt{right} function for lower priority transitions (which
+% should be performed later). The signatures differ to reflect the
+% differing usage later on. Both functions could be optimized.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_build_transition_left:NNN #1#2#3
+ { \@@_toks_put_left:Ne #2 { #1 { \tex_the:D \@@_int_eval:w #3 - #2 } } }
+\cs_new_protected:Npn \@@_build_transition_right:nNn #1#2#3
+ { \@@_toks_put_right:Ne #2 { #1 { \tex_the:D \@@_int_eval:w #3 - #2 } } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_build_new_state:}
+% Add a new empty state to the \textsc{nfa}. Then update the
+% \texttt{left}, \texttt{right}, and \texttt{max} states, so that the
+% \texttt{right} state is the new empty state, and the \texttt{left}
+% state points to the previously \enquote{current} state.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_build_new_state:
+ {
+ \@@_toks_clear:N \l_@@_max_state_int
+ \int_set_eq:NN \l_@@_left_state_int \l_@@_right_state_int
+ \int_set_eq:NN \l_@@_right_state_int \l_@@_max_state_int
+ \int_incr:N \l_@@_max_state_int
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_build_transitions_lazyness:NNNNN}
+% This function creates a new state, and puts two transitions starting
+% from the old current state. The order of the transitions is
+% controlled by |#1|, true for lazy quantifiers, and false for greedy
+% quantifiers.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_build_transitions_lazyness:NNNNN #1#2#3#4#5
+ {
+ \@@_build_new_state:
+ \@@_toks_put_right:Ne \l_@@_left_state_int
+ {
+ \if_meaning:w \c_true_bool #1
+ #2 { \tex_the:D \@@_int_eval:w #3 - \l_@@_left_state_int }
+ #4 { \tex_the:D \@@_int_eval:w #5 - \l_@@_left_state_int }
+ \else:
+ #4 { \tex_the:D \@@_int_eval:w #5 - \l_@@_left_state_int }
+ #2 { \tex_the:D \@@_int_eval:w #3 - \l_@@_left_state_int }
+ \fi:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Building classes}
+%
+% \begin{macro}{\@@_class:NnnnN}
+% \begin{macro}[rEXP]{\@@_tests_action_cost:n}
+% The arguments are: \meta{boolean} \Arg{tests} \Arg{min} \Arg{more}
+% \meta{lazyness}. First store the tests with a trailing
+% \cs{@@_action_cost:n}, in the true branch of
+% \cs{@@_break_point:TF} for positive classes, or the false branch
+% for negative classes. The integer \meta{more} is $0$ for fixed
+% repetitions, $-1$ for unbounded repetitions, and
+% $\meta{max}-\meta{min}$ for a range of repetitions.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_class:NnnnN #1#2#3#4#5
+ {
+ \cs_set:Npe \@@_tests_action_cost:n ##1
+ {
+ \exp_not:n { \exp_not:n {#2} }
+ \bool_if:NTF #1
+ { \@@_break_point:TF { \@@_action_cost:n {##1} } { } }
+ { \@@_break_point:TF { } { \@@_action_cost:n {##1} } }
+ }
+ \if_case:w - #4 \exp_stop_f:
+ \@@_class_repeat:n {#3}
+ \or: \@@_class_repeat:nN {#3} #5
+ \else: \@@_class_repeat:nnN {#3} {#4} #5
+ \fi:
+ }
+\cs_new:Npn \@@_tests_action_cost:n { \@@_action_cost:n }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_class_repeat:n}
+% This is used for a fixed number of repetitions. Build one state for
+% each repetition, with a transition controlled by the tests that we
+% have collected. That works just fine for |#1|${}=0$ repetitions:
+% nothing is built.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_class_repeat:n #1
+ {
+ \prg_replicate:nn {#1}
+ {
+ \@@_build_new_state:
+ \@@_build_transition_right:nNn \@@_tests_action_cost:n
+ \l_@@_left_state_int \l_@@_right_state_int
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_class_repeat:nN}
+% This implements unbounded repetitions of a single class (\emph{e.g.}
+% the |*| and |+| quantifiers). If the minimum number |#1| of
+% repetitions is $0$, then build a transition from the current state
+% to itself governed by the tests, and a free transition to a new
+% state (hence skipping the tests). Otherwise, call
+% \cs{@@_class_repeat:n} for the code to match |#1| repetitions,
+% and add free transitions from the last state to the previous one,
+% and to a new one. In both cases, the order of transitions is
+% controlled by the lazyness boolean |#2|.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_class_repeat:nN #1#2
+ {
+ \if_int_compare:w #1 = \c_zero_int
+ \@@_build_transitions_lazyness:NNNNN #2
+ \@@_action_free:n \l_@@_right_state_int
+ \@@_tests_action_cost:n \l_@@_left_state_int
+ \else:
+ \@@_class_repeat:n {#1}
+ \int_set_eq:NN \l_@@_internal_a_int \l_@@_left_state_int
+ \@@_build_transitions_lazyness:NNNNN #2
+ \@@_action_free:n \l_@@_right_state_int
+ \@@_action_free:n \l_@@_internal_a_int
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_class_repeat:nnN}
+% We want to build the code to match from |#1| to $|#1|+|#2|$
+% repetitions. Match |#1| repetitions (can be $0$). Compute the final
+% state of the next construction as \texttt{a}. Build $|#2|>0$ states,
+% each with a transition to the next state governed by the tests, and
+% a transition to the final state \texttt{a}. The computation of
+% \texttt{a} is safe because states are allocated in order, starting
+% from \texttt{max_state}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_class_repeat:nnN #1#2#3
+ {
+ \@@_class_repeat:n {#1}
+ \int_set:Nn \l_@@_internal_a_int
+ { \l_@@_max_state_int + #2 - \c_one_int }
+ \prg_replicate:nn { #2 }
+ {
+ \@@_build_transitions_lazyness:NNNNN #3
+ \@@_action_free:n \l_@@_internal_a_int
+ \@@_tests_action_cost:n \l_@@_right_state_int
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Building groups}
+%
+% \begin{macro}{\@@_group_aux:nnnnN}
+% Arguments: \Arg{label} \Arg{contents} \Arg{min} \Arg{more}
+% \meta{lazyness}. If \meta{min} is $0$, we need to add a state before
+% building the group, so that the thread which skips the group does
+% not also set the start-point of the submatch. After adding one more
+% state, the \texttt{left_state} is the left end of the group, from
+% which all branches stem, and the \texttt{right_state} is the
+% right end of the group, and all branches end their course in that
+% state. We store those two integers to be queried for each branch, we
+% build the \textsc{nfa} states for the contents |#2| of the group,
+% and we forget about the two integers. Once this is done, perform the
+% repetition: either exactly |#3| times, or |#3| or more times, or
+% between |#3| and $|#3|+|#4|$ times, with lazyness |#5|. The
+% \meta{label} |#1| is used for submatch tracking. Each of the three
+% auxiliaries expects \texttt{left_state} and \texttt{right_state} to
+% be set properly.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_group_aux:nnnnN #1#2#3#4#5
+ {
+ \if_int_compare:w #3 = \c_zero_int
+ \@@_build_new_state:
+ \@@_build_transition_right:nNn \@@_action_free_group:n
+ \l_@@_left_state_int \l_@@_right_state_int
+ \fi:
+ \@@_build_new_state:
+ \@@_push_lr_states:
+ #2
+ \@@_pop_lr_states:
+ \if_case:w - #4 \exp_stop_f:
+ \@@_group_repeat:nn {#1} {#3}
+ \or: \@@_group_repeat:nnN {#1} {#3} #5
+ \else: \@@_group_repeat:nnnN {#1} {#3} {#4} #5
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_group:nnnN, \@@_group_no_capture:nnnN}
+% Hand to \cs{@@_group_aux:nnnnnN} the label of that group
+% (expanded), and the group itself, with some extra commands to
+% perform.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_group:nnnN #1
+ {
+ \exp_args:No \@@_group_aux:nnnnN
+ { \int_use:N \l_@@_capturing_group_int }
+ {
+ \int_incr:N \l_@@_capturing_group_int
+ #1
+ }
+ }
+\cs_new_protected:Npn \@@_group_no_capture:nnnN
+ { \@@_group_aux:nnnnN { -1 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_group_resetting:nnnN}
+% \begin{macro}{\@@_group_resetting_loop:nnNn}
+% Again, hand the label $-1$ to \cs{@@_group_aux:nnnnN}, but this
+% time we work a little bit harder to keep track of the maximum group
+% label at the end of any branch, and to reset the group number at
+% each branch. This relies on the fact that a compiled regex always is
+% a sequence of items of the form \cs{@@_branch:n} \Arg{branch}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_group_resetting:nnnN #1
+ {
+ \@@_group_aux:nnnnN { -1 }
+ {
+ \exp_args:Noo \@@_group_resetting_loop:nnNn
+ { \int_use:N \l_@@_capturing_group_int }
+ { \int_use:N \l_@@_capturing_group_int }
+ #1
+ { ?? \prg_break:n } { }
+ \prg_break_point:
+ }
+ }
+\cs_new_protected:Npn \@@_group_resetting_loop:nnNn #1#2#3#4
+ {
+ \use_none:nn #3 { \int_set:Nn \l_@@_capturing_group_int {#1} }
+ \int_set:Nn \l_@@_capturing_group_int {#2}
+ #3 {#4}
+ \exp_args:Ne \@@_group_resetting_loop:nnNn
+ { \int_max:nn {#1} \l_@@_capturing_group_int }
+ {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_branch:n}
+% Add a free transition from the left state of the current group to a
+% brand new state, starting point of this branch. Once the branch is
+% built, add a transition from its last state to the right state of
+% the group. The left and right states of the group are extracted from
+% the relevant sequences.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_branch:n #1
+ {
+ \@@_build_new_state:
+ \seq_get:NN \l_@@_left_state_seq \l_@@_internal_a_tl
+ \int_set:Nn \l_@@_left_state_int \l_@@_internal_a_tl
+ \@@_build_transition_right:nNn \@@_action_free:n
+ \l_@@_left_state_int \l_@@_right_state_int
+ #1
+ \seq_get:NN \l_@@_right_state_seq \l_@@_internal_a_tl
+ \@@_build_transition_right:nNn \@@_action_free:n
+ \l_@@_right_state_int \l_@@_internal_a_tl
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_group_repeat:nn}
+% This function is called to repeat a group a fixed number of times
+% |#2|; if this is $0$ we remove the group altogether (but don't reset
+% the \texttt{capturing_group} label). Otherwise, the auxiliary
+% \cs{@@_group_repeat_aux:n} copies |#2| times the \tn{toks} for
+% the group, and leaves \texttt{internal_a} pointing to the left end
+% of the last repetition. We only record the submatch information at
+% the last repetition. Finally, add a state at the end (the transition
+% to it has been taken care of by the replicating auxiliary).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_group_repeat:nn #1#2
+ {
+ \if_int_compare:w #2 = \c_zero_int
+ \int_set:Nn \l_@@_max_state_int
+ { \l_@@_left_state_int - \c_one_int }
+ \@@_build_new_state:
+ \else:
+ \@@_group_repeat_aux:n {#2}
+ \@@_group_submatches:nNN {#1}
+ \l_@@_internal_a_int \l_@@_right_state_int
+ \@@_build_new_state:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_group_submatches:nNN}
+% This inserts in states |#2| and |#3| the code for tracking
+% submatches of the group |#1|, unless inhibited by a label of $-1$.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_group_submatches:nNN #1#2#3
+ {
+ \if_int_compare:w #1 > - \c_one_int
+ \@@_toks_put_left:Ne #2 { \@@_action_submatch:nN {#1} < }
+ \@@_toks_put_left:Ne #3 { \@@_action_submatch:nN {#1} > }
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_group_repeat_aux:n}
+% Here we repeat \tn{toks} ranging from \texttt{left_state} to
+% \texttt{max_state}, $|#1|>0$ times. First add a transition so that
+% the copies \enquote{chain} properly. Compute the shift
+% \texttt{c} between the original copy and the last copy we
+% want. Shift the \texttt{right_state} and \texttt{max_state} to their
+% final values. We then want to perform \texttt{c} copy operations. At
+% the end, \texttt{b} is equal to the \texttt{max_state}, and
+% \texttt{a} points to the left of the last copy of the group.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_group_repeat_aux:n #1
+ {
+ \@@_build_transition_right:nNn \@@_action_free:n
+ \l_@@_right_state_int \l_@@_max_state_int
+ \int_set_eq:NN \l_@@_internal_a_int \l_@@_left_state_int
+ \int_set_eq:NN \l_@@_internal_b_int \l_@@_max_state_int
+ \if_int_compare:w \@@_int_eval:w #1 > \c_one_int
+ \int_set:Nn \l_@@_internal_c_int
+ {
+ ( #1 - \c_one_int )
+ * ( \l_@@_internal_b_int - \l_@@_internal_a_int )
+ }
+ \int_add:Nn \l_@@_right_state_int \l_@@_internal_c_int
+ \int_add:Nn \l_@@_max_state_int \l_@@_internal_c_int
+ \@@_toks_memcpy:NNn
+ \l_@@_internal_b_int
+ \l_@@_internal_a_int
+ \l_@@_internal_c_int
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_group_repeat:nnN}
+% This function is called to repeat a group at least $n$ times; the
+% case $n=0$ is very different from $n>0$. Assume first that $n=0$.
+% Insert submatch tracking information at the start and end of the
+% group, add a free transition from the right end to the
+% \enquote{true} left state \texttt{a} (remember: in this case we had
+% added an extra state before the left state). This forms the loop,
+% which we break away from by adding a free transition from \texttt{a}
+% to a new state.
+%
+% Now consider the case $n>0$. Repeat the group $n$ times, chaining
+% various copies with a free transition. Add submatch tracking only to
+% the last copy, then add a free transition from the right end back to
+% the left end of the last copy, either before or after the transition
+% to move on towards the rest of the \textsc{nfa}. This transition can
+% end up before submatch tracking, but that is irrelevant since it
+% only does so when going again through the group, recording new
+% matches. Finally, add a state; we already have a transition pointing
+% to it from \cs{@@_group_repeat_aux:n}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_group_repeat:nnN #1#2#3
+ {
+ \if_int_compare:w #2 = \c_zero_int
+ \@@_group_submatches:nNN {#1}
+ \l_@@_left_state_int \l_@@_right_state_int
+ \int_set:Nn \l_@@_internal_a_int
+ { \l_@@_left_state_int - \c_one_int }
+ \@@_build_transition_right:nNn \@@_action_free:n
+ \l_@@_right_state_int \l_@@_internal_a_int
+ \@@_build_new_state:
+ \if_meaning:w \c_true_bool #3
+ \@@_build_transition_left:NNN \@@_action_free:n
+ \l_@@_internal_a_int \l_@@_right_state_int
+ \else:
+ \@@_build_transition_right:nNn \@@_action_free:n
+ \l_@@_internal_a_int \l_@@_right_state_int
+ \fi:
+ \else:
+ \@@_group_repeat_aux:n {#2}
+ \@@_group_submatches:nNN {#1}
+ \l_@@_internal_a_int \l_@@_right_state_int
+ \if_meaning:w \c_true_bool #3
+ \@@_build_transition_right:nNn \@@_action_free_group:n
+ \l_@@_right_state_int \l_@@_internal_a_int
+ \else:
+ \@@_build_transition_left:NNN \@@_action_free_group:n
+ \l_@@_right_state_int \l_@@_internal_a_int
+ \fi:
+ \@@_build_new_state:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_group_repeat:nnnN}
+% We wish to repeat the group between |#2| and $|#2|+|#3|$ times, with
+% a lazyness controlled by |#4|. We insert submatch tracking up front:
+% in principle, we could avoid recording submatches for the first |#2|
+% copies of the group, but that forces us to treat specially the case
+% $|#2|=0$. Repeat that group with submatch tracking $|#2|+|#3|$ times
+% (the maximum number of repetitions). Then our goal is to add |#3|
+% transitions from the end of the |#2|-th group, and each subsequent
+% groups, to the end. For a lazy quantifier, we add those transitions
+% to the left states, before submatch tracking. For the greedy case,
+% we add the transitions to the right states, after submatch tracking
+% and the transitions which go on with more repetitions. In the greedy
+% case with $|#2|=0$, the transition which skips over all copies of
+% the group must be added separately, because its starting state does
+% not follow the normal pattern: we had to add it \enquote{by hand}
+% earlier.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_group_repeat:nnnN #1#2#3#4
+ {
+ \@@_group_submatches:nNN {#1}
+ \l_@@_left_state_int \l_@@_right_state_int
+ \@@_group_repeat_aux:n { #2 + #3 }
+ \if_meaning:w \c_true_bool #4
+ \int_set_eq:NN \l_@@_left_state_int \l_@@_max_state_int
+ \prg_replicate:nn { #3 }
+ {
+ \int_sub:Nn \l_@@_left_state_int
+ { \l_@@_internal_b_int - \l_@@_internal_a_int }
+ \@@_build_transition_left:NNN \@@_action_free:n
+ \l_@@_left_state_int \l_@@_max_state_int
+ }
+ \else:
+ \prg_replicate:nn { #3 - \c_one_int }
+ {
+ \int_sub:Nn \l_@@_right_state_int
+ { \l_@@_internal_b_int - \l_@@_internal_a_int }
+ \@@_build_transition_right:nNn \@@_action_free:n
+ \l_@@_right_state_int \l_@@_max_state_int
+ }
+ \if_int_compare:w #2 = \c_zero_int
+ \int_set:Nn \l_@@_right_state_int
+ { \l_@@_left_state_int - \c_one_int }
+ \else:
+ \int_sub:Nn \l_@@_right_state_int
+ { \l_@@_internal_b_int - \l_@@_internal_a_int }
+ \fi:
+ \@@_build_transition_right:nNn \@@_action_free:n
+ \l_@@_right_state_int \l_@@_max_state_int
+ \fi:
+ \@@_build_new_state:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Others}
+%
+% \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.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_assertion:Nn #1#2
+ {
+ \@@_build_new_state:
+ \@@_toks_put_right:Ne \l_@@_left_state_int
+ {
+ \exp_not:n {#2}
+ \@@_break_point:TF
+ \bool_if:NF #1 { { } }
+ {
+ \@@_action_free:n
+ {
+ \tex_the:D \@@_int_eval:w
+ \l_@@_right_state_int - \l_@@_left_state_int
+ }
+ }
+ \bool_if:NT #1 { { } }
+ }
+ }
+\cs_new_protected:Npn \@@_b_test:
+ {
+ \group_begin:
+ \int_set_eq:NN \l_@@_curr_char_int \l_@@_last_char_int
+ \@@_prop_w:
+ \@@_break_point:TF
+ { \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}
+%
+% \begin{macro}{\@@_command_K:}
+% Change the starting point of the $0$-th submatch (full match), and
+% transition to a new state, pretending that this is a fresh thread.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_command_K:
+ {
+ \@@_build_new_state:
+ \@@_toks_put_right:Ne \l_@@_left_state_int
+ {
+ \@@_action_submatch:nN \c_zero_int <
+ \bool_set_true:N \l_@@_fresh_thread_bool
+ \@@_action_free:n
+ {
+ \tex_the:D \@@_int_eval:w
+ \l_@@_right_state_int - \l_@@_left_state_int
+ }
+ \bool_set_false:N \l_@@_fresh_thread_bool
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Matching}
+%
+% We search for matches by running all the execution threads through the
+% \textsc{nfa} in parallel, reading one token of the query at each step.
+% The \textsc{nfa} contains \enquote{free} transitions to other states,
+% and transitions which \enquote{consume} the current token. For free
+% 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_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.
+%
+% If two paths through the \textsc{nfa} \enquote{collide} in the sense
+% that they reach the same state after reading a given token, then they
+% only differ in how they previously matched, and any future execution
+% would be identical for both. (Note that this would be wrong in the
+% presence of back-references.) Hence, we only need to keep one of the
+% two threads: the thread with the highest priority. Our \textsc{nfa} is
+% built in such a way that higher priority actions always come before
+% lower priority actions, which makes things work.
+%
+% The explanation in the previous paragraph may make us think that we
+% simply need to keep track of which states were visited at a given
+% step: after all, the loop generated when matching |(a?)*| against |a|
+% is broken, isn't it? No. The group first matches |a|, as it should,
+% then repeats; it attempts to match |a| again but fails; it skips |a|,
+% and finds out that this state has already been seen at this position
+% in the query: the match stops. The capturing group is (wrongly) |a|.
+% What went wrong is that a thread collided with itself, and the later
+% version, which has gone through the group one more times with an empty
+% match, should have a higher priority than not going through the group.
+%
+% We solve this by distinguishing \enquote{normal} free transitions
+% \cs{@@_action_free:n} from transitions
+% \cs{@@_action_free_group:n} which go back to the start of the
+% group. The former keeps threads unless they have been visited by a
+% \enquote{completed} thread, while the latter kind of transition also
+% prevents going back to a state visited by the current thread.
+%
+% \subsubsection{Variables used when matching}
+%
+% \begin{variable}
+% {
+% \l_@@_min_pos_int,
+% \l_@@_max_pos_int,
+% \l_@@_curr_pos_int,
+% \l_@@_start_pos_int,
+% \l_@@_success_pos_int,
+% }
+% 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 match
+% without backtracking, keeping all threads in lockstep at the
+% \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.
+% \begin{macrocode}
+\int_new:N \l_@@_min_pos_int
+\int_new:N \l_@@_max_pos_int
+\int_new:N \l_@@_curr_pos_int
+\int_new:N \l_@@_start_pos_int
+\int_new:N \l_@@_success_pos_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \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 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{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}
+%
+% \begin{variable}{\l_@@_curr_state_int}
+% For every character in the token list, each of the active states is
+% considered in turn. The variable \cs{l_@@_curr_state_int}
+% holds the state of the \textsc{nfa} which is currently considered:
+% transitions are then given as shifts relative to the current state.
+% \begin{macrocode}
+\int_new:N \l_@@_curr_state_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {\l_@@_curr_submatches_tl, \l_@@_success_submatches_tl}
+% The submatches for the thread which is currently active are stored
+% 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}
+\tl_new:N \l_@@_curr_submatches_tl
+\tl_new:N \l_@@_success_submatches_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_step_int}
+% This integer, always even, is increased every time a character in
+% the query is read, and not reset when doing multiple matches. We
+% store in \cs{g_@@_state_active_intarray} the last step in which each
+% \meta{state} in the \textsc{nfa} was encountered. This lets us break
+% infinite loops by not visiting the same state twice in the same
+% step. In fact, the step we store is equal to \texttt{step} when we
+% have started performing the operations of \tn{toks}\meta{state}, but
+% not finished yet. However, once we finish, we store
+% $\text{\texttt{step}}+1$ in \cs{g_@@_state_active_intarray}. This is
+% needed to track submatches
+% properly (see building phase). The \texttt{step} is also used to
+% attach each set of submatch information to a given iteration (and
+% automatically discard it when it corresponds to a past step).
+% \begin{macrocode}
+\int_new:N \l_@@_step_int
+% \end{macrocode}
+% \end{variable}
+%
+% \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_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_thread_int
+\int_new:N \l_@@_max_thread_int
+% \end{macrocode}
+% \end{variable}
+%
+% \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_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_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}
+%
+% \begin{variable}{\l_@@_every_match_tl}
+% Every time a match is found, this token list is used. For single
+% matching, the token list is empty. For multiple matching, the token
+% list is set to repeat the matching, after performing some operation
+% which depends on the user function. See \cs{@@_single_match:} and
+% \cs{@@_multi_match:n}.
+% \begin{macrocode}
+\tl_new:N \l_@@_every_match_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_fresh_thread_bool, \l_@@_empty_success_bool}
+% \begin{macro}{\@@_if_two_empty_matches:F}
+% When doing multiple matches, we need to avoid infinite loops where
+% each iteration matches the same empty token list. When an empty
+% token list is matched, the next successful match of the same empty
+% token list is suppressed. We detect empty matches by setting
+% \cs{l_@@_fresh_thread_bool} to \texttt{true} for threads which
+% directly come from the start of the regex or from the |\K| command,
+% and testing that boolean whenever a thread succeeds. The function
+% \cs{@@_if_two_empty_matches:F} is redefined at every match
+% attempt, depending on whether the previous match was empty or not:
+% if it was, then the function must cancel a purported success if it
+% is empty and at the same spot as the previous match; otherwise, we
+% definitely don't have two identical empty matches, so the function
+% is \cs{use:n}.
+% \begin{macrocode}
+\bool_new:N \l_@@_fresh_thread_bool
+\bool_new:N \l_@@_empty_success_bool
+\cs_new_eq:NN \@@_if_two_empty_matches:F \use:n
+% \end{macrocode}
+% \end{macro}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \g_@@_success_bool,
+% \l_@@_saved_success_bool,
+% \l_@@_match_success_bool
+% }
+% The boolean \cs{l_@@_match_success_bool} is true if the current
+% match attempt was successful, and \cs{g_@@_success_bool} is true
+% if there was at least one successful match. This is the only global
+% variable in this whole module, but we would need it to be local when
+% matching a control sequence with |\c{...}|. This is done by saving
+% the global variable into \cs{l_@@_saved_success_bool}, which is
+% local, hence not affected by the changes due to inner regex
+% functions.
+% \begin{macrocode}
+\bool_new:N \g_@@_success_bool
+\bool_new:N \l_@@_saved_success_bool
+\bool_new:N \l_@@_match_success_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Matching: framework}
+%
+% \begin{macro}{\@@_match:n, \@@_match_cs:n}
+% \begin{macro}{\@@_match_init:}
+% 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
+% we start at step $0$; we pretend that there was a previous match
+% ending at the start of the query, which was not empty (to avoid
+% smothering an empty match at the start). Once all this is set up, we
+% are ready for the ride. Find the first match.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_match:n #1
+ {
+ \@@_match_init:
+ \@@_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_set_eq:NN \l_@@_min_thread_int \l_@@_max_thread_int
+ \@@_match_init:
+ \@@_match_once_init:
+ \str_map_inline:nn {#1}
+ {
+ \tl_if_blank:nTF {##1}
+ { \@@_match_one_token:nnN {##1} {`##1} A }
+ { \@@_match_one_token:nnN {##1} {`##1} C }
+ }
+ \@@_match_one_token:nnN { } { -2 } F
+ \prg_break_point:Nn \@@_maplike_break: { }
+ }
+\cs_new_protected:Npn \@@_match_init:
+ {
+ \bool_gset_false:N \g_@@_success_bool
+ \int_step_inline:nnn
+ \l_@@_min_state_int { \l_@@_max_state_int - \c_one_int }
+ {
+ \__kernel_intarray_gset:Nnn
+ \g_@@_state_active_intarray {##1} \c_one_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_@@_last_char_success_int { -2 }
+ \tl_build_begin:N \l_@@_matched_analysis_tl
+ \tl_clear:N \l_@@_curr_analysis_tl
+ \int_set_eq:NN \l_@@_min_submatch_int \c_one_int
+ \int_set_eq:NN \l_@@_submatch_int \l_@@_min_submatch_int
+ \bool_set_false:N \l_@@_empty_success_bool
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \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 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_init:
+ {
+ \if_meaning:w \c_true_bool \l_@@_empty_success_bool
+ \cs_set:Npn \@@_if_two_empty_matches:F
+ {
+ \int_compare:nNnF
+ \l_@@_start_pos_int = \l_@@_curr_pos_int
+ }
+ \else:
+ \cs_set_eq:NN \@@_if_two_empty_matches:F \use:n
+ \fi:
+ \int_set_eq:NN \l_@@_start_pos_int \l_@@_success_pos_int
+ \bool_set_false:N \l_@@_match_success_bool
+ \tl_set:Ne \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 - \c_one_int }
+ \int_set_eq:NN \l_@@_curr_char_int \l_@@_last_char_success_int
+ \tl_build_get_intermediate: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_begin:N \l_@@_matched_analysis_tl
+ \tl_clear:N \l_@@_curr_analysis_tl
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_single_match:, \@@_multi_match:n}
+% For a single match, the overall success is determined by whether the
+% only match attempt is a success. When doing multiple matches, the
+% overall matching is successful as soon as any match
+% succeeds. Perform the action |#1|, then find the next match.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_single_match:
+ {
+ \tl_set:Nn \l_@@_every_match_tl
+ {
+ \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_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_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_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 the
+% match. We explain the \texttt{fresh_thread} business when
+% describing \cs{@@_action_wildcard:}.
+% \begin{macrocode}
+\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
+ \cs_set_eq:NN \@@_maybe_compute_ccc: \@@_compute_case_changed_char:
+ \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:Ne \l_@@_matched_analysis_tl
+ { \exp_not:o \l_@@_curr_analysis_tl }
+ \tl_set:Nn \l_@@_curr_analysis_tl { { {#1} {#2} #3 } }
+ \use:e
+ {
+ \int_set_eq:NN \l_@@_max_thread_int \l_@@_min_thread_int
+ \int_step_function:nnN
+ \l_@@_min_thread_int
+ { \l_@@_max_thread_int - \c_one_int }
+ \@@_match_one_active:n
+ }
+ \prg_break_point:
+ \bool_set_false:N \l_@@_fresh_thread_bool
+ \if_int_compare:w \l_@@_max_thread_int > \l_@@_min_thread_int
+ \if_int_compare:w -2 < \l_@@_curr_char_int
+ \exp_after:wN \use_i:nn
+ \fi:
+ \fi:
+ \l_@@_every_match_tl
+ }
+\cs_new:Npn \@@_match_one_active:n #1
+ {
+ \@@_use_state_and_submatches:w
+ \__kernel_intarray_range_to_clist:Nnn
+ \g_@@_thread_info_intarray
+ { \c_one_int + #1 * (\l_@@_capturing_group_int * 2 + \c_one_int) }
+ { (\c_one_int + #1) * (\l_@@_capturing_group_int * 2 + \c_one_int) }
+ ;
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Using states of the \textsc{nfa}}
+%
+% \begin{macro}{\@@_use_state:}
+% Use the current \textsc{nfa} instruction. The state is initially
+% marked as belonging to the current \texttt{step}: this allows normal
+% free transition to repeat, but group-repeating transitions
+% won't. Once we are done exploring all the branches it spawned, the
+% state is marked as $\texttt{step}+1$: any thread hitting it at that
+% point will be terminated.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_use_state:
+ {
+ \__kernel_intarray_gset:Nnn \g_@@_state_active_intarray
+ \l_@@_curr_state_int \l_@@_step_int
+ \@@_toks_use:w \l_@@_curr_state_int
+ \__kernel_intarray_gset:Nnn \g_@@_state_active_intarray
+ \l_@@_curr_state_int
+ { \@@_int_eval:w \l_@@_step_int + \c_one_int \scan_stop: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \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{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: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_tl { #2 , }
+ \exp_after:wN \@@_use_state:
+ \fi:
+ \scan_stop:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Actions when matching}
+%
+% \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_one_token:nnN} too.
+% \begin{macrocode}
+\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
+ \bool_if:NT #1 { \@@_action_cost:n {0} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_action_free:n, \@@_action_free_group:n}
+% \begin{macro}{\@@_action_free_aux:nn}
+% These functions copy a thread after checking that the \textsc{nfa}
+% state has not already been used at this position. If not, store
+% submatches in the new state, and insert the instructions for that
+% state in the input stream. Then restore the old value of
+% \cs{l_@@_curr_state_int} and of the current submatches. The
+% two types of free transitions differ by how they test that the state
+% has not been encountered yet: the \texttt{group} version is
+% stricter, and will not use a state if it was used earlier in the
+% current thread, hence forcefully breaking the loop, while the
+% \enquote{normal} version will revisit a state even within the thread
+% itself.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_action_free:n
+ { \@@_action_free_aux:nn { > \l_@@_step_int \else: } }
+\cs_new_protected:Npn \@@_action_free_group:n
+ { \@@_action_free_aux:nn { < \l_@@_step_int } }
+\cs_new_protected:Npn \@@_action_free_aux:nn #1#2
+ {
+ \use:e
+ {
+ \int_add:Nn \l_@@_curr_state_int {#2}
+ \exp_not:n
+ {
+ \if_int_compare:w
+ \__kernel_intarray_item:Nn \g_@@_state_active_intarray
+ \l_@@_curr_state_int
+ #1
+ \exp_after:wN \@@_use_state:
+ \fi:
+ }
+ \int_set:Nn \l_@@_curr_state_int
+ { \int_use:N \l_@@_curr_state_int }
+ \tl_set:Nn \exp_not:N \l_@@_curr_submatches_tl
+ { \exp_not:o \l_@@_curr_submatches_tl }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_action_cost:n}
+% A transition which consumes the current character and shifts the
+% state by |#1|. The resulting state is stored in the appropriate array
+% for use at the next position, and we also store the current
+% submatches.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_action_cost:n #1
+ {
+ \exp_args:No \@@_store_state:n
+ { \tex_the:D \@@_int_eval:w \l_@@_curr_state_int + #1 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_store_state:n}
+% \begin{macro}{\@@_store_submatches:}
+% 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
+ {
+ \exp_args:No \@@_store_submatches:nn
+ \l_@@_curr_submatches_tl {#1}
+ \int_incr:N \l_@@_max_thread_int
+ }
+\cs_new_protected:Npn \@@_store_submatches:nn #1#2
+ {
+ \__kernel_intarray_gset_range_from_clist:Nnn
+ \g_@@_thread_info_intarray
+ {
+ \@@_int_eval:w
+ \c_one_int + \l_@@_max_thread_int *
+ (\l_@@_capturing_group_int * 2 + \c_one_int)
+ }
+ { #2 , #1 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_disable_submatches:}
+% Some user functions don't require tracking submatches.
+% We get a performance improvement by simply defining the
+% relevant functions to remove their argument and do nothing
+% with it.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_disable_submatches:
+ {
+ \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: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: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
+ {
+ \tl_set:Ne \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}
+%
+% \begin{macro}{\@@_action_success:}
+% There is a successful match when an execution path reaches the last
+% state in the \textsc{nfa}, unless this marks a second identical
+% empty match. Then mark that there was a successful match; it is
+% empty if it is \enquote{fresh}; and we store the current position
+% and submatches. The current step is then interrupted with
+% \cs{prg_break:}, and only paths with higher precedence are
+% pursued further. The values stored here may be overwritten by a
+% later success of a path with higher precedence.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_action_success:
+ {
+ \@@_if_two_empty_matches:F
+ {
+ \bool_set_true:N \l_@@_match_success_bool
+ \bool_set_eq:NN \l_@@_empty_success_bool
+ \l_@@_fresh_thread_bool
+ \int_set_eq:NN \l_@@_success_pos_int \l_@@_curr_pos_int
+ \int_set_eq:NN \l_@@_last_char_success_int \l_@@_last_char_int
+ \tl_build_begin:N \l_@@_matched_analysis_tl
+ \tl_set_eq:NN \l_@@_success_submatches_tl
+ \l_@@_curr_submatches_tl
+ \prg_break:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Replacement}
+%
+% \subsubsection{Variables and helpers used in replacement}
+%
+% \begin{variable}{\l_@@_replacement_csnames_int}
+% The behaviour of closing braces inside a replacement text depends on
+% whether a sequences |\c{| or |\u{| has been encountered. The number
+% of \enquote{open} such sequences that should be closed by |}| is
+% stored in \cs{l_@@_replacement_csnames_int}, and decreased by
+% $1$ by each |}|.
+% \begin{macrocode}
+\int_new:N \l_@@_replacement_csnames_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_replacement_category_tl, \l_@@_replacement_category_seq}
+% This sequence of letters is used to correctly restore categories in
+% nested constructions such as |\cL(abc\cD(_)d)|.
+% \begin{macrocode}
+\tl_new:N \l_@@_replacement_category_tl
+\seq_new:N \l_@@_replacement_category_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_balance_tl}
+% This token list holds the replacement text for
+% \cs{@@_replacement_balance_one_match:n} while it is being built
+% incrementally.
+% \begin{macrocode}
+\tl_new:N \g_@@_balance_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[rEXP]{\@@_replacement_balance_one_match:n}
+% This expects as an argument the first index of a set of entries in
+% \cs{g_@@_submatch_begin_intarray} (and related arrays) which hold the
+% submatch information for a given match. It
+% can be used within an integer expression to obtain the brace balance
+% incurred by performing the replacement on that match. This combines
+% the braces lost by removing the match, braces added by all the
+% submatches appearing in the replacement, and braces appearing
+% explicitly in the replacement. Even though it is always redefined
+% before use, we initialize it as for an empty replacement. An
+% important property is that concatenating several calls to that
+% function must result in a valid integer expression (hence a leading
+% |+| in the actual definition).
+% \begin{macrocode}
+\cs_new:Npn \@@_replacement_balance_one_match:n #1
+ { - \@@_submatch_balance:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_replacement_do_one_match:n}
+% The input is the same as \cs{@@_replacement_balance_one_match:n}.
+% This function is redefined to expand to the part of the token list
+% from the end of the previous match to a given match, followed by the
+% replacement text. Hence concatenating the result of this function
+% with all possible arguments (one call for each match), as well as
+% the range from the end of the last match to the end of the string,
+% produces the fully replaced token list. The initialization does
+% not matter, but (as an example) we set it as for an empty replacement.
+% \begin{macrocode}
+\cs_new:Npn \@@_replacement_do_one_match:n #1
+ {
+ \@@_query_range:nn
+ { \__kernel_intarray_item:Nn \g_@@_submatch_prev_intarray {#1} }
+ { \__kernel_intarray_item:Nn \g_@@_submatch_begin_intarray {#1} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_exp_not:N}
+% This function lets us navigate around the fact that the primitive
+% \cs{exp_not:n} requires a braced argument. As far as I can tell, it
+% is only needed if the user tries to include in the replacement text
+% a control sequence set equal to a macro parameter character, such as
+% \cs{c_parameter_token}. Indeed, within an \texttt{e}/\texttt{x}-expanding
+% assignment, \cs{exp_not:N}~|#| behaves as a single |#|, whereas
+% \cs{exp_not:n}~|{#}| behaves as a doubled |##|.
+% \begin{macrocode}
+\cs_new:Npn \@@_replacement_exp_not:N #1 { \exp_not:n {#1} }
+% \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}
+% \begin{macro}[rEXP]{\@@_query_range_loop:ww}
+% When it is time to extract submatches from the token list, the
+% various tokens are stored in \tn{toks} registers numbered from
+% \cs{l_@@_min_pos_int} inclusive to \cs{l_@@_max_pos_int}
+% exclusive. The function \cs{@@_query_range:nn} \Arg{min}
+% \Arg{max} unpacks registers from the position \meta{min} to the
+% position $\meta{max}-1$ included. Once this is expanded, a second
+% \texttt{e}-expansion results in the actual tokens from the
+% query. That second expansion is only done by user functions at the
+% very end of their operation, after checking (and correcting) the
+% brace balance first.
+% \begin{macrocode}
+\cs_new:Npn \@@_query_range:nn #1#2
+ {
+ \exp_after:wN \@@_query_range_loop:ww
+ \int_value:w \@@_int_eval:w #1 \exp_after:wN ;
+ \int_value:w \@@_int_eval:w #2 ;
+ \prg_break_point:
+ }
+\cs_new:Npn \@@_query_range_loop:ww #1 ; #2 ;
+ {
+ \if_int_compare:w #1 < #2 \exp_stop_f:
+ \else:
+ \prg_break:n
+ \fi:
+ \@@_toks_use:w #1 \exp_stop_f:
+ \exp_after:wN \@@_query_range_loop:ww
+ \int_value:w \@@_int_eval:w #1 + \c_one_int ; #2 ;
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_query_submatch:n}
+% Find the start and end positions for a given submatch (of a given match).
+% \begin{macrocode}
+\cs_new:Npn \@@_query_submatch:n #1
+ {
+ \@@_query_range:nn
+ { \__kernel_intarray_item:Nn \g_@@_submatch_begin_intarray {#1} }
+ { \__kernel_intarray_item:Nn \g_@@_submatch_end_intarray {#1} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_submatch_balance:n}
+% Every user function must result in a balanced token list (unbalanced
+% token lists cannot be stored by TeX). When we unpacked the query, we
+% kept track of the brace balance, hence the contribution from a given
+% 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.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_submatch_balance:n #1
+ {
+ \tex_the:D \@@_int_eval:w
+ \@@_intarray_item:NnF \g_@@_balance_intarray
+ {
+ \__kernel_intarray_item:Nn
+ \g_@@_submatch_end_intarray {#1}
+ }
+ \c_zero_int
+ -
+ \@@_intarray_item:NnF \g_@@_balance_intarray
+ {
+ \__kernel_intarray_item:Nn
+ \g_@@_submatch_begin_intarray {#1}
+ }
+ \c_zero_int
+ \scan_stop:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Framework}
+%
+% \begin{macro}{\@@_replacement:n, \@@_replacement:e}
+% \begin{macro}{\@@_replacement_apply:Nn, \@@_replacement_set:n}
+% The replacement text is built incrementally. We keep track in
+% \cs{l_@@_balance_int} of the balance of explicit begin- and
+% end-group tokens and we store in \cs{g_@@_balance_tl} some
+% code to compute the brace balance from submatches (see its
+% description). Detect unescaped right braces, and escaped characters,
+% with trailing \cs{prg_do_nothing:} because some of the later
+% function look-ahead. Once the whole replacement text has been
+% parsed, make sure that there is no open csname. Finally, define the
+% \texttt{balance_one_match} and \texttt{do_one_match} functions.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replacement:n
+ { \@@_replacement_apply:Nn \@@_replacement_set:n }
+\cs_new_protected:Npn \@@_replacement_apply:Nn #1#2
+ {
+ \group_begin:
+ \tl_build_begin:N \l_@@_build_tl
+ \int_zero:N \l_@@_balance_int
+ \tl_gclear:N \g_@@_balance_tl
+ \@@_escape_use:nnnn
+ {
+ \if_charcode:w \c_right_brace_str ##1
+ \@@_replacement_rbrace:N
+ \else:
+ \if_charcode:w \c_left_brace_str ##1
+ \@@_replacement_lbrace:N
+ \else:
+ \@@_replacement_normal:n
+ \fi:
+ \fi:
+ ##1
+ }
+ { \@@_replacement_escaped:N ##1 }
+ { \@@_replacement_normal:n ##1 }
+ {#2}
+ \prg_do_nothing: \prg_do_nothing:
+ \if_int_compare:w \l_@@_replacement_csnames_int > \c_zero_int
+ \msg_error:nne { regex } { replacement-missing-rbrace }
+ { \int_use:N \l_@@_replacement_csnames_int }
+ \tl_build_put_right:Ne \l_@@_build_tl
+ { \prg_replicate:nn \l_@@_replacement_csnames_int \cs_end: }
+ \fi:
+ \seq_if_empty:NF \l_@@_replacement_category_seq
+ {
+ \msg_error:nne { regex } { replacement-missing-rparen }
+ { \seq_count:N \l_@@_replacement_category_seq }
+ \seq_clear:N \l_@@_replacement_category_seq
+ }
+ \tl_gput_right:Ne \g_@@_balance_tl
+ { + \int_use:N \l_@@_balance_int }
+ \tl_build_end:N \l_@@_build_tl
+ \exp_args:NNo
+ \group_end:
+ #1 \l_@@_build_tl
+ }
+\cs_generate_variant:Nn \@@_replacement:n { e }
+\cs_new_protected:Npn \@@_replacement_set:n #1
+ {
+ \cs_set:Npn \@@_replacement_do_one_match:n ##1
+ {
+ \@@_query_range:nn
+ {
+ \__kernel_intarray_item:Nn
+ \g_@@_submatch_prev_intarray {##1}
+ }
+ {
+ \__kernel_intarray_item:Nn
+ \g_@@_submatch_begin_intarray {##1}
+ }
+ #1
+ }
+ \exp_args:Nno \use:n
+ { \cs_gset:Npn \@@_replacement_balance_one_match:n ##1 }
+ {
+ \g_@@_balance_tl
+ - \@@_submatch_balance:n {##1}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_case_replacement:n, \@@_case_replacement:e}
+% \begin{macrocode}
+\tl_new:N \g_@@_case_replacement_tl
+\tl_new:N \g_@@_case_balance_tl
+\cs_new_protected:Npn \@@_case_replacement:n #1
+ {
+ \tl_gset:Nn \g_@@_case_balance_tl
+ {
+ \if_case:w
+ \__kernel_intarray_item:Nn
+ \g_@@_submatch_case_intarray {##1}
+ }
+ \tl_gset_eq:NN \g_@@_case_replacement_tl \g_@@_case_balance_tl
+ \tl_map_tokens:nn {#1}
+ { \@@_replacement_apply:Nn \@@_case_replacement_aux:n }
+ \tl_gset:No \g_@@_balance_tl
+ { \g_@@_case_balance_tl \fi: }
+ \exp_args:No \@@_replacement_set:n
+ { \g_@@_case_replacement_tl \fi: }
+ }
+\cs_generate_variant:Nn \@@_case_replacement:n { e }
+\cs_new_protected:Npn \@@_case_replacement_aux:n #1
+ {
+ \tl_gput_right:Nn \g_@@_case_replacement_tl { \or: #1 }
+ \tl_gput_right:No \g_@@_case_balance_tl
+ { \exp_after:wN \or: \g_@@_balance_tl }
+ }
+% \end{macrocode}
+% \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, \@@_replacement_normal_aux:N}
+% Most characters are simply sent to the output by
+% \cs{tl_build_put_right:Nn}, unless a particular category code has been
+% requested: then \cs{@@_replacement_c_A:w} or a similar auxiliary is
+% called. One exception is right parentheses, which restore the
+% category code in place before the group started. Note that the
+% sequence is non-empty there: it contains an empty entry
+% corresponding to the initial value of
+% \cs{l_@@_replacement_category_tl}.
+% The argument |#1| is a single character (including the case of a catcode-other space).
+% In case no specific catcode is requested, we taked into account the
+% current catcode regime (at the time the replacement is performed)
+% as much as reasonable, with all impossible catcodes (escape,
+% newline, etc.) being mapped to \enquote{other}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replacement_normal:n #1
+ {
+ \int_compare:nNnTF \l_@@_replacement_csnames_int > \c_zero_int
+ { \exp_args:No \@@_replacement_put:n { \token_to_str:N #1 } }
+ {
+ \tl_if_empty:NTF \l_@@_replacement_category_tl
+ { \@@_replacement_normal_aux:N #1 }
+ { % (
+ \token_if_eq_charcode:NNTF #1 )
+ {
+ \seq_pop:NN \l_@@_replacement_category_seq
+ \l_@@_replacement_category_tl
+ }
+ {
+ \use:c { @@_replacement_c_ \l_@@_replacement_category_tl :w }
+ ? #1
+ }
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_replacement_normal_aux:N #1
+ {
+ \token_if_eq_charcode:NNTF #1 \c_space_token
+ { \@@_replacement_c_S:w }
+ {
+ \exp_after:wN \exp_after:wN
+ \if_case:w \tex_catcode:D `#1 \exp_stop_f:
+ \@@_replacement_c_O:w
+ \or: \@@_replacement_c_B:w
+ \or: \@@_replacement_c_E:w
+ \or: \@@_replacement_c_M:w
+ \or: \@@_replacement_c_T:w
+ \or: \@@_replacement_c_O:w
+ \or: \@@_replacement_c_P:w
+ \or: \@@_replacement_c_U:w
+ \or: \@@_replacement_c_D:w
+ \or: \@@_replacement_c_O:w
+ \or: \@@_replacement_c_S:w
+ \or: \@@_replacement_c_L:w
+ \or: \@@_replacement_c_O:w
+ \or: \@@_replacement_c_A:w
+ \else: \@@_replacement_c_O:w
+ \fi:
+ }
+ ? #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_escaped:N}
+% As in parsing a regular expression, we use an auxiliary built from
+% |#1| if defined. Otherwise, check for escaped digits (standing from
+% submatches from $0$ to $9$): anything else is a raw character.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replacement_escaped:N #1
+ {
+ \cs_if_exist_use:cF { @@_replacement_#1:w }
+ {
+ \if_int_compare:w \c_one_int < 1#1 \exp_stop_f:
+ \@@_replacement_put_submatch:n {#1}
+ \else:
+ \@@_replacement_normal:n {#1}
+ \fi:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Submatches}
+%
+% \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{...}|
+% construction, it must be taken into account in the brace balance.
+% Later on, |##1| will be replaced by a pointer to the $0$-th submatch for a
+% given match.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replacement_put_submatch:n #1
+ {
+ \if_int_compare:w #1 < \l_@@_capturing_group_int
+ \@@_replacement_put_submatch_aux:n {#1}
+ \else:
+ \msg_expandable_error:nnff { regex } { submatch-too-big }
+ {#1} { \int_eval:n { \l_@@_capturing_group_int - \c_one_int } }
+ \fi:
+ }
+\cs_new_protected:Npn \@@_replacement_put_submatch_aux:n #1
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \@@_query_submatch:n { \@@_int_eval:w #1 + ##1 \scan_stop: } }
+ \if_int_compare:w \l_@@_replacement_csnames_int = \c_zero_int
+ \tl_gput_right:Nn \g_@@_balance_tl
+ { + \@@_submatch_balance:n { \@@_int_eval:w #1 + ##1 \scan_stop: } }
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_g:w}
+% \begin{macro}[rEXP]{\@@_replacement_g_digits:NN}
+% Grab digits for the |\g| escape sequence in a primitive assignment
+% to the integer \cs{l_@@_internal_a_int}. At the end of the run of
+% digits, check that it ends with a right brace.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replacement_g:w #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \@@_replacement_lbrace:N
+ { \l_@@_internal_a_int = \@@_replacement_g_digits:NN }
+ { \@@_replacement_error:NNN g #1 #2 }
+ }
+\cs_new:Npn \@@_replacement_g_digits:NN #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \@@_replacement_normal:n
+ {
+ \if_int_compare:w \c_one_int < 1#2 \exp_stop_f:
+ #2
+ \exp_after:wN \use_i:nnn
+ \exp_after:wN \@@_replacement_g_digits:NN
+ \else:
+ \exp_stop_f:
+ \exp_after:wN \@@_replacement_error:NNN
+ \exp_after:wN g
+ \fi:
+ }
+ {
+ \exp_stop_f:
+ \if_meaning:w \@@_replacement_rbrace:N #1
+ \exp_args:No \@@_replacement_put_submatch:n
+ { \int_use:N \l_@@_internal_a_int }
+ \exp_after:wN \use_none:nn
+ \else:
+ \exp_after:wN \@@_replacement_error:NNN
+ \exp_after:wN g
+ \fi:
+ }
+ #1 #2
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Csnames in replacement}
+%
+% \begin{macro}{\@@_replacement_c:w}
+% |\c| may only be followed by an unescaped character. If followed by
+% a left brace, start a control sequence by calling an auxiliary
+% common with |\u|. Otherwise test whether the category is known; if
+% it is not, complain.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replacement_c:w #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \@@_replacement_normal:n
+ {
+ \cs_if_exist:cTF { @@_replacement_c_#2:w }
+ { \@@_replacement_cat:NNN #2 }
+ { \@@_replacement_error:NNN c #1#2 }
+ }
+ {
+ \token_if_eq_meaning:NNTF #1 \@@_replacement_lbrace:N
+ { \@@_replacement_cu_aux:Nw \@@_replacement_exp_not:N }
+ { \@@_replacement_error:NNN c #1#2 }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_cu_aux:Nw}
+% Start a control sequence with \cs{cs:w}, protected
+% from expansion by |#1| (either \cs{@@_replacement_exp_not:N} or
+% \cs{exp_not:V}), or turned to a string by \cs{tl_to_str:V} if inside
+% another csname construction |\c| or |\u|. We use \cs{tl_to_str:V}
+% rather than \cs{tl_to_str:N} to deal with integers and other
+% registers.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replacement_cu_aux:Nw #1
+ {
+ \if_case:w \l_@@_replacement_csnames_int
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \exp_not:n { \exp_after:wN #1 \cs:w } }
+ \else:
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \exp_not:n { \exp_after:wN \tl_to_str:V \cs:w } }
+ \fi:
+ \int_incr:N \l_@@_replacement_csnames_int
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_u:w}
+% Check that |\u| is followed by a left brace. If so, start a control
+% sequence with \cs{cs:w}, which is then unpacked either with
+% \cs{exp_not:V} or \cs{tl_to_str:V} depending on the current context.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replacement_u:w #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \@@_replacement_lbrace:N
+ { \@@_replacement_cu_aux:Nw \@@_replacement_exp_not:V }
+ { \@@_replacement_error:NNN u #1#2 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_rbrace:N}
+% Within a |\c{...}| or |\u{...}| construction, end the control
+% sequence, and decrease the brace count. Otherwise, this is a raw
+% right brace.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replacement_rbrace:N #1
+ {
+ \if_int_compare:w \l_@@_replacement_csnames_int > \c_zero_int
+ \tl_build_put_right:Nn \l_@@_build_tl { \cs_end: }
+ \int_decr:N \l_@@_replacement_csnames_int
+ \else:
+ \@@_replacement_normal:n {#1}
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_lbrace:N}
+% Within a |\c{...}| or |\u{...}| construction, this is
+% forbidden. Otherwise, this is a raw left brace.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replacement_lbrace:N #1
+ {
+ \if_int_compare:w \l_@@_replacement_csnames_int > \c_zero_int
+ \msg_error:nnn { regex } { cu-lbrace } { u }
+ \else:
+ \@@_replacement_normal:n {#1}
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Characters in replacement}
+%
+% \begin{macro}{\@@_replacement_cat:NNN}
+% Here, |#1| is a letter among |BEMTPUDSLOA| and |#2#3| denote the
+% next character. Complain if we reach the end of the replacement or
+% if the construction appears inside |\c{|\ldots{}|}| or
+% |\u{|\ldots{}|}|, and detect the case of a parenthesis. In that
+% case, store the current category in a sequence and switch to a new
+% one.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replacement_cat:NNN #1#2#3
+ {
+ \token_if_eq_meaning:NNTF \prg_do_nothing: #3
+ { \msg_error:nn { regex } { replacement-catcode-end } }
+ {
+ \int_compare:nNnTF \l_@@_replacement_csnames_int > \c_zero_int
+ {
+ \msg_error:nnnn
+ { regex } { replacement-catcode-in-cs } {#1} {#3}
+ #2 #3
+ }
+ {
+ \@@_two_if_eq:NNNNTF #2 #3 \@@_replacement_normal:n (
+ {
+ \seq_push:NV \l_@@_replacement_category_seq
+ \l_@@_replacement_category_tl
+ \tl_set:Nn \l_@@_replacement_category_tl {#1}
+ }
+ {
+ \token_if_eq_meaning:NNT #2 \@@_replacement_escaped:N
+ {
+ \@@_char_if_alphanumeric:NTF #3
+ {
+ \msg_error:nnnn
+ { regex } { replacement-catcode-escaped }
+ {#1} {#3}
+ }
+ { }
+ }
+ \use:c { @@_replacement_c_#1:w } #2 #3
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% We now need to change the category code of the null character many
+% times, hence work in a group. The catcode-specific macros below are
+% defined in alphabetical order; if you are trying to understand the
+% code, start from the end of the alphabet as those categories are
+% simpler than active or begin-group.
+% \begin{macrocode}
+\group_begin:
+% \end{macrocode}
+%
+% \begin{macro}{\@@_replacement_char:nNN}
+% The only way to produce an arbitrary character--catcode pair is to
+% use the \tn{lowercase} or \tn{uppercase} primitives. This is a
+% wrapper for our purposes. The first argument is the null character
+% with various catcodes. The second and third arguments are grabbed
+% from the input stream: |#3| is the character whose character code to
+% reproduce. We could use \cs{char_generate:nn} but only for some
+% catcodes (active characters and spaces are not supported).
+% \begin{macrocode}
+ \cs_new_protected:Npn \@@_replacement_char:nNN #1#2#3
+ {
+ \tex_lccode:D \c_zero_int = `#3 \scan_stop:
+ \tex_lowercase:D { \@@_replacement_put:n {#1} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_c_A:w}
+% For an active character, expansion must be avoided, twice because we
+% later do two \texttt{e}-expansions, to unpack \tn{toks} for the
+% query, and to expand their contents to tokens of the query.
+% \begin{macrocode}
+ \char_set_catcode_active:N \^^@
+ \cs_new_protected:Npn \@@_replacement_c_A:w
+ { \@@_replacement_char:nNN { \exp_not:n { \exp_not:N ^^@ } } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_c_B:w}
+% An explicit begin-group token increases the balance, unless within a
+% |\c{...}| or |\u{...}| construction. Add the desired begin-group
+% character, using the standard \cs{if_false:} trick. We eventually
+% \texttt{e}-expand twice. The first time must yield a balanced token
+% list, and the second one gives the bare begin-group token. The
+% \cs{exp_after:wN} is not strictly needed, but is more consistent
+% with \pkg{l3tl-analysis}.
+% \begin{macrocode}
+ \char_set_catcode_group_begin:N \^^@
+ \cs_new_protected:Npn \@@_replacement_c_B:w
+ {
+ \if_int_compare:w \l_@@_replacement_csnames_int = \c_zero_int
+ \int_incr:N \l_@@_balance_int
+ \fi:
+ \@@_replacement_char:nNN
+ { \exp_not:n { \exp_after:wN ^^@ \if_false: } \fi: } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_c_C:w}
+% This is not quite catcode-related: when the user requests a
+% character with category \enquote{control sequence}, the
+% one-character control symbol is returned. As for the active
+% character, we prepare for two \texttt{e}-expansions.
+% \begin{macrocode}
+ \cs_new_protected:Npn \@@_replacement_c_C:w #1#2
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \exp_not:N \@@_replacement_exp_not:N \exp_not:c {#2} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_c_D:w}
+% Subscripts fit the mould: \tn{lowercase} the null byte with the
+% correct category.
+% \begin{macrocode}
+ \char_set_catcode_math_subscript:N \^^@
+ \cs_new_protected:Npn \@@_replacement_c_D:w
+ { \@@_replacement_char:nNN { ^^@ } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_c_E:w}
+% Similar to the begin-group case, the second \texttt{e}-expansion
+% produces the bare end-group token.
+% \begin{macrocode}
+ \char_set_catcode_group_end:N \^^@
+ \cs_new_protected:Npn \@@_replacement_c_E:w
+ {
+ \if_int_compare:w \l_@@_replacement_csnames_int = \c_zero_int
+ \int_decr:N \l_@@_balance_int
+ \fi:
+ \@@_replacement_char:nNN
+ { \exp_not:n { \if_false: { \fi: ^^@ } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_c_L:w}
+% Simply \tn{lowercase} a letter null byte to produce an arbitrary letter.
+% \begin{macrocode}
+ \char_set_catcode_letter:N \^^@
+ \cs_new_protected:Npn \@@_replacement_c_L:w
+ { \@@_replacement_char:nNN { ^^@ } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_c_M:w}
+% No surprise here, we lowercase the null math toggle.
+% \begin{macrocode}
+ \char_set_catcode_math_toggle:N \^^@
+ \cs_new_protected:Npn \@@_replacement_c_M:w
+ { \@@_replacement_char:nNN { ^^@ } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_c_O:w}
+% Lowercase an other null byte.
+% \begin{macrocode}
+ \char_set_catcode_other:N \^^@
+ \cs_new_protected:Npn \@@_replacement_c_O:w
+ { \@@_replacement_char:nNN { ^^@ } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_c_P:w}
+% For macro parameters, expansion is a tricky issue. We need to
+% prepare for two \texttt{e}-expansions and passing through various
+% macro definitions. Note that we cannot replace one \cs{exp_not:n} by
+% doubling the macro parameter characters because this would misbehave
+% if a mischievous user asks for |\c{\cP\#}|, since that macro
+% parameter character would be doubled.
+% \begin{macrocode}
+ \char_set_catcode_parameter:N \^^@
+ \cs_new_protected:Npn \@@_replacement_c_P:w
+ {
+ \@@_replacement_char:nNN
+ { \exp_not:n { \exp_not:n { ^^@^^@^^@^^@ } } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_c_S:w}
+% Spaces are normalized on input by \TeX{} to have character code
+% $32$. It is in fact impossible to get a token with character code
+% $0$ and category code $10$. Hence we use $32$ instead of $0$ as our
+% base character.
+% \begin{macrocode}
+ \cs_new_protected:Npn \@@_replacement_c_S:w #1#2
+ {
+ \if_int_compare:w `#2 = \c_zero_int
+ \msg_error:nn { regex } { replacement-null-space }
+ \fi:
+ \tex_lccode:D `\ = `#2 \scan_stop:
+ \tex_lowercase:D { \@@_replacement_put:n {~} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_c_T:w}
+% No surprise for alignment tabs here. Those are surrounded by the
+% appropriate braces whenever necessary, hence they don't cause
+% trouble in alignment settings.
+% \begin{macrocode}
+ \char_set_catcode_alignment:N \^^@
+ \cs_new_protected:Npn \@@_replacement_c_T:w
+ { \@@_replacement_char:nNN { ^^@ } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_c_U:w}
+% Simple call to \cs{@@_replacement_char:nNN} which lowercases the
+% math superscript |^^@|.
+% \begin{macrocode}
+ \char_set_catcode_math_superscript:N \^^@
+ \cs_new_protected:Npn \@@_replacement_c_U:w
+ { \@@_replacement_char:nNN { ^^@ } }
+% \end{macrocode}
+% \end{macro}
+%
+% Restore the catcode of the null byte.
+% \begin{macrocode}
+\group_end:
+% \end{macrocode}
+%
+% \subsubsection{An error}
+%
+% \begin{macro}{\@@_replacement_error:NNN}
+% Simple error reporting by calling one of the messages
+% \texttt{replacement-c}, \texttt{replacement-g}, or
+% \texttt{replacement-u}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replacement_error:NNN #1#2#3
+ {
+ \msg_error:nne { regex } { replacement-#1 } {#3}
+ #2 #3
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{User functions}
+%
+% \begin{macro}{\regex_new:N}
+% Before being assigned a sensible value, a regex variable matches
+% nothing.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_new:N #1
+ { \cs_new_eq:NN #1 \c_@@_no_match_regex }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\l_tmpa_regex, \l_tmpb_regex, \g_tmpa_regex, \g_tmpb_regex}
+% The usual scratch space.
+% \begin{macrocode}
+\regex_new:N \l_tmpa_regex
+\regex_new:N \l_tmpb_regex
+\regex_new:N \g_tmpa_regex
+\regex_new:N \g_tmpb_regex
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\regex_set:Nn, \regex_gset:Nn, \regex_const:Nn}
+% Compile, then store the result in the user variable with the
+% appropriate assignment function.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_set:Nn #1#2
+ {
+ \@@_compile:n {#2}
+ \tl_set_eq:NN #1 \l_@@_internal_regex
+ }
+\cs_new_protected:Npn \regex_gset:Nn #1#2
+ {
+ \@@_compile:n {#2}
+ \tl_gset_eq:NN #1 \l_@@_internal_regex
+ }
+\cs_new_protected:Npn \regex_const:Nn #1#2
+ {
+ \@@_compile:n {#2}
+ \tl_const:Ne #1 { \exp_not:o \l_@@_internal_regex }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \regex_show:n, \regex_log:n, \@@_show:Nn,
+% \regex_show:N, \regex_log:N, \@@_show:NN
+% }
+% User functions: the \texttt{n} variant requires compilation first.
+% Then show the variable with some appropriate text. The auxiliary
+% \cs{@@_show:N} is defined in a different section.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_show:n { \@@_show:Nn \msg_show:nneeee }
+\cs_new_protected:Npn \regex_log:n { \@@_show:Nn \msg_log:nneeee }
+\cs_new_protected:Npn \@@_show:Nn #1#2
+ {
+ \@@_compile:n {#2}
+ \@@_show:N \l_@@_internal_regex
+ #1 { regex } { show }
+ { \tl_to_str:n {#2} } { }
+ { \l_@@_internal_a_tl } { }
+ }
+\cs_new_protected:Npn \regex_show:N { \@@_show:NN \msg_show:nneeee }
+\cs_new_protected:Npn \regex_log:N { \@@_show:NN \msg_log:nneeee }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \__kernel_chk_tl_type:NnnT #2 { regex }
+ { \exp_args:No \@@_clean_regex:n {#2} }
+ {
+ \@@_show:N #2
+ #1 { regex } { show }
+ { } { \token_to_str:N #2 }
+ { \l_@@_internal_a_tl } { }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[TF]
+% {
+% \regex_match:nn, \regex_match:nV,
+% \regex_match:Nn, \regex_match:NV
+% }
+% Those conditionals are based on a common auxiliary defined
+% later. Its first argument builds the \textsc{nfa} corresponding to
+% the regex, and the second argument is the query token list. Once we
+% have performed the match, convert the resulting boolean to
+% \cs{prg_return_true:} or \texttt{false}.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \regex_match:nn #1#2 { T , F , TF }
+ {
+ \@@_if_match:nn { \@@_build:n {#1} } {#2}
+ \@@_return:
+ }
+\prg_generate_conditional_variant:Nnn \regex_match:nn { nV } { T , F , TF }
+\prg_new_protected_conditional:Npnn \regex_match:Nn #1#2 { T , F , TF }
+ {
+ \@@_if_match:nn { \@@_build:N #1 } {#2}
+ \@@_return:
+ }
+\prg_generate_conditional_variant:Nnn \regex_match:Nn { NV } { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {\regex_count:nnN, \regex_count:nVN, \regex_count:NnN, \regex_count:NVN}
+% Again, use an auxiliary whose first argument builds the \textsc{nfa}.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_count:nnN #1
+ { \@@_count:nnN { \@@_build:n {#1} } }
+\cs_new_protected:Npn \regex_count:NnN #1
+ { \@@_count:nnN { \@@_build:N #1 } }
+\cs_generate_variant:Nn \regex_count:nnN { nV }
+\cs_generate_variant:Nn \regex_count:NnN { NV }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[noTF]{\regex_match_case:nn}
+% The auxiliary errors if |#1| has an odd number of items, and
+% otherwise it sets \cs{g_@@_case_int} according to which case was
+% found (zero if not found). The \texttt{true} branch leaves the
+% corresponding code in the input stream.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_match_case:nnTF #1#2#3
+ {
+ \@@_match_case:nnTF {#1} {#2}
+ {
+ \tl_item:nn {#1} { 2 * \g_@@_case_int }
+ #3
+ }
+ }
+\cs_new_protected:Npn \regex_match_case:nn #1#2
+ { \regex_match_case:nnTF {#1} {#2} { } { } }
+\cs_new_protected:Npn \regex_match_case:nnT #1#2#3
+ { \regex_match_case:nnTF {#1} {#2} {#3} { } }
+\cs_new_protected:Npn \regex_match_case:nnF #1#2
+ { \regex_match_case:nnTF {#1} {#2} { } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[noTF]
+% {
+% \regex_extract_once:nnN, \regex_extract_once:nVN,
+% \regex_extract_once:NnN, \regex_extract_once:NVN,
+% \regex_extract_all:nnN, \regex_extract_all:nVN,
+% \regex_extract_all:NnN, \regex_extract_all:NVN,
+% \regex_replace_once:nnN, \regex_replace_once:nVN,
+% \regex_replace_once:NnN, \regex_replace_once:NVN,
+% \regex_replace_all:nnN, \regex_replace_all:nVN,
+% \regex_replace_all:NnN, \regex_replace_all:NVN,
+% \regex_split:NnN, \regex_split:NVN,
+% \regex_split:nnN, \regex_split:nVN
+% }
+% We define here $40$ user functions, following a common pattern in
+% terms of \texttt{:nnN} auxiliaries, defined in the coming
+% subsections. The auxiliary is handed \cs{@@_build:n} or
+% \cs{@@_build:N} with the appropriate regex argument, then all
+% other necessary arguments (replacement text, token list, \emph{etc.}
+% The conditionals call \cs{@@_return:} to return either
+% \texttt{true} or \texttt{false} once matching has been performed.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3
+ {
+ \cs_new_protected:Npn #2 ##1 { #1 { \@@_build:n {##1} } }
+ \cs_new_protected:Npn #3 ##1 { #1 { \@@_build:N ##1 } }
+ \prg_new_protected_conditional:Npnn #2 ##1##2##3 { T , F , TF }
+ { #1 { \@@_build:n {##1} } {##2} ##3 \@@_return: }
+ \prg_new_protected_conditional:Npnn #3 ##1##2##3 { T , F , TF }
+ { #1 { \@@_build:N ##1 } {##2} ##3 \@@_return: }
+ \cs_generate_variant:Nn #2 { nV }
+ \prg_generate_conditional_variant:Nnn #2 { nV } { T , F , TF }
+ \cs_generate_variant:Nn #3 { NV }
+ \prg_generate_conditional_variant:Nnn #3 { NV } { T , F , TF }
+ }
+\@@_tmp:w \@@_extract_once:nnN
+ \regex_extract_once:nnN \regex_extract_once:NnN
+\@@_tmp:w \@@_extract_all:nnN
+ \regex_extract_all:nnN \regex_extract_all:NnN
+\@@_tmp:w \@@_replace_once:nnN
+ \regex_replace_once:nnN \regex_replace_once:NnN
+\@@_tmp:w \@@_replace_all:nnN
+ \regex_replace_all:nnN \regex_replace_all:NnN
+\@@_tmp:w \@@_split:nnN \regex_split:nnN \regex_split:NnN
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[noTF]{\regex_replace_case_once:nN}
+% If the input is bad (odd number of items) then take the false
+% branch. Otherwise, use the same auxiliary as
+% \cs{regex_replace_once:nnN}, but with more complicated code to build
+% the automaton, and to find what replacement text to use. The
+% \cs{tl_item:nn} is only expanded once we know the value of
+% \cs{g_@@_case_int}, namely which case matched.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_replace_case_once:nNTF #1#2
+ {
+ \int_if_odd:nTF { \tl_count:n {#1} }
+ {
+ \msg_error:nneeee { regex } { case-odd }
+ { \token_to_str:N \regex_replace_case_once:nN(TF) } { code }
+ { \tl_count:n {#1} } { \tl_to_str:n {#1} }
+ \use_ii:nn
+ }
+ {
+ \@@_replace_once_aux:nnN
+ { \@@_case_build:e { \@@_tl_odd_items:n {#1} } }
+ { \@@_replacement:e { \tl_item:nn {#1} { 2 * \g_@@_case_int } } }
+ #2
+ \bool_if:NTF \g_@@_success_bool
+ }
+ }
+\cs_new_protected:Npn \regex_replace_case_once:nN #1#2
+ { \regex_replace_case_once:nNTF {#1} {#2} { } { } }
+\cs_new_protected:Npn \regex_replace_case_once:nNT #1#2#3
+ { \regex_replace_case_once:nNTF {#1} {#2} {#3} { } }
+\cs_new_protected:Npn \regex_replace_case_once:nNF #1#2
+ { \regex_replace_case_once:nNTF {#1} {#2} { } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[noTF]{\regex_replace_case_all:nN}
+% If the input is bad (odd number of items) then take the false
+% branch. Otherwise, use the same auxiliary as
+% \cs{regex_replace_all:nnN}, but with more complicated code to build
+% the automaton, and to find what replacement text to use.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_replace_case_all:nNTF #1#2
+ {
+ \int_if_odd:nTF { \tl_count:n {#1} }
+ {
+ \msg_error:nneeee { regex } { case-odd }
+ { \token_to_str:N \regex_replace_case_all:nN(TF) } { code }
+ { \tl_count:n {#1} } { \tl_to_str:n {#1} }
+ \use_ii:nn
+ }
+ {
+ \@@_replace_all_aux:nnN
+ { \@@_case_build:e { \@@_tl_odd_items:n {#1} } }
+ { \@@_case_replacement:e { \@@_tl_even_items:n {#1} } }
+ #2
+ \bool_if:NTF \g_@@_success_bool
+ }
+ }
+\cs_new_protected:Npn \regex_replace_case_all:nN #1#2
+ { \regex_replace_case_all:nNTF {#1} {#2} { } { } }
+\cs_new_protected:Npn \regex_replace_case_all:nNT #1#2#3
+ { \regex_replace_case_all:nNTF {#1} {#2} {#3} { } }
+\cs_new_protected:Npn \regex_replace_case_all:nNF #1#2
+ { \regex_replace_case_all:nNTF {#1} {#2} { } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Variables and helpers for user functions}
+%
+% \begin{variable}{\l_@@_match_count_int}
+% The number of matches found so far is stored
+% in \cs{l_@@_match_count_int}. This is only used
+% in the \cs{regex_count:nnN} functions.
+% \begin{macrocode}
+\int_new:N \l_@@_match_count_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_begin_flag, \l_@@_end_flag}
+% Those flags are raised to indicate begin-group or end-group tokens
+% that had to be added when extracting submatches.
+% \begin{macrocode}
+\flag_new:N \l_@@_begin_flag
+\flag_new:N \l_@@_end_flag
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_min_submatch_int, \l_@@_submatch_int, \l_@@_zeroth_submatch_int}
+% The end-points of each submatch are stored in two arrays whose index \meta{submatch} ranges
+% from \cs{l_@@_min_submatch_int} (inclusive) to
+% \cs{l_@@_submatch_int} (exclusive). Each successful match comes
+% with a $0$-th submatch (the full match), and one match for each
+% capturing group: submatches corresponding to the last successful
+% match are labelled starting at \texttt{zeroth_submatch}. The entry
+% \cs{l_@@_zeroth_submatch_int} in \cs{g_@@_submatch_prev_intarray} holds
+% the position at which that match attempt started: this is used for
+% splitting and replacements.
+% \begin{macrocode}
+\int_new:N \l_@@_min_submatch_int
+\int_new:N \l_@@_submatch_int
+\int_new:N \l_@@_zeroth_submatch_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_submatch_prev_intarray, \g_@@_submatch_begin_intarray, \g_@@_submatch_end_intarray, \g_@@_submatch_case_intarray}
+% Hold the place where the match attempt begun, the end-points of each
+% submatch, and which regex case the match corresponds to, respectively.
+% \begin{macrocode}
+\intarray_new:Nn \g_@@_submatch_prev_intarray { 65536 }
+\intarray_new:Nn \g_@@_submatch_begin_intarray { 65536 }
+\intarray_new:Nn \g_@@_submatch_end_intarray { 65536 }
+\intarray_new:Nn \g_@@_submatch_case_intarray { 65536 }
+% \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{variable}{\l_@@_added_begin_int, \l_@@_added_end_int}
+% Keep track of the number of left/right braces to add when performing
+% a regex operation such as a replacement.
+% \begin{macrocode}
+\int_new:N \l_@@_added_begin_int
+\int_new:N \l_@@_added_end_int
+% \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
+% not. It is used by all user conditionals.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_return:
+ {
+ \if_meaning:w \c_true_bool \g_@@_success_bool
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \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}
+% We don't track submatches, and stop after a single match. Build the
+% \textsc{nfa} with |#1|, and perform the match on the query |#2|.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_if_match:nn #1#2
+ {
+ \group_begin:
+ \@@_disable_submatches:
+ \@@_single_match:
+ #1
+ \@@_match:n {#2}
+ \group_end:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_match_case:nnTF}
+% \begin{macro}[EXP]{\@@_match_case_aux:nn}
+% The code would get badly messed up if the number of items in |#1|
+% were not even, so we catch this case, then follow the same code as
+% \cs{regex_match:nnTF} but using \cs{@@_case_build:n} and without
+% returning a result.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_match_case:nnTF #1#2
+ {
+ \int_if_odd:nTF { \tl_count:n {#1} }
+ {
+ \msg_error:nneeee { regex } { case-odd }
+ { \token_to_str:N \regex_match_case:nn(TF) } { code }
+ { \tl_count:n {#1} } { \tl_to_str:n {#1} }
+ \use_ii:nn
+ }
+ {
+ \@@_if_match:nn
+ { \@@_case_build:e { \@@_tl_odd_items:n {#1} } }
+ {#2}
+ \bool_if:NTF \g_@@_success_bool
+ }
+ }
+\cs_new:Npn \@@_match_case_aux:nn #1#2 { \exp_not:n { {#1} } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_count:nnN}
+% Again, we don't care about submatches. Instead of aborting after the
+% first \enquote{longest match} is found, we search for multiple
+% matches, incrementing \cs{l_@@_match_count_int} every time to
+% record the number of matches. Build the \textsc{nfa} and match. At
+% the end, store the result in the user's variable.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_count:nnN #1#2#3
+ {
+ \group_begin:
+ \@@_disable_submatches:
+ \int_zero:N \l_@@_match_count_int
+ \@@_multi_match:n { \int_incr:N \l_@@_match_count_int }
+ #1
+ \@@_match:n {#2}
+ \exp_args:NNNo
+ \group_end:
+ \int_set:Nn #3 { \int_use:N \l_@@_match_count_int }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Extracting submatches}
+%
+% \begin{macro}{\@@_extract_once:nnN, \@@_extract_all:nnN}
+% Match once or multiple times. After each match (or after the only
+% match), extract the submatches using \cs{@@_extract:}. At the
+% end, store the sequence containing all the submatches into the user
+% variable |#3| after closing the group.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_extract_once:nnN #1#2#3
+ {
+ \group_begin:
+ \@@_single_match:
+ #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
+ {
+ \group_begin:
+ \@@_multi_match:n { \@@_extract: }
+ #1
+ \@@_match:n {#2}
+ \@@_query_set:n {#2}
+ \@@_group_end_extract_seq:N #3
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_split:nnN}
+% Splitting at submatches is a bit more tricky. For each match,
+% extract all submatches, and replace the zeroth submatch by the part
+% of the query between the start of the match attempt and the start of
+% the zeroth submatch. This is inhibited if the delimiter matched an
+% empty token list at the start of this match attempt. After the last
+% match, store the last part of the token list, which ranges from the
+% start of the match attempt to the end of the query. This step is
+% inhibited if the last match was empty and at the very end: decrement
+% \cs{l_@@_submatch_int}, which controls which matches will be used.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_split:nnN #1#2#3
+ {
+ \group_begin:
+ \@@_multi_match:n
+ {
+ \if_int_compare:w
+ \l_@@_start_pos_int < \l_@@_success_pos_int
+ \@@_extract:
+ \__kernel_intarray_gset:Nnn \g_@@_submatch_prev_intarray
+ \l_@@_zeroth_submatch_int \c_zero_int
+ \__kernel_intarray_gset:Nnn \g_@@_submatch_end_intarray
+ \l_@@_zeroth_submatch_int
+ {
+ \__kernel_intarray_item:Nn \g_@@_submatch_begin_intarray
+ \l_@@_zeroth_submatch_int
+ }
+ \__kernel_intarray_gset:Nnn \g_@@_submatch_begin_intarray
+ \l_@@_zeroth_submatch_int
+ \l_@@_start_pos_int
+ \fi:
+ }
+ #1
+ \@@_match:n {#2}
+ \@@_query_set:n {#2}
+ \__kernel_intarray_gset:Nnn \g_@@_submatch_prev_intarray
+ \l_@@_submatch_int \c_zero_int
+ \__kernel_intarray_gset:Nnn \g_@@_submatch_end_intarray
+ \l_@@_submatch_int
+ \l_@@_max_pos_int
+ \__kernel_intarray_gset:Nnn \g_@@_submatch_begin_intarray
+ \l_@@_submatch_int
+ \l_@@_start_pos_int
+ \int_incr:N \l_@@_submatch_int
+ \if_meaning:w \c_true_bool \l_@@_empty_success_bool
+ \if_int_compare:w \l_@@_start_pos_int = \l_@@_max_pos_int
+ \int_decr:N \l_@@_submatch_int
+ \fi:
+ \fi:
+ \@@_group_end_extract_seq:N #3
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_group_end_extract_seq:N}
+% \begin{macro}{\@@_extract_seq:N}
+% \begin{macro}{\@@_extract_seq:NNn}
+% \begin{macro}{\@@_extract_seq_loop:Nw}
+% The end-points of submatches are stored as entries of two arrays
+% from \cs{l_@@_min_submatch_int} to \cs{l_@@_submatch_int}
+% (exclusive). Extract the relevant ranges into \cs{g_@@_internal_tl},
+% separated by \cs{@@_tmp:w} |{}|. We keep track in the two flags
+% \texttt{__regex_begin} and \texttt{__regex_end} of the number of
+% begin-group or end-group tokens added to make each of these items
+% overall balanced. At this step, |}{| is counted as being balanced
+% (same number of begin-group and end-group tokens). This problem is
+% caught by \cs{@@_extract_check:w}, explained later. After
+% complaining about any begin-group or end-group tokens we had to add,
+% we are ready to construct the user's sequence outside the group.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_group_end_extract_seq:N #1
+ {
+ \flag_clear:N \l_@@_begin_flag
+ \flag_clear:N \l_@@_end_flag
+ \cs_set_eq:NN \@@_tmp:w \scan_stop:
+ \__kernel_tl_gset:Nx \g_@@_internal_tl
+ {
+ \int_step_function:nnN \l_@@_min_submatch_int
+ { \l_@@_submatch_int - \c_one_int } \@@_extract_seq_aux:n
+ \@@_tmp:w
+ }
+ \int_set:Nn \l_@@_added_begin_int
+ { \flag_height:N \l_@@_begin_flag }
+ \int_set:Nn \l_@@_added_end_int
+ { \flag_height:N \l_@@_end_flag }
+ \tex_afterassignment:D \@@_extract_check:w
+ \__kernel_tl_gset:Nx \g_@@_internal_tl
+ { \g_@@_internal_tl \if_false: { \fi: } }
+ \int_compare:nNnT
+ { \l_@@_added_begin_int + \l_@@_added_end_int } > \c_zero_int
+ {
+ \msg_error:nneee { regex } { result-unbalanced }
+ { splitting~or~extracting~submatches }
+ { \int_use:N \l_@@_added_begin_int }
+ { \int_use:N \l_@@_added_end_int }
+ }
+ \group_end:
+ \@@_extract_seq:N #1
+ }
+\cs_gset_protected:Npn \@@_extract_seq:N #1
+ {
+ \seq_clear:N #1
+ \cs_set_eq:NN \@@_tmp:w \@@_extract_seq_loop:Nw
+ \exp_after:wN \@@_extract_seq:NNn
+ \exp_after:wN #1
+ \g_@@_internal_tl \use_none:nnn
+ }
+\cs_new_protected:Npn \@@_extract_seq:NNn #1#2#3
+ { #3 #2 #1 \prg_do_nothing: }
+\cs_new_protected:Npn \@@_extract_seq_loop:Nw #1#2 \@@_tmp:w #3
+ {
+ \seq_put_right:No #1 {#2}
+ #3 \@@_extract_seq_loop:Nw #1 \prg_do_nothing:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_extract_seq_aux:n, \@@_extract_seq_aux:ww}
+% The \texttt{:n} auxiliary builds one item of the sequence of
+% submatches. First compute the brace balance of the submatch, then
+% extract the submatch from the query, adding the appropriate braces
+% and raising a flag if the submatch is not balanced.
+% \begin{macrocode}
+\cs_new:Npn \@@_extract_seq_aux:n #1
+ {
+ \@@_tmp:w { }
+ \exp_after:wN \@@_extract_seq_aux:ww
+ \int_value:w \@@_submatch_balance:n {#1} ; #1;
+ }
+\cs_new:Npn \@@_extract_seq_aux:ww #1; #2;
+ {
+ \if_int_compare:w #1 < \c_zero_int
+ \prg_replicate:nn {-#1}
+ {
+ \flag_raise:N \l_@@_begin_flag
+ \exp_not:n { { \if_false: } \fi: }
+ }
+ \fi:
+ \@@_query_submatch:n {#2}
+ \if_int_compare:w #1 > \c_zero_int
+ \prg_replicate:nn {#1}
+ {
+ \flag_raise:N \l_@@_end_flag
+ \exp_not:n { \if_false: { \fi: } }
+ }
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_extract_check:w, \@@_extract_check:n,
+% \@@_extract_check_loop:w, \@@_extract_check_end:w
+% }
+% In \cs{@@_group_end_extract_seq:N} we had to expand
+% \cs{g_@@_internal_tl} to turn \cs{if_false:} constructions into
+% actual begin-group and end-group tokens. This is done with a
+% \cs{__kernel_tl_gset:Nx} assignment, and \cs{@@_extract_check:w} is
+% run immediately after this assignment ends, thanks to the
+% \tn{afterassignment} primitive. If all of the items were properly
+% balanced (enough begin-group tokens before end-group tokens, so |}{|
+% is not) then \cs{@@_extract_check:w} is called just before the
+% closing brace of the \cs{__kernel_tl_gset:Nx} (thanks to our sneaky
+% \cs{if_false:} |{| \cs{fi:} |}| construction), and finds that there
+% is nothing left to expand. If any of the items is unbalanced, the
+% assignment gets ended early by an extra end-group token, and our
+% check finds more tokens needing to be expanded in a new
+% \cs{__kernel_tl_gset:Nx} assignment. We need to add a begin-group
+% and an end-group tokens to the unbalanced item, namely to the last
+% item found so far, which we reach through a loop.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_extract_check:w
+ {
+ \exp_after:wN \@@_extract_check:n
+ \exp_after:wN { \if_false: } \fi:
+ }
+\cs_new_protected:Npn \@@_extract_check:n #1
+ {
+ \tl_if_empty:nF {#1}
+ {
+ \int_incr:N \l_@@_added_begin_int
+ \int_incr:N \l_@@_added_end_int
+ \tex_afterassignment:D \@@_extract_check:w
+ \__kernel_tl_gset:Nx \g_@@_internal_tl
+ {
+ \exp_after:wN \@@_extract_check_loop:w
+ \g_@@_internal_tl
+ \@@_tmp:w \@@_extract_check_end:w
+ #1
+ }
+ }
+ }
+\cs_new:Npn \@@_extract_check_loop:w #1 \@@_tmp:w #2
+ {
+ #2
+ \exp_not:o {#1}
+ \@@_tmp:w { }
+ \@@_extract_check_loop:w \prg_do_nothing:
+ }
+% \end{macrocode}
+% Arguments of \cs{@@_extract_check_end:w} are: |#1| is the part of
+% the item before the extra end-group token; |#2| is junk; |#3| is
+% \cs{prg_do_nothing:} followed by the not-yet-expanded part of the
+% item after the extra end-group token. In the replacement text, the
+% first brace and the \cs{if_false:} |{| \cs{fi:} |}| construction are
+% the added begin-group and end-group tokens (the latter being not-yet
+% expanded, just like~|#3|), while the closing brace after
+% \cs{exp_not:o} |{#1}| replaces the extra end-group token that had
+% ended the assignment early. In particular this means that the
+% character code of that end-group token is lost.
+% \begin{macrocode}
+\cs_new:Npn \@@_extract_check_end:w
+ \exp_not:o #1#2 \@@_extract_check_loop:w #3 \@@_tmp:w
+ {
+ { \exp_not:o {#1} }
+ #3
+ \if_false: { \fi: }
+ \@@_tmp:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_extract:, \@@_extract_aux:w}
+% 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:
+ {
+ \if_meaning:w \c_true_bool \g_@@_success_bool
+ \int_set_eq:NN \l_@@_zeroth_submatch_int \l_@@_submatch_int
+ \prg_replicate:nn \l_@@_capturing_group_int
+ {
+ \__kernel_intarray_gset:Nnn \g_@@_submatch_prev_intarray
+ \l_@@_submatch_int \c_zero_int
+ \__kernel_intarray_gset:Nnn \g_@@_submatch_case_intarray
+ \l_@@_submatch_int \c_zero_int
+ \int_incr:N \l_@@_submatch_int
+ }
+ \__kernel_intarray_gset:Nnn \g_@@_submatch_prev_intarray
+ \l_@@_zeroth_submatch_int \l_@@_start_pos_int
+ \__kernel_intarray_gset:Nnn \g_@@_submatch_case_intarray
+ \l_@@_zeroth_submatch_int \g_@@_case_int
+ \int_zero:N \l_@@_internal_a_int
+ \exp_after:wN \@@_extract_aux:w \l_@@_success_submatches_tl
+ \prg_break_point: \@@_use_none_delimit_by_q_recursion_stop:w ,
+ \q_@@_recursion_stop
+ \fi:
+ }
+\cs_new_protected:Npn \@@_extract_aux:w #1 ,
+ {
+ \prg_break: #1 \prg_break_point:
+ \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:
+ \__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_incr:N \l_@@_internal_a_int
+ \@@_extract_aux:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Replacement}
+%
+% \begin{macro}{\@@_replace_once:nnN, \@@_replace_once_aux:nnN}
+% Build the \textsc{nfa} and the replacement functions, then find a
+% single match. If the match failed, simply exit the
+% group. Otherwise, we do the replacement. Extract submatches. Compute
+% the brace balance corresponding to replacing this match by the
+% replacement (this depends on submatches). Prepare the replaced token
+% list: the replacement function produces the tokens from the start of
+% the query to the start of the match and the replacement text for
+% this match; we need to add the tokens from the end of the match to
+% the end of the query. Finally, store the result in the user's
+% variable after closing the group: this step involves an additional
+% \texttt{e}-expansion, and checks that braces are balanced in the
+% final result.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replace_once:nnN #1#2
+ { \@@_replace_once_aux:nnN {#1} { \@@_replacement:n {#2} } }
+\cs_new_protected:Npn \@@_replace_once_aux:nnN #1#2#3
+ {
+ \group_begin:
+ \@@_single_match:
+ #1
+ \exp_args:No \@@_match:n {#3}
+ \bool_if:NTF \g_@@_success_bool
+ {
+ \@@_extract:
+ \exp_args:No \@@_query_set:n {#3}
+ #2
+ \int_set:Nn \l_@@_balance_int
+ { \@@_replacement_balance_one_match:n \l_@@_zeroth_submatch_int }
+ \__kernel_tl_set:Nx \l_@@_internal_a_tl
+ {
+ \@@_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
+ }
+ \@@_group_end_replace:N #3
+ }
+ { \group_end: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_replace_all:nnN}
+% Match multiple times, and for every match, extract submatches and
+% additionally store the position at which the match attempt started.
+% The entries from \cs{l_@@_min_submatch_int} to
+% \cs{l_@@_submatch_int} hold information about submatches of every
+% match in order; each match corresponds to
+% \cs{l_@@_capturing_group_int} consecutive entries.
+% Compute the brace balance corresponding to doing all the
+% replacements: this is the sum of brace balances for replacing each
+% match. Join together the replacement texts for each match (including
+% the part of the query before the match), and the end of the query.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replace_all:nnN #1#2
+ { \@@_replace_all_aux:nnN {#1} { \@@_replacement:n {#2} } }
+\cs_new_protected:Npn \@@_replace_all_aux:nnN #1#2#3
+ {
+ \group_begin:
+ \@@_multi_match:n { \@@_extract: }
+ #1
+ \exp_args:No \@@_match:n {#3}
+ \exp_args:No \@@_query_set:n {#3}
+ #2
+ \int_set:Nn \l_@@_balance_int
+ {
+ \c_zero_int
+ \int_step_function:nnnN
+ \l_@@_min_submatch_int
+ \l_@@_capturing_group_int
+ { \l_@@_submatch_int - \c_one_int }
+ \@@_replacement_balance_one_match:n
+ }
+ \__kernel_tl_set:Nx \l_@@_internal_a_tl
+ {
+ \int_step_function:nnnN
+ \l_@@_min_submatch_int
+ \l_@@_capturing_group_int
+ { \l_@@_submatch_int - \c_one_int }
+ \@@_replacement_do_one_match:n
+ \@@_query_range:nn
+ \l_@@_start_pos_int \l_@@_max_pos_int
+ }
+ \@@_group_end_replace:N #3
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_group_end_replace:N, \@@_group_end_replace_try:,
+% \@@_group_end_replace_check:w, \@@_group_end_replace_check:n
+% }
+% At this stage \cs{l_@@_internal_a_tl} (|e|-expands to the desired
+% result). Guess from \cs{l_@@_balance_int} the number of braces to
+% add before or after the result then try expanding. The simplest
+% case is when \cs{l_@@_internal_a_tl} together with the braces we
+% insert via \cs{prg_replicate:nn} give a balanced result, and the
+% assignment ends at the \cs{if_false:} |{| \cs{fi:} |}| construction:
+% then \cs{@@_group_end_replace_check:w} sees that there is no
+% material left and we successfully found the result. The harder case
+% is that expanding \cs{l_@@_internal_a_tl} may produce extra closing
+% braces and end the assignment early. Then we grab the remaining code
+% using; importantly, what follows has not yet been expanded so that
+% \cs{@@_group_end_replace_check:n} grabs everything until the last
+% brace in \cs{@@_group_end_replace_try:}, letting us try again with
+% an extra surrounding pair of braces.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_group_end_replace:N #1
+ {
+ \int_set:Nn \l_@@_added_begin_int
+ { \int_max:nn { - \l_@@_balance_int } \c_zero_int }
+ \int_set:Nn \l_@@_added_end_int
+ { \int_max:nn \l_@@_balance_int \c_zero_int }
+ \@@_group_end_replace_try:
+ \int_compare:nNnT { \l_@@_added_begin_int + \l_@@_added_end_int }
+ > \c_zero_int
+ {
+ \msg_error:nneee { regex } { result-unbalanced }
+ { replacing } { \int_use:N \l_@@_added_begin_int }
+ { \int_use:N \l_@@_added_end_int }
+ }
+ \group_end:
+ \tl_set_eq:NN #1 \g_@@_internal_tl
+ }
+\cs_new_protected:Npn \@@_group_end_replace_try:
+ {
+ \tex_afterassignment:D \@@_group_end_replace_check:w
+ \__kernel_tl_gset:Nx \g_@@_internal_tl
+ {
+ \prg_replicate:nn \l_@@_added_begin_int { { \if_false: } \fi: }
+ \l_@@_internal_a_tl
+ \prg_replicate:nn \l_@@_added_end_int { \if_false: { \fi: } }
+ \if_false: { \fi: }
+ }
+ }
+\cs_new_protected:Npn \@@_group_end_replace_check:w
+ {
+ \exp_after:wN \@@_group_end_replace_check:n
+ \exp_after:wN { \if_false: } \fi:
+ }
+\cs_new_protected:Npn \@@_group_end_replace_check:n #1
+ {
+ \tl_if_empty:nF {#1}
+ {
+ \int_incr:N \l_@@_added_begin_int
+ \int_incr:N \l_@@_added_end_int
+ \@@_group_end_replace_try:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \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_begin: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. 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. To be more precise, |#1| consists
+% of tokens that \texttt{o}-expand and \texttt{e}-expand to the last
+% token seen, for example it is \cs{exp_not:N} \meta{cs} for a control
+% sequence. This means that just doing \cs{exp_after:wN}
+% \cs{l_@@_peek_true_tl} |#1| would be unsafe because the expansion of
+% \meta{cs} would be suppressed.
+% \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_args:NNo \use:nn \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:e} 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{e}-expansion, \cs{use:e} 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:e
+ {
+ \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{e}-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{e}-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:w #1 + ##1 \scan_stop: } }
+ \else:
+ \tl_build_put_right:Nn \l_@@_build_tl
+ {
+ \exp:w
+ \@@_query_submatch:n { \@@_int_eval:w #1 + ##1 \scan_stop: }
+ \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}
+%
+% Messages for the preparsing phase.
+% \begin{macrocode}
+\use:e
+ {
+ \msg_new:nnn { regex } { trailing-backslash }
+ { Trailing~'\iow_char:N\\'~in~regex~or~replacement. }
+ \msg_new:nnn { regex } { x-missing-rbrace }
+ {
+ Missing~brace~'\iow_char:N\}'~in~regex~
+ '...\iow_char:N\\x\iow_char:N\{...##1'.
+ }
+ \msg_new:nnn { regex } { x-overflow }
+ {
+ Character~code~##1~too~large~in~
+ \iow_char:N\\x\iow_char:N\{##2\iow_char:N\}~regex.
+ }
+ }
+% \end{macrocode}
+%
+% Invalid quantifier.
+% \begin{macrocode}
+\msg_new:nnnn { regex } { invalid-quantifier }
+ { Braced~quantifier~'#1'~may~not~be~followed~by~'#2'. }
+ {
+ The~character~'#2'~is~invalid~in~the~braced~quantifier~'#1'.~
+ The~only~valid~quantifiers~are~'*',~'?',~'+',~'{<int>}',~
+ '{<min>,}'~and~'{<min>,<max>}',~optionally~followed~by~'?'.
+ }
+% \end{macrocode}
+%
+% Messages for missing or extra closing brackets and parentheses, with
+% some fancy singular/plural handling for the case of parentheses.
+% \begin{macrocode}
+\msg_new:nnnn { regex } { missing-rbrack }
+ { Missing~right~bracket~inserted~in~regular~expression. }
+ {
+ LaTeX~was~given~a~regular~expression~where~a~character~class~
+ was~started~with~'[',~but~the~matching~']'~is~missing.
+ }
+\msg_new:nnnn { regex } { missing-rparen }
+ {
+ Missing~right~
+ \int_compare:nTF { #1 = 1 } { parenthesis } { parentheses } ~
+ inserted~in~regular~expression.
+ }
+ {
+ LaTeX~was~given~a~regular~expression~with~\int_eval:n {#1} ~
+ more~left~parentheses~than~right~parentheses.
+ }
+\msg_new:nnnn { regex } { extra-rparen }
+ { Extra~right~parenthesis~ignored~in~regular~expression. }
+ {
+ LaTeX~came~across~a~closing~parenthesis~when~no~submatch~group~
+ was~open.~The~parenthesis~will~be~ignored.
+ }
+% \end{macrocode}
+%
+% Some escaped alphanumerics are not allowed everywhere.
+% \begin{macrocode}
+\msg_new:nnnn { regex } { bad-escape }
+ {
+ Invalid~escape~'\iow_char:N\\#1'~
+ \@@_if_in_cs:TF { within~a~control~sequence. }
+ {
+ \@@_if_in_class:TF
+ { in~a~character~class. }
+ { following~a~category~test. }
+ }
+ }
+ {
+ The~escape~sequence~'\iow_char:N\\#1'~may~not~appear~
+ \@@_if_in_cs:TF
+ {
+ within~a~control~sequence~test~introduced~by~
+ '\iow_char:N\\c\iow_char:N\{'.
+ }
+ {
+ \@@_if_in_class:TF
+ { within~a~character~class~ }
+ { following~a~category~test~such~as~'\iow_char:N\\cL'~ }
+ because~it~does~not~match~exactly~one~character.
+ }
+ }
+% \end{macrocode}
+%
+% Range errors.
+% \begin{macrocode}
+\msg_new:nnnn { regex } { range-missing-end }
+ { Invalid~end-point~for~range~'#1-#2'~in~character~class. }
+ {
+ The~end-point~'#2'~of~the~range~'#1-#2'~may~not~serve~as~an~
+ end-point~for~a~range:~alphanumeric~characters~should~not~be~
+ escaped,~and~non-alphanumeric~characters~should~be~escaped.
+ }
+\msg_new:nnnn { regex } { range-backwards }
+ { Range~'[#1-#2]'~out~of~order~in~character~class. }
+ {
+ In~ranges~of~characters~'[x-y]'~appearing~in~character~classes,~
+ the~first~character~code~must~not~be~larger~than~the~second.~
+ Here,~'#1'~has~character~code~\int_eval:n {`#1},~while~
+ '#2'~has~character~code~\int_eval:n {`#2}.
+ }
+% \end{macrocode}
+%
+% Errors related to |\c| and |\u|.
+% \begin{macrocode}
+\msg_new:nnnn { regex } { c-bad-mode }
+ { Invalid~nested~'\iow_char:N\\c'~escape~in~regular~expression. }
+ {
+ The~'\iow_char:N\\c'~escape~cannot~be~used~within~
+ a~control~sequence~test~'\iow_char:N\\c{...}'~
+ nor~another~category~test.~
+ To~combine~several~category~tests,~use~'\iow_char:N\\c[...]'.
+ }
+\msg_new:nnnn { regex } { c-C-invalid }
+ { '\iow_char:N\\cC'~should~be~followed~by~'.'~or~'(',~not~'#1'. }
+ {
+ The~'\iow_char:N\\cC'~construction~restricts~the~next~item~to~be~a~
+ control~sequence~or~the~next~group~to~be~made~of~control~sequences.~
+ It~only~makes~sense~to~follow~it~by~'.'~or~by~a~group.
+ }
+\msg_new:nnnn { regex } { cu-lbrace }
+ { Left~braces~must~be~escaped~in~'\iow_char:N\\#1{...}'. }
+ {
+ Constructions~such~as~'\iow_char:N\\#1{...\iow_char:N\{...}'~are~
+ not~allowed~and~should~be~replaced~by~
+ '\iow_char:N\\#1{...\token_to_str:N\{...}'.
+ }
+\msg_new:nnnn { regex } { c-lparen-in-class }
+ { Catcode~test~cannot~apply~to~group~in~character~class }
+ {
+ Construction~such~as~'\iow_char:N\\cL(abc)'~are~not~allowed~inside~a~
+ class~'[...]'~because~classes~do~not~match~multiple~characters~at~once.
+ }
+\msg_new:nnnn { regex } { c-missing-rbrace }
+ { Missing~right~brace~inserted~for~'\iow_char:N\\c'~escape. }
+ {
+ LaTeX~was~given~a~regular~expression~where~a~
+ '\iow_char:N\\c\iow_char:N\{...'~construction~was~not~ended~
+ with~a~closing~brace~'\iow_char:N\}'.
+ }
+\msg_new:nnnn { regex } { c-missing-rbrack }
+ { Missing~right~bracket~inserted~for~'\iow_char:N\\c'~escape. }
+ {
+ A~construction~'\iow_char:N\\c[...'~appears~in~a~
+ regular~expression,~but~the~closing~']'~is~not~present.
+ }
+\msg_new:nnnn { regex } { c-missing-category }
+ { Invalid~character~'#1'~following~'\iow_char:N\\c'~escape. }
+ {
+ In~regular~expressions,~the~'\iow_char:N\\c'~escape~sequence~
+ may~only~be~followed~by~a~left~brace,~a~left~bracket,~or~a~
+ capital~letter~representing~a~character~category,~namely~
+ one~of~'ABCDELMOPSTU'.
+ }
+\msg_new:nnnn { regex } { c-trailing }
+ { Trailing~category~code~escape~'\iow_char:N\\c'... }
+ {
+ A~regular~expression~ends~with~'\iow_char:N\\c'~followed~
+ by~a~letter.~It~will~be~ignored.
+ }
+\msg_new:nnnn { regex } { u-missing-lbrace }
+ { Missing~left~brace~following~'\iow_char:N\\u'~escape. }
+ {
+ The~'\iow_char:N\\u'~escape~sequence~must~be~followed~by~
+ a~brace~group~with~the~name~of~the~variable~to~use.
+ }
+\msg_new:nnnn { regex } { u-missing-rbrace }
+ { Missing~right~brace~inserted~for~'\iow_char:N\\u'~escape. }
+ {
+ LaTeX~
+ \str_if_eq:eeTF { } {#2}
+ { reached~the~end~of~the~string~ }
+ { encountered~an~escaped~alphanumeric~character '\iow_char:N\\#2'~ }
+ when~parsing~the~argument~of~an~
+ '\iow_char:N\\u\iow_char:N\{...\}'~escape.
+ }
+% \end{macrocode}
+%
+% Errors when encountering the \textsc{posix} syntax |[:...:]|.
+% \begin{macrocode}
+\msg_new:nnnn { regex } { posix-unsupported }
+ { POSIX~collating~element~'[#1 ~ #1]'~not~supported. }
+ {
+ The~'[.foo.]'~and~'[=bar=]'~syntaxes~have~a~special~meaning~
+ in~POSIX~regular~expressions.~This~is~not~supported~by~LaTeX.~
+ Maybe~you~forgot~to~escape~a~left~bracket~in~a~character~class?
+ }
+\msg_new:nnnn { regex } { posix-unknown }
+ { POSIX~class~'[:#1:]'~unknown. }
+ {
+ '[:#1:]'~is~not~among~the~known~POSIX~classes~
+ '[:alnum:]',~'[:alpha:]',~'[:ascii:]',~'[:blank:]',~
+ '[:cntrl:]',~'[:digit:]',~'[:graph:]',~'[:lower:]',~
+ '[:print:]',~'[:punct:]',~'[:space:]',~'[:upper:]',~
+ '[:word:]',~and~'[:xdigit:]'.
+ }
+\msg_new:nnnn { regex } { posix-missing-close }
+ { Missing~closing~':]'~for~POSIX~class. }
+ { The~POSIX~syntax~'#1'~must~be~followed~by~':]',~not~'#2'. }
+% \end{macrocode}
+%
+% In various cases, the result of a \pkg{l3regex} operation can leave us
+% with an unbalanced token list, which we must re-balance by adding
+% begin-group or end-group character tokens.
+% \begin{macrocode}
+\msg_new:nnnn { regex } { result-unbalanced }
+ { Missing~brace~inserted~when~#1. }
+ {
+ LaTeX~was~asked~to~do~some~regular~expression~operation,~
+ and~the~resulting~token~list~would~not~have~the~same~number~
+ of~begin-group~and~end-group~tokens.~Braces~were~inserted:~
+ #2~left,~#3~right.
+ }
+% \end{macrocode}
+%
+% Error message for unknown options.
+% \begin{macrocode}
+\msg_new:nnnn { regex } { unknown-option }
+ { Unknown~option~'#1'~for~regular~expressions. }
+ {
+ The~only~available~option~is~'case-insensitive',~toggled~by~
+ '(?i)'~and~'(?-i)'.
+ }
+\msg_new:nnnn { regex } { special-group-unknown }
+ { Unknown~special~group~'#1~...'~in~a~regular~expression. }
+ {
+ The~only~valid~constructions~starting~with~'(?'~are~
+ '(?:~...~)',~'(?|~...~)',~'(?i)',~and~'(?-i)'.
+ }
+% \end{macrocode}
+%
+% Errors in the replacement text.
+% \begin{macrocode}
+\msg_new:nnnn { regex } { replacement-c }
+ { Misused~'\iow_char:N\\c'~command~in~a~replacement~text. }
+ {
+ In~a~replacement~text,~the~'\iow_char:N\\c'~escape~sequence~
+ can~be~followed~by~one~of~the~letters~'ABCDELMOPSTU'~
+ or~a~brace~group,~not~by~'#1'.
+ }
+\msg_new:nnnn { regex } { replacement-u }
+ { Misused~'\iow_char:N\\u'~command~in~a~replacement~text. }
+ {
+ In~a~replacement~text,~the~'\iow_char:N\\u'~escape~sequence~
+ must~be~~followed~by~a~brace~group~holding~the~name~of~the~
+ variable~to~use.
+ }
+\msg_new:nnnn { regex } { replacement-g }
+ {
+ Missing~brace~for~the~'\iow_char:N\\g'~construction~
+ in~a~replacement~text.
+ }
+ {
+ In~the~replacement~text~for~a~regular~expression~search,~
+ submatches~are~represented~either~as~'\iow_char:N \\g{dd..d}',~
+ or~'\\d',~where~'d'~are~single~digits.~Here,~a~brace~is~missing.
+ }
+\msg_new:nnnn { regex } { replacement-catcode-end }
+ {
+ Missing~character~for~the~'\iow_char:N\\c<category><character>'~
+ construction~in~a~replacement~text.
+ }
+ {
+ In~a~replacement~text,~the~'\iow_char:N\\c'~escape~sequence~
+ can~be~followed~by~one~of~the~letters~'ABCDELMOPSTU'~representing~
+ the~character~category.~Then,~a~character~must~follow.~LaTeX~
+ reached~the~end~of~the~replacement~when~looking~for~that.
+ }
+\msg_new:nnnn { regex } { replacement-catcode-escaped }
+ {
+ Escaped~letter~or~digit~after~category~code~in~replacement~text.
+ }
+ {
+ In~a~replacement~text,~the~'\iow_char:N\\c'~escape~sequence~
+ can~be~followed~by~one~of~the~letters~'ABCDELMOPSTU'~representing~
+ the~character~category.~Then,~a~character~must~follow,~not~
+ '\iow_char:N\\#2'.
+ }
+\msg_new:nnnn { regex } { replacement-catcode-in-cs }
+ {
+ Category~code~'\iow_char:N\\c#1#3'~ignored~inside~
+ '\iow_char:N\\c\{...\}'~in~a~replacement~text.
+ }
+ {
+ In~a~replacement~text,~the~category~codes~of~the~argument~of~
+ '\iow_char:N\\c\{...\}'~are~ignored~when~building~the~control~
+ sequence~name.
+ }
+\msg_new:nnnn { regex } { replacement-null-space }
+ { TeX~cannot~build~a~space~token~with~character~code~0. }
+ {
+ You~asked~for~a~character~token~with~category~space,~
+ and~character~code~0,~for~instance~through~
+ '\iow_char:N\\cS\iow_char:N\\x00'.~
+ This~specific~case~is~impossible~and~will~be~replaced~
+ by~a~normal~space.
+ }
+\msg_new:nnnn { regex } { replacement-missing-rbrace }
+ { Missing~right~brace~inserted~in~replacement~text. }
+ {
+ There~ \int_compare:nTF { #1 = 1 } { was } { were } ~ #1~
+ missing~right~\int_compare:nTF { #1 = 1 } { brace } { braces } .
+ }
+\msg_new:nnnn { regex } { replacement-missing-rparen }
+ { Missing~right~parenthesis~inserted~in~replacement~text. }
+ {
+ There~ \int_compare:nTF { #1 = 1 } { was } { were } ~ #1~
+ missing~right~
+ \int_compare:nTF { #1 = 1 } { parenthesis } { parentheses } .
+ }
+\msg_new:nnn { regex } { submatch-too-big }
+ { Submatch~#1~used~but~regex~only~has~#2~group(s) }
+% \end{macrocode}
+%
+% Some escaped alphanumerics are not allowed everywhere.
+% \begin{macrocode}
+\msg_new:nnnn { regex } { backwards-quantifier }
+ { Quantifer~"{#1,#2}"~is~backwards. }
+ { The~values~given~in~a~quantifier~must~be~in~order. }
+% \end{macrocode}
+%
+% Used in user commands, and when showing a regex.
+% \begin{macrocode}
+\msg_new:nnnn { regex } { case-odd }
+ { #1~with~odd~number~of~items }
+ {
+ There~must~be~a~#2~part~for~each~regex:~
+ found~odd~number~of~items~(#3)~in\\
+ \iow_indent:n {#4}
+ }
+\msg_new:nnn { regex } { show }
+ {
+ >~Compiled~regex~
+ \tl_if_empty:nTF {#1} { variable~ #2 } { {#1} } :
+ #3
+ }
+\prop_gput:Nnn \g_msg_module_name_prop { regex } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { regex } { }
+% \end{macrocode}
+%
+% \begin{macro}{\@@_msg_repeated:nnN}
+% This is not technically a message, but seems related enough to go
+% there. The arguments are: |#1| is the minimum number of repetitions;
+% |#2| is the number of allowed extra repetitions ($-1$ for infinite
+% number), and |#3| tells us about lazyness.
+% \begin{macrocode}
+\cs_new:Npn \@@_msg_repeated:nnN #1#2#3
+ {
+ \str_if_eq:eeF { #1 #2 } { 1 0 }
+ {
+ , ~ repeated ~
+ \int_case:nnF {#2}
+ {
+ { -1 } { #1~or~more~times,~\bool_if:NTF #3 { lazy } { greedy } }
+ { 0 } { #1~times }
+ }
+ {
+ between~#1~and~\int_eval:n {#1+#2}~times,~
+ \bool_if:NTF #3 { lazy } { greedy }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Code for tracing}
+%
+% There is a more extensive implementation of tracing in the l3trial
+% package \pkg{l3trace}. Function names are a bit different but could
+% be merged.
+%
+% \begin{macro}
+% {\@@_trace_push:nnN, \@@_trace_pop:nnN, \@@_trace:nne}
+% Here |#1| is the module name (\texttt{regex}) and |#2| is
+% typically~1. If the module's current tracing level is less than
+% |#2| show nothing, otherwise write |#3| to the terminal.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_trace_push:nnN #1#2#3
+ { \@@_trace:nne {#1} {#2} { entering~ \token_to_str:N #3 } }
+\cs_new_protected:Npn \@@_trace_pop:nnN #1#2#3
+ { \@@_trace:nne {#1} {#2} { leaving~ \token_to_str:N #3 } }
+\cs_new_protected:Npn \@@_trace:nne #1#2#3
+ {
+ \int_compare:nNnF
+ { \int_use:c { g_@@_trace_#1_int } } < {#2}
+ { \iow_term:e { Trace:~#3 } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\g_@@_trace_regex_int}
+% No tracing when that is zero.
+% \begin{macrocode}
+\int_new:N \g_@@_trace_regex_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_trace_states:n}
+% This function lists the contents of all states of the \textsc{nfa},
+% stored in \tn{toks} from $0$ to \cs{l_@@_max_state_int}
+% (excluded).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_trace_states:n #1
+ {
+ \int_step_inline:nnn
+ \l_@@_min_state_int
+ { \l_@@_max_state_int - \c_one_int }
+ {
+ \@@_trace:nne { regex } {#1}
+ { \iow_char:N \\toks ##1 = { \@@_toks_use:w ##1 } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
+% \endinput
+%^^A NOT IMPLEMENTED
+%^^A \p{xx} a character with the xx property
+%^^A \P{xx} a character without the xx property
+%^^A (?=...) positive look ahead
+%^^A (?!...) negative look ahead
+%^^A (?<=...) positive look behind
+%^^A (?<!...) negative look behind
+%^^A (?<name>...) or (?'name'...) or (?P<name>...)
+%^^A named capturing group
+%^^A \R a newline sequence
+%^^A \X an extended Unicode sequence
+%^^A (?C) or (?Cn) callout with data n
+%^^A (?R) recurse whole pattern
+%^^A (?[+-]n) or \g<[+-]n> or (?&name) or (?P>name) or \g<name>
+%^^A call subpattern
+%^^A (?([+-]n)... or (?(<name>)...
+%^^A reference condition
+%^^A (?(R)... or (?(Rn)... or (?(R&name)...
+%^^A recursion condition
+%^^A (?(DEFINE)... define subpattern for reference
+%^^A (?(assert)... assertion condition
+%^^A (?(?=..)..|..) positive/negative look ahead/behind condition
+%^^A (*ACCEPT) force successful match
+%^^A (*FAIL) force backtrack; synonym (*F)
+%^^A (*COMMIT) overall failure, no advance of starting point
+%^^A (*PRUNE) advance to next starting character
+%^^A (*SKIP) advance start to current matching position
+%^^A (*THEN) local failure, backtrack to next alternation
+%^^A (*CR) or (*LF) or (*CRLF) or (*ANYCRLF) or (*ANY)
+%^^A newline convention
+%^^A (*BSR_ANYCRLF) or (*BSR_UNICODE)
+%^^A change what \R matches.
+%^^A
+%^^A \cx "control-x", where x is any ASCII character
+%^^A \C one byte, even in UTF-8 mode (best avoided)
+%^^A + possessive quantifiers
+%^^A (?>...) atomic, non-capturing group
+%^^A (?#....) comment (not nestable)
+%^^A (?Jms-UxX) options (duplicate names; multiline; single line;
+%^^A unset what follows; ungreedy; extended;
+%^^A error on bad escapes)
+%^^A (?i:...|...) convenient shorthand for (?:(?i)...|...)
+%^^A (*NO_START_OPT) no start-match optimization (PCRE_NO_START_OPTIMIZE)
+%^^A (*UTF8) set UTF-8 mode (PCRE_UTF8)
+%^^A (*UCP) set PCRE_UCP (use Unicode properties for \d etc)
+%^^A \n or \gn or \g{[-]n} or \g{name} or (?P=name)
+%^^A or \k<name> or \k'name' or \k{name}
+%^^A back-references
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3seq.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3seq.dtx
new file mode 100644
index 00000000000..6be32f7a831
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3seq.dtx
@@ -0,0 +1,2681 @@
+% \iffalse meta-comment
+%
+%% File: l3seq.dtx
+%
+% Copyright (C) 1990-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3seq} module\\ Sequences and stacks^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \LaTeX3 implements a \enquote{sequence} data type, which contain
+% an ordered list of entries which may contain any \meta{balanced text}.
+% It is possible to map functions to sequences such that the function
+% is applied to every item in the sequence.
+%
+% Sequences are also used to implement stack functions in \LaTeX3. This
+% is achieved using a number of dedicated stack functions.
+%
+% \section{Creating and initialising sequences}
+%
+% \begin{function}{\seq_new:N, \seq_new:c}
+% \begin{syntax}
+% \cs{seq_new:N} \meta{seq~var}
+% \end{syntax}
+% Creates a new \meta{seq~var} or raises an error if the name is
+% already taken. The declaration is global. The \meta{seq~var}
+% initially contains no items.
+% \end{function}
+%
+% \begin{function}{\seq_clear:N, \seq_clear:c, \seq_gclear:N, \seq_gclear:c}
+% \begin{syntax}
+% \cs{seq_clear:N} \meta{seq~var}
+% \end{syntax}
+% Clears all items from the \meta{seq~var}.
+% \end{function}
+%
+% \begin{function}
+% {\seq_clear_new:N, \seq_clear_new:c, \seq_gclear_new:N, \seq_gclear_new:c}
+% \begin{syntax}
+% \cs{seq_clear_new:N} \meta{seq~var}
+% \end{syntax}
+% Ensures that the \meta{seq~var} exists globally by applying
+% \cs{seq_new:N} if necessary, then applies
+% \cs[index=seq_clear:N]{seq_(g)clear:N} to leave
+% the \meta{seq~var} empty.
+% \end{function}
+%
+% \begin{function}
+% {
+% \seq_set_eq:NN, \seq_set_eq:cN, \seq_set_eq:Nc, \seq_set_eq:cc,
+% \seq_gset_eq:NN, \seq_gset_eq:cN, \seq_gset_eq:Nc, \seq_gset_eq:cc
+% }
+% \begin{syntax}
+% \cs{seq_set_eq:NN} \meta{seq~var_1} \meta{seq~var_2}
+% \end{syntax}
+% Sets the content of \meta{seq~var_1} equal to that of
+% \meta{seq~var_2}.
+% \end{function}
+%
+% \begin{function}[added = 2014-07-17]
+% {
+% \seq_set_from_clist:NN, \seq_set_from_clist:cN,
+% \seq_set_from_clist:Nc, \seq_set_from_clist:cc,
+% \seq_set_from_clist:Nn, \seq_set_from_clist:cn,
+% \seq_gset_from_clist:NN, \seq_gset_from_clist:cN,
+% \seq_gset_from_clist:Nc, \seq_gset_from_clist:cc,
+% \seq_gset_from_clist:Nn, \seq_gset_from_clist:cn
+% }
+% \begin{syntax}
+% \cs{seq_set_from_clist:NN} \meta{seq~var} \meta{comma-list}
+% \end{syntax}
+% Converts the data in the \meta{comma list} into a \meta{seq~var}:
+% the original \meta{comma list} is unchanged.
+% \end{function}
+%
+% \begin{function}[added = 2017-11-28]
+% {\seq_const_from_clist:Nn, \seq_const_from_clist:cn}
+% \begin{syntax}
+% \cs{seq_const_from_clist:Nn} \meta{seq~var} \Arg{comma-list}
+% \end{syntax}
+% Creates a new constant \meta{seq~var} or raises an error if the name
+% is already taken. The \meta{seq~var} is set globally to contain the
+% items in the \meta{comma list}.
+% \end{function}
+%
+% \begin{function}[added = 2011-08-15, updated = 2012-07-02]
+% {
+% \seq_set_split:Nnn ,
+% \seq_set_split:NVn , \seq_set_split:NnV , \seq_set_split:NVV ,
+% \seq_set_split:Nne , \seq_set_split:Nee ,
+% \seq_gset_split:Nnn,
+% \seq_gset_split:NVn , \seq_gset_split:NnV, \seq_gset_split:NVV,
+% \seq_gset_split:Nne , \seq_gset_split:Nee
+% }
+% \begin{syntax}
+% \cs{seq_set_split:Nnn} \meta{seq~var} \Arg{delimiter} \Arg{token list}
+% \end{syntax}
+% Splits the \meta{token list} into \meta{items} separated
+% by \meta{delimiter}, and assigns the result to the \meta{seq~var}.
+% Spaces on both sides of each \meta{item} are ignored,
+% then one set of outer braces is removed (if any);
+% this space trimming behaviour is identical to that of
+% \pkg{l3clist} functions. Empty \meta{items} are preserved by
+% \cs{seq_set_split:Nnn}, and can be removed afterwards using
+% \cs{seq_remove_all:Nn} \meta{seq~var} |{}|.
+% The \meta{delimiter} may not contain |{|, |}| or |#|
+% (assuming \TeX{}'s normal category code r\'egime).
+% If the \meta{delimiter} is empty, the \meta{token list} is split
+% into \meta{items} as a \meta{token list}.
+% See also \cs{seq_set_split_keep_spaces:Nnn}, which omits space stripping.
+% \end{function}
+%
+% \begin{function}[added = 2021-03-24]
+% {
+% \seq_set_split_keep_spaces:Nnn , \seq_set_split_keep_spaces:NnV ,
+% \seq_gset_split_keep_spaces:Nnn, \seq_gset_split_keep_spaces:NnV
+% }
+% \begin{syntax}
+% \cs{seq_set_split_keep_spaces:Nnn} \meta{seq~var} \Arg{delimiter} \Arg{token list}
+% \end{syntax}
+% Splits the \meta{token list} into \meta{items} separated
+% by \meta{delimiter}, and assigns the result to the \meta{seq~var}.
+% One set of outer braces is removed (if any) but any surrounding spaces
+% are retained: any braces \emph{inside} one or more spaces are
+% therefore kept. Empty \meta{items} are preserved by
+% \cs{seq_set_split_keep_spaces:Nnn}, and can be removed afterwards using
+% \cs{seq_remove_all:Nn} \meta{seq~var} |{}|.
+% The \meta{delimiter} may not contain |{|, |}| or |#|
+% (assuming \TeX{}'s normal category code r\'egime).
+% If the \meta{delimiter} is empty, the \meta{token list} is split
+% into \meta{items} as a \meta{token list}.
+% See also \cs{seq_set_split:Nnn}, which removes spaces around the delimiters.
+% \end{function}
+%
+% \begin{function}[added = 2012-06-15]
+% {\seq_set_filter:NNn, \seq_gset_filter:NNn}
+% \begin{syntax}
+% \cs{seq_set_filter:NNn} \meta{seq~var_1} \meta{seq~var_2} \Arg{inline boolexpr}
+% \end{syntax}
+% Evaluates the \meta{inline boolexpr} for every \meta{item} stored
+% within the \meta{seq~var_2}. The \meta{inline boolexpr}
+% receives the \meta{item} as |#1|. The sequence of all \meta{items}
+% for which the \meta{inline boolexpr} evaluated to \texttt{true}
+% is assigned to \meta{seq~var_1}.
+% \begin{texnote}
+% Contrarily to other mapping functions, \cs{seq_map_break:} cannot
+% be used in this function, and would lead to low-level \TeX{} errors.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}
+% {\seq_concat:NNN, \seq_concat:ccc, \seq_gconcat:NNN, \seq_gconcat:ccc}
+% \begin{syntax}
+% \cs{seq_concat:NNN} \meta{seq~var_1} \meta{seq~var_2} \meta{seq~var_3}
+% \end{syntax}
+% Concatenates the content of \meta{seq~var_2} and \meta{seq~var_3}
+% together and saves the result in \meta{seq~var_1}. The items in
+% \meta{seq~var_2} are placed at the left side of the new sequence.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added=2012-03-03]
+% {\seq_if_exist:N, \seq_if_exist:c}
+% \begin{syntax}
+% \cs{seq_if_exist_p:N} \meta{seq~var}
+% \cs{seq_if_exist:NTF} \meta{seq~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{seq~var} is currently defined. This does not
+% check that the \meta{seq~var} really is a sequence variable.
+% \end{function}
+%
+% \section{Appending data to sequences}
+%
+% \begin{function}{
+% \seq_put_left:Nn, \seq_put_left:NV, \seq_put_left:Nv, \seq_put_left:Ne,
+% \seq_put_left:No,
+% \seq_put_left:cn, \seq_put_left:cV, \seq_put_left:cv, \seq_put_left:ce,
+% \seq_put_left:co,
+% \seq_gput_left:Nn, \seq_gput_left:NV, \seq_gput_left:Nv, \seq_gput_left:Ne,
+% \seq_gput_left:No,
+% \seq_gput_left:cn, \seq_gput_left:cV, \seq_gput_left:cv, \seq_gput_left:ce,
+% \seq_gput_left:co
+% }
+% \begin{syntax}
+% \cs{seq_put_left:Nn} \meta{seq~var} \Arg{item}
+% \end{syntax}
+% Appends the \meta{item} to the left of the \meta{seq~var}.
+% \end{function}
+%
+% \begin{function}{
+% \seq_put_right:Nn, \seq_put_right:NV, \seq_put_right:Nv, \seq_put_right:Ne,
+% \seq_put_right:No,
+% \seq_put_right:cn, \seq_put_right:cV, \seq_put_right:cv, \seq_put_right:ce,
+% \seq_put_right:co,
+% \seq_gput_right:Nn, \seq_gput_right:NV, \seq_gput_right:Nv, \seq_gput_right:Ne,
+% \seq_gput_right:No,
+% \seq_gput_right:cn, \seq_gput_right:cV, \seq_gput_right:cv, \seq_gput_right:ce,
+% \seq_gput_right:co,
+% }
+% \begin{syntax}
+% \cs{seq_put_right:Nn} \meta{seq~var} \Arg{item}
+% \end{syntax}
+% Appends the \meta{item} to the right of the \meta{seq~var}.
+% \end{function}
+%
+% \section{Recovering items from sequences}
+%
+% Items can be recovered from either the left or the right of sequences.
+% For implementation reasons, the actions at the left of the sequence are
+% faster than those acting on the right. These functions all assign the
+% recovered material locally, \emph{i.e.}~setting the
+% \meta{token list variable} used with \cs{tl_set:Nn} and \emph{never}
+% \cs{tl_gset:Nn}.
+%
+% \begin{function}[updated = 2012-05-14]{\seq_get_left:NN, \seq_get_left:cN}
+% \begin{syntax}
+% \cs{seq_get_left:NN} \meta{seq~var} \meta{token list variable}
+% \end{syntax}
+% Stores the left-most item from a \meta{seq~var} in the
+% \meta{token list variable} without removing it from the
+% \meta{seq~var}. The \meta{token list variable} is assigned locally.
+% If \meta{seq~var} is empty the \meta{token list variable}
+% is set to the special marker \cs{q_no_value}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-05-19]{\seq_get_right:NN, \seq_get_right:cN}
+% \begin{syntax}
+% \cs{seq_get_right:NN} \meta{seq~var} \meta{token list variable}
+% \end{syntax}
+% Stores the right-most item from a \meta{seq~var} in the
+% \meta{token list variable} without removing it from the
+% \meta{seq~var}. The \meta{token list variable} is assigned locally.
+% If \meta{seq~var} is empty the \meta{token list variable}
+% is set to the special marker \cs{q_no_value}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-05-14]{\seq_pop_left:NN, \seq_pop_left:cN}
+% \begin{syntax}
+% \cs{seq_pop_left:NN} \meta{seq~var} \meta{token list variable}
+% \end{syntax}
+% Pops the left-most item from a \meta{seq~var} into the
+% \meta{token list variable}, \emph{i.e.}~removes the item from the
+% sequence and stores it in the \meta{token list variable}.
+% Both of the variables are assigned locally. If \meta{seq~var} is
+% empty the \meta{token list variable} is set to
+% the special marker \cs{q_no_value}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-05-14]{\seq_gpop_left:NN, \seq_gpop_left:cN}
+% \begin{syntax}
+% \cs{seq_gpop_left:NN} \meta{seq~var} \meta{token list variable}
+% \end{syntax}
+% Pops the left-most item from a \meta{seq~var} into the
+% \meta{token list variable}, \emph{i.e.}~removes the item from the
+% sequence and stores it in the \meta{token list variable}.
+% The \meta{seq~var} is modified globally, while the assignment of
+% the \meta{token list variable} is local.
+% If \meta{seq~var} is empty the \meta{token list variable} is set to
+% the special marker \cs{q_no_value}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-05-19]{\seq_pop_right:NN, \seq_pop_right:cN}
+% \begin{syntax}
+% \cs{seq_pop_right:NN} \meta{seq~var} \meta{token list variable}
+% \end{syntax}
+% Pops the right-most item from a \meta{seq~var} into the
+% \meta{token list variable}, \emph{i.e.}~removes the item from the
+% sequence and stores it in the \meta{token list variable}.
+% Both of the variables are assigned locally. If \meta{seq~var} is
+% empty the \meta{token list variable} is set to
+% the special marker \cs{q_no_value}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-05-19]{\seq_gpop_right:NN, \seq_gpop_right:cN}
+% \begin{syntax}
+% \cs{seq_gpop_right:NN} \meta{seq~var} \meta{token list variable}
+% \end{syntax}
+% Pops the right-most item from a \meta{seq~var} into the
+% \meta{token list variable}, \emph{i.e.}~removes the item from the
+% sequence and stores it in the \meta{token list variable}.
+% The \meta{seq~var} is modified globally, while the assignment of
+% the \meta{token list variable} is local.
+% If \meta{seq~var} is empty the \meta{token list variable} is set to
+% the special marker \cs{q_no_value}.
+% \end{function}
+%
+% \begin{function}[added = 2014-07-17, EXP]
+% {
+% \seq_item:Nn, \seq_item:NV, \seq_item:Ne,
+% \seq_item:cn, \seq_item:cV, \seq_item:ce
+% }
+% \begin{syntax}
+% \cs{seq_item:Nn} \meta{seq~var} \Arg{integer expression}
+% \end{syntax}
+% Indexing items in the \meta{seq~var} from~$1$ at the top (left), this
+% function evaluates the \meta{integer expression} and leaves the
+% appropriate item from the sequence in the input stream. If the
+% \meta{integer expression} is negative, indexing occurs from the
+% bottom (right) of the sequence. If the \meta{integer expression}
+% is larger than the number of items in the \meta{seq~var} (as
+% calculated by \cs{seq_count:N}) then the function expands to
+% nothing.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{item}
+% does not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2016-12-06]{\seq_rand_item:N, \seq_rand_item:c}
+% \begin{syntax}
+% \cs{seq_rand_item:N} \meta{seq~var}
+% \end{syntax}
+% Selects a pseudo-random item of the \meta{seq~var}. If the
+% \meta{seq~var} is empty the result is empty.
+% This is not available in older versions of \XeTeX{}.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{item}
+% does not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \section{Recovering values from sequences with branching}
+%
+% The functions in this section combine tests for non-empty sequences
+% with recovery of an item from the sequence. They offer increased readability
+% and performance over separate testing and recovery phases.
+%
+% \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19]
+% {\seq_get_left:NN, \seq_get_left:cN}
+% \begin{syntax}
+% \cs{seq_get_left:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{seq~var} is empty, leaves the \meta{false code} in the
+% input stream. The value of the \meta{token list variable} is
+% not defined in this case and should not be relied upon. If the
+% \meta{seq~var} is non-empty, stores the left-most item from the
+% \meta{seq~var}
+% in the \meta{token list variable} without removing it from the
+% \meta{seq~var}, then leaves the \meta{true code} in the input stream.
+% The \meta{token list variable} is assigned locally.
+% \end{function}
+%
+% \begin{function}[TF, added = 2012-05-19]
+% {\seq_get_right:NN, \seq_get_right:cN}
+% \begin{syntax}
+% \cs{seq_get_right:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{seq~var} is empty, leaves the \meta{false code} in the
+% input stream. The value of the \meta{token list variable} is
+% not defined in this case and should not be relied upon. If the
+% \meta{seq~var} is non-empty, stores the right-most item from the
+% \meta{seq~var}
+% in the \meta{token list variable} without removing it from the
+% \meta{seq~var}, then leaves the \meta{true code} in the input stream.
+% The \meta{token list variable} is assigned locally.
+% \end{function}
+%
+% \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19]
+% {\seq_pop_left:NN, \seq_pop_left:cN}
+% \begin{syntax}
+% \cs{seq_pop_left:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{seq~var} is empty, leaves the \meta{false code} in the
+% input stream. The value of the \meta{token list variable} is
+% not defined in this case and should not be relied upon. If the
+% \meta{seq~var} is non-empty, pops the left-most item from the
+% \meta{seq~var}
+% in the \meta{token list variable}, \emph{i.e.}~removes the item from the
+% \meta{seq~var}, then leaves the \meta{true code} in the input stream.
+% Both the \meta{seq~var} and the \meta{token list variable} are assigned
+% locally.
+% \end{function}
+%
+% \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19]
+% {\seq_gpop_left:NN, \seq_gpop_left:cN}
+% \begin{syntax}
+% \cs{seq_gpop_left:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{seq~var} is empty, leaves the \meta{false code} in the
+% input stream. The value of the \meta{token list variable} is
+% not defined in this case and should not be relied upon. If the
+% \meta{seq~var} is non-empty, pops the left-most item from the \meta{seq~var}
+% in the \meta{token list variable}, \emph{i.e.}~removes the item from the
+% \meta{seq~var}, then leaves the \meta{true code} in the input stream.
+% The \meta{seq~var} is modified globally, while the \meta{token list variable}
+% is assigned locally.
+% \end{function}
+%
+% \begin{function}[TF, added = 2012-05-19]
+% {\seq_pop_right:NN, \seq_pop_right:cN}
+% \begin{syntax}
+% \cs{seq_pop_right:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{seq~var} is empty, leaves the \meta{false code} in the
+% input stream. The value of the \meta{token list variable} is
+% not defined in this case and should not be relied upon. If the
+% \meta{seq~var} is non-empty, pops the right-most item from the \meta{seq~var}
+% in the \meta{token list variable}, \emph{i.e.}~removes the item from the
+% \meta{seq~var}, then leaves the \meta{true code} in the input stream.
+% Both the \meta{seq~var} and the \meta{token list variable} are assigned
+% locally.
+% \end{function}
+%
+% \begin{function}[TF, added = 2012-05-19]
+% {\seq_gpop_right:NN, \seq_gpop_right:cN}
+% \begin{syntax}
+% \cs{seq_gpop_right:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{seq~var} is empty, leaves the \meta{false code} in the
+% input stream. The value of the \meta{token list variable} is
+% not defined in this case and should not be relied upon. If the
+% \meta{seq~var} is non-empty, pops the right-most item from the \meta{seq~var}
+% in the \meta{token list variable}, \emph{i.e.}~removes the item from the
+% \meta{seq~var}, then leaves the \meta{true code} in the input stream.
+% The \meta{seq~var} is modified globally, while the
+% \meta{token list variable} is assigned locally.
+% \end{function}
+%
+% \section{Modifying sequences}
+%
+% While sequences are normally used as ordered lists, it may be
+% necessary to modify the content. The functions here may be used
+% to update sequences, while retaining the order of the unaffected
+% entries.
+%
+% \begin{function}
+% {
+% \seq_remove_duplicates:N, \seq_remove_duplicates:c,
+% \seq_gremove_duplicates:N, \seq_gremove_duplicates:c
+% }
+% \begin{syntax}
+% \cs{seq_remove_duplicates:N} \meta{seq~var}
+% \end{syntax}
+% Removes duplicate items from the \meta{seq~var}, leaving the
+% left most copy of each item in the \meta{seq~var}. The \meta{item}
+% comparison takes place on a token basis, as for \cs{tl_if_eq:nnTF}.
+% \begin{texnote}
+% This function iterates through every item in the \meta{seq~var} and
+% does a comparison with the \meta{items} already checked. It is therefore
+% relatively slow with large sequences.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}
+% {
+% \seq_remove_all:Nn, \seq_remove_all:NV, \seq_remove_all:Ne,
+% \seq_remove_all:cn, \seq_remove_all:cV, \seq_remove_all:ce,
+% \seq_gremove_all:Nn, \seq_gremove_all:NV, \seq_gremove_all:Ne,
+% \seq_gremove_all:cn, \seq_gremove_all:cV, \seq_gremove_all:ce
+% }
+% \begin{syntax}
+% \cs{seq_remove_all:Nn} \meta{seq~var} \Arg{item}
+% \end{syntax}
+% Removes every occurrence of \meta{item} from the \meta{seq~var}.
+% The \meta{item} comparison takes place on a token basis, as for
+% \cs{tl_if_eq:nnTF}.
+% \end{function}
+%
+% \begin{function}[added = 2021-04-29, noTF]
+% {\seq_set_item:Nnn, \seq_set_item:cnn, \seq_gset_item:Nnn, \seq_gset_item:cnn}
+% \begin{syntax}
+% \cs{seq_set_item:Nnn} \meta{seq~var} \Arg{int expr} \Arg{item}
+% \cs{seq_set_item:NnnTF} \meta{seq~var} \Arg{int expr} \Arg{item} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Removes the item of \meta{seq~var} at the position given by
+% evaluating the \meta{int expr} and replaces it by
+% \meta{item}. Items are indexed from $1$ on the left/top of the
+% \meta{seq~var}, or from $-1$ on the right/bottom. If the
+% \meta{int expr} is zero or is larger (in absolute value)
+% than the number of items in the sequence, the \meta{seq~var} is not
+% modified. In these cases, \cs{seq_set_item:Nnn} raises an error
+% while \cs{seq_set_item:NnnTF} runs the \meta{false code}. In cases
+% where the assignment was successful, \meta{true code} is run
+% afterwards.
+% \end{function}
+%
+% \begin{function}[added = 2014-07-18]
+% {
+% \seq_reverse:N, \seq_reverse:c,
+% \seq_greverse:N, \seq_greverse:c
+% }
+% \begin{syntax}
+% \cs{seq_reverse:N} \meta{seq~var}
+% \end{syntax}
+% Reverses the order of the items stored in the \meta{seq~var}.
+% \end{function}
+%
+% \begin{function}[added = 2017-02-06]
+% {\seq_sort:Nn, \seq_sort:cn, \seq_gsort:Nn, \seq_gsort:cn}
+% \begin{syntax}
+% \cs{seq_sort:Nn} \meta{seq~var} \Arg{comparison code}
+% \end{syntax}
+% Sorts the items in the \meta{seq~var} according to the
+% \meta{comparison code}, and assigns the result to
+% \meta{seq~var}. The details of sorting comparison are
+% described in Section~\ref{sec:l3sort:mech}.
+% \end{function}
+%
+% \begin{function}[added = 2018-04-29]
+% {\seq_shuffle:N, \seq_shuffle:c, \seq_gshuffle:N, \seq_gshuffle:c}
+% \begin{syntax}
+% \cs{seq_shuffle:N} \meta{seq~var}
+% \end{syntax}
+% Sets the \meta{seq~var} to the result of placing the items of the
+% \meta{seq~var} in a random order. Each item is (roughly) as likely
+% to end up in any given position.
+% \begin{texnote}
+% For sequences with more than $13$ items or so, only a small
+% proportion of all possible permutations can be reached, because
+% the random seed \cs{sys_rand_seed:} only has $28$-bits. The use
+% of \tn{toks} internally means that sequences with more than
+% $32767$ or $65535$ items (depending on the engine) cannot be
+% shuffled.
+% \end{texnote}
+% \end{function}
+%
+% \section{Sequence conditionals}
+%
+% \begin{function}[EXP,pTF]{\seq_if_empty:N, \seq_if_empty:c}
+% \begin{syntax}
+% \cs{seq_if_empty_p:N} \meta{seq~var}
+% \cs{seq_if_empty:NTF} \meta{seq~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{seq~var} is empty (containing no items).
+% \end{function}
+%
+% \begin{function}[TF]
+% {
+% \seq_if_in:Nn, \seq_if_in:NV, \seq_if_in:Nv, \seq_if_in:Ne,
+% \seq_if_in:No,
+% \seq_if_in:cn, \seq_if_in:cV, \seq_if_in:cv, \seq_if_in:ce,
+% \seq_if_in:co,
+% }
+% \begin{syntax}
+% \cs{seq_if_in:NnTF} \meta{seq~var} \Arg{item} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{item} is present in the \meta{seq~var}.
+% \end{function}
+%
+% \section{Mapping over sequences}
+%
+% All mappings are done at the current group level, \emph{i.e.}~any
+% local assignments made by the \meta{function} or \meta{code} discussed
+% below remain in effect after the loop.
+%
+% \begin{function}[rEXP, updated = 2012-06-29]
+% {\seq_map_function:NN, \seq_map_function:cN}
+% \begin{syntax}
+% \cs{seq_map_function:NN} \meta{seq~var} \meta{function}
+% \end{syntax}
+% Applies \meta{function} to every \meta{item} stored in the
+% \meta{seq~var}. The \meta{function} will receive one argument for
+% each iteration. The \meta{items} are returned from left to right.
+% To pass further arguments to the \meta{function}, see
+% \cs{seq_map_tokens:Nn}.
+% The function \cs{seq_map_inline:Nn} is faster than
+% \cs{seq_map_function:NN} for sequences with more than about~$10$
+% items.
+% \end{function}
+%
+% \begin{function}[updated = 2012-06-29]
+% {\seq_map_inline:Nn, \seq_map_inline:cn}
+% \begin{syntax}
+% \cs{seq_map_inline:Nn} \meta{seq~var} \Arg{inline function}
+% \end{syntax}
+% Applies \meta{inline function} to every \meta{item} stored
+% within the \meta{seq~var}. The \meta{inline function} should
+% consist of code which will receive the \meta{item} as |#1|.
+% The \meta{items} are returned from left to right.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2019-08-30]
+% {\seq_map_tokens:Nn, \seq_map_tokens:cn}
+% \begin{syntax}
+% \cs{seq_map_tokens:Nn} \meta{seq~var} \Arg{code}
+% \end{syntax}
+% Analogue of \cs{seq_map_function:NN} which maps several tokens
+% instead of a single function. The \meta{code} receives each item in
+% the \meta{seq~var} as a trailing brace group. For instance,
+% \begin{verbatim}
+% \seq_map_tokens:Nn \l_my_seq { \prg_replicate:nn { 2 } }
+% \end{verbatim}
+% expands to twice each item in the \meta{seq~var}: for each item in
+% |\l_my_seq| the function \cs{prg_replicate:nn} receives |2| and
+% \meta{item} as its two arguments. The function
+% \cs{seq_map_inline:Nn} is typically faster but it is not expandable.
+% \end{function}
+%
+% \begin{function}[updated = 2012-06-29]
+% {
+% \seq_map_variable:NNn, \seq_map_variable:Ncn,
+% \seq_map_variable:cNn, \seq_map_variable:ccn
+% }
+% \begin{syntax}
+% \cs{seq_map_variable:NNn} \meta{seq~var} \meta{variable} \Arg{code}
+% \end{syntax}
+% Stores each \meta{item} of the \meta{seq~var} in turn in the (token
+% list) \meta{variable} and applies the \meta{code}. The \meta{code}
+% will usually make use of the \meta{variable}, but this is not
+% enforced. The assignments to the \meta{variable} are local. Its
+% value after the loop is the last \meta{item} in the \meta{seq~var},
+% or its original value if the \meta{seq~var} is empty. The
+% \meta{items} are returned from left to right.
+% \end{function}
+%
+% \begin{function}[rEXP,added = 2018-05-03]{\seq_map_indexed_function:NN}
+% \begin{syntax}
+% \cs{seq_map_indexed_function:NN} \meta{seq~var} \meta{function}
+% \end{syntax}
+% Applies \meta{function} to every entry in the \meta{seq~var}.
+% The \meta{function} should have signature |:nn|. It
+% receives two arguments for each iteration: the \meta{index} (namely
+% |1| for the first entry, then |2| and so on) and the \meta{item}.
+% \end{function}
+%
+% \begin{function}[added = 2018-05-03]{\seq_map_indexed_inline:Nn}
+% \begin{syntax}
+% \cs{seq_map_indexed_inline:Nn} \meta{seq~var} \Arg{inline function}
+% \end{syntax}
+% Applies \meta{inline function} to every entry in the \meta{seq~var}.
+% The \meta{inline function} should consist of code which
+% receives the \meta{index} (namely |1| for the first entry, then |2|
+% and so on) as~|#1| and the \meta{item} as~|#2|.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2023-05-10]
+% {
+% \seq_map_pairwise_function:NNN, \seq_map_pairwise_function:NcN,
+% \seq_map_pairwise_function:cNN, \seq_map_pairwise_function:ccN
+% }
+% \begin{syntax}
+% \cs{seq_map_pairwise_function:NNN} \meta{seq_1} \meta{seq_2} \meta{function}
+% \end{syntax}
+% Applies \meta{function} to every pair of items
+% \meta{seq_1-item}--\meta{seq_2-item} from the two sequences, returning
+% items from both sequences from left to right. The \meta{function}
+% receives two \texttt{n}-type arguments for each iteration. The mapping
+% terminates when
+% the end of either sequence is reached (\emph{i.e.}~whichever sequence has
+% fewer items determines how many iterations
+% occur).
+% \end{function}
+%
+% \begin{function}[rEXP, updated = 2012-06-29]{\seq_map_break:}
+% \begin{syntax}
+% \cs{seq_map_break:}
+% \end{syntax}
+% Used to terminate a \cs[no-index]{seq_map_\ldots} function before all
+% entries in the \meta{seq~var} have been processed. This
+% normally takes place within a conditional statement, for example
+% \begin{verbatim}
+% \seq_map_inline:Nn \l_my_seq
+% {
+% \str_if_eq:nnTF { #1 } { bingo }
+% { \seq_map_break: }
+% {
+% % Do something useful
+% }
+% }
+% \end{verbatim}
+% Use outside of a \cs[no-index]{seq_map_\ldots} scenario leads to low
+% level \TeX{} errors.
+% \begin{texnote}
+% When the mapping is broken, additional tokens may be inserted
+% before further items are taken
+% from the input stream. This depends on the design of the mapping
+% function.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[rEXP, updated = 2012-06-29]{\seq_map_break:n}
+% \begin{syntax}
+% \cs{seq_map_break:n} \Arg{code}
+% \end{syntax}
+% Used to terminate a \cs[no-index]{seq_map_\ldots} function before all
+% entries in the \meta{seq~var} have been processed, inserting
+% the \meta{code} after the mapping has ended. This
+% normally takes place within a conditional statement, for example
+% \begin{verbatim}
+% \seq_map_inline:Nn \l_my_seq
+% {
+% \str_if_eq:nnTF { #1 } { bingo }
+% { \seq_map_break:n { <code> } }
+% {
+% % Do something useful
+% }
+% }
+% \end{verbatim}
+% Use outside of a \cs[no-index]{seq_map_\ldots} scenario leads to low
+% level \TeX{} errors.
+% \begin{texnote}
+% When the mapping is broken, additional tokens may be inserted
+% before the \meta{code} is
+% inserted into the input stream.
+% This depends on the design of the mapping function.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2011-12-22, updated = 2020-07-16]
+% {\seq_set_map:NNn, \seq_gset_map:NNn}
+% \begin{syntax}
+% \cs{seq_set_map:NNn} \meta{seq~var_1} \meta{seq~var_2} \Arg{inline function}
+% \end{syntax}
+% Applies \meta{inline function} to every \meta{item} stored
+% within the \meta{seq~var_2}. The \meta{inline function} should
+% consist of code which will receive the \meta{item} as |#1|.
+% The sequence resulting applying \meta{inline function} to each
+% \meta{item} is assigned to \meta{seq~var_1}.
+% \begin{texnote}
+% Contrarily to other mapping functions, \cs{seq_map_break:} cannot
+% be used in this function, and would lead to low-level \TeX{} errors.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2020-07-16, updated = 2023-10-26]
+% {\seq_set_map_e:NNn, \seq_gset_map_e:NNn}
+% \begin{syntax}
+% \cs{seq_set_map_e:NNn} \meta{seq~var_1} \meta{seq~var_2} \Arg{inline function}
+% \end{syntax}
+% Applies \meta{inline function} to every \meta{item} stored
+% within the \meta{seq~var_2}. The \meta{inline function} should
+% consist of code which will receive the \meta{item} as |#1|.
+% The sequence resulting from \texttt{e}-expanding
+% \meta{inline function} applied to each \meta{item}
+% is assigned to \meta{seq~var_1}. As such, the code
+% in \meta{inline function} should be expandable.
+% \begin{texnote}
+% Contrarily to other mapping functions, \cs{seq_map_break:} cannot
+% be used in this function, and would lead to low-level \TeX{} errors.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2012-07-13]{\seq_count:N, \seq_count:c}
+% \begin{syntax}
+% \cs{seq_count:N} \meta{seq~var}
+% \end{syntax}
+% Leaves the number of items in the \meta{seq~var} in the input
+% stream as an \meta{integer denotation}. The total number of items
+% in a \meta{seq~var} includes those which are empty and duplicates,
+% \emph{i.e.}~every item in a \meta{seq~var} is unique.
+% \end{function}
+%
+% \section{Using the content of sequences directly}
+%
+% \begin{function}[EXP, added = 2013-05-26]{\seq_use:Nnnn, \seq_use:cnnn}
+% \begin{syntax}
+% \cs{seq_use:Nnnn} \meta{seq~var} \Arg{separator~between~two} \Arg{separator~between~more~than~two} \Arg{separator~between~final~two}
+% \end{syntax}
+% Places the contents of the \meta{seq~var} in the input stream, with
+% the appropriate \meta{separator} between the items. Namely, if the
+% sequence has more than two items, the \meta{separator between more
+% than two} is placed between each pair of items except the last,
+% for which the \meta{separator between final two} is used. If the
+% sequence has exactly two items, then they are placed in the input stream
+% separated by the \meta{separator between two}. If the sequence has
+% a single item, it is placed in the input stream, and an empty sequence
+% produces no output. An error is raised if the variable does
+% not exist or if it is invalid.
+%
+% For example,
+% \begin{verbatim}
+% \seq_set_split:Nnn \l_tmpa_seq { | } { a | b | c | {de} | f }
+% \seq_use:Nnnn \l_tmpa_seq { ~and~ } { ,~ } { ,~and~ }
+% \end{verbatim}
+% inserts \enquote{\texttt{a, b, c, de, and f}} in the input
+% stream. The first separator argument is not used in this case
+% because the sequence has more than $2$ items.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{items}
+% do not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2013-05-26]{\seq_use:Nn, \seq_use:cn}
+% \begin{syntax}
+% \cs{seq_use:Nn} \meta{seq~var} \Arg{separator}
+% \end{syntax}
+% Places the contents of the \meta{seq~var} in the input stream, with
+% the \meta{separator} between the items. If the sequence has
+% a single item, it is placed in the input stream with no \meta{separator},
+% and an empty sequence produces no output. An error is raised if
+% the variable does not exist or if it is invalid.
+%
+% For example,
+% \begin{verbatim}
+% \seq_set_split:Nnn \l_tmpa_seq { | } { a | b | c | {de} | f }
+% \seq_use:Nn \l_tmpa_seq { ~and~ }
+% \end{verbatim}
+% inserts \enquote{\texttt{a and b and c and de and f}} in the input
+% stream.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{items}
+% do not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \section{Sequences as stacks}
+%
+% Sequences can be used as stacks, where data is pushed to and popped
+% from the top of the sequence. (The left of a sequence is the top, for
+% performance reasons.) The stack functions for sequences are not
+% intended to be mixed with the general ordered data functions detailed
+% in the previous section: a sequence should either be used as an
+% ordered data type or as a stack, but not in both ways.
+%
+% \begin{function}[updated = 2012-05-14]{\seq_get:NN, \seq_get:cN}
+% \begin{syntax}
+% \cs{seq_get:NN} \meta{seq~var} \meta{token list variable}
+% \end{syntax}
+% Reads the top item from a \meta{seq~var} into the
+% \meta{token list variable} without removing it from the
+% \meta{seq~var}. The \meta{token list variable} is assigned locally.
+% If \meta{seq~var} is empty the \meta{token list variable} is set to
+% the special marker \cs{q_no_value}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-05-14]{\seq_pop:NN, \seq_pop:cN}
+% \begin{syntax}
+% \cs{seq_pop:NN} \meta{seq~var} \meta{token list variable}
+% \end{syntax}
+% Pops the top item from a \meta{seq~var} into the
+% \meta{token list variable}. Both of the variables are assigned
+% locally. If \meta{seq~var} is empty the \meta{token list variable}
+% is set to the special marker \cs{q_no_value}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-05-14]{\seq_gpop:NN, \seq_gpop:cN}
+% \begin{syntax}
+% \cs{seq_gpop:NN} \meta{seq~var} \meta{token list variable}
+% \end{syntax}
+% Pops the top item from a \meta{seq~var} into the
+% \meta{token list variable}. The \meta{seq~var} is modified globally,
+% while the \meta{token list variable} is assigned locally. If
+% \meta{seq~var} is empty the \meta{token list variable} is set to
+% the special marker \cs{q_no_value}.
+% \end{function}
+%
+% \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19]{\seq_get:NN, \seq_get:cN}
+% \begin{syntax}
+% \cs{seq_get:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{seq~var} is empty, leaves the \meta{false code} in the
+% input stream. The value of the \meta{token list variable} is
+% not defined in this case and should not be relied upon. If the
+% \meta{seq~var} is non-empty, stores the top item from a
+% \meta{seq~var} in the \meta{token list variable} without removing it from
+% the \meta{seq~var}. The \meta{token list variable} is assigned locally.
+% \end{function}
+%
+% \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19]{\seq_pop:NN, \seq_pop:cN}
+% \begin{syntax}
+% \cs{seq_pop:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{seq~var} is empty, leaves the \meta{false code} in the
+% input stream. The value of the \meta{token list variable} is
+% not defined in this case and should not be relied upon. If the
+% \meta{seq~var} is non-empty, pops the top item from the
+% \meta{seq~var} in the \meta{token list variable}, \emph{i.e.}~removes the
+% item from the \meta{seq~var}. Both the \meta{seq~var} and the
+% \meta{token list variable} are assigned locally.
+% \end{function}
+%
+% \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19]{\seq_gpop:NN, \seq_gpop:cN}
+% \begin{syntax}
+% \cs{seq_gpop:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{seq~var} is empty, leaves the \meta{false code} in the
+% input stream. The value of the \meta{token list variable} is
+% not defined in this case and should not be relied upon. If the
+% \meta{seq~var} is non-empty, pops the top item from the \meta{seq~var}
+% in the \meta{token list variable}, \emph{i.e.}~removes the item from the
+% \meta{seq~var}. The \meta{seq~var} is modified globally, while the
+% \meta{token list variable} is assigned locally.
+% \end{function}
+%
+% \begin{function}
+% {
+% \seq_push:Nn, \seq_push:NV, \seq_push:Nv, \seq_push:Ne,
+% \seq_push:No,
+% \seq_push:cn, \seq_push:cV, \seq_push:cv, \seq_push:ce,
+% \seq_push:co,
+% \seq_gpush:Nn, \seq_gpush:NV, \seq_gpush:Nv, \seq_gpush:Ne,
+% \seq_gpush:No,
+% \seq_gpush:cn, \seq_gpush:cV, \seq_gpush:cv, \seq_gpush:ce,
+% \seq_gpush:co
+% }
+% \begin{syntax}
+% \cs{seq_push:Nn} \meta{seq~var} \Arg{item}
+% \end{syntax}
+% Adds the \Arg{item} to the top of the \meta{seq~var}.
+% \end{function}
+%
+% \section{Sequences as sets}
+%
+% Sequences can also be used as sets, such that all of their items are
+% distinct. Usage of sequences as sets is not currently widespread,
+% hence no specific set function is provided. Instead, it is explained
+% here how common set operations can be performed by combining several
+% functions described in earlier sections. When using sequences to
+% implement sets, one should be careful not to rely on the order of
+% items in the sequence representing the set.
+%
+% Sets should not contain several occurrences of a given item. To make
+% sure that a \meta{seq~var} only has distinct items, use
+% \cs{seq_remove_duplicates:N} \meta{seq~var}. This function
+% is relatively slow, and to avoid performance issues one should only
+% use it when necessary.
+%
+% Some operations on a set \meta{seq~var} are straightforward. For
+% instance, \cs{seq_count:N} \meta{seq~var} expands to the number of
+% items, while \cs{seq_if_in:NnTF} \meta{seq~var} \Arg{item} tests if
+% the \meta{item} is in the set.
+%
+% Adding an \meta{item} to a set \meta{seq~var} can be done by appending
+% it to the \meta{seq~var} if it is not already in the \meta{seq~var}:
+% \begin{quote}\ttfamily\parskip=0pt\obeylines
+% \cs{seq_if_in:NnF} \meta{seq~var} \Arg{item}
+% | |\{ \cs{seq_put_right:Nn} \meta{seq~var} \Arg{item} \}
+% \end{quote}
+% Removing an \meta{item} from a set \meta{seq~var} can be done using
+% \cs{seq_remove_all:Nn},
+% \begin{quote}\ttfamily
+% \cs{seq_remove_all:Nn} \meta{seq~var} \Arg{item}
+% \end{quote}
+%
+% The intersection of two sets \meta{seq~var_1} and \meta{seq~var_2} can
+% be stored into \meta{seq~var_3} by collecting items of
+% \meta{seq~var_1} which are in \meta{seq~var_2}.
+% \begin{quote}\ttfamily\parskip=0pt\obeylines
+% \cs{seq_clear:N} \meta{seq~var_3}
+% \cs{seq_map_inline:Nn} \meta{seq~var_1}
+% | |\{
+% | |\cs{seq_if_in:NnT} \meta{seq~var_2} \{\#1\}
+% | |\{ \cs{seq_put_right:Nn} \meta{seq~var_3} \{\#1\} \}
+% | |\}
+% \end{quote}
+% The code as written here only works if \meta{seq~var_3} is different
+% from the other two sequence variables. To cover all cases, items
+% should first be collected in a sequence
+% |\l__|\meta{pkg}|_internal_seq|, then \meta{seq~var_3} should be set
+% equal to this internal sequence. The same remark applies to other set
+% functions.
+%
+% The union of two sets \meta{seq~var_1} and \meta{seq~var_2} can be
+% stored into \meta{seq~var_3} through
+% \begin{quote}\ttfamily
+% \cs{seq_concat:NNN} \meta{seq~var_3} \meta{seq~var_1} \meta{seq~var_2} \\
+% \cs{seq_remove_duplicates:N} \meta{seq~var_3}
+% \end{quote}
+% or by adding items to (a copy of) \meta{seq~var_1} one by one
+% \begin{quote}\ttfamily\parskip=0pt\obeylines
+% \cs{seq_set_eq:NN} \meta{seq~var_3} \meta{seq~var_1}
+% \cs{seq_map_inline:Nn} \meta{seq~var_2}
+% | |\{
+% | |\cs{seq_if_in:NnF} \meta{seq~var_3} \{\#1\}
+% | |\{ \cs{seq_put_right:Nn} \meta{seq~var_3} \{\#1\} \}
+% | |\}
+% \end{quote}
+% The second approach is faster than the first when the \meta{seq~var_2}
+% is short compared to \meta{seq~var_1}.
+%
+% The difference of two sets \meta{seq~var_1} and \meta{seq~var_2} can
+% be stored into \meta{seq~var_3} by removing items of the
+% \meta{seq~var_2} from (a copy of) the \meta{seq~var_1} one by one.
+% \begin{quote}\ttfamily\parskip=0pt\obeylines
+% \cs{seq_set_eq:NN} \meta{seq~var_3} \meta{seq~var_1}
+% \cs{seq_map_inline:Nn} \meta{seq~var_2}
+% | |\{ \cs{seq_remove_all:Nn} \meta{seq~var_3} \{\#1\} \}
+% \end{quote}
+%
+% The symmetric difference of two sets \meta{seq~var_1} and
+% \meta{seq~var_2} can be stored into \meta{seq~var_3} by computing the
+% difference between \meta{seq~var_1} and \meta{seq~var_2} and storing
+% the result as |\l__|\meta{pkg}|_internal_seq|, then the difference
+% between \meta{seq~var_2} and \meta{seq~var_1}, and finally
+% concatenating the two differences to get the symmetric differences.
+% \begin{quote}\ttfamily\parskip=0pt\obeylines
+% \cs{seq_set_eq:NN} |\l__|\meta{pkg}|_internal_seq| \meta{seq~var_1}
+% \cs{seq_map_inline:Nn} \meta{seq~var_2}
+% | |\{ \cs{seq_remove_all:Nn} |\l__|\meta{pkg}|_internal_seq| \{\#1\} \}
+% \cs{seq_set_eq:NN} \meta{seq~var_3} \meta{seq~var_2}
+% \cs{seq_map_inline:Nn} \meta{seq~var_1}
+% | |\{ \cs{seq_remove_all:Nn} \meta{seq~var_3} \{\#1\} \}
+% \cs{seq_concat:NNN} \meta{seq~var_3} \meta{seq~var_3} |\l__|\meta{pkg}|_internal_seq|
+% \end{quote}
+%
+% \section{Constant and scratch sequences}
+%
+% \begin{variable}[added = 2012-07-02]{\c_empty_seq}
+% Constant that is always empty.
+% \end{variable}
+%
+% \begin{variable}[added = 2012-04-26]{\l_tmpa_seq, \l_tmpb_seq}
+% Scratch sequences for local assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \begin{variable}[added = 2012-04-26]{\g_tmpa_seq, \g_tmpb_seq}
+% Scratch sequences for global assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \section{Viewing sequences}
+%
+% \begin{function}[updated = 2021-04-29]{\seq_show:N, \seq_show:c}
+% \begin{syntax}
+% \cs{seq_show:N} \meta{seq~var}
+% \end{syntax}
+% Displays the entries in the \meta{seq~var} in the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-12, updated = 2021-04-29]{\seq_log:N, \seq_log:c}
+% \begin{syntax}
+% \cs{seq_log:N} \meta{seq~var}
+% \end{syntax}
+% Writes the entries in the \meta{seq~var} in the log file.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3seq} implementation}
+%
+% \TestFiles{m3seq002,m3seq003}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=seq>
+% \end{macrocode}
+%
+% A sequence is a control sequence whose top-level expansion is of the
+% form \enquote{\cs{s_@@} \cs{@@_item:n} \marg{item_1} \ldots
+% \cs{@@_item:n} \marg{item_n}}, with a leading scan
+% mark followed by $n$~items of the same form. An
+% earlier implementation used the structure \enquote{\cs{seq_elt:w}
+% \meta{item_1} \cs{seq_elt_end:} \ldots \cs{seq_elt:w} \meta{item_n}
+% \cs{seq_elt_end:}}. This allowed rapid searching using a delimited
+% function, but was not suitable for items containing |{|, |}| and |#|
+% tokens, and also lead to the loss of surrounding braces around items
+%
+% \begin{function}[EXP]{\@@_item:n}
+% \begin{syntax}
+% \cs{@@_item:n} \Arg{item}
+% \end{syntax}
+% The internal token used to begin each sequence entry. If expanded
+% outside of a mapping or manipulation function, an error is
+% raised. The definition should always be set globally.
+% \end{function}
+%
+% \begin{function}{\@@_push_item_def:n, \@@_push_item_def:e}
+% \begin{syntax}
+% \cs{@@_push_item_def:n} \Arg{code}
+% \end{syntax}
+% Saves the definition of \cs{@@_item:n} and redefines it to
+% accept one parameter and expand to \meta{code}. This function
+% should always be balanced by use of \cs{@@_pop_item_def:}.
+% \end{function}
+%
+% \begin{function}{\@@_pop_item_def:}
+% \begin{syntax}
+% \cs{@@_pop_item_def:}
+% \end{syntax}
+% Restores the definition of \cs{@@_item:n} most recently saved by
+% \cs{@@_push_item_def:n}. This function should always be used in
+% a balanced pair with \cs{@@_push_item_def:n}.
+% \end{function}
+%
+% \begin{variable}{\s_@@}
+% This private scan mark.
+% \begin{macrocode}
+\scan_new:N \s_@@
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\s_@@_mark,\s_@@_stop}
+% Private scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_item:n}
+% The delimiter is always defined, but when used incorrectly simply
+% removes its argument and hits an undefined control sequence to
+% raise an error.
+% \begin{macrocode}
+\cs_new:Npn \@@_item:n
+ {
+ \msg_expandable_error:nn { seq } { misused }
+ \use_none:n
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_internal_a_tl, \l_@@_internal_b_tl}
+% Scratch space for various internal uses.
+% \begin{macrocode}
+\tl_new:N \l_@@_internal_a_tl
+\tl_new:N \l_@@_internal_b_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_tmp:w}
+% Scratch function for internal use.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_tmp:w ?
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\c_empty_seq}
+% A sequence with no item, following the structure mentioned above.
+% \begin{macrocode}
+\tl_const:Nn \c_empty_seq { \s_@@ }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Allocation and initialisation}
+%
+% \begin{macro}{\seq_new:N, \seq_new:c}
+% \UnitTested
+% Sequences are initialized to \cs{c_empty_seq}.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_eq:NN #1 \c_empty_seq
+ }
+\cs_generate_variant:Nn \seq_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\seq_clear:N, \seq_clear:c}
+% \UnitTested
+% \begin{macro}{\seq_gclear:N, \seq_gclear:c}
+% \UnitTested
+% Clearing a sequence is similar to setting it equal to the empty one.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_clear:N #1
+ { \seq_set_eq:NN #1 \c_empty_seq }
+\cs_generate_variant:Nn \seq_clear:N { c }
+\cs_new_protected:Npn \seq_gclear:N #1
+ { \seq_gset_eq:NN #1 \c_empty_seq }
+\cs_generate_variant:Nn \seq_gclear:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_clear_new:N, \seq_clear_new:c}
+% \UnitTested
+% \begin{macro}{\seq_gclear_new:N, \seq_gclear_new:c}
+% \UnitTested
+% Once again we copy code from the token list functions.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_clear_new:N #1
+ { \seq_if_exist:NTF #1 { \seq_clear:N #1 } { \seq_new:N #1 } }
+\cs_generate_variant:Nn \seq_clear_new:N { c }
+\cs_new_protected:Npn \seq_gclear_new:N #1
+ { \seq_if_exist:NTF #1 { \seq_gclear:N #1 } { \seq_new:N #1 } }
+\cs_generate_variant:Nn \seq_gclear_new:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_set_eq:NN, \seq_set_eq:cN, \seq_set_eq:Nc, \seq_set_eq:cc}
+% \UnitTested
+% \begin{macro}
+% {\seq_gset_eq:NN, \seq_gset_eq:cN, \seq_gset_eq:Nc, \seq_gset_eq:cc}
+% \UnitTested
+% Copying a sequence is the same as copying the underlying token list.
+% \begin{macrocode}
+\cs_new_eq:NN \seq_set_eq:NN \tl_set_eq:NN
+\cs_new_eq:NN \seq_set_eq:Nc \tl_set_eq:Nc
+\cs_new_eq:NN \seq_set_eq:cN \tl_set_eq:cN
+\cs_new_eq:NN \seq_set_eq:cc \tl_set_eq:cc
+\cs_new_eq:NN \seq_gset_eq:NN \tl_gset_eq:NN
+\cs_new_eq:NN \seq_gset_eq:Nc \tl_gset_eq:Nc
+\cs_new_eq:NN \seq_gset_eq:cN \tl_gset_eq:cN
+\cs_new_eq:NN \seq_gset_eq:cc \tl_gset_eq:cc
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \seq_set_from_clist:NN, \seq_set_from_clist:cN,
+% \seq_set_from_clist:Nc, \seq_set_from_clist:cc,
+% \seq_set_from_clist:Nn, \seq_set_from_clist:cn
+% }
+% \begin{macro}
+% {
+% \seq_gset_from_clist:NN, \seq_gset_from_clist:cN,
+% \seq_gset_from_clist:Nc, \seq_gset_from_clist:cc,
+% \seq_gset_from_clist:Nn, \seq_gset_from_clist:cn
+% }
+% Setting a sequence from a comma-separated list is done using a simple
+% mapping.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_set_from_clist:NN #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \s_@@ \clist_map_function:NN #2 \@@_wrap_item:n }
+ }
+\cs_new_protected:Npn \seq_set_from_clist:Nn #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \s_@@ \clist_map_function:nN {#2} \@@_wrap_item:n }
+ }
+\cs_new_protected:Npn \seq_gset_from_clist:NN #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \s_@@ \clist_map_function:NN #2 \@@_wrap_item:n }
+ }
+\cs_new_protected:Npn \seq_gset_from_clist:Nn #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \s_@@ \clist_map_function:nN {#2} \@@_wrap_item:n }
+ }
+\cs_generate_variant:Nn \seq_set_from_clist:NN { Nc }
+\cs_generate_variant:Nn \seq_set_from_clist:NN { c , cc }
+\cs_generate_variant:Nn \seq_set_from_clist:Nn { c }
+\cs_generate_variant:Nn \seq_gset_from_clist:NN { Nc }
+\cs_generate_variant:Nn \seq_gset_from_clist:NN { c , cc }
+\cs_generate_variant:Nn \seq_gset_from_clist:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_const_from_clist:Nn, \seq_const_from_clist:cn}
+% Almost identical to \cs{seq_set_from_clist:Nn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_const_from_clist:Nn #1#2
+ {
+ \tl_const:Ne #1
+ { \s_@@ \clist_map_function:nN {#2} \@@_wrap_item:n }
+ }
+\cs_generate_variant:Nn \seq_const_from_clist:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \seq_set_split:Nnn ,
+% \seq_set_split:NVn , \seq_set_split:NnV , \seq_set_split:NVV ,
+% \seq_set_split:Nne , \seq_set_split:Nee ,
+% \seq_set_split:Nnx , \seq_set_split:Nxx ,
+% \seq_gset_split:Nnn,
+% \seq_gset_split:NVn, \seq_gset_split:NnV, \seq_gset_split:NVV,
+% \seq_gset_split:Nne, \seq_gset_split:Nee,
+% \seq_gset_split:Nnx, \seq_gset_split:Nxx
+% }
+% \begin{macro}
+% {
+% \seq_set_split_keep_spaces:Nnn , \seq_set_split_keep_spaces:NnV ,
+% \seq_gset_split_keep_spaces:Nnn, \seq_gset_split_keep_spaces:NnV
+% }
+% \begin{macro}{\@@_set_split:NNnn}
+% \begin{macro}
+% {
+% \@@_set_split:Nw, \@@_set_split:w,
+% \@@_set_split_end:
+% }
+% When the separator is empty, everything is very simple, just map
+% \cs{@@_wrap_item:n} through the items of the last argument.
+% For non-trivial separators, the goal is to split a given token list
+% at the marker, strip spaces from each item, and remove one set of
+% outer braces if after removing leading and trailing
+% spaces the item is enclosed within braces. After
+% \cs{tl_replace_all:Nnn}, the token list \cs{l_@@_internal_a_tl}
+% is a repetition of the pattern
+% \cs{@@_set_split:Nw} \cs{prg_do_nothing:}
+% \meta{item with spaces} \cs{@@_set_split_end:}.
+% Then, \texttt{e}-expansion causes \cs{@@_set_split:Nw}
+% to trim spaces, and leaves its result as
+% \cs{@@_set_split:w} \meta{trimmed item}
+% \cs{@@_set_split_end:}. This is then converted
+% to the \pkg{l3seq} internal structure by another
+% \texttt{e}-expansion. In the first step, we insert
+% \cs{prg_do_nothing:} to avoid losing braces too early:
+% that would cause space trimming to act within those
+% lost braces. The second step is solely there to strip
+% braces which are outermost after space trimming.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_set_split:Nnn
+ { \@@_set_split:NNNnn \__kernel_tl_set:Nx \tl_trim_spaces:n }
+\cs_new_protected:Npn \seq_gset_split:Nnn
+ { \@@_set_split:NNNnn \__kernel_tl_gset:Nx \tl_trim_spaces:n }
+\cs_new_protected:Npn \seq_set_split_keep_spaces:Nnn
+ { \@@_set_split:NNNnn \__kernel_tl_set:Nx \exp_not:n }
+\cs_new_protected:Npn \seq_gset_split_keep_spaces:Nnn
+ { \@@_set_split:NNNnn \__kernel_tl_gset:Nx \exp_not:n }
+\cs_new_protected:Npn \@@_set_split:NNNnn #1#2#3#4#5
+ {
+ \tl_if_empty:nTF {#4}
+ {
+ \tl_set:Nn \l_@@_internal_a_tl
+ { \tl_map_function:nN {#5} \@@_wrap_item:n }
+ }
+ {
+ \tl_set:Nn \l_@@_internal_a_tl
+ {
+ \@@_set_split:Nw #2 \prg_do_nothing:
+ #5
+ \@@_set_split_end:
+ }
+ \tl_replace_all:Nnn \l_@@_internal_a_tl {#4}
+ {
+ \@@_set_split_end:
+ \@@_set_split:Nw #2 \prg_do_nothing:
+ }
+ \__kernel_tl_set:Nx \l_@@_internal_a_tl { \l_@@_internal_a_tl }
+ }
+ #1 #3 { \s_@@ \l_@@_internal_a_tl }
+ }
+\cs_new:Npn \@@_set_split:Nw #1#2 \@@_set_split_end:
+ {
+ \exp_not:N \@@_set_split:w
+ \exp_args:No #1 {#2}
+ \exp_not:N \@@_set_split_end:
+ }
+\cs_new:Npn \@@_set_split:w #1 \@@_set_split_end:
+ { \@@_wrap_item:n {#1} }
+\cs_generate_variant:Nn \seq_set_split:Nnn { NV , NnV , NVV , Nne , Nee }
+\cs_generate_variant:Nn \seq_set_split:Nnn { Nnx , Nxx }
+\cs_generate_variant:Nn \seq_gset_split:Nnn { NV , NnV , NVV , Nne , Nee }
+\cs_generate_variant:Nn \seq_gset_split:Nnn { Nnx , Nxx }
+\cs_generate_variant:Nn \seq_set_split_keep_spaces:Nnn { NnV }
+\cs_generate_variant:Nn \seq_gset_split_keep_spaces:Nnn { NnV }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_set_filter:NNn, \seq_gset_filter:NNn}
+% \begin{macro}{\@@_set_filter:NNNn}
+% Similar to \cs{seq_map_inline:Nn}, without a
+% \cs{prg_break_point:} because the user's code
+% is performed within the evaluation of a boolean expression,
+% and skipping out of that would break horribly.
+% The \cs{@@_wrap_item:n} function inserts the relevant
+% \cs{@@_item:n} without expansion in the input stream,
+% hence in the \texttt{e}-expanding assignment.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_set_filter:NNn
+ { \@@_set_filter:NNNn \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \seq_gset_filter:NNn
+ { \@@_set_filter:NNNn \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \@@_set_filter:NNNn #1#2#3#4
+ {
+ \@@_push_item_def:n { \bool_if:nT {#4} { \@@_wrap_item:n {##1} } }
+ #1 #2 { #3 }
+ \@@_pop_item_def:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_concat:NNN, \seq_concat:ccc}
+% \UnitTested
+% \begin{macro}{\seq_gconcat:NNN, \seq_gconcat:ccc}
+% \UnitTested
+% When concatenating sequences, one must remove the leading \cs{s_@@}
+% of the second sequence. The result starts with \cs{s_@@} (of the
+% first sequence), which stops \texttt{f}-expansion.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_concat:NNN #1#2#3
+ { \tl_set:Nf #1 { \exp_after:wN \use_i:nn \exp_after:wN #2 #3 } }
+\cs_new_protected:Npn \seq_gconcat:NNN #1#2#3
+ { \tl_gset:Nf #1 { \exp_after:wN \use_i:nn \exp_after:wN #2 #3 } }
+\cs_generate_variant:Nn \seq_concat:NNN { ccc }
+\cs_generate_variant:Nn \seq_gconcat:NNN { ccc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\seq_if_exist:N, \seq_if_exist:c}
+% Copies of the \texttt{cs} functions defined in \pkg{l3basics}.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \seq_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \seq_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Appending data to either end}
+%
+% \begin{macro}{
+% \seq_put_left:Nn, \seq_put_left:NV, \seq_put_left:Nv, \seq_put_left:Ne,
+% \seq_put_left:No, \seq_put_left:Nx,
+% \seq_put_left:cn, \seq_put_left:cV, \seq_put_left:cv,\seq_put_left:ce,
+% \seq_put_left:co, \seq_put_left:cx
+% }
+% \UnitTested
+% \begin{macro}{
+% \seq_gput_left:Nn, \seq_gput_left:NV, \seq_gput_left:Nv, \seq_gput_left:Ne,
+% \seq_gput_left:No, \seq_gput_left:Nx,
+% \seq_gput_left:cn, \seq_gput_left:cV, \seq_gput_left:cv, \seq_gput_left:ce,
+% \seq_gput_left:co, \seq_gput_left:cx
+% }
+% \begin{macro}[EXP]{\@@_put_left_aux:w}
+% When adding to the left of a sequence, remove \cs{s_@@}. This is
+% done by \cs{@@_put_left_aux:w}, which also stops
+% \texttt{f}-expansion.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_put_left:Nn #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ {
+ \exp_not:n { \s_@@ \@@_item:n {#2} }
+ \exp_not:f { \exp_after:wN \@@_put_left_aux:w #1 }
+ }
+ }
+\cs_new_protected:Npn \seq_gput_left:Nn #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ {
+ \exp_not:n { \s_@@ \@@_item:n {#2} }
+ \exp_not:f { \exp_after:wN \@@_put_left_aux:w #1 }
+ }
+ }
+\cs_new:Npn \@@_put_left_aux:w \s_@@ { \exp_stop_f: }
+\cs_generate_variant:Nn \seq_put_left:Nn { NV , Nv , Ne , No , Nx }
+\cs_generate_variant:Nn \seq_put_left:Nn { c , cV , cv , ce , co ,cx }
+\cs_generate_variant:Nn \seq_gput_left:Nn { NV , Nv , Ne , No , Nx }
+\cs_generate_variant:Nn \seq_gput_left:Nn { c , cV , cv , ce , co , cx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \seq_put_right:Nn, \seq_put_right:NV, \seq_put_right:Nv, \seq_put_right:Ne,
+% \seq_put_right:No, \seq_put_right:Nx,
+% \seq_put_right:cn, \seq_put_right:cV, \seq_put_right:cv, \seq_put_right:cx,
+% \seq_put_right:co, \seq_put_right:cx
+% }
+% \UnitTested
+% \begin{macro}
+% {
+% \seq_gput_right:Nn, \seq_gput_right:NV, \seq_gput_right:Nv, \seq_gput_right:Ne,
+% \seq_gput_right:No, \seq_gput_right:Nx,
+% \seq_gput_right:cn, \seq_gput_right:cV, \seq_gput_right:cv, \seq_gput_right:ce,
+% \seq_gput_right:co, \seq_gput_right:cx
+% }
+% Since there is no trailing marker, adding an item to the right of a
+% sequence simply means wrapping it in \cs{@@_item:n}.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_put_right:Nn #1#2
+ { \tl_put_right:Nn #1 { \@@_item:n {#2} } }
+\cs_new_protected:Npn \seq_gput_right:Nn #1#2
+ { \tl_gput_right:Nn #1 { \@@_item:n {#2} } }
+\cs_generate_variant:Nn \seq_put_right:Nn { NV , Nv , Ne , No , Nx }
+\cs_generate_variant:Nn \seq_put_right:Nn { c , cV , cv , ce , co , cx }
+\cs_generate_variant:Nn \seq_gput_right:Nn { NV , Nv , Ne , No , Nx }
+\cs_generate_variant:Nn \seq_gput_right:Nn { c , cV , cv , ce , co , cx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Modifying sequences}
+%
+% \begin{macro}{\@@_wrap_item:n}
+% This function converts its argument to a proper sequence item
+% in an \texttt{e}-expansion context.
+% \begin{macrocode}
+\cs_new:Npn \@@_wrap_item:n #1 { \exp_not:n { \@@_item:n {#1} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_remove_seq}
+% An internal sequence for the removal routines.
+% \begin{macrocode}
+\seq_new:N \l_@@_remove_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\seq_remove_duplicates:N, \seq_remove_duplicates:c}
+% \UnitTested
+% \begin{macro}{\seq_gremove_duplicates:N, \seq_gremove_duplicates:c}
+% \UnitTested
+% \begin{macro}{\@@_remove_duplicates:NN}
+% Removing duplicates means making a new list then copying it.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_remove_duplicates:N
+ { \@@_remove_duplicates:NN \seq_set_eq:NN }
+\cs_new_protected:Npn \seq_gremove_duplicates:N
+ { \@@_remove_duplicates:NN \seq_gset_eq:NN }
+\cs_new_protected:Npn \@@_remove_duplicates:NN #1#2
+ {
+ \seq_clear:N \l_@@_remove_seq
+ \seq_map_inline:Nn #2
+ {
+ \seq_if_in:NnF \l_@@_remove_seq {##1}
+ { \seq_put_right:Nn \l_@@_remove_seq {##1} }
+ }
+ #1 #2 \l_@@_remove_seq
+ }
+\cs_generate_variant:Nn \seq_remove_duplicates:N { c }
+\cs_generate_variant:Nn \seq_gremove_duplicates:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \seq_remove_all:Nn, \seq_remove_all:NV, \seq_remove_all:Ne,
+% \seq_remove_all:Nx,
+% \seq_remove_all:cn, \seq_remove_all:cV, \seq_remove_all:ce,
+% \seq_remove_all:cx,
+% }
+% \UnitTested
+% \begin{macro}
+% {
+% \seq_gremove_all:Nn, \seq_gremove_all:NV, \seq_gremove_all:Ne,
+% \seq_gremove_all:Nx,
+% \seq_gremove_all:cn, \seq_gremove_all:cV, \seq_gremove_all:ce,
+% \seq_gremove_all:Nx
+% }
+% \UnitTested
+% \begin{macro}{\@@_remove_all_aux:NNn}
+% The idea of the code here is to avoid a relatively expensive addition of
+% items one at a time to an intermediate sequence.
+% The approach taken is therefore similar to
+% that in \cs{@@_pop_right:NNN}, using a \enquote{flexible}
+% \texttt{e}-type expansion to do most of the work. As \cs{tl_if_eq:nnT}
+% is not expandable, a two-part strategy is needed. First, the
+% \texttt{e}-type expansion uses \cs{str_if_eq:nnT} to find potential
+% matches. If one is found, the expansion is halted and the necessary
+% set up takes place to use the \cs{tl_if_eq:NNT} test. The \texttt{e}-type
+% is started again, including all of the items copied already. This
+% happens repeatedly until the entire sequence has been scanned. The code
+% is set up to avoid needing an intermediate scratch list: the lead-off
+% \texttt{e}-type expansion (|#1 #2 {#2}|) ensures that nothing is lost.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_remove_all:Nn
+ { \@@_remove_all_aux:NNn \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \seq_gremove_all:Nn
+ { \@@_remove_all_aux:NNn \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \@@_remove_all_aux:NNn #1#2#3
+ {
+ \@@_push_item_def:n
+ {
+ \str_if_eq:nnT {##1} {#3}
+ {
+ \if_false: { \fi: }
+ \tl_set:Nn \l_@@_internal_b_tl {##1}
+ #1 #2
+ { \if_false: } \fi:
+ \exp_not:o {#2}
+ \tl_if_eq:NNT \l_@@_internal_a_tl \l_@@_internal_b_tl
+ { \use_none:nn }
+ }
+ \@@_wrap_item:n {##1}
+ }
+ \tl_set:Nn \l_@@_internal_a_tl {#3}
+ #1 #2 {#2}
+ \@@_pop_item_def:
+ }
+\cs_generate_variant:Nn \seq_remove_all:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \seq_remove_all:Nn { Nx , cx }
+\cs_generate_variant:Nn \seq_gremove_all:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \seq_gremove_all:Nn { Nx , cx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_int_eval:w}
+% Useful to more quickly go through items.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_int_eval:w \tex_numexpr:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[noTF]{\seq_set_item:Nnn, \seq_set_item:cnn, \seq_gset_item:Nnn, \seq_gset_item:cnn}
+% \begin{macro}{\@@_set_item:NnnNN, \@@_set_item:nnNNNN, \@@_set_item_false:nnNNNN, \@@_set_item:nNnnNNNN}
+% \begin{macro}[rEXP]{\@@_set_item:wn, \@@_set_item_end:w}
+% The conditionals are distinguished from the |Nnn| versions by the
+% last argument \cs{use_ii:nn} vs \cs{use_i:nn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_set_item:Nnn #1#2#3
+ { \@@_set_item:NnnNN #1 {#2} {#3} \__kernel_tl_set:Nx \use_i:nn }
+\cs_new_protected:Npn \seq_gset_item:Nnn #1#2#3
+ { \@@_set_item:NnnNN #1 {#2} {#3} \__kernel_tl_gset:Nx \use_i:nn }
+\cs_generate_variant:Nn \seq_set_item:Nnn { c }
+\cs_generate_variant:Nn \seq_gset_item:Nnn { c }
+\prg_new_protected_conditional:Npnn \seq_set_item:Nnn #1#2#3 { TF , T , F }
+ { \@@_set_item:NnnNN #1 {#2} {#3} \__kernel_tl_set:Nx \use_ii:nn }
+\prg_new_protected_conditional:Npnn \seq_gset_item:Nnn #1#2#3 { TF , T , F }
+ { \@@_set_item:NnnNN #1 {#2} {#3} \__kernel_tl_gset:Nx \use_ii:nn }
+\prg_generate_conditional_variant:Nnn \seq_set_item:Nnn { c } { TF , T , F }
+\prg_generate_conditional_variant:Nnn \seq_gset_item:Nnn { c } { TF , T , F }
+% \end{macrocode}
+% Save the item to be stored and evaluate the position and the sequence
+% length only once. Then depending on the sign of the position, check
+% that it is not bigger than the length (in absolute value) nor zero.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_set_item:NnnNN #1#2#3
+ {
+ \tl_set:Nn \l_@@_internal_a_tl { \@@_item:n {#3} }
+ \exp_args:Nff \@@_set_item:nnNNNN
+ { \int_eval:n {#2} } { \seq_count:N #1 } #1 \use_none:nn
+ }
+\cs_new_protected:Npn \@@_set_item:nnNNNN #1#2
+ {
+ \int_compare:nNnTF {#1} > 0
+ { \int_compare:nNnF {#1} > {#2} { \@@_set_item:nNnnNNNN { #1 - 1 } } }
+ {
+ \int_compare:nNnF {#1} < {-#2}
+ {
+ \int_compare:nNnF {#1} = 0
+ { \@@_set_item:nNnnNNNN { #2 + #1 } }
+ }
+ }
+ \@@_set_item_false:nnNNNN {#1} {#2}
+ }
+% \end{macrocode}
+% If the position is not ok, \cs{@@_set_item_false:nnNNNN} calls an
+% error or returns \texttt{false} (depending on the \cs{use_i:nn} vs
+% \cs{use_ii:nn} argument mentioned above).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_set_item_false:nnNNNN #1#2#3#4#5#6
+ {
+ #6
+ {
+ \msg_error:nneee { seq } { item-too-large }
+ { \token_to_str:N #3 } {#2} {#1}
+ }
+ { \prg_return_false: }
+ }
+% \end{macrocode}
+% If the position is ok, \cs{@@_set_item:nNnnNNNN} makes the assignment
+% and returns \texttt{true} (in the case of conditionnals). Here |#1|
+% is an integer expression (position minus one), it needs to be
+% evaluated. The sequence |#5| starts with \cs{s_@@} (even if empty),
+% which stops the integer expression and is absorbed by it. The
+% \cs{if_meaning:w} test is slightly faster than an integer test (but
+% only works when testing against zero, hence the offset we chose in
+% the position). When we are done skipping items, insert the saved
+% item \cs{l_@@_internal_a_tl}. For |put| functions the last argument
+% of \cs{@@_set_item_end:w} is \cs{use_none:nn} and it absorbs the
+% item |#2| that we are removing: this is only useful for the |pop|
+% functions.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_set_item:nNnnNNNN #1#2#3#4#5#6#7#8
+ {
+ #7 #5
+ {
+ \s_@@
+ \exp_after:wN \@@_set_item:wn
+ \int_value:w \@@_int_eval:w #1
+ #5 \s_@@_stop #6
+ }
+ #8 { } { \prg_return_true: }
+ }
+\cs_new:Npn \@@_set_item:wn #1 \@@_item:n #2
+ {
+ \if_meaning:w 0 #1 \@@_set_item_end:w \fi:
+ \exp_not:n { \@@_item:n {#2} }
+ \exp_after:wN \@@_set_item:wn
+ \int_value:w \@@_int_eval:w #1 - 1 \s_@@
+ }
+\cs_new:Npn \@@_set_item_end:w #1 \exp_not:n #2 #3 \s_@@ #4 \s_@@_stop #5
+ {
+ #1
+ \exp_not:o \l_@@_internal_a_tl
+ \exp_not:n {#4}
+ #5 #2
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\seq_reverse:N, \seq_reverse:c, \seq_greverse:N, \seq_greverse:c}
+% \begin{macro}{\@@_reverse:NN}
+% \begin{macro}[EXP]{\@@_reverse_item:nwn}
+% Previously, \cs{seq_reverse:N} was coded by collecting the items
+% in reverse order after an \cs{exp_stop_f:} marker.
+% \begin{verbatim}
+% \cs_new_protected:Npn \seq_reverse:N #1
+% {
+% \cs_set_eq:NN \@@_item:n \@@_reverse_item:nw
+% \tl_set:Nf #2 { #2 \exp_stop_f: }
+% }
+% \cs_new:Npn \@@_reverse_item:nw #1 #2 \exp_stop_f:
+% {
+% #2 \exp_stop_f:
+% \@@_item:n {#1}
+% }
+% \end{verbatim}
+% At first, this seems optimal, since we can forget about each item
+% as soon as it is placed after \cs{exp_stop_f:}. Unfortunately,
+% \TeX{}'s usual tail recursion does not take place in this case:
+% since the following \cs{@@_reverse_item:nw} only reads
+% tokens until \cs{exp_stop_f:}, and never reads the
+% |\@@_item:n {#1}| left by the previous call, \TeX{} cannot
+% remove that previous call from the stack, and in particular
+% must retain the various macro parameters in memory, until the
+% end of the replacement text is reached. The stack is thus
+% only flushed after all the \cs{@@_reverse_item:nw} are
+% expanded. Keeping track of the arguments of all those calls
+% uses up a memory quadratic in the length of the sequence.
+% \TeX{} can then not cope with more than a few thousand items.
+%
+% Instead, we collect the items in the argument
+% of \cs{exp_not:n}. The previous calls are cleanly removed
+% from the stack, and the memory consumption becomes linear.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_reverse:N
+ { \@@_reverse:NN \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \seq_greverse:N
+ { \@@_reverse:NN \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \@@_reverse:NN #1 #2
+ {
+ \cs_set_eq:NN \@@_tmp:w \@@_item:n
+ \cs_set_eq:NN \@@_item:n \@@_reverse_item:nwn
+ #1 #2 { #2 \exp_not:n { } }
+ \cs_set_eq:NN \@@_item:n \@@_tmp:w
+ }
+\cs_new:Npn \@@_reverse_item:nwn #1 #2 \exp_not:n #3
+ {
+ #2
+ \exp_not:n { \@@_item:n {#1} #3 }
+ }
+\cs_generate_variant:Nn \seq_reverse:N { c }
+\cs_generate_variant:Nn \seq_greverse:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_sort:Nn, \seq_sort:cn, \seq_gsort:Nn, \seq_gsort:cn}
+% Implemented in \pkg{l3sort}.
+% \end{macro}
+%
+% \subsection{Sequence conditionals}
+%
+% \begin{macro}[pTF]{\seq_if_empty:N, \seq_if_empty:c}
+% \UnitTested
+% Similar to token lists, we compare with the empty sequence.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \seq_if_empty:N #1 { p , T , F , TF }
+ {
+ \if_meaning:w #1 \c_empty_seq
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \seq_if_empty:N
+ { c } { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\seq_shuffle:N, \seq_shuffle:c, \seq_gshuffle:N, \seq_gshuffle:c}
+% \begin{macro}{\@@_shuffle:NN}
+% \begin{macro}{\@@_shuffle_item:n}
+% \begin{variable}{\g_@@_internal_seq}
+% We apply the Fisher--Yates shuffle, storing items in \tn{toks}
+% registers. We use the primitive \cs{tex_uniformdeviate:D} for
+% speed reasons. Its non-uniformity is of order its argument divided
+% by $2^{28}$, not too bad for small lists. For sequences with more
+% than $13$ elements there are more possible permutations than
+% possible seeds ($13!>2^{28}$) so the question of uniformity is
+% somewhat moot. The integer variables are declared in \pkg{l3int}:
+% load-order issues.
+% \begin{macrocode}
+\seq_new:N \g_@@_internal_seq
+\cs_new_protected:Npn \seq_shuffle:N { \@@_shuffle:NN \seq_set_eq:NN }
+\cs_new_protected:Npn \seq_gshuffle:N { \@@_shuffle:NN \seq_gset_eq:NN }
+\cs_new_protected:Npn \@@_shuffle:NN #1#2
+ {
+ \int_compare:nNnTF { \seq_count:N #2 } > \c_max_register_int
+ {
+ \msg_error:nne { seq } { shuffle-too-large }
+ { \token_to_str:N #2 }
+ }
+ {
+ \group_begin:
+ \int_zero:N \l_@@_internal_a_int
+ \@@_push_item_def:
+ \cs_gset_eq:NN \@@_item:n \@@_shuffle_item:n
+ #2
+ \@@_pop_item_def:
+ \seq_gclear:N \g_@@_internal_seq
+ \int_step_inline:nn \l_@@_internal_a_int
+ {
+ \seq_gput_right:Ne \g_@@_internal_seq
+ { \tex_the:D \tex_toks:D ##1 }
+ }
+ \group_end:
+ #1 #2 \g_@@_internal_seq
+ \seq_gclear:N \g_@@_internal_seq
+ }
+ }
+\cs_new_protected:Npn \@@_shuffle_item:n
+ {
+ \int_incr:N \l_@@_internal_a_int
+ \int_set:Nn \l_@@_internal_b_int
+ { 1 + \tex_uniformdeviate:D \l_@@_internal_a_int }
+ \tex_toks:D \l_@@_internal_a_int
+ = \tex_toks:D \l_@@_internal_b_int
+ \tex_toks:D \l_@@_internal_b_int
+ }
+\cs_generate_variant:Nn \seq_shuffle:N { c }
+\cs_generate_variant:Nn \seq_gshuffle:N { c }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[TF]
+% {
+% \seq_if_in:Nn, \seq_if_in:NV, \seq_if_in:Nv, \seq_if_in:Ne,
+% \seq_if_in:No, \seq_if_in:Nx,
+% \seq_if_in:cn, \seq_if_in:cV, \seq_if_in:cv,\seq_if_in:ce,
+% \seq_if_in:co, \seq_if_in:cx
+% }
+% \UnitTested
+% \begin{macro}{\@@_if_in:}
+% The approach here is to define \cs{@@_item:n} to compare its
+% argument with the test sequence. If the two items are equal, the
+% mapping is terminated and \cs{group_end:} \cs{prg_return_true:}
+% is inserted after skipping over the rest of the recursion. On the
+% other hand, if there is no match then the loop breaks, returning
+% \cs{prg_return_false:}.
+% Everything is inside a group so that \cs{@@_item:n} is preserved
+% in nested situations.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \seq_if_in:Nn #1#2
+ { T , F , TF }
+ {
+ \group_begin:
+ \tl_set:Nn \l_@@_internal_a_tl {#2}
+ \cs_set_protected:Npn \@@_item:n ##1
+ {
+ \tl_set:Nn \l_@@_internal_b_tl {##1}
+ \if_meaning:w \l_@@_internal_a_tl \l_@@_internal_b_tl
+ \exp_after:wN \@@_if_in:
+ \fi:
+ }
+ #1
+ \group_end:
+ \prg_return_false:
+ \prg_break_point:
+ }
+\cs_new:Npn \@@_if_in:
+ { \prg_break:n { \group_end: \prg_return_true: } }
+\prg_generate_conditional_variant:Nnn \seq_if_in:Nn
+ { NV , Nv , Ne , No , Nx , c , cV , cv , ce , co , cx } { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Recovering data from sequences}
+%
+% \begin{macro}{\@@_pop:NNNN, \@@_pop_TF:NNNN}
+% The two \texttt{pop} functions share their emptiness tests. We also
+% use a common emptiness test for all branching \texttt{get} and
+% \texttt{pop} functions.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_pop:NNNN #1#2#3#4
+ {
+ \if_meaning:w #3 \c_empty_seq
+ \tl_set:Nn #4 { \q_no_value }
+ \else:
+ #1#2#3#4
+ \fi:
+ }
+\cs_new_protected:Npn \@@_pop_TF:NNNN #1#2#3#4
+ {
+ \if_meaning:w #3 \c_empty_seq
+ % \tl_set:Nn #4 { \q_no_value }
+ \prg_return_false:
+ \else:
+ #1#2#3#4
+ \prg_return_true:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\seq_get_left:NN, \seq_get_left:cN}
+% \UnitTested
+% \begin{macro}{\@@_get_left:wnw}
+% Getting an item from the left of a sequence is pretty easy: just
+% trim off the first item after \cs{@@_item:n} at the start. We
+% append a \cs{q_no_value} item to cover the case of an empty sequence
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_get_left:NN #1#2
+ {
+ \__kernel_tl_set:Nx #2
+ {
+ \exp_after:wN \@@_get_left:wnw
+ #1 \@@_item:n { \q_no_value } \s_@@_stop
+ }
+ }
+\cs_new:Npn \@@_get_left:wnw #1 \@@_item:n #2#3 \s_@@_stop
+ { \exp_not:n {#2} }
+\cs_generate_variant:Nn \seq_get_left:NN { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_pop_left:NN, \seq_pop_left:cN}
+% \UnitTested
+% \begin{macro}{\seq_gpop_left:NN, \seq_gpop_left:cN}
+% \UnitTested
+% \begin{macro}{\@@_pop_left:NNN, \@@_pop_left:wnwNNN}
+% The approach to popping an item is pretty similar to that to get
+% an item, with the only difference being that the sequence itself has
+% to be redefined. This makes it more sensible to use an auxiliary
+% function for the local and global cases.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_pop_left:NN
+ { \@@_pop:NNNN \@@_pop_left:NNN \tl_set:Nn }
+\cs_new_protected:Npn \seq_gpop_left:NN
+ { \@@_pop:NNNN \@@_pop_left:NNN \tl_gset:Nn }
+\cs_new_protected:Npn \@@_pop_left:NNN #1#2#3
+ { \exp_after:wN \@@_pop_left:wnwNNN #2 \s_@@_stop #1#2#3 }
+\cs_new_protected:Npn \@@_pop_left:wnwNNN
+ #1 \@@_item:n #2#3 \s_@@_stop #4#5#6
+ {
+ #4 #5 { #1 #3 }
+ \tl_set:Nn #6 {#2}
+ }
+\cs_generate_variant:Nn \seq_pop_left:NN { c }
+\cs_generate_variant:Nn \seq_gpop_left:NN { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_get_right:NN, \seq_get_right:cN}
+% \UnitTested
+% \begin{macro}[EXP]{\@@_get_right_loop:nw, \@@_get_right_end:NnN}
+% First remove \cs{s_@@} and prepend \cs{q_no_value}. The first
+% argument of \cs{@@_get_right_loop:nw} is the last item found, and
+% the second argument is empty until the end of the loop, where it is
+% code that applies \cs{exp_not:n} to the last item and ends the loop.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_get_right:NN #1#2
+ {
+ \__kernel_tl_set:Nx #2
+ {
+ \exp_after:wN \use_i_ii:nnn
+ \exp_after:wN \@@_get_right_loop:nw
+ \exp_after:wN \q_no_value
+ #1
+ \@@_get_right_end:NnN \@@_item:n
+ }
+ }
+\cs_new:Npn \@@_get_right_loop:nw #1#2 \@@_item:n
+ {
+ #2 \use_none:n {#1}
+ \@@_get_right_loop:nw
+ }
+\cs_new:Npn \@@_get_right_end:NnN #1#2#3 { \exp_not:n {#2} }
+\cs_generate_variant:Nn \seq_get_right:NN { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_pop_right:NN, \seq_pop_right:cN}
+% \UnitTested
+% \begin{macro}{\seq_gpop_right:NN, \seq_gpop_right:cN}
+% \UnitTested
+% \begin{macro}{\@@_pop_right:NNN, \@@_pop_right_loop:nn}
+% The approach to popping from the right is a bit more involved, but does
+% use some of the same ideas as getting from the right. What is needed is a
+% \enquote{flexible length} way to set a token list variable. This is
+% supplied by the |{ \if_false: } \fi:| \ldots
+% |\if_false: { \fi: }| construct. Using an \texttt{e}-type
+% expansion and a \enquote{non-expanding} definition for \cs{@@_item:n},
+% the left-most $n - 1$ entries in a sequence of $n$ items are stored
+% back in the sequence. That needs a loop of unknown length, hence using the
+% strange \cs{if_false:} way of including braces. When the last item
+% of the sequence is reached, the closing brace for the assignment is
+% inserted, and |\tl_set:Nn #3| is inserted in front of the final
+% entry. This therefore does the pop assignment. One more iteration
+% is performed, with an empty argument and \cs{use_none:nn}, which
+% finally stops the loop.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_pop_right:NN
+ { \@@_pop:NNNN \@@_pop_right:NNN \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \seq_gpop_right:NN
+ { \@@_pop:NNNN \@@_pop_right:NNN \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \@@_pop_right:NNN #1#2#3
+ {
+ \cs_set_eq:NN \@@_tmp:w \@@_item:n
+ \cs_set_eq:NN \@@_item:n \scan_stop:
+ #1 #2
+ { \if_false: } \fi: \s_@@
+ \exp_after:wN \use_i:nnn
+ \exp_after:wN \@@_pop_right_loop:nn
+ #2
+ {
+ \if_false: { \fi: }
+ \__kernel_tl_set:Nx #3
+ }
+ { } \use_none:nn
+ \cs_set_eq:NN \@@_item:n \@@_tmp:w
+ }
+\cs_new:Npn \@@_pop_right_loop:nn #1#2
+ {
+ #2 { \exp_not:n {#1} }
+ \@@_pop_right_loop:nn
+ }
+\cs_generate_variant:Nn \seq_pop_right:NN { c }
+\cs_generate_variant:Nn \seq_gpop_right:NN { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[TF]{\seq_get_left:NN, \seq_get_left:cN}
+% \begin{macro}[TF]{\seq_get_right:NN, \seq_get_right:cN}
+% Getting from the left or right with a check on the results. The
+% first argument to \cs{@@_pop_TF:NNNN} is left unused.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \seq_get_left:NN #1#2 { T , F , TF }
+ { \@@_pop_TF:NNNN \prg_do_nothing: \seq_get_left:NN #1#2 }
+\prg_new_protected_conditional:Npnn \seq_get_right:NN #1#2 { T , F , TF }
+ { \@@_pop_TF:NNNN \prg_do_nothing: \seq_get_right:NN #1#2 }
+\prg_generate_conditional_variant:Nnn \seq_get_left:NN
+ { c } { T , F , TF }
+\prg_generate_conditional_variant:Nnn \seq_get_right:NN
+ { c } { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[TF]{\seq_pop_left:NN, \seq_pop_left:cN}
+% \begin{macro}[TF]{\seq_gpop_left:NN, \seq_gpop_left:cN}
+% \begin{macro}[TF]{\seq_pop_right:NN, \seq_pop_right:cN}
+% \begin{macro}[TF]{\seq_gpop_right:NN, \seq_gpop_right:cN}
+% More or less the same for popping.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \seq_pop_left:NN #1#2
+ { T , F , TF }
+ { \@@_pop_TF:NNNN \@@_pop_left:NNN \tl_set:Nn #1 #2 }
+\prg_new_protected_conditional:Npnn \seq_gpop_left:NN #1#2
+ { T , F , TF }
+ { \@@_pop_TF:NNNN \@@_pop_left:NNN \tl_gset:Nn #1 #2 }
+\prg_new_protected_conditional:Npnn \seq_pop_right:NN #1#2
+ { T , F , TF }
+ { \@@_pop_TF:NNNN \@@_pop_right:NNN \__kernel_tl_set:Nx #1 #2 }
+\prg_new_protected_conditional:Npnn \seq_gpop_right:NN #1#2
+ { T , F , TF }
+ { \@@_pop_TF:NNNN \@@_pop_right:NNN \__kernel_tl_gset:Nx #1 #2 }
+\prg_generate_conditional_variant:Nnn \seq_pop_left:NN { c }
+ { T , F , TF }
+\prg_generate_conditional_variant:Nnn \seq_gpop_left:NN { c }
+ { T , F , TF }
+\prg_generate_conditional_variant:Nnn \seq_pop_right:NN { c }
+ { T , F , TF }
+\prg_generate_conditional_variant:Nnn \seq_gpop_right:NN { c }
+ { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\seq_item:Nn, \seq_item:NV, \seq_item:Ne, \seq_item:cn, \seq_item:cV, \seq_item:ce}
+% \begin{macro}{\@@_item:wNn, \@@_item:nN, \@@_item:nwn}
+% The idea here is to find the offset of the item from the left, then use
+% a loop to grab the correct item. If the resulting offset is too large,
+% then the argument delimited by \cs{@@_item:n} is \cs{prg_break:} instead
+% of being empty, terminating the loop and returning nothing at all.
+% \begin{macrocode}
+\cs_new:Npn \seq_item:Nn #1
+ { \exp_after:wN \@@_item:wNn #1 \s_@@_stop #1 }
+\cs_new:Npn \@@_item:wNn \s_@@ #1 \s_@@_stop #2#3
+ {
+ \exp_args:Nf \@@_item:nwn
+ { \exp_args:Nf \@@_item:nN { \int_eval:n {#3} } #2 }
+ #1
+ \prg_break: \@@_item:n { }
+ \prg_break_point:
+ }
+\cs_new:Npn \@@_item:nN #1#2
+ {
+ \int_compare:nNnTF {#1} < 0
+ { \int_eval:n { \seq_count:N #2 + 1 + #1 } }
+ {#1}
+ }
+\cs_new:Npn \@@_item:nwn #1#2 \@@_item:n #3
+ {
+ #2
+ \int_compare:nNnTF {#1} = 1
+ { \prg_break:n { \exp_not:n {#3} } }
+ { \exp_args:Nf \@@_item:nwn { \int_eval:n { #1 - 1 } } }
+ }
+\cs_generate_variant:Nn \seq_item:Nn { NV , Ne , c , cV , ce }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_rand_item:N, \seq_rand_item:c}
+% Importantly, \cs{seq_item:Nn} only evaluates its argument once.
+% \begin{macrocode}
+\cs_new:Npn \seq_rand_item:N #1
+ {
+ \seq_if_empty:NF #1
+ { \seq_item:Nn #1 { \int_rand:nn { 1 } { \seq_count:N #1 } } }
+ }
+\cs_generate_variant:Nn \seq_rand_item:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Mapping over sequences}
+%
+% \begin{macro}{\seq_map_break:}
+% \UnitTested
+% \begin{macro}{\seq_map_break:n}
+% \UnitTested
+% To break a function, the special token \cs{prg_break_point:Nn} is
+% used to find the end of the code. Any ending code is then inserted
+% before the return value of \cs{seq_map_break:n} is inserted.
+% \begin{macrocode}
+\cs_new:Npn \seq_map_break:
+ { \prg_map_break:Nn \seq_map_break: { } }
+\cs_new:Npn \seq_map_break:n
+ { \prg_map_break:Nn \seq_map_break: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_map_function:NN, \seq_map_function:cN}
+% \UnitTested
+% \begin{macro}[rEXP]{\@@_map_function:Nw}
+% The idea here is to apply the code of |#2| to each item in the
+% sequence without altering the definition of \cs{@@_item:n}. The
+% even-numbered arguments of \cs{@@_map_function:Nw} delimited by
+% \cs{@@_item:n} are almost always empty, except
+% at the end of the loop where it is \cs{prg_break:}. This allows to
+% break the loop without needing to do a (relatively-expensive) quark
+% test.
+% \begin{macrocode}
+\cs_new:Npn \seq_map_function:NN #1#2
+ {
+ \exp_after:wN \use_i_ii:nnn
+ \exp_after:wN \@@_map_function:Nw
+ \exp_after:wN #2
+ #1
+ \prg_break:
+ \@@_item:n { } \@@_item:n { } \@@_item:n { } \@@_item:n { }
+ \prg_break_point:
+ \prg_break_point:Nn \seq_map_break: { }
+ }
+\cs_new:Npn \@@_map_function:Nw #1
+ #2 \@@_item:n #3
+ #4 \@@_item:n #5
+ #6 \@@_item:n #7
+ #8 \@@_item:n #9
+ {
+ #2 #1 {#3}
+ #4 #1 {#5}
+ #6 #1 {#7}
+ #8 #1 {#9}
+ \@@_map_function:Nw #1
+ }
+\cs_generate_variant:Nn \seq_map_function:NN { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_push_item_def:n, \@@_push_item_def:e}
+% \begin{macro}{\@@_push_item_def:}
+% \begin{macro}{\@@_pop_item_def:}
+% The definition of \cs{@@_item:n} needs to be saved and restored at
+% various points within the mapping and manipulation code. That is handled
+% here: as always, this approach uses global assignments.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_push_item_def:n
+ {
+ \@@_push_item_def:
+ \cs_gset:Npn \@@_item:n ##1
+ }
+\cs_new_protected:Npn \@@_push_item_def:e
+ {
+ \@@_push_item_def:
+ \cs_gset:Npe \@@_item:n ##1
+ }
+\cs_new_protected:Npn \@@_push_item_def:
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \cs_gset_eq:cN { @@_map_ \int_use:N \g__kernel_prg_map_int :w }
+ \@@_item:n
+ }
+\cs_new_protected:Npn \@@_pop_item_def:
+ {
+ \cs_gset_eq:Nc \@@_item:n
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w }
+ \int_gdecr:N \g__kernel_prg_map_int
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_map_inline:Nn, \seq_map_inline:cn}
+% \UnitTested
+% The idea here is that \cs{@@_item:n} is already \enquote{applied} to
+% each item in a sequence, and so an in-line mapping is just a case of
+% redefining \cs{@@_item:n}.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_map_inline:Nn #1#2
+ {
+ \@@_push_item_def:n {#2}
+ #1
+ \prg_break_point:Nn \seq_map_break: { \@@_pop_item_def: }
+ }
+\cs_generate_variant:Nn \seq_map_inline:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\seq_map_tokens:Nn, \seq_map_tokens:cn}
+% \begin{macro}{\@@_map_tokens:nw}
+% This is based on the function mapping but using the same tricks as
+% described for \cs{prop_map_tokens:Nn}. The idea is to remove the leading
+% \cs{s_@@} and apply the tokens such that they are safe with the
+% break points, hence the \cs{use:n}.
+% \begin{macrocode}
+\cs_new:Npn \seq_map_tokens:Nn #1#2
+ {
+ \exp_last_unbraced:Nno
+ \use_i:nn { \@@_map_tokens:nw {#2} } #1
+ \prg_break:
+ \@@_item:n { } \@@_item:n { } \@@_item:n { } \@@_item:n { }
+ \prg_break_point:
+ \prg_break_point:Nn \seq_map_break: { }
+ }
+\cs_generate_variant:Nn \seq_map_tokens:Nn { c }
+\cs_new:Npn \@@_map_tokens:nw #1
+ #2 \@@_item:n #3
+ #4 \@@_item:n #5
+ #6 \@@_item:n #7
+ #8 \@@_item:n #9
+ {
+ #2 \use:n {#1} {#3}
+ #4 \use:n {#1} {#5}
+ #6 \use:n {#1} {#7}
+ #8 \use:n {#1} {#9}
+ \@@_map_tokens:nw {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \seq_map_variable:NNn, \seq_map_variable:Ncn,
+% \seq_map_variable:cNn, \seq_map_variable:ccn
+% }
+% \UnitTested
+% This is just a specialised version of the in-line mapping function,
+% using an \texttt{e}-type expansion for the code set up so that the
+% number of |#| tokens required is as expected.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_map_variable:NNn #1#2#3
+ {
+ \@@_push_item_def:e
+ {
+ \tl_set:Nn \exp_not:N #2 {##1}
+ \exp_not:n {#3}
+ }
+ #1
+ \prg_break_point:Nn \seq_map_break: { \@@_pop_item_def: }
+ }
+\cs_generate_variant:Nn \seq_map_variable:NNn { Nc }
+\cs_generate_variant:Nn \seq_map_variable:NNn { c , cc }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \seq_map_indexed_function:NN, \seq_map_indexed_inline:Nn,
+% \@@_map_indexed:nNN, \@@_map_indexed:Nw
+% }
+% Similar to \cs{seq_map_function:NN} but we keep track of the item
+% index as a |;|-delimited argument of \cs{@@_map_indexed:Nw}.
+% \begin{macrocode}
+\cs_new:Npn \seq_map_indexed_function:NN #1#2
+ {
+ \@@_map_indexed:NN #1#2
+ \prg_break_point:Nn \seq_map_break: { }
+ }
+\cs_new_protected:Npn \seq_map_indexed_inline:Nn #1#2
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \cs_gset_protected:cpn
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w } ##1##2 {#2}
+ \exp_args:NNc \@@_map_indexed:NN #1
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w }
+ \prg_break_point:Nn \seq_map_break:
+ { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+\cs_new:Npn \@@_map_indexed:NN #1#2
+ {
+ \exp_after:wN \@@_map_indexed:Nw
+ \exp_after:wN #2
+ \int_value:w 1
+ \exp_after:wN \use_i:nn
+ \exp_after:wN ;
+ #1
+ \prg_break: \@@_item:n { } \prg_break_point:
+ }
+\cs_new:Npn \@@_map_indexed:Nw #1#2 ; #3 \@@_item:n #4
+ {
+ #3
+ #1 {#2} {#4}
+ \exp_after:wN \@@_map_indexed:Nw
+ \exp_after:wN #1
+ \int_value:w \int_eval:w 1 + #2 ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \seq_map_pairwise_function:NNN, \seq_map_pairwise_function:NcN,
+% \seq_map_pairwise_function:cNN, \seq_map_pairwise_function:ccN
+% }
+% \begin{macro}
+% {
+% \@@_map_pairwise_function:wNN, \@@_map_pairwise_function:wNw,
+% \@@_map_pairwise_function:Nnnwnn
+% }
+% The idea is to first expand both sequences, adding the
+% usual |{ ? \prg_break: } { }| to the end of each one. This is
+% most conveniently done in two steps using an auxiliary function.
+% The mapping then throws away the first tokens of |#2| and |#5|,
+% which for items in both sequences are \cs{s_@@}
+% \cs{@@_item:n}. The function to be mapped are then be applied to
+% the two entries. When the code hits the end of one of the
+% sequences, the break material stops the entire loop and tidy up.
+% This avoids needing to find the count of the two sequences, or
+% worrying about which is longer.
+% \begin{macrocode}
+\cs_new:Npn \seq_map_pairwise_function:NNN #1#2#3
+ { \exp_after:wN \@@_map_pairwise_function:wNN #2 \s_@@_stop #1 #3 }
+\cs_new:Npn \@@_map_pairwise_function:wNN \s_@@ #1 \s_@@_stop #2#3
+ {
+ \exp_after:wN \@@_map_pairwise_function:wNw #2 \s_@@_stop #3
+ #1 { ? \prg_break: } { }
+ \prg_break_point:
+ }
+\cs_new:Npn \@@_map_pairwise_function:wNw \s_@@ #1 \s_@@_stop #2
+ {
+ \@@_map_pairwise_function:Nnnwnn #2
+ #1 { ? \prg_break: } { }
+ \s_@@_stop
+ }
+\cs_new:Npn \@@_map_pairwise_function:Nnnwnn #1#2#3#4 \s_@@_stop #5#6
+ {
+ \use_none:n #2
+ \use_none:n #5
+ #1 {#3} {#6}
+ \@@_map_pairwise_function:Nnnwnn #1 #4 \s_@@_stop
+ }
+\cs_generate_variant:Nn \seq_map_pairwise_function:NNN { Nc , c , cc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_set_map_e:NNn, \seq_gset_map_e:NNn}
+% \begin{macro}{\@@_set_map_e:NNNn}
+% Very similar to \cs{seq_set_filter:NNn}. We could actually
+% merge the two within a single function, but it would have weird
+% semantics.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_set_map_e:NNn
+ { \@@_set_map_e:NNNn \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \seq_gset_map_e:NNn
+ { \@@_set_map_e:NNNn \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \@@_set_map_e:NNNn #1#2#3#4
+ {
+ \@@_push_item_def:n { \exp_not:N \@@_item:n {#4} }
+ #1 #2 { #3 }
+ \@@_pop_item_def:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_set_map:NNn, \seq_gset_map:NNn}
+% \begin{macro}{\@@_set_map:NNNn}
+% Similar to \cs{seq_set_map_e:NNn}, but prevents expansion of the
+% <inline function>.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_set_map:NNn
+ { \@@_set_map:NNNn \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \seq_gset_map:NNn
+ { \@@_set_map:NNNn \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \@@_set_map:NNNn #1#2#3#4
+ {
+ \@@_push_item_def:n { \exp_not:n { \@@_item:n {#4} } }
+ #1 #2 { #3 }
+ \@@_pop_item_def:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_count:N, \seq_count:c}
+% \begin{macro}{\@@_count:w, \@@_count_end:w}
+% Since counting the items in a sequence is quite common, we optimize
+% it by grabbing $8$~items at a time and correspondingly adding $8$ to
+% an integer expression. At the end of the loop, |#9| is
+% \cs{@@_count_end:w} instead of being empty. It removes |8+| and
+% instead places the number of \cs{@@_item:n} that \cs{@@_count:w}
+% grabbed before reaching the end of the sequence.
+% \begin{macrocode}
+\cs_new:Npn \seq_count:N #1
+ {
+ \int_eval:n
+ {
+ \exp_after:wN \use_i:nn
+ \exp_after:wN \@@_count:w
+ #1
+ \@@_count_end:w \@@_item:n 7
+ \@@_count_end:w \@@_item:n 6
+ \@@_count_end:w \@@_item:n 5
+ \@@_count_end:w \@@_item:n 4
+ \@@_count_end:w \@@_item:n 3
+ \@@_count_end:w \@@_item:n 2
+ \@@_count_end:w \@@_item:n 1
+ \@@_count_end:w \@@_item:n 0
+ \prg_break_point:
+ }
+ }
+\cs_new:Npn \@@_count:w
+ #1 \@@_item:n #2 \@@_item:n #3 \@@_item:n #4 \@@_item:n
+ #5 \@@_item:n #6 \@@_item:n #7 \@@_item:n #8 #9 \@@_item:n
+ { #9 8 + \@@_count:w }
+\cs_new:Npn \@@_count_end:w 8 + \@@_count:w #1#2 \prg_break_point: {#1}
+\cs_generate_variant:Nn \seq_count:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Using sequences}
+%
+% \begin{macro}[EXP]{\seq_use:Nnnn, \seq_use:cnnn}
+% \begin{macro}[EXP]
+% {\@@_use:NNnNnn, \@@_use_setup:w, \@@_use:nwwwwnwn, \@@_use:nwwn}
+% \begin{macro}[EXP]{\seq_use:Nn, \seq_use:cn}
+% See \cs{clist_use:Nnnn} for a general explanation. The main
+% difference is that we use \cs{@@_item:n} as a delimiter rather than
+% commas. We also need to add \cs{@@_item:n} at various places, and
+% \cs{s_@@}.
+% \begin{macrocode}
+\cs_new:Npn \seq_use:Nnnn #1#2#3#4
+ {
+ \seq_if_exist:NTF #1
+ {
+ \int_case:nnF { \seq_count:N #1 }
+ {
+ { 0 } { }
+ { 1 } { \exp_after:wN \@@_use:NNnNnn #1 ? { } { } }
+ { 2 } { \exp_after:wN \@@_use:NNnNnn #1 {#2} }
+ }
+ {
+ \exp_after:wN \@@_use_setup:w #1 \@@_item:n
+ \s_@@_mark { \@@_use:nwwwwnwn {#3} }
+ \s_@@_mark { \@@_use:nwwn {#4} }
+ \s_@@_stop { }
+ }
+ }
+ {
+ \msg_expandable_error:nnn
+ { kernel } { bad-variable } {#1}
+ }
+ }
+\cs_generate_variant:Nn \seq_use:Nnnn { c }
+\cs_new:Npn \@@_use:NNnNnn #1#2#3#4#5#6 { \exp_not:n { #3 #6 #5 } }
+\cs_new:Npn \@@_use_setup:w \s_@@ { \@@_use:nwwwwnwn { } }
+\cs_new:Npn \@@_use:nwwwwnwn
+ #1 \@@_item:n #2 \@@_item:n #3 \@@_item:n #4#5
+ \s_@@_mark #6#7 \s_@@_stop #8
+ {
+ #6 \@@_item:n {#3} \@@_item:n {#4} #5
+ \s_@@_mark {#6} #7 \s_@@_stop { #8 #1 #2 }
+ }
+\cs_new:Npn \@@_use:nwwn #1 \@@_item:n #2 #3 \s_@@_stop #4
+ { \exp_not:n { #4 #1 #2 } }
+\cs_new:Npn \seq_use:Nn #1#2
+ { \seq_use:Nnnn #1 {#2} {#2} {#2} }
+\cs_generate_variant:Nn \seq_use:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Sequence stacks}
+%
+% The same functions as for sequences, but with the correct naming.
+%
+% \begin{macro}{
+% \seq_push:Nn, \seq_push:NV, \seq_push:Nv, \seq_push:Ne,
+% \seq_push:No, \seq_push:Nx,
+% \seq_push:cn, \seq_push:cV, \seq_push:cv, \seq_push:ce,
+% \seq_push:co, \seq_push:cx
+% }
+% \UnitTested
+% \begin{macro}{
+% \seq_gpush:Nn, \seq_gpush:NV, \seq_gpush:Nv, \seq_gpush:Ne,
+% \seq_gpush:No, \seq_gpush:Nx,
+% \seq_gpush:cn, \seq_gpush:cV, \seq_gpush:cv, \seq_gpush:ce,
+% \seq_gpush:co, \seq_gpush:cx
+% }
+% \UnitTested
+% Pushing to a sequence is the same as adding on the left.
+% \begin{macrocode}
+\cs_new_eq:NN \seq_push:Nn \seq_put_left:Nn
+\cs_generate_variant:Nn \seq_push:Nn { NV , Nv , Ne , c , cV , cv , ce }
+\cs_generate_variant:Nn \seq_push:Nn { No , Nx , co , cx }
+\cs_new_eq:NN \seq_gpush:Nn \seq_gput_left:Nn
+\cs_generate_variant:Nn \seq_gpush:Nn { NV , Nv , Ne , c , cV , cv , ce }
+\cs_generate_variant:Nn \seq_gpush:Nn { No , Nx , co , cx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_get:NN, \seq_get:cN}
+% \UnitTested
+% \begin{macro}{\seq_pop:NN, \seq_pop:cN}
+% \UnitTested
+% \begin{macro}{\seq_gpop:NN, \seq_gpop:cN}
+% \UnitTested
+% In most cases, getting items from the stack does not need to specify
+% that this is from the left. So alias are provided.
+% \begin{macrocode}
+\cs_new_eq:NN \seq_get:NN \seq_get_left:NN
+\cs_new_eq:NN \seq_get:cN \seq_get_left:cN
+\cs_new_eq:NN \seq_pop:NN \seq_pop_left:NN
+\cs_new_eq:NN \seq_pop:cN \seq_pop_left:cN
+\cs_new_eq:NN \seq_gpop:NN \seq_gpop_left:NN
+\cs_new_eq:NN \seq_gpop:cN \seq_gpop_left:cN
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[TF]{\seq_get:NN, \seq_get:cN}
+% \begin{macro}[TF]{\seq_pop:NN, \seq_pop:cN}
+% \begin{macro}[TF]{\seq_gpop:NN, \seq_gpop:cN}
+% More copies.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \seq_get:NN \seq_get_left:NN { T , F , TF }
+\prg_new_eq_conditional:NNn \seq_get:cN \seq_get_left:cN { T , F , TF }
+\prg_new_eq_conditional:NNn \seq_pop:NN \seq_pop_left:NN { T , F , TF }
+\prg_new_eq_conditional:NNn \seq_pop:cN \seq_pop_left:cN { T , F , TF }
+\prg_new_eq_conditional:NNn \seq_gpop:NN \seq_gpop_left:NN { T , F , TF }
+\prg_new_eq_conditional:NNn \seq_gpop:cN \seq_gpop_left:cN { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Viewing sequences}
+%
+% \begin{macro}{\seq_show:N, \seq_show:c, \seq_log:N, \seq_log:c, \@@_show:NN}
+% \begin{macro}[rEXP]{\@@_show_validate:nn}
+% \UnitTested
+% Apply the general \cs{__kernel_chk_tl_type:NnnT}.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_show:N { \@@_show:NN \msg_show:nneeee }
+\cs_generate_variant:Nn \seq_show:N { c }
+\cs_new_protected:Npn \seq_log:N { \@@_show:NN \msg_log:nneeee }
+\cs_generate_variant:Nn \seq_log:N { c }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \__kernel_chk_tl_type:NnnT #2 { seq }
+ {
+ \s_@@
+ \exp_after:wN \use_i:nn \exp_after:wN \@@_show_validate:nn #2
+ \q_recursion_tail \q_recursion_tail \q_recursion_stop
+ }
+ {
+ #1 { seq } { show }
+ { \token_to_str:N #2 }
+ { \seq_map_function:NN #2 \msg_show_item:n }
+ { } { }
+ }
+ }
+\cs_new:Npn \@@_show_validate:nn #1#2
+ {
+ \quark_if_recursion_tail_stop:n {#2}
+ \@@_wrap_item:n {#2}
+ \@@_show_validate:nn
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Scratch sequences}
+%
+% \begin{variable}{\l_tmpa_seq, \l_tmpb_seq, \g_tmpa_seq, \g_tmpb_seq}
+% Temporary comma list variables.
+% \begin{macrocode}
+\seq_new:N \l_tmpa_seq
+\seq_new:N \l_tmpb_seq
+\seq_new:N \g_tmpa_seq
+\seq_new:N \g_tmpb_seq
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3skip.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3skip.dtx
new file mode 100644
index 00000000000..a3d9256223d
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3skip.dtx
@@ -0,0 +1,2600 @@
+% \iffalse meta-comment
+%
+%% File: l3skip.dtx
+%
+% Copyright (C) 2004-2011 Frank Mittelbach, The LaTeX Project
+% (C) 2012-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3skip} module\\ Dimensions and skips^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \LaTeX3 provides two general length variables: \texttt{dim} and
+% \texttt{skip}. Lengths stored as \texttt{dim} variables have a fixed
+% length, whereas \texttt{skip} lengths have a rubber (stretch/shrink)
+% component. In addition, the \texttt{muskip} type is available for
+% use in math mode: this is a special form of \texttt{skip} where the
+% lengths involved are determined by the current math font (in
+% \texttt{mu)}. There are common features in the creation and setting of
+% length variables, but for clarity the functions are grouped by variable
+% type.
+%
+% Many functions take
+% \emph{dimension expressions} (\enquote{\meta{dim expr}}) or
+% \emph{skip expressions} (\enquote{\meta{skip expr}}) as arguments.
+%
+%
+% \section{Creating and initialising \texttt{dim} variables}
+%
+% \begin{function}{\dim_new:N, \dim_new:c}
+% \begin{syntax}
+% \cs{dim_new:N} \meta{dimension}
+% \end{syntax}
+% Creates a new \meta{dimension} or raises an error if the name is
+% already taken. The declaration is global. The \meta{dimension}
+% is initially equal to $0$\,pt.
+% \end{function}
+%
+% \begin{function}[added = 2012-03-05]{\dim_const:Nn, \dim_const:cn}
+% \begin{syntax}
+% \cs{dim_const:Nn} \meta{dimension} \Arg{dim expr}
+% \end{syntax}
+% Creates a new constant \meta{dimension} or raises an error if the
+% name is already taken. The value of the \meta{dimension} is set
+% globally to the \meta{dim expr}.
+% \end{function}
+%
+% \begin{function}{\dim_zero:N, \dim_zero:c, \dim_gzero:N, \dim_gzero:c}
+% \begin{syntax}
+% \cs{dim_zero:N} \meta{dimension}
+% \end{syntax}
+% Sets \meta{dimension} to $0$\,pt.
+% \end{function}
+%
+% \begin{function}[added = 2012-01-07]
+% {\dim_zero_new:N, \dim_zero_new:c, \dim_gzero_new:N, \dim_gzero_new:c}
+% \begin{syntax}
+% \cs{dim_zero_new:N} \meta{dimension}
+% \end{syntax}
+% Ensures that the \meta{dimension} exists globally by applying
+% \cs{dim_new:N} if necessary, then applies
+% \cs[index=dim_zero:N]{dim_(g)zero:N} to leave
+% the \meta{dimension} set to zero.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added=2012-03-03]{\dim_if_exist:N, \dim_if_exist:c}
+% \begin{syntax}
+% \cs{dim_if_exist_p:N} \meta{dimension}
+% \cs{dim_if_exist:NTF} \meta{dimension} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{dimension} is currently defined. This does
+% not check that the \meta{dimension} really is a dimension variable.
+% \end{function}
+%
+% \section{Setting \texttt{dim} variables}
+%
+% \begin{function}[updated = 2011-10-22]
+% {\dim_add:Nn, \dim_add:cn, \dim_gadd:Nn, \dim_gadd:cn}
+% \begin{syntax}
+% \cs{dim_add:Nn} \meta{dimension} \Arg{dim expr}
+% \end{syntax}
+% Adds the result of the \meta{dim expr} to the current
+% content of the \meta{dimension}.
+% \end{function}
+%
+% \begin{function}[updated = 2011-10-22]
+% {\dim_set:Nn, \dim_set:cn, \dim_gset:Nn, \dim_gset:cn}
+% \begin{syntax}
+% \cs{dim_set:Nn} \meta{dimension} \Arg{dim expr}
+% \end{syntax}
+% Sets \meta{dimension} to the value of \meta{dim expr}, which
+% must evaluate to a length with units.
+% \end{function}
+%
+% \begin{function}
+% {
+% \dim_set_eq:NN, \dim_set_eq:cN, \dim_set_eq:Nc, \dim_set_eq:cc,
+% \dim_gset_eq:NN, \dim_gset_eq:cN, \dim_gset_eq:Nc, \dim_gset_eq:cc
+% }
+% \begin{syntax}
+% \cs{dim_set_eq:NN} \meta{dimension_1} \meta{dimension_2}
+% \end{syntax}
+% Sets the content of \meta{dimension_1} equal to that of
+% \meta{dimension_2}.
+% \end{function}
+%
+% \begin{function}[updated = 2011-10-22]
+% {\dim_sub:Nn, \dim_sub:cn, \dim_gsub:Nn, \dim_gsub:cn}
+% \begin{syntax}
+% \cs{dim_sub:Nn} \meta{dimension} \Arg{dim expr}
+% \end{syntax}
+% Subtracts the result of the \meta{dim expr} from the
+% current content of the \meta{dimension}.
+% \end{function}
+%
+% \section{Utilities for dimension calculations}
+%
+% \begin{function}[updated = 2012-09-26, EXP]{\dim_abs:n}
+% \begin{syntax}
+% \cs{dim_abs:n} \Arg{dim expr}
+% \end{syntax}
+% Converts the \meta{dim expr} to its absolute value, leaving the result
+% in the input stream as a \meta{dimension denotation}.
+% \end{function}
+%
+% \begin{function}[added = 2012-09-09, updated = 2012-09-26, EXP]
+% {\dim_max:nn, \dim_min:nn}
+% \begin{syntax}
+% \cs{dim_max:nn} \Arg{dim expr_1} \Arg{dim expr_2}
+% \cs{dim_min:nn} \Arg{dim expr_1} \Arg{dim expr_2}
+% \end{syntax}
+% Evaluates the two \meta{dim exprs} and leaves either the
+% maximum or minimum value in the input stream as appropriate, as a
+% \meta{dimension denotation}.
+% \end{function}
+%
+% \begin{function}[updated = 2011-10-22, rEXP]{\dim_ratio:nn}
+% \begin{syntax}
+% \cs{dim_ratio:nn} \Arg{dim expr_1} \Arg{dim expr_2}
+% \end{syntax}
+% Parses the two \meta{dim exprs} and converts the ratio of
+% the two to a form suitable for use inside a \meta{dim expr}.
+% This ratio is then left in the input stream, allowing syntax such as
+% \begin{verbatim}
+% \dim_set:Nn \l_my_dim
+% { 10 pt * \dim_ratio:nn { 5 pt } { 10 pt } }
+% \end{verbatim}
+% The output of \cs{dim_ratio:nn} on full expansion is a ratio expression
+% between two integers, with all distances converted to scaled points.
+% Thus
+% \begin{verbatim}
+% \tl_set:Ne \l_my_tl { \dim_ratio:nn { 5 pt } { 10 pt } }
+% \tl_show:N \l_my_tl
+% \end{verbatim}
+% displays |327680/655360| on the terminal.
+% \end{function}
+%
+% \section{Dimension expression conditionals}
+%
+% \begin{function}[EXP,pTF]{\dim_compare:nNn}
+% \begin{syntax}
+% \cs{dim_compare_p:nNn} \Arg{dim expr_1} \meta{relation} \Arg{dim expr_2} \\
+% \cs{dim_compare:nNnTF}
+% ~~\Arg{dim expr_1} \meta{relation} \Arg{dim expr_2}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% This function first evaluates each of the \meta{dim exprs}
+% as described for \cs{dim_eval:n}. The two results are then
+% compared using the \meta{relation}:
+% \begin{center}
+% \begin{tabular}{ll}
+% Equal & |=| \\
+% Greater than & |>| \\
+% Less than & |<| \\
+% \end{tabular}
+% \end{center}
+% This function is less flexible than \cs{dim_compare:nTF} but around
+% $5$~times faster.
+% \end{function}
+%
+% \begin{function}[updated = 2013-01-13, EXP, pTF]{\dim_compare:n}
+% \begin{syntax}
+% \cs{dim_compare_p:n} \\
+% ~~\{ \\
+% ~~~~\meta{dim expr_1} \meta{relation_1} \\
+% ~~~~\ldots{} \\
+% ~~~~\meta{dim expr_N} \meta{relation_N} \\
+% ~~~~\meta{dim expr_{N+1}} \\
+% ~~\} \\
+% \cs{dim_compare:nTF}
+% ~~\{ \\
+% ~~~~\meta{dim expr_1} \meta{relation_1} \\
+% ~~~~\ldots{} \\
+% ~~~~\meta{dim expr_N} \meta{relation_N} \\
+% ~~~~\meta{dim expr_{N+1}} \\
+% ~~\} \\
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% This function evaluates the \meta{dim exprs} as
+% described for \cs{dim_eval:n} and compares consecutive result using
+% the corresponding \meta{relation}, namely it compares
+% \meta{dim expr_1} and \meta{dim expr_2} using the \meta{relation_1},
+% then \meta{dim expr_2} and \meta{dim expr_3} using the
+% \meta{relation_2}, until finally comparing \meta{dim expr_N} and
+% \meta{dim expr_{N+1}} using the \meta{relation_N}. The test yields
+% \texttt{true} if all comparisons are \texttt{true}. Each
+% \meta{dim expr} is evaluated only once, and the
+% evaluation is lazy, in the sense that if one comparison is
+% \texttt{false}, then no other \meta{dim expr} is
+% evaluated and no other comparison is performed. The
+% \meta{relations} can be any of the following:
+% \begin{center}
+% \begin{tabular}{ll}
+% Equal & |=| or |==| \\
+% Greater than or equal to & |>=| \\
+% Greater than & |>| \\
+% Less than or equal to & |<=| \\
+% Less than & |<| \\
+% Not equal & |!=| \\
+% \end{tabular}
+% \end{center}
+% This function is more flexible than \cs{dim_compare:nNnTF} but
+% around $5$~times slower.
+% \end{function}
+%
+% \begin{function}[added = 2013-07-24, EXP, noTF]{\dim_case:nn}
+% \begin{syntax}
+% \cs{dim_case:nnTF} \Arg{test dim expr} \\
+% ~~|{| \\
+% ~~~~\Arg{dim expr case_1} \Arg{code case_1} \\
+% ~~~~\Arg{dim expr case_2} \Arg{code case_2} \\
+% ~~~~\ldots \\
+% ~~~~\Arg{dim expr case_n} \Arg{code case_n} \\
+% ~~|}| \\
+% ~~\Arg{true code}
+% ~~\Arg{false code}
+% \end{syntax}
+% This function evaluates the \meta{test dim expr} and
+% compares this in turn to each of the
+% \meta{dim expr cases}. If the two are equal 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 function
+% \cs{dim_case:nn}, which does nothing if there is no match, is also
+% available. For example
+% \begin{verbatim}
+% \dim_set:Nn \l_tmpa_dim { 5 pt }
+% \dim_case:nnF
+% { 2 \l_tmpa_dim }
+% {
+% { 5 pt } { Small }
+% { 4 pt + 6 pt } { Medium }
+% { - 10 pt } { Negative }
+% }
+% { No idea! }
+% \end{verbatim}
+% leaves \enquote{\texttt{Medium}} in the input stream.
+% \end{function}
+%
+% \section{Dimension expression loops}
+%
+% \begin{function}[rEXP]{\dim_do_until:nNnn}
+% \begin{syntax}
+% \cs{dim_do_until:nNnn} \Arg{dim expr_1} \meta{relation} \Arg{dim expr_2} \Arg{code}
+% \end{syntax}
+% Places the \meta{code} in the input stream for \TeX{} to process, and
+% then evaluates the relationship between the two
+% \meta{dim exprs} as described for \cs{dim_compare:nNnTF}.
+% If the test is \texttt{false} then the \meta{code} is inserted
+% into the input stream again and a loop occurs until the
+% \meta{relation} is \texttt{true}.
+% \end{function}
+%
+% \begin{function}[rEXP]{\dim_do_while:nNnn}
+% \begin{syntax}
+% \cs{dim_do_while:nNnn} \Arg{dim expr_1} \meta{relation} \Arg{dim expr_2} \Arg{code}
+% \end{syntax}
+% Places the \meta{code} in the input stream for \TeX{} to process, and
+% then evaluates the relationship between the two
+% \meta{dim exprs} as described for \cs{dim_compare:nNnTF}.
+% If the test is \texttt{true} then the \meta{code} is inserted
+% into the input stream again and a loop occurs until the
+% \meta{relation} is \texttt{false}.
+% \end{function}
+%
+% \begin{function}[rEXP]{\dim_until_do:nNnn}
+% \begin{syntax}
+% \cs{dim_until_do:nNnn} \Arg{dim expr_1} \meta{relation} \Arg{dim expr_2} \Arg{code}
+% \end{syntax}
+% Evaluates the relationship between the two \meta{dim exprs}
+% as described for \cs{dim_compare:nNnTF}, and then places the
+% \meta{code} in the input stream if the \meta{relation} is
+% \texttt{false}. After the \meta{code} has been processed by \TeX{} the
+% test is repeated, and a loop occurs until the test is
+% \texttt{true}.
+% \end{function}
+%
+% \begin{function}[rEXP]{\dim_while_do:nNnn}
+% \begin{syntax}
+% \cs{dim_while_do:nNnn} \Arg{dim expr_1} \meta{relation} \Arg{dim expr_2} \Arg{code}
+% \end{syntax}
+% Evaluates the relationship between the two \meta{dim exprs}
+% as described for \cs{dim_compare:nNnTF}, and then places the
+% \meta{code} in the input stream if the \meta{relation} is
+% \texttt{true}. After the \meta{code} has been processed by \TeX{} the
+% test is repeated, and a loop occurs until the test is
+% \texttt{false}.
+% \end{function}
+%
+% \begin{function}[updated = 2013-01-13, rEXP]{\dim_do_until:nn}
+% \begin{syntax}
+% \cs{dim_do_until:nn} \Arg{dimension relation} \Arg{code}
+% \end{syntax}
+% Places the \meta{code} in the input stream for \TeX{} to process, and
+% then evaluates the \meta{dimension relation}
+% as described for \cs{dim_compare:nTF}.
+% If the test is \texttt{false} then the \meta{code} is inserted
+% into the input stream again and a loop occurs until the
+% \meta{relation} is \texttt{true}.
+% \end{function}
+%
+% \begin{function}[updated = 2013-01-13, rEXP]{\dim_do_while:nn}
+% \begin{syntax}
+% \cs{dim_do_while:nn} \Arg{dimension relation} \Arg{code}
+% \end{syntax}
+% Places the \meta{code} in the input stream for \TeX{} to process, and
+% then evaluates the \meta{dimension relation}
+% as described for \cs{dim_compare:nTF}.
+% If the test is \texttt{true} then the \meta{code} is inserted
+% into the input stream again and a loop occurs until the
+% \meta{relation} is \texttt{false}.
+% \end{function}
+%
+% \begin{function}[updated = 2013-01-13, rEXP]{\dim_until_do:nn}
+% \begin{syntax}
+% \cs{dim_until_do:nn} \Arg{dimension relation} \Arg{code}
+% \end{syntax}
+% Evaluates the \meta{dimension relation}
+% as described for \cs{dim_compare:nTF}, and then places the
+% \meta{code} in the input stream if the \meta{relation} is
+% \texttt{false}. After the \meta{code} has been processed by \TeX{} the
+% test is repeated, and a loop occurs until the test is
+% \texttt{true}.
+% \end{function}
+%
+% \begin{function}[updated = 2013-01-13, rEXP]{\dim_while_do:nn}
+% \begin{syntax}
+% \cs{dim_while_do:nn} \Arg{dimension relation} \Arg{code}
+% \end{syntax}
+% Evaluates the \meta{dimension relation}
+% as described for \cs{dim_compare:nTF}, and then places the
+% \meta{code} in the input stream if the \meta{relation} is
+% \texttt{true}. After the \meta{code} has been processed by \TeX{} the
+% test is repeated, and a loop occurs until the test is
+% \texttt{false}.
+% \end{function}
+%
+% \section{Dimension step functions}
+%
+% \begin{function}[added = 2018-02-18, rEXP]
+% {\dim_step_function:nnnN}
+% \begin{syntax}
+% \cs{dim_step_function:nnnN} \Arg{initial value} \Arg{step} \Arg{final value} \meta{function}
+% \end{syntax}
+% This function first evaluates the \meta{initial value}, \meta{step}
+% and \meta{final value}, all of which should be dimension expressions.
+% The \meta{function} is then placed in front of each \meta{value}
+% from the \meta{initial value} to the \meta{final value} in turn
+% (using \meta{step} between each \meta{value}). The \meta{step} must
+% be non-zero. If the \meta{step} is positive, the loop stops when
+% the \meta{value} becomes larger than the \meta{final value}. If the
+% \meta{step} is negative, the loop stops when the \meta{value}
+% becomes smaller than the \meta{final value}. The \meta{function}
+% should absorb one argument.
+% \end{function}
+%
+% \begin{function}[added = 2018-02-18]
+% {\dim_step_inline:nnnn}
+% \begin{syntax}
+% \cs{dim_step_inline:nnnn} \Arg{initial value} \Arg{step} \Arg{final value} \Arg{code}
+% \end{syntax}
+% This function first evaluates the \meta{initial value}, \meta{step}
+% and \meta{final value}, all of which should be dimension expressions.
+% Then for each \meta{value} from the \meta{initial value} to the
+% \meta{final value} in turn (using \meta{step} between each
+% \meta{value}), the \meta{code} is inserted into the input stream
+% with |#1| replaced by the current \meta{value}. Thus the
+% \meta{code} should define a function of one argument~(|#1|).
+% \end{function}
+%
+% \begin{function}[added = 2018-02-18]
+% {\dim_step_variable:nnnNn}
+% \begin{syntax}
+% \cs{dim_step_variable:nnnNn} \\
+% ~~\Arg{initial value} \Arg{step} \Arg{final value} \meta{tl~var} \Arg{code}
+% \end{syntax}
+% This function first evaluates the \meta{initial value}, \meta{step}
+% and \meta{final value}, all of which should be dimension expressions.
+% Then for each \meta{value} from the \meta{initial value} to the
+% \meta{final value} in turn (using \meta{step} between each
+% \meta{value}), the \meta{code} is inserted into the input stream,
+% with the \meta{tl~var} defined as the current \meta{value}. Thus
+% the \meta{code} should make use of the \meta{tl~var}.
+% \end{function}
+%
+% \section{Using \texttt{dim} expressions and variables}
+%
+% \begin{function}[updated = 2011-10-22, EXP]{\dim_eval:n}
+% \begin{syntax}
+% \cs{dim_eval:n} \Arg{dim expr}
+% \end{syntax}
+% Evaluates the \meta{dim expr}, expanding any
+% dimensions and token list variables within the \meta{expression}
+% to their content (without requiring \cs{dim_use:N}/\cs{tl_use:N})
+% and applying the standard mathematical rules. The result of the
+% calculation is left in the input stream as a
+% \meta{dimension denotation} after two expansions. This is
+% expressed in points (\texttt{pt}), and requires suitable
+% termination if used in a \TeX{}-style assignment as it is \emph{not}
+% an \meta{internal dimension}.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-11-03]{\dim_sign:n}
+% \begin{syntax}
+% \cs{dim_sign:n} \Arg{dim expr}
+% \end{syntax}
+% Evaluates the \meta{dim expr} then leaves $1$ or $0$ or $-1$ in the
+% input stream according to the sign of the result.
+% \end{function}
+%
+% \begin{function}[EXP]{\dim_use:N, \dim_use:c}
+% \begin{syntax}
+% \cs{dim_use:N} \meta{dimension}
+% \end{syntax}
+% Recovers the content of a \meta{dimension} and places it directly
+% in the input stream. An error is raised if the variable does
+% not exist or if it is invalid. Can be omitted in places where a
+% \meta{dimension} is required (such as in the argument of
+% \cs{dim_eval:n}).
+% \begin{texnote}
+% \cs{dim_use:N} is the \TeX{} primitive \tn{the}: this is one of
+% several \LaTeX3 names for this primitive.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2014-07-15, EXP]{\dim_to_decimal:n}
+% \begin{syntax}
+% \cs{dim_to_decimal:n} \Arg{dim expr}
+% \end{syntax}
+% Evaluates the \meta{dim expr}, and leaves the result,
+% expressed in points (\texttt{pt}) in the input stream, with \emph{no
+% units}. The result is rounded by \TeX{} to at most five decimal
+% places. If the decimal part of the result is zero, it is omitted,
+% together with the decimal marker.
+%
+% For example
+% \begin{verbatim}
+% \dim_to_decimal:n { 1bp }
+% \end{verbatim}
+% leaves |1.00374| in the input stream, \emph{i.e.}~the magnitude of
+% one \enquote{big point} when converted to (\TeX{}) points.
+% \end{function}
+%
+% \begin{function}[added = 2014-07-15, updated = 2023-05-20, EXP]
+% {\dim_to_decimal_in_bp:n}
+% \begin{syntax}
+% \cs{dim_to_decimal_in_bp:n} \Arg{dim expr}
+% \end{syntax}
+% Evaluates the \meta{dim expr}, and leaves the result,
+% expressed in big points (\texttt{bp}) in the input stream, with \emph{no
+% units}. The result is rounded by \TeX{} to at most five decimal
+% places. If the decimal part of the result is zero, it is omitted,
+% together with the decimal marker.
+%
+% For example
+% \begin{verbatim}
+% \dim_to_decimal_in_bp:n { 1pt }
+% \end{verbatim}
+% leaves |0.99628| in the input stream, \emph{i.e.}~the magnitude of
+% one (\TeX{}) point when converted to big points.
+% \begin{texnote}
+% The implementation of this function is re-entrant: the result of
+% \begin{verbatim}
+% \dim_compare:nNnTF
+% { <x>bp } =
+% { \dim_to_decimal_in_bp:n { <x>bp } bp }
+% \end{verbatim}
+% will be logically \texttt{true}. The decimal representations may
+% differ provided they produce the same \TeX{} dimension.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2023-05-20, EXP]
+% {
+% \dim_to_decimal_in_cc:n ,
+% \dim_to_decimal_in_cm:n ,
+% \dim_to_decimal_in_dd:n ,
+% \dim_to_decimal_in_in:n ,
+% \dim_to_decimal_in_mm:n ,
+% \dim_to_decimal_in_pc:n
+% }
+% \begin{syntax}
+% \cs{dim_to_decimal_in_cm:n} \Arg{dim expr}
+% \end{syntax}
+% Evaluates the \meta{dim expr}, and leaves the result,
+% expressed with the appropriate scaling in the input stream, with
+% \emph{no units}. If the decimal part of the result is zero, it is omitted,
+% together with the decimal marker. The precisions of the result is limited
+% to a maximum of five decimal places with trailing zeros omitted.
+%
+% The maximum \TeX{} allowable dimension value (available as
+% \tn{maxdimen} in plain \TeX{} and \LaTeX{} and \cs{c_max_dim} in
+% \pkg{expl3}) can only be expressed exactly in the units
+% \texttt{pt}, \texttt{bp} and \texttt{sp}. The maximum allowable
+% input values to five decimal places are\\
+% \begin{center}
+% \begin{tabular}{@{}>{$}r<{$}@{\,}l@{}}
+% 1276.00215 & cc \\
+% 575.83174 & cm \\
+% 15312.02584 & dd \\
+% 226.70540 & in \\
+% 5758.31742 & mm \\
+% 1365.33333 & pc \\
+% \end{tabular}
+% \end{center}
+% (Note that these are not all equal, but rather any larger value will overflow
+% due to the way \TeX{} converts to \texttt{sp}.)
+% Values given to five decimal places larger that these will result in \TeX{}
+% errors; the behavior if additional decimal places are given depends on the
+% \TeX{} internals and thus larger values are \emph{not} supported by
+% \pkg{expl3}.
+% \begin{texnote}
+% The implementation of these functions is re-entrant: the result of
+% \begin{verbatim}
+% \dim_compare:nNnTF
+% { <x><unit> } =
+% { \dim_to_decimal_in_<unit>:n { <x><unit> } <unit> }
+% \end{verbatim}
+% will be logically \texttt{true}. The decimal representations may
+% differ provided they produce the same \TeX{} dimension.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2015-05-18, EXP]{\dim_to_decimal_in_sp:n}
+% \begin{syntax}
+% \cs{dim_to_decimal_in_sp:n} \Arg{dim expr}
+% \end{syntax}
+% Evaluates the \meta{dim expr}, and leaves the result,
+% expressed in scaled points (\texttt{sp}) in the input stream, with \emph{no
+% units}. The result is necessarily an integer.
+% \end{function}
+%
+% \begin{function}[added = 2014-07-15, updated = 2023-05-20, EXP]
+% {\dim_to_decimal_in_unit:nn}
+% \begin{syntax}
+% \cs{dim_to_decimal_in_unit:nn} \Arg{dim expr_1} \Arg{dim expr_2}
+% \end{syntax}
+% Evaluates the \meta{dim exprs}, and leaves the value of
+% \meta{dim expr_1}, expressed in a unit given by \meta{dim expr_2}, in
+% the input stream. If the decimal part of the result
+% is zero, it is omitted, together with the decimal marker.
+% The precisions of the result is limited
+% to a maximum of five decimal places with trailing zeros omitted.
+%
+% For example
+% \begin{verbatim}
+% \dim_to_decimal_in_unit:nn { 1bp } { 1mm }
+% \end{verbatim}
+% leaves |0.35278| in the input stream, \emph{i.e.}~the magnitude of
+% one big point when expressed in millimetres. The conversions do
+% \emph{not} guarantee that \TeX{} would yield identical results
+% for the direct input in an equality test, thus for instance
+% \begin{verbatim}
+% \dim_compare:nNnTF
+% { 1bp } =
+% { \dim_to_decimal_in_unit:nn { 1bp } { 1mm } mm }
+% \end{verbatim}
+% will take the \texttt{false} branch.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2012-05-08, tested = m3fp-convert002]
+% {\dim_to_fp:n}
+% \begin{syntax}
+% \cs{dim_to_fp:n} \Arg{dim expr}
+% \end{syntax}
+% Expands to an internal floating point number equal to the value of
+% the \meta{dim expr} in \texttt{pt}. Since dimension expressions are
+% evaluated much faster than their floating point equivalent,
+% \cs{dim_to_fp:n} can be used to speed up parts of a computation
+% where a low precision and a smaller range are acceptable.
+% \end{function}
+%
+% \section{Viewing \texttt{dim} variables}
+%
+% \begin{function}{\dim_show:N, \dim_show:c}
+% \begin{syntax}
+% \cs{dim_show:N} \meta{dimension}
+% \end{syntax}
+% Displays the value of the \meta{dimension} on the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2011-11-22, updated = 2015-08-07]{\dim_show:n}
+% \begin{syntax}
+% \cs{dim_show:n} \Arg{dim expr}
+% \end{syntax}
+% Displays the result of evaluating the \meta{dim expr}
+% on the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2015-08-03]{\dim_log:N, \dim_log:c}
+% \begin{syntax}
+% \cs{dim_log:N} \meta{dimension}
+% \end{syntax}
+% Writes the value of the \meta{dimension} in the log file.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2015-08-07]{\dim_log:n}
+% \begin{syntax}
+% \cs{dim_log:n} \Arg{dim expr}
+% \end{syntax}
+% Writes the result of evaluating the \meta{dim expr}
+% in the log file.
+% \end{function}
+%
+% \section{Constant dimensions}
+%
+% \begin{variable}{\c_max_dim}
+% The maximum value that can be stored as a dimension. This can also
+% be used as a component of a skip.
+% \end{variable}
+%
+% \begin{variable}{\c_zero_dim}
+% A zero length as a dimension. This can also be used as a component
+% of a skip.
+% \end{variable}
+%
+% \section{Scratch dimensions}
+%
+% \begin{variable}{\l_tmpa_dim, \l_tmpb_dim}
+% Scratch dimension for local assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \begin{variable}{\g_tmpa_dim, \g_tmpb_dim}
+% Scratch dimension for global assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \section{Creating and initialising \texttt{skip} variables}
+%
+% \begin{function}{\skip_new:N, \skip_new:c}
+% \begin{syntax}
+% \cs{skip_new:N} \meta{skip}
+% \end{syntax}
+% Creates a new \meta{skip} or raises an error if the name is
+% already taken. The declaration is global. The \meta{skip}
+% is initially equal to $0$\,pt.
+% \end{function}
+%
+% \begin{function}[added = 2012-03-05]{\skip_const:Nn, \skip_const:cn}
+% \begin{syntax}
+% \cs{skip_const:Nn} \meta{skip} \Arg{skip expr}
+% \end{syntax}
+% Creates a new constant \meta{skip} or raises an error if the
+% name is already taken. The value of the \meta{skip} is set
+% globally to the \meta{skip expr}.
+% \end{function}
+%
+% \begin{function}{\skip_zero:N, \skip_zero:c, \skip_gzero:N, \skip_gzero:c}
+% \begin{syntax}
+% \cs{skip_zero:N} \meta{skip}
+% \end{syntax}
+% Sets \meta{skip} to $0$\,pt.
+% \end{function}
+%
+% \begin{function}[added = 2012-01-07]
+% {\skip_zero_new:N, \skip_zero_new:c, \skip_gzero_new:N, \skip_gzero_new:c}
+% \begin{syntax}
+% \cs{skip_zero_new:N} \meta{skip}
+% \end{syntax}
+% Ensures that the \meta{skip} exists globally by applying
+% \cs{skip_new:N} if necessary, then applies
+% \cs[index=skip_zero:N]{skip_(g)zero:N} to leave
+% the \meta{skip} set to zero.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added=2012-03-03]
+% {\skip_if_exist:N, \skip_if_exist:c}
+% \begin{syntax}
+% \cs{skip_if_exist_p:N} \meta{skip}
+% \cs{skip_if_exist:NTF} \meta{skip} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{skip} is currently defined. This does not
+% check that the \meta{skip} really is a skip variable.
+% \end{function}
+%
+% \section{Setting \texttt{skip} variables}
+%
+% \begin{function}[updated = 2011-10-22]
+% {\skip_add:Nn, \skip_add:cn, \skip_gadd:Nn, \skip_gadd:cn}
+% \begin{syntax}
+% \cs{skip_add:Nn} \meta{skip} \Arg{skip expr}
+% \end{syntax}
+% Adds the result of the \meta{skip expr} to the current
+% content of the \meta{skip}.
+% \end{function}
+%
+% \begin{function}[updated = 2011-10-22]
+% {\skip_set:Nn, \skip_set:cn, \skip_gset:Nn, \skip_gset:cn}
+% \begin{syntax}
+% \cs{skip_set:Nn} \meta{skip} \Arg{skip expr}
+% \end{syntax}
+% Sets \meta{skip} to the value of \meta{skip expr}, which
+% must evaluate to a length with units and may include a rubber
+% component (for example |1 cm plus 0.5 cm|.
+% \end{function}
+%
+% \begin{function}
+% {
+% \skip_set_eq:NN, \skip_set_eq:cN, \skip_set_eq:Nc, \skip_set_eq:cc,
+% \skip_gset_eq:NN, \skip_gset_eq:cN, \skip_gset_eq:Nc, \skip_gset_eq:cc
+% }
+% \begin{syntax}
+% \cs{skip_set_eq:NN} \meta{skip_1} \meta{skip_2}
+% \end{syntax}
+% Sets the content of \meta{skip_1} equal to that of \meta{skip_2}.
+% \end{function}
+%
+% \begin{function}[updated = 2011-10-22]
+% {\skip_sub:Nn, \skip_sub:cn, \skip_gsub:Nn, \skip_gsub:cn}
+% \begin{syntax}
+% \cs{skip_sub:Nn} \meta{skip} \Arg{skip expr}
+% \end{syntax}
+% Subtracts the result of the \meta{skip expr} from the
+% current content of the \meta{skip}.
+% \end{function}
+%
+% \section{Skip expression conditionals}
+%
+% \begin{function}[EXP,pTF]{\skip_if_eq:nn}
+% \begin{syntax}
+% \cs{skip_if_eq_p:nn} \Arg{skip expr_1} \Arg{skip expr_2}
+% \cs{skip_if_eq:nnTF}
+% ~~\Arg{skip expr_1} \Arg{skip expr_2}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% This function first evaluates each of the
+% \meta{skip exprs} as described for \cs{skip_eval:n}.
+% The two results are then compared for exact equality,
+% \emph{i.e.}~both the fixed and rubber components must be the same
+% for the test to be true.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added = 2012-03-05]{\skip_if_finite:n}
+% \begin{syntax}
+% \cs{skip_if_finite_p:n} \Arg{skip expr}
+% \cs{skip_if_finite:nTF} \Arg{skip expr} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Evaluates the \meta{skip expr} as described for \cs{skip_eval:n},
+% and then tests if all of its components are finite.
+% \end{function}
+%
+% \section{Using \texttt{skip} expressions and variables}
+%
+% \begin{function}[updated = 2011-10-22, EXP]{\skip_eval:n}
+% \begin{syntax}
+% \cs{skip_eval:n} \Arg{skip expr}
+% \end{syntax}
+% Evaluates the \meta{skip expr}, expanding any skips
+% and token list variables within the \meta{expression}
+% to their content (without requiring \cs{skip_use:N}/\cs{tl_use:N})
+% and applying the standard mathematical rules. The result of the
+% calculation is left in the input stream as a \meta{glue denotation}
+% after two expansions. This is expressed in points (\texttt{pt}),
+% and requires suitable termination if used in a \TeX{}-style
+% assignment as it is \emph{not} an \meta{internal glue}.
+% \end{function}
+%
+% \begin{function}[EXP]{\skip_use:N, \skip_use:c}
+% \begin{syntax}
+% \cs{skip_use:N} \meta{skip}
+% \end{syntax}
+% Recovers the content of a \meta{skip} and places it directly
+% in the input stream. An error is raised if the variable does
+% not exist or if it is invalid. Can be omitted in places where a
+% \meta{dimension} or \meta{skip} is required (such as in the argument of
+% \cs{skip_eval:n}).
+% \begin{texnote}
+% \cs{skip_use:N} is the \TeX{} primitive \tn{the}: this is one of
+% several \LaTeX3 names for this primitive.
+% \end{texnote}
+% \end{function}
+%
+% \section{Viewing \texttt{skip} variables}
+%
+% \begin{function}[updated = 2015-08-03]{\skip_show:N, \skip_show:c}
+% \begin{syntax}
+% \cs{skip_show:N} \meta{skip}
+% \end{syntax}
+% Displays the value of the \meta{skip} on the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2011-11-22, updated = 2015-08-07]{\skip_show:n}
+% \begin{syntax}
+% \cs{skip_show:n} \Arg{skip expr}
+% \end{syntax}
+% Displays the result of evaluating the \meta{skip expr}
+% on the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2015-08-03]{\skip_log:N, \skip_log:c}
+% \begin{syntax}
+% \cs{skip_log:N} \meta{skip}
+% \end{syntax}
+% Writes the value of the \meta{skip} in the log file.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2015-08-07]{\skip_log:n}
+% \begin{syntax}
+% \cs{skip_log:n} \Arg{skip expr}
+% \end{syntax}
+% Writes the result of evaluating the \meta{skip expr}
+% in the log file.
+% \end{function}
+%
+% \section{Constant skips}
+%
+% \begin{variable}[updated = 2012-11-02]{\c_max_skip}
+% The maximum value that can be stored as a skip (equal to
+% \cs{c_max_dim} in length), with no stretch nor shrink component.
+% \end{variable}
+%
+% \begin{variable}[updated = 2012-11-01]{\c_zero_skip}
+% A zero length as a skip, with no stretch nor shrink component.
+% \end{variable}
+%
+% \section{Scratch skips}
+%
+% \begin{variable}{\l_tmpa_skip, \l_tmpb_skip}
+% Scratch skip for local assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \begin{variable}{\g_tmpa_skip, \g_tmpb_skip}
+% Scratch skip for global assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \section{Inserting skips into the output}
+%
+% \begin{function}[updated = 2011-10-22]
+% {\skip_horizontal:N, \skip_horizontal:c, \skip_horizontal:n}
+% \begin{syntax}
+% \cs{skip_horizontal:N} \meta{skip}
+% \cs{skip_horizontal:n} \Arg{skip expr}
+% \end{syntax}
+% Inserts a horizontal \meta{skip} into the current list.
+% The argument can also be a \meta{dim}.
+% \begin{texnote}
+% \cs{skip_horizontal:N} is the \TeX{} primitive \tn{hskip}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[updated = 2011-10-22]
+% {\skip_vertical:N, \skip_vertical:c, \skip_vertical:n}
+% \begin{syntax}
+% \cs{skip_vertical:N} \meta{skip}
+% \cs{skip_vertical:n} \Arg{skip expr}
+% \end{syntax}
+% Inserts a vertical \meta{skip} into the current list.
+% The argument can also be a \meta{dim}.
+% \begin{texnote}
+% \cs{skip_vertical:N} is the \TeX{} primitive \tn{vskip}.
+% \end{texnote}
+% \end{function}
+%
+% \section{Creating and initialising \texttt{muskip} variables}
+%
+% \begin{function}{\muskip_new:N, \muskip_new:c}
+% \begin{syntax}
+% \cs{muskip_new:N} \meta{muskip}
+% \end{syntax}
+% Creates a new \meta{muskip} or raises an error if the name is
+% already taken. The declaration is global. The \meta{muskip}
+% is initially equal to $0$\,mu.
+% \end{function}
+%
+% \begin{function}[added = 2012-03-05]{\muskip_const:Nn, \muskip_const:cn}
+% \begin{syntax}
+% \cs{muskip_const:Nn} \meta{muskip} \Arg{muskip expr}
+% \end{syntax}
+% Creates a new constant \meta{muskip} or raises an error if the
+% name is already taken. The value of the \meta{muskip} is set
+% globally to the \meta{muskip expr}.
+% \end{function}
+%
+% \begin{function}
+% {\muskip_zero:N, \muskip_zero:c, \muskip_gzero:N, \muskip_gzero:c}
+% \begin{syntax}
+% \cs{skip_zero:N} \meta{muskip}
+% \end{syntax}
+% Sets \meta{muskip} to $0$\,mu.
+% \end{function}
+%
+% \begin{function}[added = 2012-01-07]
+% {
+% \muskip_zero_new:N, \muskip_zero_new:c,
+% \muskip_gzero_new:N, \muskip_gzero_new:c
+% }
+% \begin{syntax}
+% \cs{muskip_zero_new:N} \meta{muskip}
+% \end{syntax}
+% Ensures that the \meta{muskip} exists globally by applying
+% \cs{muskip_new:N} if necessary, then applies
+% \cs[index=muskip_zero:N]{muskip_(g)zero:N}
+% to leave the \meta{muskip} set to zero.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added=2012-03-03]
+% {\muskip_if_exist:N, \muskip_if_exist:c}
+% \begin{syntax}
+% \cs{muskip_if_exist_p:N} \meta{muskip}
+% \cs{muskip_if_exist:NTF} \meta{muskip} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{muskip} is currently defined. This does not
+% check that the \meta{muskip} really is a muskip variable.
+% \end{function}
+%
+% \section{Setting \texttt{muskip} variables}
+%
+% \begin{function}[updated = 2011-10-22]
+% {\muskip_add:Nn, \muskip_add:cn, \muskip_gadd:Nn, \muskip_gadd:cn}
+% \begin{syntax}
+% \cs{muskip_add:Nn} \meta{muskip} \Arg{muskip expr}
+% \end{syntax}
+% Adds the result of the \meta{muskip expr} to the current
+% content of the \meta{muskip}.
+% \end{function}
+%
+% \begin{function}[updated = 2011-10-22]
+% {\muskip_set:Nn, \muskip_set:cn, \muskip_gset:Nn, \muskip_gset:cn}
+% \begin{syntax}
+% \cs{muskip_set:Nn} \meta{muskip} \Arg{muskip expr}
+% \end{syntax}
+% Sets \meta{muskip} to the value of \meta{muskip expr}, which
+% must evaluate to a math length with units and may include a rubber
+% component (for example |1 mu plus 0.5 mu|.
+% \end{function}
+%
+% \begin{function}
+% {
+% \muskip_set_eq:NN, \muskip_set_eq:cN,
+% \muskip_set_eq:Nc, \muskip_set_eq:cc,
+% \muskip_gset_eq:NN, \muskip_gset_eq:cN,
+% \muskip_gset_eq:Nc, \muskip_gset_eq:cc
+% }
+% \begin{syntax}
+% \cs{muskip_set_eq:NN} \meta{muskip_1} \meta{muskip_2}
+% \end{syntax}
+% Sets the content of \meta{muskip_1} equal to that of
+% \meta{muskip_2}.
+% \end{function}
+%
+% \begin{function}[updated = 2011-10-22]
+% {\muskip_sub:Nn, \muskip_sub:cn, \muskip_gsub:Nn, \muskip_gsub:cn}
+% \begin{syntax}
+% \cs{muskip_sub:Nn} \meta{muskip} \Arg{muskip expr}
+% \end{syntax}
+% Subtracts the result of the \meta{muskip expr} from the
+% current content of the \meta{muskip}.
+% \end{function}
+%
+% \section{Using \texttt{muskip} expressions and variables}
+%
+% \begin{function}[updated = 2011-10-22, EXP]{\muskip_eval:n}
+% \begin{syntax}
+% \cs{muskip_eval:n} \Arg{muskip expr}
+% \end{syntax}
+% Evaluates the \meta{muskip expr}, expanding any skips
+% and token list variables within the \meta{expression}
+% to their content (without requiring \cs{muskip_use:N}/\cs{tl_use:N})
+% and applying the standard mathematical rules. The result of the
+% calculation is left in the input stream as a \meta{muglue denotation}
+% after two expansions. This is expressed in \texttt{mu},
+% and requires suitable termination if used in a \TeX{}-style
+% assignment as it is \emph{not} an \meta{internal muglue}.
+% \end{function}
+%
+% \begin{function}[EXP]{\muskip_use:N, \muskip_use:c}
+% \begin{syntax}
+% \cs{muskip_use:N} \meta{muskip}
+% \end{syntax}
+% Recovers the content of a \meta{skip} and places it directly
+% in the input stream. An error is raised if the variable does
+% not exist or if it is invalid. Can be omitted in places where a
+% \meta{dimension} is required (such as in the argument of
+% \cs{muskip_eval:n}).
+% \begin{texnote}
+% \cs{muskip_use:N} is the \TeX{} primitive \tn{the}: this is one of
+% several \LaTeX3 names for this primitive.
+% \end{texnote}
+% \end{function}
+%
+% \section{Viewing \texttt{muskip} variables}
+%
+% \begin{function}[updated = 2015-08-03]{\muskip_show:N, \muskip_show:c}
+% \begin{syntax}
+% \cs{muskip_show:N} \meta{muskip}
+% \end{syntax}
+% Displays the value of the \meta{muskip} on the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2011-11-22, updated = 2015-08-07]{\muskip_show:n}
+% \begin{syntax}
+% \cs{muskip_show:n} \Arg{muskip expr}
+% \end{syntax}
+% Displays the result of evaluating the \meta{muskip expr}
+% on the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2015-08-03]{\muskip_log:N, \muskip_log:c}
+% \begin{syntax}
+% \cs{muskip_log:N} \meta{muskip}
+% \end{syntax}
+% Writes the value of the \meta{muskip} in the log file.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2015-08-07]{\muskip_log:n}
+% \begin{syntax}
+% \cs{muskip_log:n} \Arg{muskip expr}
+% \end{syntax}
+% Writes the result of evaluating the \meta{muskip expr}
+% in the log file.
+% \end{function}
+%
+% \section{Constant muskips}
+%
+% \begin{variable}{\c_max_muskip}
+% The maximum value that can be stored as a muskip, with no stretch
+% nor shrink component.
+% \end{variable}
+%
+% \begin{variable}{\c_zero_muskip}
+% A zero length as a muskip, with no stretch nor shrink component.
+% \end{variable}
+%
+% \section{Scratch muskips}
+%
+% \begin{variable}{\l_tmpa_muskip, \l_tmpb_muskip}
+% Scratch muskip for local assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \begin{variable}{\g_tmpa_muskip, \g_tmpb_muskip}
+% Scratch muskip for global assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \section{Primitive conditional}
+%
+% \begin{function}[EXP]{\if_dim:w}
+% \begin{syntax}
+% \cs{if_dim:w} \meta{dimen_1} \meta{relation} \meta{dimen_2}
+% ~~\meta{true code}
+% \cs{else:}
+% ~~\meta{false}
+% \cs{fi:}
+% \end{syntax}
+% Compare two dimensions. The \meta{relation} is one of
+% |<|, |=| or |>| with category code $12$.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{ifdim}.
+% \end{texnote}
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3skip} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=dim>
+% \end{macrocode}
+%
+% \subsection{Length primitives renamed}
+%
+% \begin{macro}{\if_dim:w}
+% \begin{macro}{\@@_eval:w}
+% \begin{macro}{\@@_eval_end:}
+% Primitives renamed.
+% \begin{macrocode}
+\cs_new_eq:NN \if_dim:w \tex_ifdim:D
+\cs_new_eq:NN \@@_eval:w \tex_dimexpr:D
+\cs_new_eq:NN \@@_eval_end: \tex_relax:D
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Internal auxiliaries}
+%
+% \begin{variable}{\s_@@_mark,\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_use_none_delimit_by_s_stop:w}
+% Functions to gobble up to a scan mark.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_none_delimit_by_s_stop:w #1 \s_@@_stop { }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Creating and initialising \texttt{dim} variables}
+%
+% \begin{macro}{\dim_new:N, \dim_new:c}
+% Allocating \meta{dim} registers \ldots
+% \begin{macrocode}
+\cs_new_protected:Npn \dim_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs:w newdimen \cs_end: #1
+ }
+\cs_generate_variant:Nn \dim_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\dim_const:Nn, \dim_const:cn}
+% Contrarily to integer constants, we cannot avoid using a register,
+% even for constants. We cannot use \cs{dim_gset:Nn} because
+% debugging code would complain that the constant is not a global
+% variable. Since \cs{dim_const:Nn} does not need to be fast, use
+% \cs{dim_eval:n} to avoid needing a debugging patch that wraps the
+% expression in checking code.
+% \begin{macrocode}
+\cs_new_protected:Npn \dim_const:Nn #1#2
+ {
+ \dim_new:N #1
+ \tex_global:D #1 = \dim_eval:n {#2} \scan_stop:
+ }
+\cs_generate_variant:Nn \dim_const:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\dim_zero:N, \dim_zero:c}
+% \begin{macro}{\dim_gzero:N, \dim_gzero:c}
+% Reset the register to zero. Using \cs{c_zero_skip} deals with the
+% case where the variable passed is incorrectly a skip (for example a
+% \LaTeXe{} length). Besides, these functions are then simply copied
+% for \cs{skip_zero:N} and related functions.
+% \begin{macrocode}
+\cs_new_protected:Npn \dim_zero:N #1 { #1 = \c_zero_skip }
+\cs_new_protected:Npn \dim_gzero:N #1
+ { \tex_global:D #1 = \c_zero_skip }
+\cs_generate_variant:Nn \dim_zero:N { c }
+\cs_generate_variant:Nn \dim_gzero:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\dim_zero_new:N, \dim_zero_new:c, \dim_gzero_new:N, \dim_gzero_new:c}
+% Create a register if needed, otherwise clear it.
+% \begin{macrocode}
+\cs_new_protected:Npn \dim_zero_new:N #1
+ { \dim_if_exist:NTF #1 { \dim_zero:N #1 } { \dim_new:N #1 } }
+\cs_new_protected:Npn \dim_gzero_new:N #1
+ { \dim_if_exist:NTF #1 { \dim_gzero:N #1 } { \dim_new:N #1 } }
+\cs_generate_variant:Nn \dim_zero_new:N { c }
+\cs_generate_variant:Nn \dim_gzero_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\dim_if_exist:N, \dim_if_exist:c}
+% Copies of the \texttt{cs} functions defined in \pkg{l3basics}.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \dim_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \dim_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Setting \texttt{dim} variables}
+%
+% \begin{macro}{\dim_set:Nn, \dim_set:cn}
+% \begin{macro}{\dim_gset:Nn, \dim_gset:cn}
+% Setting dimensions is easy enough but when debugging we want both to
+% check that the variable is correctly local/global and to wrap the
+% expression in some code. The \cs{scan_stop:} deals with the case
+% where the variable passed is a skip (for example a \LaTeXe{}
+% length).
+% \begin{macrocode}
+\cs_new_protected:Npn \dim_set:Nn #1#2
+ { #1 = \@@_eval:w #2 \@@_eval_end: \scan_stop: }
+\cs_new_protected:Npn \dim_gset:Nn #1#2
+ { \tex_global:D #1 = \@@_eval:w #2 \@@_eval_end: \scan_stop: }
+\cs_generate_variant:Nn \dim_set:Nn { c }
+\cs_generate_variant:Nn \dim_gset:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\dim_set_eq:NN, \dim_set_eq:cN, \dim_set_eq:Nc, \dim_set_eq:cc}
+% \begin{macro}
+% {\dim_gset_eq:NN, \dim_gset_eq:cN, \dim_gset_eq:Nc, \dim_gset_eq:cc}
+% All straightforward, with a \cs{scan_stop:} to deal with the case
+% where |#1| is (incorrectly) a skip.
+% \begin{macrocode}
+\cs_new_protected:Npn \dim_set_eq:NN #1#2
+ { #1 = #2 \scan_stop: }
+\cs_generate_variant:Nn \dim_set_eq:NN { c , Nc , cc }
+\cs_new_protected:Npn \dim_gset_eq:NN #1#2
+ { \tex_global:D #1 = #2 \scan_stop: }
+\cs_generate_variant:Nn \dim_gset_eq:NN { c , Nc , cc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\dim_add:Nn, \dim_add:cn}
+% \begin{macro}{\dim_gadd:Nn, \dim_gadd:cn}
+% \begin{macro}{\dim_sub:Nn, \dim_sub:cn}
+% \begin{macro}{\dim_gsub:Nn, \dim_gsub:cn}
+% Using |by| here would slow things down just to detect nonsensical
+% cases such as passing |\dimen 123| as the first argument.
+% Using \cs{scan_stop:} deals with skip variables. Since
+% debugging checks that the variable is correctly local/global, the
+% global versions cannot be defined as \cs{tex_global:D} followed by
+% the local versions.
+% \begin{macrocode}
+\cs_new_protected:Npn \dim_add:Nn #1#2
+ { \tex_advance:D #1 \@@_eval:w #2 \@@_eval_end: \scan_stop: }
+\cs_new_protected:Npn \dim_gadd:Nn #1#2
+ {
+ \tex_global:D \tex_advance:D #1
+ \@@_eval:w #2 \@@_eval_end: \scan_stop:
+ }
+\cs_generate_variant:Nn \dim_add:Nn { c }
+\cs_generate_variant:Nn \dim_gadd:Nn { c }
+\cs_new_protected:Npn \dim_sub:Nn #1#2
+ { \tex_advance:D #1 - \@@_eval:w #2 \@@_eval_end: \scan_stop: }
+\cs_new_protected:Npn \dim_gsub:Nn #1#2
+ {
+ \tex_global:D \tex_advance:D #1
+ -\@@_eval:w #2 \@@_eval_end: \scan_stop:
+ }
+\cs_generate_variant:Nn \dim_sub:Nn { c }
+\cs_generate_variant:Nn \dim_gsub:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Utilities for dimension calculations}
+%
+% \begin{macro}[EXP]{\dim_abs:n}
+% \begin{macro}[EXP]{\@@_abs:N}
+% \UnitTested
+% \begin{macro}[EXP]{\dim_max:nn}
+% \begin{macro}[EXP]{\dim_min:nn}
+% \begin{macro}[EXP]{\@@_maxmin:wwN}
+% \UnitTested
+% \UnitTested
+% Functions for $\min$, $\max$, and absolute value with only one evaluation.
+% The absolute value is evaluated by removing a leading~|-| if present.
+% \begin{macrocode}
+\cs_new:Npn \dim_abs:n #1
+ {
+ \exp_after:wN \@@_abs:N
+ \dim_use:N \@@_eval:w #1 \@@_eval_end:
+ }
+\cs_new:Npn \@@_abs:N #1
+ { \if_meaning:w - #1 \else: \exp_after:wN #1 \fi: }
+\cs_new:Npn \dim_max:nn #1#2
+ {
+ \dim_use:N \@@_eval:w \exp_after:wN \@@_maxmin:wwN
+ \dim_use:N \@@_eval:w #1 \exp_after:wN ;
+ \dim_use:N \@@_eval:w #2 ;
+ >
+ \@@_eval_end:
+ }
+\cs_new:Npn \dim_min:nn #1#2
+ {
+ \dim_use:N \@@_eval:w \exp_after:wN \@@_maxmin:wwN
+ \dim_use:N \@@_eval:w #1 \exp_after:wN ;
+ \dim_use:N \@@_eval:w #2 ;
+ <
+ \@@_eval_end:
+ }
+\cs_new:Npn \@@_maxmin:wwN #1 ; #2 ; #3
+ {
+ \if_dim:w #1 #3 #2 ~
+ #1
+ \else:
+ #2
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\dim_ratio:nn}
+% \begin{macro}{\@@_ratio:n}
+% With dimension expressions, something like |10 pt * ( 5 pt / 10 pt )| does
+% not work. Instead, the ratio part needs to be converted to an integer
+% expression. Using \cs{int_value:w} forces everything into |sp|, avoiding
+% any decimal parts.
+% \begin{macrocode}
+\cs_new:Npn \dim_ratio:nn #1#2
+ { \@@_ratio:n {#1} / \@@_ratio:n {#2} }
+\cs_new:Npn \@@_ratio:n #1
+ { \int_value:w \@@_eval:w (#1) \@@_eval_end: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Dimension expression conditionals}
+%
+% \begin{macro}[pTF, EXP]{\dim_compare:nNn}
+% Simple comparison.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \dim_compare:nNn #1#2#3 { p , T , F , TF }
+ {
+ \if_dim:w \@@_eval:w #1 #2 \@@_eval:w #3 \@@_eval_end:
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF, EXP]{\dim_compare:n}
+% \begin{macro}[EXP]{\@@_compare:w, \@@_compare:wNN}
+% \begin{macro}[EXP]
+% {
+% \@@_compare_=:w,
+% \@@_compare_!:w,
+% \@@_compare_<:w,
+% \@@_compare_>:w
+% }
+% \begin{macro}{\@@_compare_error:}
+% This code is adapted from the \cs{int_compare:nTF} function. First
+% make sure that there is at least one relation operator, by
+% evaluating a dimension expression with a trailing
+% \cs{@@_compare_error:}. Just like for integers, the looping
+% auxiliary \cs{@@_compare:wNN} closes a primitive conditional and
+% opens a new one. It is actually easier to grab a dimension operand
+% than an integer one, because once evaluated, dimensions all end with
+% \texttt{pt} (with category other). Thus we do not need specific
+% auxiliaries for the three \enquote{simple} relations |<|, |=|,
+% and~|>|.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \dim_compare:n #1 { p , T , F , TF }
+ {
+ \exp_after:wN \@@_compare:w
+ \dim_use:N \@@_eval:w #1 \@@_compare_error:
+ }
+\cs_new:Npn \@@_compare:w #1 \@@_compare_error:
+ {
+ \exp_after:wN \if_false: \exp:w \exp_end_continue_f:w
+ \@@_compare:wNN #1 ? { = \@@_compare_end:w \else: } \s_@@_stop
+ }
+\exp_args:Nno \use:nn
+ { \cs_new:Npn \@@_compare:wNN #1 } { \tl_to_str:n {pt} #2#3 }
+ {
+ \if_meaning:w = #3
+ \use:c { @@_compare_#2:w }
+ \fi:
+ #1 pt \exp_stop_f:
+ \prg_return_false:
+ \exp_after:wN \@@_use_none_delimit_by_s_stop:w
+ \fi:
+ \reverse_if:N \if_dim:w #1 pt #2
+ \exp_after:wN \@@_compare:wNN
+ \dim_use:N \@@_eval:w #3
+ }
+\cs_new:cpn { @@_compare_ ! :w }
+ #1 \reverse_if:N #2 ! #3 = { #1 #2 = #3 }
+\cs_new:cpn { @@_compare_ = :w }
+ #1 \@@_eval:w = { #1 \@@_eval:w }
+\cs_new:cpn { @@_compare_ < :w }
+ #1 \reverse_if:N #2 < #3 = { #1 #2 > #3 }
+\cs_new:cpn { @@_compare_ > :w }
+ #1 \reverse_if:N #2 > #3 = { #1 #2 < #3 }
+\cs_new:Npn \@@_compare_end:w #1 \prg_return_false: #2 \s_@@_stop
+ { #1 \prg_return_false: \else: \prg_return_true: \fi: }
+\cs_new_protected:Npn \@@_compare_error:
+ {
+ \if_int_compare:w \c_zero_int \c_zero_int \fi:
+ =
+ \@@_compare_error:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP, noTF]{\dim_case:nn}
+% \begin{macro}{\@@_case:nnTF}
+% \begin{macro}{\@@_case:nw, \@@_case_end:nw}
+% For dimension cases, the first task to fully expand the check
+% condition. The over all idea is then much the same as for
+% \cs{str_case:nnTF} as described in \pkg{l3basics}.
+% \begin{macrocode}
+\cs_new:Npn \dim_case:nnTF #1
+ {
+ \exp:w
+ \exp_args:Nf \@@_case:nnTF { \dim_eval:n {#1} }
+ }
+\cs_new:Npn \dim_case:nnT #1#2#3
+ {
+ \exp:w
+ \exp_args:Nf \@@_case:nnTF { \dim_eval:n {#1} } {#2} {#3} { }
+ }
+\cs_new:Npn \dim_case:nnF #1#2
+ {
+ \exp:w
+ \exp_args:Nf \@@_case:nnTF { \dim_eval:n {#1} } {#2} { }
+ }
+\cs_new:Npn \dim_case:nn #1#2
+ {
+ \exp:w
+ \exp_args:Nf \@@_case:nnTF { \dim_eval:n {#1} } {#2} { } { }
+ }
+\cs_new:Npn \@@_case:nnTF #1#2#3#4
+ { \@@_case:nw {#1} #2 {#1} { } \s_@@_mark {#3} \s_@@_mark {#4} \s_@@_stop }
+\cs_new:Npn \@@_case:nw #1#2#3
+ {
+ \dim_compare:nNnTF {#1} = {#2}
+ { \@@_case_end:nw {#3} }
+ { \@@_case:nw {#1} }
+ }
+\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}
+% \end{macro}
+%
+% \subsection{Dimension expression loops}
+%
+% \begin{macro}{\dim_while_do:nn}
+% \begin{macro}{\dim_until_do:nn}
+% \begin{macro}{\dim_do_while:nn}
+% \begin{macro}{\dim_do_until:nn}
+% |while_do| and |do_while| functions for dimensions. Same as for the
+% |int| type only the names have changed.
+% \begin{macrocode}
+\cs_new:Npn \dim_while_do:nn #1#2
+ {
+ \dim_compare:nT {#1}
+ {
+ #2
+ \dim_while_do:nn {#1} {#2}
+ }
+ }
+\cs_new:Npn \dim_until_do:nn #1#2
+ {
+ \dim_compare:nF {#1}
+ {
+ #2
+ \dim_until_do:nn {#1} {#2}
+ }
+ }
+\cs_new:Npn \dim_do_while:nn #1#2
+ {
+ #2
+ \dim_compare:nT {#1}
+ { \dim_do_while:nn {#1} {#2} }
+ }
+\cs_new:Npn \dim_do_until:nn #1#2
+ {
+ #2
+ \dim_compare:nF {#1}
+ { \dim_do_until:nn {#1} {#2} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\dim_while_do:nNnn}
+% \begin{macro}{\dim_until_do:nNnn}
+% \begin{macro}{\dim_do_while:nNnn}
+% \begin{macro}{\dim_do_until:nNnn}
+% |while_do| and |do_while| functions for dimensions. Same as for the
+% |int| type only the names have changed.
+% \begin{macrocode}
+\cs_new:Npn \dim_while_do:nNnn #1#2#3#4
+ {
+ \dim_compare:nNnT {#1} #2 {#3}
+ {
+ #4
+ \dim_while_do:nNnn {#1} #2 {#3} {#4}
+ }
+ }
+\cs_new:Npn \dim_until_do:nNnn #1#2#3#4
+ {
+ \dim_compare:nNnF {#1} #2 {#3}
+ {
+ #4
+ \dim_until_do:nNnn {#1} #2 {#3} {#4}
+ }
+ }
+\cs_new:Npn \dim_do_while:nNnn #1#2#3#4
+ {
+ #4
+ \dim_compare:nNnT {#1} #2 {#3}
+ { \dim_do_while:nNnn {#1} #2 {#3} {#4} }
+ }
+\cs_new:Npn \dim_do_until:nNnn #1#2#3#4
+ {
+ #4
+ \dim_compare:nNnF {#1} #2 {#3}
+ { \dim_do_until:nNnn {#1} #2 {#3} {#4} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Dimension step functions}
+%
+% \begin{macro}{\dim_step_function:nnnN}
+% \begin{macro}{\@@_step:wwwN, \@@_step:NnnnN}
+% Before all else, evaluate the initial value, step, and final value.
+% Repeating a function by steps first needs a check on the direction
+% of the steps. After that, do the function for the start value then
+% step and loop around. It would be more symmetrical to test for a
+% step size of zero before checking the sign, but we optimize for the
+% most frequent case (positive step).
+% \begin{macrocode}
+\cs_new:Npn \dim_step_function:nnnN #1#2#3
+ {
+ \exp_after:wN \@@_step:wwwN
+ \tex_the:D \@@_eval:w #1 \exp_after:wN ;
+ \tex_the:D \@@_eval:w #2 \exp_after:wN ;
+ \tex_the:D \@@_eval:w #3 ;
+ }
+\cs_new:Npn \@@_step:wwwN #1; #2; #3; #4
+ {
+ \dim_compare:nNnTF {#2} > \c_zero_dim
+ { \@@_step:NnnnN > }
+ {
+ \dim_compare:nNnTF {#2} = \c_zero_dim
+ {
+ \msg_expandable_error:nnn { kernel } { zero-step } {#4}
+ \use_none:nnnn
+ }
+ { \@@_step:NnnnN < }
+ }
+ {#1} {#2} {#3} #4
+ }
+\cs_new:Npn \@@_step:NnnnN #1#2#3#4#5
+ {
+ \dim_compare:nNnF {#2} #1 {#4}
+ {
+ #5 {#2}
+ \exp_args:NNf \@@_step:NnnnN
+ #1 { \dim_eval:n { #2 + #3 } } {#3} {#4} #5
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\dim_step_inline:nnnn}
+% \begin{macro}{\dim_step_variable:nnnNn}
+% \begin{macro}{\@@_step:NNnnnn}
+% The approach here is to build a function, with a global integer
+% required to make the nesting safe (as seen in other in line
+% functions), and map that function using \cs{dim_step_function:nnnN}.
+% We put a \cs{prg_break_point:Nn} so that \texttt{map_break}
+% functions from other modules correctly decrement \cs{g__kernel_prg_map_int}
+% before looking for their own break point. The first argument is
+% \cs{scan_stop:}, so that no breaking function recognizes this break
+% point as its own.
+% \begin{macrocode}
+\cs_new_protected:Npn \dim_step_inline:nnnn
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:NNc \@@_step:NNnnnn
+ \cs_gset_protected:Npn
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w }
+ }
+\cs_new_protected:Npn \dim_step_variable:nnnNn #1#2#3#4#5
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:NNc \@@_step:NNnnnn
+ \cs_gset_protected:Npe
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w }
+ {#1}{#2}{#3}
+ {
+ \tl_set:Nn \exp_not:N #4 {##1}
+ \exp_not:n {#5}
+ }
+ }
+\cs_new_protected:Npn \@@_step:NNnnnn #1#2#3#4#5#6
+ {
+ #1 #2 ##1 {#6}
+ \dim_step_function:nnnN {#3} {#4} {#5} #2
+ \prg_break_point:Nn \scan_stop: { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Using \texttt{dim} expressions and variables}
+%
+% \begin{macro}{\dim_eval:n}
+% Evaluating a dimension expression expandably.
+% \begin{macrocode}
+\cs_new:Npn \dim_eval:n #1
+ { \dim_use:N \@@_eval:w #1 \@@_eval_end: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\dim_sign:n, \@@_sign:Nw}
+% See \cs{dim_abs:n}. Contrarily to \cs{int_sign:n} the case of a
+% zero dimension cannot be distinguished from a positive dimension by
+% looking only at the first character, since |0.2pt| and |0pt| start
+% the same way. We need explicit comparisons. We start by
+% distinguishing the most common case of a positive dimension.
+% \begin{macrocode}
+\cs_new:Npn \dim_sign:n #1
+ {
+ \int_value:w \exp_after:wN \@@_sign:Nw
+ \dim_use:N \@@_eval:w #1 \@@_eval_end: ;
+ \exp_stop_f:
+ }
+\cs_new:Npn \@@_sign:Nw #1#2 ;
+ {
+ \if_dim:w #1#2 > \c_zero_dim
+ 1
+ \else:
+ \if_meaning:w - #1
+ -1
+ \else:
+ 0
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\dim_use:N, \dim_use:c}
+% Accessing a \meta{dim}. We hand-code the |c| variant for some speed gain.
+% \begin{macrocode}
+\cs_new_eq:NN \dim_use:N \tex_the:D
+\cs_new:Npn \dim_use:c #1 { \tex_the:D \cs:w #1 \cs_end: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\dim_to_decimal:n}
+% \begin{macro}[EXP]{\@@_to_decimal:w}
+% A function which comes up often enough to deserve a place in the
+% kernel. Evaluate the dimension expression~|#1| then remove the
+% trailing \texttt{pt}. When debugging is enabled, the argument is
+% put in parentheses as this prevents the dimension expression from
+% terminating early and leaving extra tokens lying around. This is
+% used a lot by low-level manipulations.
+% \begin{macrocode}
+\cs_new:Npn \dim_to_decimal:n #1
+ {
+ \exp_after:wN
+ \@@_to_decimal:w \dim_use:N \@@_eval:w #1 \@@_eval_end:
+ }
+\use:e
+ {
+ \cs_new:Npn \exp_not:N \@@_to_decimal:w
+ #1 . #2 \tl_to_str:n { pt }
+ }
+ {
+ \int_compare:nNnTF {#2} > \c_zero_int
+ { #1 . #2 }
+ { #1 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\dim_to_fp:n}
+% Defined in \pkg{l3fp-convert}, documented here.
+% \end{macro}
+%
+% \subsection{Conversion of \texttt{dim} to other units}
+%
+% The conversion from \texttt{pt} or \texttt{sp} to other units is complicated
+% by the fact that \TeX{}'s conversion to \texttt{sp} involves rounding and
+% hard-coded ratios. In order to give re-entrant outcomes, we therefore need
+% to do quite a bit of work: see
+% \url{https://github.com/latex3/latex3/issues/954} for detailed discussion.
+% After dealing with the trivial case, we therefore have some work to do.
+% The code to do this is contributed by Ruixi Zhang.
+%
+% \begin{macro}[EXP]{\dim_to_decimal_in_sp:n}
+% The one easy case: the only requirement here is that we avoid an
+% overflow.
+% \begin{macrocode}
+\cs_new:Npn \dim_to_decimal_in_sp:n #1
+ { \int_value:w \@@_eval:w #1 \@@_eval_end: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \dim_to_decimal_in_bp:n ,
+% \dim_to_decimal_in_cc:n ,
+% \dim_to_decimal_in_cm:n ,
+% \dim_to_decimal_in_dd:n ,
+% \dim_to_decimal_in_in:n ,
+% \dim_to_decimal_in_mm:n ,
+% \dim_to_decimal_in_pc:n
+% }
+% \begin{macro}[EXP]{\@@_to_decimal_aux:w}
+% We first set up a helper macro \cs[no-index]{@@_tmp:w} which takes two
+% arguments. The first argument is one of the following engine-defined
+% units: |in|,~|pc|, |cm|, |mm|, |bp|, |dd|, |cc|, |nd|, and~|nc|.
+% The second argument is $\frac{1}{2}\delta^{-1}$ in reduced fraction,
+% where $\delta>1$~is the engine-defined conversion factor for each unit.
+% Note that $\delta$~must be strictly larger than~$1$ for the following
+% algorithm to work.
+%
+% Here is how the algorithm works: Suppose that a user inputs a
+% non-negative dimension in a unit that has conversion factor~$\delta>1$.
+% Then this dimension is internally represented as $X$\,sp, where
+% $X=\lfloor N\delta\rfloor$ for some integer $N\ge0$. We then seek a
+% formula to express this $N$ using~$X$.
+% The \cs[no-index]{dim_to_decimal_in_<unit>:n} functions shall return
+% the number $N/2^{16}$ in decimal. This way, we guarantee the returned
+% decimal followed by the original unit will parse to exactly~$X$\,sp.
+%
+% So how do we get $N$ from~$X$? Well, since $X=\lfloor N\delta\rfloor$,
+% we have $X\le N\delta<X+1$ and $X\delta^{-1}\le N<(X+1)\delta^{-1}$.
+% Let's focus on the midpoint of this bounding interval for~$N$. The
+% midpoint is $(X+\frac{1}{2})\delta^{-1}$. The fact $\delta>1$ implies
+% that the bounding interval is shorter than~$1$ in length. Thus,
+% (1)~$\hbox{midpoint}+\frac{1}{2}>N$ and
+% (2)~$\hbox{midpoint}+\frac{1}{2}<N+1$. In other words,
+% $N=\lfloor\hbox{midpoint}+\frac{1}{2}\rfloor$. As long as we can
+% rewrite the midpoint as the result of a \enquote{scaling operation} of \eTeX,
+% the $\lfloor\ldots+\frac{1}{2}\rfloor$ part will follow naturally.
+% Indeed we can: $\hbox{midpoint}=(2X+1)\times(\frac{1}{2}\delta^{-1})$.
+%
+% Addendum: If $\delta\ge2$, then the bounding interval for~$N$ is at
+% most~$\frac{1}{2}$ wide in length. In this case, the leftpoint
+% $X\delta^{-1}$ suffices as $N=\lfloor X\delta^{-1}+\frac{1}{2}\rfloor$.
+% Six out of the nine units listed above can be handled in this way,
+% which is much simpler than using midpoint. But three remaining units
+% have $1<\delta<2$; they are |bp|~($\delta=7227/7200$),
+% |nd|~($\delta=685/642$), and |dd|~($\delta=1238/1157$),
+% and these three must be handled using midpoint.
+% For consistency, we shall use the midpoint approach for all nine units.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \@@_tmp:w #1#2
+ {
+ \cs_new:cpn { dim_to_decimal_in_ #1 :n } ##1
+ {
+ \exp_after:wN \@@_to_decimal_aux:w
+ \int_value:w \@@_eval:w ##1 \@@_eval_end: ; #2 ;
+ }
+ }
+% \end{macrocode}
+% Conversions to other units are now coded.
+% Consult the pdf\/\TeX{} source for each conversion factor~$\delta$.
+% Each factor $\frac{1}{2}\delta^{-1}$ is hand-coded
+% for accuracy (and speed). As the units \texttt{nc} and \texttt{nd}
+% are not supported by \XeTeX{} or (u)p\TeX{}, they are not included
+% here.
+% \begin{macrocode}
+ \@@_tmp:w { in } { 50 / 7227 } % delta = 7227/100
+ \@@_tmp:w { pc } { 1 / 24 } % delta = 12/1
+ \@@_tmp:w { cm } { 127 / 7227 } % delta = 7227/254
+ \@@_tmp:w { mm } { 1270 / 7227 } % delta = 7227/2540
+ \@@_tmp:w { bp } { 400 / 803 } % delta = 7227/7200
+ \@@_tmp:w { dd } { 1157 / 2476 } % delta = 1238/1157
+ \@@_tmp:w { cc } { 1157 / 29712 } % delta = 14856/1157
+\group_end:
+% \end{macrocode}
+% The tokens after \cs{@@_to_decimal_aux:w} shall have the following form:
+% |<number>;<half of delta inverse>;|, where |<number>| represents the
+% input dimension in |sp| unit.
+% If |<number>| is positive, then |#1| is its leading digit and |#2|
+% (possibly empty) is all the remaining digits;
+% If |<number>| is zero, then |#1| is~|0|$_{12}$ and |#2| is empty;
+% If |<number>| is negative, then |#1| is its sign~|-|$_{12}$ and |#2|
+% is all its digits.
+% In all three cases, |#1#2| is the original |<number>|. We can use |#1|
+% to decide whether to use the |-1| formula or the |+1| formula.
+% \begin{macrocode}
+\cs_new:Npn \@@_to_decimal_aux:w #1#2 ; #3 ;
+ {
+ \dim_to_decimal:n
+ {
+% \end{macrocode}
+% We need different formulae depending on whether the user input dimension
+% is negative or not.
+% For negative dimension (internally represented as $X$\,sp), the formula
+% is $(2X-1)\times(\frac{1}{2}\delta^{-1})$.
+% For non-negative dimension, the formula
+% is $(2X+1)\times(\frac{1}{2}\delta^{-1})$.
+% The intermediate step doubles the dimension~$X$.
+% To avoid overflow, we must invoke \cs[no-index]{int_eval:n}.
+% \begin{macrocode}
+ \int_eval:n
+ { ( 2 * #1#2 \if:w #1 - - \else: + \fi: 1 ) * #3 }
+% \end{macrocode}
+% Now we append~|sp| to finish the dimension specification.
+% \begin{macrocode}
+ sp
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\dim_to_decimal_in_unit:nn}
+% \begin{macrocode}
+\cs_new:Npn \dim_to_decimal_in_unit:nn #1#2
+ {
+ \exp_after:wN \@@_chk_unit:w
+ \int_value:w \@@_eval:w #2 \@@_eval_end: ; {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[EXP]{\@@_chk_unit:w}
+% The tokens after \cs{@@_chk_unit:w} shall have the following form:
+% |<number2>;{<dimexpr1>}|, where |<number2>| represents |<dimexpr2>| in
+% |sp| unit.
+% If |#1| is~|0|$_{12}$, the \enquote{unit} |<dimexpr2>| must also be zero.
+% So we throw out a \enquote{division by zero} error message at this point.
+% Otherwise, if |#1| is~|-|$_{12}$, we shall negate both |<dimexpr1>| and
+% |<dimexpr2>| for later procedures.
+% \begin{macrocode}
+\cs_new:Npn \@@_chk_unit:w #1#2;#3
+ {
+ \token_if_eq_charcode:NNTF #1 0
+ { \msg_expandable_error:nn { dim } { zero-unit } }
+ {
+ \exp_after:wN \@@_branch_unit:w
+ \int_value:w \if:w #1 - - \fi: \@@_eval:w #3 \exp_after:wN ;
+ \int_value:w \if:w #1 - - \fi: #1#2 ;
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[EXP]{\@@_branch_unit:w}
+% The tokens after \cs{@@_branch_unit:w} shall have the following form:
+% |<number1>;<number2>;|, where |<number1>| represents |<dimexpr1>| in
+% |sp| unit (whose sign is taken care of) and |<number2>| represents the
+% absolute value of |<dimexpr2>| in |sp| unit (which is strictly positive).
+%
+% As explained, the formulae $(2X\pm1)\times(\frac{1}{2}\delta^{-1})$ work
+% if and only if $\delta=|<number2>|/65536>1$. This corresponds to
+% |<dimexpr2>| strictly larger than 1\,pt in absolute value.
+% In this case, we simply call \cs{@@_to_decimal_aux:w} and supply
+% $\frac{1}{2}\delta^{-1}=32768/|<number2>|$ as |<half of delta inverse>|.
+%
+% Otherwise if $|<number2>|=65536$, then |<dimexpr2>| is 1\,pt in absolute
+% value and we call \cs{dim_to_decimal:n} directly.
+%
+% Otherwise $0<|<number2>|<65536$ and we shall proceed differently.
+%
+% For unit less than 1\,pt, write $n=|<number2>|$, then $\delta=n/65536<1$.
+% The midpoint formulae are not optimal. Let's go back to the inequalities
+% $X\delta^{-1}\le N<(X+1)\delta^{-1}$. Since now $\delta<1$, the bounding
+% interval is wider than~$1$ in length. Consider the ceiling integer
+% $M=\lceil X\delta^{-1}\rceil$, then $X\delta^{-1}\le M<(X+1)\delta^{-1}$,
+% or equivalently $X\le M\delta<X+1$, and thus $\lfloor M\delta\rfloor=X$.
+% The key point here is that we \emph{don't} need to solve for~$N$;
+% in fact, any integer that can reproduce~$X$ (such as~$M$) is good enough.
+% So the algorithm goes like this: (1)~Compute rounding of $X\delta^{-1}$,
+% i.e., $M'=\lfloor X\delta^{-1}+\frac{1}{2}\rfloor$; this $M'$ could be
+% either $M$ or $M-1$. (2)~Check if $\lfloor M'\delta\rfloor=X$, i.e.,
+% whether our candidate $M'$ can reproduce~$X$. If so, then this $M'$ is
+% good enough; if not, then we add one to~$M'$.
+%
+% But when $0<n<65536$, we cannot delay the problem of overflow any more.
+% For $X\delta^{-1}=X\times65536/n$, where $X$ can go up to $2^{30}-1$ and
+% $n$ can be as small as~$1$, the result is well over $2^{31}-1$ (largest
+% integer allowed within |\numexpr|).
+% For example, |\dim_to_decimal_in_unit:nn { \maxdimen } { 1sp }|.
+% Here, all inputs are legal, so we should be able to output |1073741823|
+% \emph{without} causing arithmetic overflow.
+%
+% As a workaround, let's write $X=qn+r$ with some $q\ge0$ and $0\le r<n$.
+% Then $X\delta^{-1}=65536q+65536r/n$, and so
+% $M'=65536q+\lfloor65536r/n+\frac{1}{2}\rfloor=65536q+R'$.
+% Computing $R'$ will never overflow. If this $R'$ can reproduce~$r$, then
+% it is good enough; otherwise we add one to~$R'$. In the end, we shall
+% output $q+R'/65536$ in decimal.
+%
+% Note: $q=\lfloor X/n\rfloor=\lfloor\frac{2X-n}{2n}+\frac{1}{2}\rfloor$
+% represents the \enquote{integer} part, while $0\le R'\le65536$ represents the
+% \enquote{fractional} part. (Can $R'=65536$ really happen? Didn't investigate.)
+% \begin{macrocode}
+\cs_new:Npn \@@_branch_unit:w #1;#2;
+ {
+ \int_compare:nNnTF {#2} > { 65536 }
+ { \@@_to_decimal_aux:w #1 ; 32768 / #2 ; }
+ {
+ \int_compare:nNnTF {#2} = { 65536 }
+ { \dim_to_decimal:n { #1sp } }
+ { \@@_get_quotient:w #1 ; #2 ; }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[EXP]{\@@_get_quotient:w}
+% We wish to get the quotient $q$ via rounding of $\frac{2X-n}{2n}$.
+% When $0\le X<n/2$, we have $\frac{2X-n}{2n}<0$. So, strictly speaking,
+% |\numexpr| performs its rounding as
+% $\lceil\frac{2X-n}{2n}-\frac{1}{2}\rceil$, not exactly what we want.
+% However, lucky for us, only $X=0$ makes
+% $\lceil\frac{2X-n}{2n}-\frac{1}{2}\rceil=-1\neq0$ (we want~$0$);
+% all other $0<X<n/2$ make $\lceil\frac{2X-n}{2n}-\frac{1}{2}\rceil=0=q$.
+% Thus, let's filter out $X=0$ early.
+% If $X\neq0$, we extract its sign and leave the sign to the back.
+% The sign does not participate in any calculations (also the code works
+% with positive integers only). The sign is used at the last stages when
+% we parse the decimal output.
+%
+% After \cs{@@_get_quotient:w} has done its job, either we have the
+% decimal~|0|, or we have \cs{@@_get_remainder:w} followed by
+% $q$|;|$\lvert X\rvert$|;|$n$|;<sign of X>;|.
+% \begin{macrocode}
+\cs_new:Npn \@@_get_quotient:w #1#2;#3;
+ {
+ \token_if_eq_charcode:NNTF #1 0
+ { 0 }
+ {
+ \token_if_eq_charcode:NNTF #1 -
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_get_remainder:w
+ \int_eval:n { ( 2 * #2 - #3 ) / ( 2 * #3 ) } ;
+ #2 ; #3 ; - ;
+ }
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_get_remainder:w
+ \int_eval:n { ( 2 * #1#2 - #3 ) / ( 2 * #3 ) } ;
+ #1#2 ; #3 ; ;
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[EXP]{\@@_get_remainder:w}
+% \cs{@@_get_remainder:w} does not need to read the sign.
+% After finding the remainder~$r$, the number~$\lvert X\rvert$ is no longer
+% needed. We should then have \cs{@@_convert_remainder:w} followed by
+% $r$|;|$n$|;|$q$|;<sign of X>;|.
+% \begin{macrocode}
+\cs_new:Npn \@@_get_remainder:w #1;#2;#3;
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_convert_remainder:w
+ \int_eval:n { #2 - #1 * #3 } ;
+ #3 ; #1 ;
+ }
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[EXP]{\@@_convert_remainder:w}
+% This is trivial. We compute $R'=\lfloor65536r/n+\frac{1}{2}\rfloor$,
+% then leave \cs{@@_test_candidate:w} followed by
+% $R'$|;|$r$|;|$n$|;|$q$|;<sign of X>;|.
+% \begin{macrocode}
+\cs_new:Npn \@@_convert_remainder:w #1;#2;
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_test_candidate:w
+ \int_eval:n { #1 * 65536 / #2 } ;
+ #1 ; #2 ;
+ }
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[EXP]{\@@_test_candidate:w}
+% Now the fun part: We take $R'$, $r$ and~$n$ to test whether
+% $r=\lfloor R'\delta\rfloor$. This is done as a dimension comparison.
+% The left-hand side, $r$, is simply |r sp|. The right-hand side,
+% $\lfloor R'\delta\rfloor$, is exactly |<R' as decimal><dimen = n sp>|.
+% If the result is true, then we've found~$R'$;
+% otherwise we add one to~$R'$.
+% After this step, $r$ and~$n$ are no longer needed. We should then have
+% \cs{@@_parse_decimal:w} followed by $R'$|;|$q$|;<sign of X>;|.
+% \begin{macrocode}
+\cs_new:Npn \@@_test_candidate:w #1;#2;#3;
+ {
+ \dim_compare:nNnTF { #2sp } =
+ { \dim_to_decimal:n { #1sp } \@@_eval:w #3sp \@@_eval_end: }
+ { \@@_parse_decimal:w #1 ; }
+ {
+ \@@_parse_decimal:w \int_eval:n { #1 + 1 } ;
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[EXP]{\@@_parse_decimal:w, \@@_parse_decimal_aux:w}
+% The Grand Finale: We sum $q$ and $R'/65536$ together, and negate the
+% result if necessary. These are all done expandably.
+% If $0<R'/65536<1$, the integer summation is naturally terminated at the
+% decimal point. If $R'/65536=0$ (or~$1$?), the summation is terminated
+% at the semicolon. The auxiliary function \cs{@@_parse_decimal_aux:w}
+% takes care of both cases.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_decimal:w #1;#2;#3;
+ {
+ \exp_after:wN \@@_parse_decimal_aux:w
+ \int_value:w #3 \int_eval:w #2 + \dim_to_decimal:n { #1sp } ;
+ }
+\cs_new:Npn \@@_parse_decimal_aux:w #1 ; {#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Viewing \texttt{dim} variables}
+%
+% \begin{macro}{\dim_show:N, \dim_show:c}
+% Diagnostics.
+% \begin{macrocode}
+\cs_new_eq:NN \dim_show:N \__kernel_register_show:N
+\cs_generate_variant:Nn \dim_show:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\dim_show:n}
+% Diagnostics. We don't use the \TeX{} primitive \tn{showthe} to show
+% dimension expressions: this gives a more unified output.
+% \begin{macrocode}
+\cs_new_protected:Npn \dim_show:n
+ { \__kernel_msg_show_eval:Nn \dim_eval:n }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\dim_log:N, \dim_log:c, \dim_log:n}
+% Diagnostics. Redirect output of \cs{dim_show:n} to the log.
+% \begin{macrocode}
+\cs_new_eq:NN \dim_log:N \__kernel_register_log:N
+\cs_new_eq:NN \dim_log:c \__kernel_register_log:c
+\cs_new_protected:Npn \dim_log:n
+ { \__kernel_msg_log_eval:Nn \dim_eval:n }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Constant dimensions}
+%
+% \begin{variable}{\c_zero_dim, \c_max_dim}
+% Constant dimensions.
+% \begin{macrocode}
+\dim_const:Nn \c_zero_dim { 0 pt }
+\dim_const:Nn \c_max_dim { 16383.99999 pt }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Scratch dimensions}
+%
+% \begin{variable}{\l_tmpa_dim, \l_tmpb_dim}
+% \begin{variable}{\g_tmpa_dim, \g_tmpb_dim}
+% We provide two local and two global scratch registers, maybe we
+% need more or less.
+% \begin{macrocode}
+\dim_new:N \l_tmpa_dim
+\dim_new:N \l_tmpb_dim
+\dim_new:N \g_tmpa_dim
+\dim_new:N \g_tmpb_dim
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \subsection{Creating and initialising \texttt{skip} variables}
+%
+% \begin{macrocode}
+%<@@=skip>
+% \end{macrocode}
+%
+% \begin{variable}{\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\skip_new:N, \skip_new:c}
+% Allocation of a new internal registers.
+% \begin{macrocode}
+\cs_new_protected:Npn \skip_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs:w newskip \cs_end: #1
+ }
+\cs_generate_variant:Nn \skip_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\skip_const:Nn, \skip_const:cn}
+% Contrarily to integer constants, we cannot avoid using a register,
+% even for constants. See \cs{dim_const:Nn} for why we cannot use
+% \cs{skip_gset:Nn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \skip_const:Nn #1#2
+ {
+ \skip_new:N #1
+ \tex_global:D #1 = \skip_eval:n {#2} \scan_stop:
+ }
+\cs_generate_variant:Nn \skip_const:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\skip_zero:N, \skip_zero:c}
+% \begin{macro}{\skip_gzero:N, \skip_gzero:c}
+% Reset the register to zero.
+% \begin{macrocode}
+\cs_new_eq:NN \skip_zero:N \dim_zero:N
+\cs_new_eq:NN \skip_gzero:N \dim_gzero:N
+\cs_generate_variant:Nn \skip_zero:N { c }
+\cs_generate_variant:Nn \skip_gzero:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\skip_zero_new:N, \skip_zero_new:c, \skip_gzero_new:N, \skip_gzero_new:c}
+% Create a register if needed, otherwise clear it.
+% \begin{macrocode}
+\cs_new_protected:Npn \skip_zero_new:N #1
+ { \skip_if_exist:NTF #1 { \skip_zero:N #1 } { \skip_new:N #1 } }
+\cs_new_protected:Npn \skip_gzero_new:N #1
+ { \skip_if_exist:NTF #1 { \skip_gzero:N #1 } { \skip_new:N #1 } }
+\cs_generate_variant:Nn \skip_zero_new:N { c }
+\cs_generate_variant:Nn \skip_gzero_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\skip_if_exist:N, \skip_if_exist:c}
+% Copies of the \texttt{cs} functions defined in \pkg{l3basics}.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \skip_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \skip_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Setting \texttt{skip} variables}
+%
+% \begin{macro}{\skip_set:Nn, \skip_set:cn}
+% \begin{macro}{\skip_gset:Nn, \skip_gset:cn}
+% Much the same as for dimensions.
+% \begin{macrocode}
+\cs_new_protected:Npn \skip_set:Nn #1#2
+ { #1 = \tex_glueexpr:D #2 \scan_stop: }
+\cs_new_protected:Npn \skip_gset:Nn #1#2
+ { \tex_global:D #1 = \tex_glueexpr:D #2 \scan_stop: }
+\cs_generate_variant:Nn \skip_set:Nn { c }
+\cs_generate_variant:Nn \skip_gset:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\skip_set_eq:NN, \skip_set_eq:cN, \skip_set_eq:Nc, \skip_set_eq:cc}
+% \begin{macro}
+% {\skip_gset_eq:NN, \skip_gset_eq:cN, \skip_gset_eq:Nc, \skip_gset_eq:cc}
+% All straightforward.
+% \begin{macrocode}
+\cs_new_protected:Npn \skip_set_eq:NN #1#2 { #1 = #2 }
+\cs_generate_variant:Nn \skip_set_eq:NN { c , Nc , cc }
+\cs_new_protected:Npn \skip_gset_eq:NN #1#2 { \tex_global:D #1 = #2 }
+\cs_generate_variant:Nn \skip_gset_eq:NN { c , Nc , cc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\skip_add:Nn, \skip_add:cn}
+% \begin{macro}{\skip_gadd:Nn, \skip_gadd:cn}
+% \begin{macro}{\skip_sub:Nn, \skip_sub:cn}
+% \begin{macro}{\skip_gsub:Nn, \skip_gsub:cn}
+% Using |by| here deals with the (incorrect) case |\skip123|.
+% \begin{macrocode}
+\cs_new_protected:Npn \skip_add:Nn #1#2
+ { \tex_advance:D #1 \tex_glueexpr:D #2 \scan_stop: }
+\cs_new_protected:Npn \skip_gadd:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 \tex_glueexpr:D #2 \scan_stop: }
+\cs_generate_variant:Nn \skip_add:Nn { c }
+\cs_generate_variant:Nn \skip_gadd:Nn { c }
+\cs_new_protected:Npn \skip_sub:Nn #1#2
+ { \tex_advance:D #1 - \tex_glueexpr:D #2 \scan_stop: }
+\cs_new_protected:Npn \skip_gsub:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 - \tex_glueexpr:D #2 \scan_stop: }
+\cs_generate_variant:Nn \skip_sub:Nn { c }
+\cs_generate_variant:Nn \skip_gsub:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Skip expression conditionals}
+%
+% \begin{macro}[pTF]{\skip_if_eq:nn}
+% Comparing skips means doing two expansions to make strings, and then
+% testing them. As a result, only equality is tested.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \skip_if_eq:nn #1#2 { p , T , F , TF }
+ {
+ \str_if_eq:eeTF { \skip_eval:n {#1} } { \skip_eval:n {#2} }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP,pTF]{\skip_if_finite:n}
+% \begin{macro}[EXP]{\@@_if_finite:wwNw}
+% With \eTeX{}, we have an easy access to the order of infinities of
+% the stretch and shrink components of a skip. However, to access
+% both, we either need to evaluate the expression twice, or evaluate
+% it, then call an auxiliary to extract both pieces of information
+% from the result. Since we are going to need an auxiliary anyways,
+% it is quicker to make it search for the string \texttt{fil} which
+% characterizes infinite glue.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \prg_new_conditional:Npnn \skip_if_finite:n ##1 { p , T , F , TF }
+ {
+ \exp_after:wN \@@_if_finite:wwNw
+ \skip_use:N \tex_glueexpr:D ##1 ; \prg_return_false:
+ #1 ; \prg_return_true: \s_@@_stop
+ }
+ \cs_new:Npn \@@_if_finite:wwNw ##1 #1 ##2 ; ##3 ##4 \s_@@_stop {##3}
+ }
+\exp_args:No \@@_tmp:w { \tl_to_str:n { fil } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Using \texttt{skip} expressions and variables}
+%
+% \begin{macro}{\skip_eval:n}
+% Evaluating a skip expression expandably.
+% \begin{macrocode}
+\cs_new:Npn \skip_eval:n #1
+ { \skip_use:N \tex_glueexpr:D #1 \scan_stop: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\skip_use:N, \skip_use:c}
+% Accessing a \meta{skip}.
+% \begin{macrocode}
+\cs_new_eq:NN \skip_use:N \dim_use:N
+\cs_new_eq:NN \skip_use:c \dim_use:c
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Inserting skips into the output}
+%
+% \begin{macro}{\skip_horizontal:N, \skip_horizontal:c, \skip_horizontal:n}
+% \begin{macro}{\skip_vertical:N, \skip_vertical:c, \skip_vertical:n}
+% Inserting skips.
+% \begin{macrocode}
+\cs_new_eq:NN \skip_horizontal:N \tex_hskip:D
+\cs_new:Npn \skip_horizontal:n #1
+ { \skip_horizontal:N \tex_glueexpr:D #1 \scan_stop: }
+\cs_new_eq:NN \skip_vertical:N \tex_vskip:D
+\cs_new:Npn \skip_vertical:n #1
+ { \skip_vertical:N \tex_glueexpr:D #1 \scan_stop: }
+\cs_generate_variant:Nn \skip_horizontal:N { c }
+\cs_generate_variant:Nn \skip_vertical:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Viewing \texttt{skip} variables}
+%
+% \begin{macro}{\skip_show:N, \skip_show:c}
+% Diagnostics.
+% \begin{macrocode}
+\cs_new_eq:NN \skip_show:N \__kernel_register_show:N
+\cs_generate_variant:Nn \skip_show:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\skip_show:n}
+% Diagnostics. We don't use the \TeX{} primitive \tn{showthe} to show
+% skip expressions: this gives a more unified output.
+% \begin{macrocode}
+\cs_new_protected:Npn \skip_show:n
+ { \__kernel_msg_show_eval:Nn \skip_eval:n }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\skip_log:N, \skip_log:c, \skip_log:n}
+% Diagnostics. Redirect output of \cs{skip_show:n} to the log.
+% \begin{macrocode}
+\cs_new_eq:NN \skip_log:N \__kernel_register_log:N
+\cs_new_eq:NN \skip_log:c \__kernel_register_log:c
+\cs_new_protected:Npn \skip_log:n
+ { \__kernel_msg_log_eval:Nn \skip_eval:n }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Constant skips}
+%
+% \begin{macro}{\c_zero_skip, \c_max_skip}
+% Skips with no rubber component are just dimensions but need to terminate
+% correctly.
+% \begin{macrocode}
+\skip_const:Nn \c_zero_skip { \c_zero_dim }
+\skip_const:Nn \c_max_skip { \c_max_dim }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Scratch skips}
+%
+% \begin{variable}{\l_tmpa_skip, \l_tmpb_skip}
+% \begin{variable}{\g_tmpa_skip, \g_tmpb_skip}
+% We provide two local and two global scratch registers, maybe we
+% need more or less.
+% \begin{macrocode}
+\skip_new:N \l_tmpa_skip
+\skip_new:N \l_tmpb_skip
+\skip_new:N \g_tmpa_skip
+\skip_new:N \g_tmpb_skip
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \subsection{Creating and initialising \texttt{muskip} variables}
+%
+% \begin{macro}{\muskip_new:N, \muskip_new:c}
+% And then we add muskips.
+% \begin{macrocode}
+\cs_new_protected:Npn \muskip_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs:w newmuskip \cs_end: #1
+ }
+\cs_generate_variant:Nn \muskip_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\muskip_const:Nn, \muskip_const:cn}
+% See \cs{skip_const:Nn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \muskip_const:Nn #1#2
+ {
+ \muskip_new:N #1
+ \tex_global:D #1 = \muskip_eval:n {#2} \scan_stop:
+ }
+\cs_generate_variant:Nn \muskip_const:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\muskip_zero:N, \muskip_zero:c}
+% \begin{macro}{\muskip_gzero:N, \muskip_gzero:c}
+% Reset the register to zero.
+% \begin{macrocode}
+\cs_new_protected:Npn \muskip_zero:N #1
+ { #1 = \c_zero_muskip }
+\cs_new_protected:Npn \muskip_gzero:N #1
+ { \tex_global:D #1 = \c_zero_muskip }
+\cs_generate_variant:Nn \muskip_zero:N { c }
+\cs_generate_variant:Nn \muskip_gzero:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \muskip_zero_new:N, \muskip_zero_new:c,
+% \muskip_gzero_new:N, \muskip_gzero_new:c
+% }
+% Create a register if needed, otherwise clear it.
+% \begin{macrocode}
+\cs_new_protected:Npn \muskip_zero_new:N #1
+ { \muskip_if_exist:NTF #1 { \muskip_zero:N #1 } { \muskip_new:N #1 } }
+\cs_new_protected:Npn \muskip_gzero_new:N #1
+ { \muskip_if_exist:NTF #1 { \muskip_gzero:N #1 } { \muskip_new:N #1 } }
+\cs_generate_variant:Nn \muskip_zero_new:N { c }
+\cs_generate_variant:Nn \muskip_gzero_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\muskip_if_exist:N, \muskip_if_exist:c}
+% Copies of the \texttt{cs} functions defined in \pkg{l3basics}.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \muskip_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \muskip_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Setting \texttt{muskip} variables}
+%
+% \begin{macro}{\muskip_set:Nn, \muskip_set:cn}
+% \begin{macro}{\muskip_gset:Nn, \muskip_gset:cn}
+% This should be pretty familiar.
+% \begin{macrocode}
+\cs_new_protected:Npn \muskip_set:Nn #1#2
+ { #1 = \tex_muexpr:D #2 \scan_stop: }
+\cs_new_protected:Npn \muskip_gset:Nn #1#2
+ { \tex_global:D #1 = \tex_muexpr:D #2 \scan_stop: }
+\cs_generate_variant:Nn \muskip_set:Nn { c }
+\cs_generate_variant:Nn \muskip_gset:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \muskip_set_eq:NN, \muskip_set_eq:cN,
+% \muskip_set_eq:Nc, \muskip_set_eq:cc
+% }
+% \begin{macro}
+% {
+% \muskip_gset_eq:NN, \muskip_gset_eq:cN,
+% \muskip_gset_eq:Nc, \muskip_gset_eq:cc
+% }
+% All straightforward.
+% \begin{macrocode}
+\cs_new_protected:Npn \muskip_set_eq:NN #1#2 { #1 = #2 }
+\cs_generate_variant:Nn \muskip_set_eq:NN { c , Nc , cc }
+\cs_new_protected:Npn \muskip_gset_eq:NN #1#2 { \tex_global:D #1 = #2 }
+\cs_generate_variant:Nn \muskip_gset_eq:NN { c , Nc , cc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\muskip_add:Nn, \muskip_add:cn}
+% \begin{macro}{\muskip_gadd:Nn, \muskip_gadd:cn}
+% \begin{macro}{\muskip_sub:Nn, \muskip_sub:cn}
+% \begin{macro}{\muskip_gsub:Nn, \muskip_gsub:cn}
+% Using |by| here deals with the (incorrect) case |\muskip123|.
+% \begin{macrocode}
+\cs_new_protected:Npn \muskip_add:Nn #1#2
+ { \tex_advance:D #1 \tex_muexpr:D #2 \scan_stop: }
+\cs_new_protected:Npn \muskip_gadd:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 \tex_muexpr:D #2 \scan_stop: }
+\cs_generate_variant:Nn \muskip_add:Nn { c }
+\cs_generate_variant:Nn \muskip_gadd:Nn { c }
+\cs_new_protected:Npn \muskip_sub:Nn #1#2
+ { \tex_advance:D #1 - \tex_muexpr:D #2 \scan_stop: }
+\cs_new_protected:Npn \muskip_gsub:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 - \tex_muexpr:D #2 \scan_stop: }
+\cs_generate_variant:Nn \muskip_sub:Nn { c }
+\cs_generate_variant:Nn \muskip_gsub:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Using \texttt{muskip} expressions and variables}
+%
+% \begin{macro}{\muskip_eval:n}
+% Evaluating a muskip expression expandably.
+% \begin{macrocode}
+\cs_new:Npn \muskip_eval:n #1
+ { \muskip_use:N \tex_muexpr:D #1 \scan_stop: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\muskip_use:N, \muskip_use:c}
+% Accessing a \meta{muskip}.
+% \begin{macrocode}
+\cs_new_eq:NN \muskip_use:N \dim_use:N
+\cs_new_eq:NN \muskip_use:c \dim_use:c
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Viewing \texttt{muskip} variables}
+%
+% \begin{macro}{\muskip_show:N, \muskip_show:c}
+% Diagnostics.
+% \begin{macrocode}
+\cs_new_eq:NN \muskip_show:N \__kernel_register_show:N
+\cs_generate_variant:Nn \muskip_show:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\muskip_show:n}
+% Diagnostics. We don't use the \TeX{} primitive \tn{showthe} to show
+% muskip expressions: this gives a more unified output.
+% \begin{macrocode}
+\cs_new_protected:Npn \muskip_show:n
+ { \__kernel_msg_show_eval:Nn \muskip_eval:n }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\muskip_log:N, \muskip_log:c, \muskip_log:n}
+% Diagnostics. Redirect output of \cs{muskip_show:n} to the log.
+% \begin{macrocode}
+\cs_new_eq:NN \muskip_log:N \__kernel_register_log:N
+\cs_new_eq:NN \muskip_log:c \__kernel_register_log:c
+\cs_new_protected:Npn \muskip_log:n
+ { \__kernel_msg_log_eval:Nn \muskip_eval:n }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Constant muskips}
+%
+% \begin{macro}{\c_zero_muskip}
+% \begin{macro}{\c_max_muskip}
+% Constant muskips given by their value.
+% \begin{macrocode}
+\muskip_const:Nn \c_zero_muskip { 0 mu }
+\muskip_const:Nn \c_max_muskip { 16383.99999 mu }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Scratch muskips}
+%
+% \begin{variable}{\l_tmpa_muskip, \l_tmpb_muskip}
+% \begin{variable}{\g_tmpa_muskip, \g_tmpb_muskip}
+% We provide two local and two global scratch registers, maybe we
+% need more or less.
+% \begin{macrocode}
+\muskip_new:N \l_tmpa_muskip
+\muskip_new:N \l_tmpb_muskip
+\muskip_new:N \g_tmpa_muskip
+\muskip_new:N \g_tmpb_muskip
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3sort.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3sort.dtx
new file mode 100644
index 00000000000..81a025b4744
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3sort.dtx
@@ -0,0 +1,1097 @@
+% \iffalse
+%
+%% File l3sort.dtx
+%
+% Copyright (C) 2012-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3sort} module\\ Sorting functions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{Controlling sorting}
+%
+% \label{sec:l3sort:mech}
+%
+% \LaTeX3 comes with a facility to sort list variables (sequences,
+% token lists, or comma-lists) according to some user-defined
+% comparison. For instance,
+% \begin{verbatim}
+% \clist_set:Nn \l_foo_clist { 3 , 01 , -2 , 5 , +1 }
+% \clist_sort:Nn \l_foo_clist
+% {
+% \int_compare:nNnTF { #1 } > { #2 }
+% { \sort_return_swapped: }
+% { \sort_return_same: }
+% }
+% \end{verbatim}
+% results in \cs[no-index]{l_foo_clist} holding the values
+% |{ -2 , 01 , +1 , 3 , 5 }| sorted in non-decreasing order.
+%
+% The code defining the comparison should call
+% \cs{sort_return_swapped:} if the two items given as |#1|
+% and |#2| are not in the correct order, and otherwise it
+% should call \cs{sort_return_same:} to indicate that
+% the order of this pair of items should not be changed.
+%
+% For instance, a \meta{comparison code} consisting only
+% of \cs{sort_return_same:} with no test yields a trivial
+% sort: the final order is identical to the original order.
+% Conversely, using a \meta{comparison code} consisting only
+% of \cs{sort_return_swapped:} reverses the list (in a fairly
+% inefficient way).
+%
+% \begin{texnote}
+% The current implementation is limited to sorting approximately
+% $20000$ items ($40000$ in \LuaTeX{}), depending on what other
+% packages are loaded.
+%
+% Internally, the code from \pkg{l3sort} stores items in \tn{toks}
+% registers allocated locally. Thus, the \meta{comparison code}
+% should not call \tn{newtoks} or other commands that allocate new
+% \tn{toks} registers. On the other hand, altering the value of a
+% previously allocated \tn{toks} register is not a problem.
+% \end{texnote}
+%
+% \begin{function}[added = 2017-02-06]{\sort_return_same:, \sort_return_swapped:}
+% \begin{syntax}
+% \cs{seq_sort:Nn} \meta{seq~var}
+% ~~|{| \ldots{} \cs{sort_return_same:} or \cs{sort_return_swapped:} \ldots{} |}|
+% \end{syntax}
+% Indicates whether to keep the order or swap the order of two items
+% that are compared in the sorting code. Only one of the
+% \cs[no-index]{sort_return_\ldots{}} functions should be used by the
+% code, according to the results of some tests on the items |#1| and
+% |#2| to be compared.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3sort} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=sort>
+% \end{macrocode}
+%
+% \subsection{Variables}
+%
+% \begin{variable}{\g_@@_internal_seq, \g_@@_internal_tl}
+% Sorting happens in a group; the result is stored in those global
+% variables before being copied outside the group to the proper
+% places. For seq and tl this is more efficient than using \cs{use:e}
+% (or some \cs{exp_args:NNNe}) to smuggle the definition outside the
+% group since \TeX{} does not need to re-read tokens. For clist we
+% don't gain anything since the result is converted from seq to clist
+% anyways.
+% \begin{macrocode}
+\seq_new:N \g_@@_internal_seq
+\tl_new:N \g_@@_internal_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \l_@@_length_int, \l_@@_min_int, \l_@@_top_int, \l_@@_max_int,
+% \l_@@_true_max_int
+% }
+% The sequence has \cs{l_@@_length_int} items and is stored from
+% \cs{l_@@_min_int} to $\cs{l_@@_top_int}-1$. While reading the
+% sequence in memory, we check that \cs{l_@@_top_int} remains at most
+% \cs{l_@@_max_int}, precomputed by \cs{@@_compute_range:}. That
+% bound is such that the merge sort only uses \tn{toks} registers
+% less than \cs{l_@@_true_max_int}, namely those that have not been
+% allocated for use in other code: the user's comparison code could
+% alter these.
+% \begin{macrocode}
+\int_new:N \l_@@_length_int
+\int_new:N \l_@@_min_int
+\int_new:N \l_@@_top_int
+\int_new:N \l_@@_max_int
+\int_new:N \l_@@_true_max_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_block_int}
+% Merge sort is done in several passes. In each pass, blocks of size
+% \cs{l_@@_block_int} are merged in pairs. The block size starts
+% at $1$, and, for a length in the range $[2^k+1,2^{k+1}]$, reaches
+% $2^{k}$ in the last pass.
+% \begin{macrocode}
+\int_new:N \l_@@_block_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_begin_int}
+% \begin{variable}{\l_@@_end_int}
+% When merging two blocks, \cs{l_@@_begin_int} marks the lowest
+% index in the two blocks, and \cs{l_@@_end_int} marks the
+% highest index, plus $1$.
+% \begin{macrocode}
+\int_new:N \l_@@_begin_int
+\int_new:N \l_@@_end_int
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_A_int}
+% \begin{variable}{\l_@@_B_int}
+% \begin{variable}{\l_@@_C_int}
+% When merging two blocks (whose end-points are \texttt{beg}
+% and \texttt{end}), $A$ starts from the high end of the low
+% block, and decreases until reaching \texttt{beg}. The index
+% $B$ starts from the top of the range and marks the register
+% in which a sorted item should be put. Finally, $C$ points
+% to the copy of the high block in the interval of registers
+% starting at \cs{l_@@_length_int}, upwards. $C$ starts
+% from the upper limit of that range.
+% \begin{macrocode}
+\int_new:N \l_@@_A_int
+\int_new:N \l_@@_B_int
+\int_new:N \l_@@_C_int
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\s_@@_mark,\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Finding available \tn{toks} registers}
+%
+% \begin{macro}{\@@_shrink_range:}
+% \begin{macro}{\@@_shrink_range_loop:}
+% After \cs{@@_compute_range:} (defined below) determines that
+% \tn{toks} registers between \cs{l_@@_min_int} (included) and
+% \cs{l_@@_true_max_int} (excluded) have not yet been assigned,
+% \cs{@@_shrink_range:} computes \cs{l_@@_max_int} to reflect the need
+% for a buffer when merging blocks in the merge sort. Given
+% $2^{n}\leq A\leq 2^{n}+2^{n-1}$ registers we can sort $\lfloor
+% A/2\rfloor+2^{n-2}$ items while if we have $2^{n}+2^{n-1}\leq A\leq
+% 2^{n+1}$ registers we can sort $A-2^{n-1}$ items. We first find out
+% a power $2^{n}$ such that $2^{n}\leq A\leq 2^{n+1}$ by repeatedly
+% halving \cs{l_@@_block_int}, starting at $2^{15}$ or $2^{14}$ namely
+% half the total number of registers, then we use the formulas and set
+% \cs{l_@@_max_int}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_shrink_range:
+ {
+ \int_set:Nn \l_@@_A_int
+ { \l_@@_true_max_int - \l_@@_min_int + 1 }
+ \int_set:Nn \l_@@_block_int { \c_max_register_int / 2 }
+ \@@_shrink_range_loop:
+ \int_set:Nn \l_@@_max_int
+ {
+ \int_compare:nNnTF
+ { \l_@@_block_int * 3 / 2 } > \l_@@_A_int
+ {
+ \l_@@_min_int
+ + ( \l_@@_A_int - 1 ) / 2
+ + \l_@@_block_int / 4
+ - 1
+ }
+ { \l_@@_true_max_int - \l_@@_block_int / 2 }
+ }
+ }
+\cs_new_protected:Npn \@@_shrink_range_loop:
+ {
+ \if_int_compare:w \l_@@_A_int < \l_@@_block_int
+ \tex_divide:D \l_@@_block_int 2 \exp_stop_f:
+ \exp_after:wN \@@_shrink_range_loop:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_compute_range:, \@@_redefine_compute_range:}
+% \begin{variable}{\c_@@_max_length_int}
+% First find out what \tn{toks} have not yet been assigned. There are
+% many cases. In \LaTeXe{} with no package, available \tn{toks} range
+% from $\tn{count}15+1$ to \cs{c_max_register_int} included (this was
+% not altered despite the 2015 changes). When \tn{loctoks} is
+% defined, namely in plain (e)\TeX{}, or when the package \pkg{etex}
+% is loaded in \LaTeXe{}, redefine \cs{@@_compute_range:} to use the
+% range $\tn{count}265$ to $\tn{count}275-1$. The \pkg{elocalloc}
+% package also defines \tn{loctoks} but uses yet another number for
+% the upper bound, namely \cs{e@alloc@top} (minus one). We must check
+% for \tn{loctoks} every time a sorting function is called, as
+% \pkg{etex} or \pkg{elocalloc} could be loaded.
+%
+% In \ConTeXt{} MkIV the range is from
+% $|\c_syst_last_allocated_toks|+1$ to \cs{c_max_register_int}, and in
+% MkII it is from $|\lastallocatedtoks|+1$ to \cs{c_max_register_int}.
+% In all these cases, call \cs{@@_shrink_range:}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compute_range:
+ {
+ \int_set:Nn \l_@@_min_int { \tex_count:D 15 + 1 }
+ \int_set:Nn \l_@@_true_max_int { \c_max_register_int + 1 }
+ \@@_shrink_range:
+ \if_meaning:w \loctoks \tex_undefined:D \else:
+ \if_meaning:w \loctoks \scan_stop: \else:
+ \@@_redefine_compute_range:
+ \@@_compute_range:
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \@@_redefine_compute_range:
+ {
+ \cs_if_exist:cTF { ver@elocalloc.sty }
+ {
+ \cs_gset_protected:Npn \@@_compute_range:
+ {
+ \int_set:Nn \l_@@_min_int { \tex_count:D 265 }
+ \int_set_eq:NN \l_@@_true_max_int \e@alloc@top
+ \@@_shrink_range:
+ }
+ }
+ {
+ \cs_gset_protected:Npn \@@_compute_range:
+ {
+ \int_set:Nn \l_@@_min_int { \tex_count:D 265 }
+ \int_set:Nn \l_@@_true_max_int { \tex_count:D 275 }
+ \@@_shrink_range:
+ }
+ }
+ }
+\cs_if_exist:NT \loctoks { \@@_redefine_compute_range: }
+\tl_map_inline:nn { \lastallocatedtoks \c_syst_last_allocated_toks }
+ {
+ \cs_if_exist:NT #1
+ {
+ \cs_gset_protected:Npn \@@_compute_range:
+ {
+ \int_set:Nn \l_@@_min_int { #1 + 1 }
+ \int_set:Nn \l_@@_true_max_int { \c_max_register_int + 1 }
+ \@@_shrink_range:
+ }
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \subsection{Protected user commands}
+%
+% \begin{macro}{\@@_main:NNNn}
+% Sorting happens in three steps. First store items in \tn{toks}
+% registers ranging from \cs{l_@@_min_int} to $\cs{l_@@_top_int}-1$,
+% while checking that the list is not too long. If we reach the
+% maximum length, that's an error; exit the group. Secondly, sort the
+% array of \tn{toks} registers, using the user-defined sorting
+% function: \cs{@@_level:} calls \cs{@@_compare:nn} as needed.
+% Finally, unpack the \tn{toks} registers (now sorted) into the target
+% tl, or into \cs{g_@@_internal_seq} for seq and clist. This is done
+% by \cs{@@_seq:NNNNn} and \cs{@@_tl:NNn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_main:NNNn #1#2#3#4
+ {
+ \@@_disable_toksdef:
+ \@@_compute_range:
+ \int_set_eq:NN \l_@@_top_int \l_@@_min_int
+ #1 #3
+ {
+ \if_int_compare:w \l_@@_top_int = \l_@@_max_int
+ \@@_too_long_error:NNw #2 #3
+ \fi:
+ \tex_toks:D \l_@@_top_int {##1}
+ \int_incr:N \l_@@_top_int
+ }
+ \int_set:Nn \l_@@_length_int
+ { \l_@@_top_int - \l_@@_min_int }
+ \cs_set:Npn \@@_compare:nn ##1 ##2 {#4}
+ \int_set:Nn \l_@@_block_int { 1 }
+ \@@_level:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\tl_sort:Nn, \tl_sort:cn, \tl_gsort:Nn, \tl_gsort:cn}
+% \begin{macro}{\@@_tl:NNn}
+% \begin{macro}[EXP]{\@@_tl_toks:w}
+% Call the main sorting function then unpack \tn{toks} registers
+% outside the group into the target token list. The unpacking is done
+% by \cs{@@_tl_toks:w}; registers are numbered from \cs{l_@@_min_int}
+% to $\cs{l_@@_top_int}-1$. For expansion behaviour we need a couple
+% of primitives. The \cs{tl_gclear:N} reduces memory usage. The
+% \cs{prg_break_point:} is used by \cs{@@_main:NNNn} when the list is
+% too long.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_sort:Nn { \@@_tl:NNn \tl_set_eq:NN }
+\cs_generate_variant:Nn \tl_sort:Nn { c }
+\cs_new_protected:Npn \tl_gsort:Nn { \@@_tl:NNn \tl_gset_eq:NN }
+\cs_generate_variant:Nn \tl_gsort:Nn { c }
+\cs_new_protected:Npn \@@_tl:NNn #1#2#3
+ {
+ \group_begin:
+ \@@_main:NNNn \tl_map_inline:Nn \tl_map_break:n #2 {#3}
+ \__kernel_tl_gset:Nx \g_@@_internal_tl
+ { \@@_tl_toks:w \l_@@_min_int ; }
+ \group_end:
+ #1 #2 \g_@@_internal_tl
+ \tl_gclear:N \g_@@_internal_tl
+ \prg_break_point:
+ }
+\cs_new:Npn \@@_tl_toks:w #1 ;
+ {
+ \if_int_compare:w #1 < \l_@@_top_int
+ { \tex_the:D \tex_toks:D #1 }
+ \exp_after:wN \@@_tl_toks:w
+ \int_value:w \int_eval:n { #1 + 1 } \exp_after:wN ;
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_sort:Nn, \seq_sort:cn, \seq_gsort:Nn, \seq_gsort:cn}
+% \begin{macro}{\clist_sort:Nn, \clist_sort:cn, \clist_gsort:Nn, \clist_gsort:cn}
+% \begin{macro}{\@@_seq:NNNNn}
+% Use the same general framework for seq and clist. Apply the general
+% sorting code, then unpack \tn{toks} into \cs{g_@@_internal_seq}.
+% Outside the group copy or convert (for clist) the data to the target
+% variable. The \cs{seq_gclear:N} reduces memory usage. The
+% \cs{prg_break_point:} is used by \cs{@@_main:NNNn} when the list is
+% too long.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_sort:Nn
+ { \@@_seq:NNNNn \seq_map_inline:Nn \seq_map_break:n \seq_set_eq:NN }
+\cs_generate_variant:Nn \seq_sort:Nn { c }
+\cs_new_protected:Npn \seq_gsort:Nn
+ { \@@_seq:NNNNn \seq_map_inline:Nn \seq_map_break:n \seq_gset_eq:NN }
+\cs_generate_variant:Nn \seq_gsort:Nn { c }
+\cs_new_protected:Npn \clist_sort:Nn
+ {
+ \@@_seq:NNNNn \clist_map_inline:Nn \clist_map_break:n
+ \clist_set_from_seq:NN
+ }
+\cs_generate_variant:Nn \clist_sort:Nn { c }
+\cs_new_protected:Npn \clist_gsort:Nn
+ {
+ \@@_seq:NNNNn \clist_map_inline:Nn \clist_map_break:n
+ \clist_gset_from_seq:NN
+ }
+\cs_generate_variant:Nn \clist_gsort:Nn { c }
+\cs_new_protected:Npn \@@_seq:NNNNn #1#2#3#4#5
+ {
+ \group_begin:
+ \@@_main:NNNn #1 #2 #4 {#5}
+ \seq_gclear:N \g_@@_internal_seq
+ \int_step_inline:nnn
+ \l_@@_min_int { \l_@@_top_int - 1 }
+ {
+ \seq_gput_right:Ne \g_@@_internal_seq
+ { \tex_the:D \tex_toks:D ##1 }
+ }
+ \group_end:
+ #3 #4 \g_@@_internal_seq
+ \seq_gclear:N \g_@@_internal_seq
+ \prg_break_point:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Merge sort}
+%
+% \begin{macro}{\@@_level:}
+% This function is called once blocks of size \cs{l_@@_block_int}
+% (initially $1$) are each sorted. If the whole list fits in one
+% block, then we are done (this also takes care of the case of an
+% empty list or a list with one item). Otherwise, go through pairs
+% of blocks starting from $0$, then double the block size, and repeat.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_level:
+ {
+ \if_int_compare:w \l_@@_block_int < \l_@@_length_int
+ \l_@@_end_int \l_@@_min_int
+ \@@_merge_blocks:
+ \tex_advance:D \l_@@_block_int \l_@@_block_int
+ \exp_after:wN \@@_level:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_merge_blocks:}
+% This function is called to merge a pair of blocks, starting at
+% the last value of \cs{l_@@_end_int} (end-point of the previous
+% pair of blocks). If shifting by one block to the right we reach
+% the end of the list, then this pass has ended: the end of the
+% list is sorted already. Otherwise, store the result of that shift in $A$,
+% which indexes the first block starting from the top end.
+% Then locate the end-point (maximum) of the second block: shift
+% \texttt{end} upwards by one more block, but keeping it
+% $\leq\texttt{top}$. Copy this upper block of \tn{toks}
+% registers in registers above \texttt{length}, indexed by $C$:
+% this is covered by \cs{@@_copy_block:}. Once this is done we
+% are ready to do the actual merger using \cs{@@_merge_blocks_aux:},
+% after shifting $A$, $B$ and $C$ so that they point to the largest
+% index in their respective ranges rather than pointing just beyond
+% those ranges. Of course, once that pair of blocks is merged,
+% move on to the next pair.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_merge_blocks:
+ {
+ \l_@@_begin_int \l_@@_end_int
+ \tex_advance:D \l_@@_end_int \l_@@_block_int
+ \if_int_compare:w \l_@@_end_int < \l_@@_top_int
+ \l_@@_A_int \l_@@_end_int
+ \tex_advance:D \l_@@_end_int \l_@@_block_int
+ \if_int_compare:w \l_@@_end_int > \l_@@_top_int
+ \l_@@_end_int \l_@@_top_int
+ \fi:
+ \l_@@_B_int \l_@@_A_int
+ \l_@@_C_int \l_@@_top_int
+ \@@_copy_block:
+ \int_decr:N \l_@@_A_int
+ \int_decr:N \l_@@_B_int
+ \int_decr:N \l_@@_C_int
+ \exp_after:wN \@@_merge_blocks_aux:
+ \exp_after:wN \@@_merge_blocks:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_copy_block:}
+% We wish to store a copy of the \enquote{upper} block of
+% \tn{toks} registers, ranging between the initial value of
+% \cs{l_@@_B_int} (included) and \cs{l_@@_end_int}
+% (excluded) into a new range starting at the initial value
+% of \cs{l_@@_C_int}, namely \cs{l_@@_top_int}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_copy_block:
+ {
+ \tex_toks:D \l_@@_C_int \tex_toks:D \l_@@_B_int
+ \int_incr:N \l_@@_C_int
+ \int_incr:N \l_@@_B_int
+ \if_int_compare:w \l_@@_B_int = \l_@@_end_int
+ \use_i:nn
+ \fi:
+ \@@_copy_block:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_merge_blocks_aux:}
+% At this stage, the first block starts at \cs{l_@@_begin_int},
+% and ends at \cs{l_@@_A_int}, and the second block starts at
+% \cs{l_@@_top_int} and ends at \cs{l_@@_C_int}. The result
+% of the merger is stored at positions indexed by \cs{l_@@_B_int},
+% which starts at $\cs{l_@@_end_int}-1$ and decreases down to
+% \cs{l_@@_begin_int}, covering the full range of the two blocks.
+% In other words, we are building the merger starting with the
+% largest values.
+% The comparison function is defined to return either
+% \texttt{swapped} or \texttt{same}. Of course, this
+% means the arguments need to be given in the order they
+% appear originally in the list.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_merge_blocks_aux:
+ {
+ \exp_after:wN \@@_compare:nn \exp_after:wN
+ { \tex_the:D \tex_toks:D \exp_after:wN \l_@@_A_int \exp_after:wN }
+ \exp_after:wN { \tex_the:D \tex_toks:D \l_@@_C_int }
+ \prg_do_nothing:
+ \@@_return_mark:w
+ \@@_return_mark:w
+ \s_@@_mark
+ \@@_return_none_error:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\sort_return_same:, \sort_return_swapped:}
+% \begin{macro}{\@@_return_mark:w}
+% \begin{macro}{\@@_return_none_error:, \@@_return_two_error:}
+% Each comparison should call \cs{sort_return_same:} or
+% \cs{sort_return_swapped:} exactly once. If neither is called,
+% \cs{@@_return_none_error:} is called, since the \texttt{return_mark}
+% removes tokens until \cs{s_@@_mark}. If one is called, the
+% \texttt{return_mark} auxiliary removes everything except
+% \cs{@@_return_same:w} (or its \texttt{swapped} analogue) followed by
+% \cs{@@_return_none_error:}. Finally if two or more are called,
+% \cs{@@_return_two_error:} ends up before any \cs{@@_return_mark:w},
+% so that it produces an error.
+% \begin{macrocode}
+\cs_new_protected:Npn \sort_return_same:
+ #1 \@@_return_mark:w #2 \s_@@_mark
+ {
+ #1
+ #2
+ \@@_return_two_error:
+ \@@_return_mark:w
+ \s_@@_mark
+ \@@_return_same:w
+ }
+\cs_new_protected:Npn \sort_return_swapped:
+ #1 \@@_return_mark:w #2 \s_@@_mark
+ {
+ #1
+ #2
+ \@@_return_two_error:
+ \@@_return_mark:w
+ \s_@@_mark
+ \@@_return_swapped:w
+ }
+\cs_new_protected:Npn \@@_return_mark:w #1 \s_@@_mark { }
+\cs_new_protected:Npn \@@_return_none_error:
+ {
+ \msg_error:nnee { sort } { return-none }
+ { \tex_the:D \tex_toks:D \l_@@_A_int }
+ { \tex_the:D \tex_toks:D \l_@@_C_int }
+ \@@_return_same:w \@@_return_none_error:
+ }
+\cs_new_protected:Npn \@@_return_two_error:
+ {
+ \msg_error:nnee { sort } { return-two }
+ { \tex_the:D \tex_toks:D \l_@@_A_int }
+ { \tex_the:D \tex_toks:D \l_@@_C_int }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_return_same:w}
+% If the comparison function returns \texttt{same},
+% then the second argument fed to \cs{@@_compare:nn}
+% should remain to the right of the other one. Since
+% we build the merger starting from the right, we copy
+% that \tn{toks} register into the allotted range, then
+% shift the pointers $B$ and $C$, and go on to do one
+% more step in the merger, unless the second block has
+% been exhausted: then the remainder of the first block
+% is already in the correct registers and we are done
+% with merging those two blocks.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_return_same:w #1 \@@_return_none_error:
+ {
+ \tex_toks:D \l_@@_B_int \tex_toks:D \l_@@_C_int
+ \int_decr:N \l_@@_B_int
+ \int_decr:N \l_@@_C_int
+ \if_int_compare:w \l_@@_C_int < \l_@@_top_int
+ \use_i:nn
+ \fi:
+ \@@_merge_blocks_aux:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_return_swapped:w}
+% If the comparison function returns \texttt{swapped},
+% then the next item to add to the merger is the first
+% argument, contents of the \tn{toks} register $A$.
+% Then shift the pointers $A$ and $B$ to the left, and
+% go for one more step for the merger, unless the left
+% block was exhausted ($A$ goes below the threshold).
+% In that case, all remaining \tn{toks} registers in
+% the second block, indexed by $C$, are copied
+% to the merger by \cs{@@_merge_blocks_end:}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_return_swapped:w #1 \@@_return_none_error:
+ {
+ \tex_toks:D \l_@@_B_int \tex_toks:D \l_@@_A_int
+ \int_decr:N \l_@@_B_int
+ \int_decr:N \l_@@_A_int
+ \if_int_compare:w \l_@@_A_int < \l_@@_begin_int
+ \@@_merge_blocks_end: \use_i:nn
+ \fi:
+ \@@_merge_blocks_aux:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_merge_blocks_end:}
+% This function's task is to copy the \tn{toks} registers
+% in the block indexed by $C$ to the merger indexed by $B$.
+% The end can equally be detected by checking when $B$ reaches
+% the threshold \texttt{begin}, or when $C$ reaches
+% \texttt{top}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_merge_blocks_end:
+ {
+ \tex_toks:D \l_@@_B_int \tex_toks:D \l_@@_C_int
+ \int_decr:N \l_@@_B_int
+ \int_decr:N \l_@@_C_int
+ \if_int_compare:w \l_@@_B_int < \l_@@_begin_int
+ \use_i:nn
+ \fi:
+ \@@_merge_blocks_end:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Expandable sorting}
+%
+% Sorting expandably is very different from sorting and assigning to a
+% variable. Since tokens cannot be stored, they must remain in the
+% input stream, and be read through at every step. It is thus
+% necessarily much slower (at best $O(n^2\ln n)$) than non-expandable
+% sorting functions ($O(n\ln n)$).
+%
+% A prototypical version of expandable quicksort is as follows. If the
+% argument has no item, return nothing, otherwise partition, using the
+% first item as a pivot (argument |#4| of \cs{@@:nnNnn}). The
+% arguments of \cs{@@:nnNnn} are 1.~items less than |#4|, 2.~items
+% greater or equal to |#4|, 3.~comparison, 4.~pivot, 5.~next item to
+% test. If |#5| is the tail of the list, call \cs{tl_sort:nN} on |#1|
+% and on |#2|, placing |#4| in between; |\use:ff| expands the parts to
+% make \cs{tl_sort:nN} \texttt{f}-expandable. Otherwise, compare |#4|
+% and |#5| using |#3|. If they are ordered, place |#5| amongst the
+% \enquote{greater} items, otherwise amongst the \enquote{lesser} items,
+% and continue partitioning.
+% \begin{verbatim}
+% \cs_new:Npn \tl_sort:nN #1#2
+% {
+% \tl_if_blank:nF {#1}
+% {
+% \__sort:nnNnn { } { } #2
+% #1 \q__sort_recursion_tail \q__sort_recursion_stop
+% }
+% }
+% \cs_new:Npn \__sort:nnNnn #1#2#3#4#5
+% {
+% \quark_if_recursion_tail_stop_do:nn {#5}
+% { \use:ff { \tl_sort:nN {#1} #3 {#4} } { \tl_sort:nN {#2} #3 } }
+% #3 {#4} {#5}
+% { \__sort:nnNnn {#1} { #2 {#5} } #3 {#4} }
+% { \__sort:nnNnn { #1 {#5} } {#2} #3 {#4} }
+% }
+% \cs_generate_variant:Nn \use:nn { ff }
+% \end{verbatim}
+% There are quite a few optimizations available here: the code below is
+% less legible, but more than twice as fast.
+%
+% In the simple version of the code, \cs{@@:nnNnn} is called
+% \(O(n\ln n)\) times on average (the number of comparisons required by
+% the quicksort algorithm). Hence most of our focus is on
+% optimizing that function.
+%
+% The first speed up is to avoid testing for the end of the list at
+% every call to \cs{@@:nnNnn}. For this, the list is prepared by
+% changing each \meta{item} of the original token list into
+% \meta{command} \Arg{item}, just like sequences are stored. We arrange
+% things such that the \meta{command} is the \meta{conditional} provided
+% by the user: the loop over the \meta{prepared tokens} then looks like
+% \begin{quote}
+% \ttfamily
+% \cs{cs_new:Npn}~\cs{@@_loop:wNn}~\ldots{}~|#6#7|\\
+% ~~|{|\\
+% ~~~~|#6|~\Arg{pivot}~|{#7}|~\meta{loop big}~\meta{loop small}\\
+% ~~~~~~\meta{extra arguments}\\
+% ~~|}|\\
+% \cs{@@_loop:wNn}~\ldots{}~\meta{prepared tokens}\\
+% ~~\meta{end-loop}~|{}|~\cs{s_@@_stop}
+% \end{quote}
+% In this example, which matches the structure of
+% \cs{@@_quick_split_i:NnnnnNn} and a few other functions below, the
+% \cs{@@_loop:wNn} auxiliary normally receives the user's
+% \meta{conditional} as~|#6| and an \meta{item} as~|#7|. This is
+% compared to the \meta{pivot} (the argument~|#5|, not shown here), and
+% the \meta{conditional} leaves the \meta{loop big} or \meta{loop small}
+% auxiliary, which both have the same form as \cs{@@_loop:wNn},
+% receiving the next pair \meta{conditional} \Arg{item} as |#6|
+% and~|#7|. At the end, |#6| is the \meta{end-loop} function, which
+% terminates the loop.
+%
+% The second speed up is to minimize the duplicated tokens between the
+% \texttt{true} and \texttt{false} branches of the conditional. For
+% this, we introduce two versions of \cs{@@:nnNnn}, which receive
+% the new item as~|#1| and place it either into the list~|#2| of items
+% less than the pivot~|#4| or into the list~|#3| of items greater or
+% equal to the pivot.
+% \begin{verbatim}
+% \cs_new:Npn \__sort_i:nnnnNn #1#2#3#4#5#6
+% {
+% #5 {#4} {#6} \__sort_ii:nnnnNn \__sort_i:nnnnNn
+% {#6} { #2 {#1} } {#3} {#4}
+% }
+% \cs_new:Npn \__sort_ii:nnnnNn #1#2#3#4#5#6
+% {
+% #5 {#4} {#6} \__sort_ii:nnnnNn \__sort_i:nnnnNn
+% {#6} {#2} { #3 {#1} } {#4}
+% }
+% \end{verbatim}
+% Note that the two functions have the form of \cs{@@_loop:wNn} above,
+% receiving as~|#5| the conditional or a function to end the loop. In
+% fact, the lists~|#2| and~|#3| must be made of pairs \meta{conditional}
+% \Arg{item}, so we have to replace~|{#6}| above by |{|~|#5|~|{#6}|~|}|,
+% and |{#1}|~by~|#1|. The actual functions have one more argument, so
+% all argument numbers are shifted compared to this code.
+%
+% The third speed up is to avoid |\use:ff| using a continuation-passing
+% style: \cs{@@_quick_split:NnNn} expects a list followed by
+% \cs{s_@@_mark} \Arg{code}, and expands to \meta{code} \meta{sorted list}.
+% Sorting the two parts of the list around the pivot is done with
+% \begin{quote}
+% \ttfamily
+% \cs{@@_quick_split:NnNn} |#2| \ldots{} \cs{s_@@_mark}\\
+% ~~|{|\\
+% ~~~~\cs{@@_quick_split:NnNn} |#1| \ldots{} \cs{s_@@_mark} \Arg{code}\\
+% ~~~~\Arg{pivot}\\
+% ~~|}|
+% \end{quote}
+% Items which are larger than the \meta{pivot} are sorted, then placed
+% after code that sorts the smaller items, and after the (braced)
+% \meta{pivot}.
+%
+% The fourth speed up is avoid the recursive call to \cs{tl_sort:nN}
+% with an empty first argument. For this, we introduce functions
+% similar to the \cs{@@_i:nnnnNn} of the last example, but aware of
+% whether the list of \meta{conditional} \Arg{item} read so far that are
+% less than the pivot, and the list of those greater or equal, are empty
+% or not: see \cs{@@_quick_split:NnNn} and functions defined below.
+% Knowing whether the lists are empty or not is useless if we do not use
+% distinct ending codes as appropriate. The splitting auxiliaries
+% communicate to the \meta{end-loop} function (that is initially placed
+% after the ``prepared'' list) by placing a specific ending function,
+% ignored when looping, but useful at the end. In fact, the
+% \meta{end-loop} function does nothing but place the appropriate ending
+% function in front of all its arguments. The ending functions take
+% care of sorting non-empty sublists, placing the pivot in between, and
+% the continuation before.
+%
+% The final change in fact slows down the code a little, but is required
+% to avoid memory issues: schematically, when \TeX{} encounters
+% \begin{verbatim}
+% \use:n { \use:n { \use:n { ... } ... } ... }
+% \end{verbatim}
+% the argument of the first \cs{use:n} is not completely read by the
+% second \cs{use:n}, hence must remain in memory; then the argument of
+% the second \cs{use:n} is not completely read when grabbing the
+% argument of the third \cs{use:n}, hence must remain in memory, and so
+% on. The memory consumption grows quadratically with the number of
+% nested \cs{use:n}. In practice, this means that we must read
+% everything until a trailing \cs{s_@@_stop} once in a while, otherwise
+% sorting lists of more than a few thousand items would exhaust a
+% typical \TeX{}'s memory.
+%
+% \begin{macro}[EXP]{\tl_sort:nN}
+% \begin{macro}[EXP]
+% {
+% \@@_quick_prepare:Nnnn,
+% \@@_quick_prepare_end:NNNnw,
+% \@@_quick_cleanup:w
+% }
+% The code within the \cs{exp_not:f} sorts the list, leaving in most
+% cases a leading \cs{exp_not:f}, which stops the expansion, letting
+% the result be return within \cs{exp_not:n}. We filter out the case
+% of a list with no item, which would otherwise cause problems. Then
+% prepare the token list~|#1| by inserting the conditional~|#2| before
+% each item. The \texttt{prepare} auxiliary receives the conditional
+% as~|#1|, the prepared token list so far as~|#2|, the next prepared
+% item as~|#3|, and the item after that as~|#4|. The loop ends
+% when~|#4| contains \cs{prg_break_point:}, then the
+% \texttt{prepare_end} auxiliary finds the prepared token list
+% as~|#4|. The scene is then set up for \cs{@@_quick_split:NnNn},
+% which sorts the prepared list and perform the post action placed
+% after \cs{s_@@_mark}, namely removing the trailing \cs{s_@@_stop} and
+% \cs{s_@@_stop} and leaving \cs{exp_stop_f:} to stop
+% \texttt{f}-expansion.
+% \begin{macrocode}
+\cs_new:Npn \tl_sort:nN #1#2
+ {
+ \exp_not:f
+ {
+ \tl_if_blank:nF {#1}
+ {
+ \@@_quick_prepare:Nnnn #2 { } { }
+ #1
+ { \prg_break_point: \@@_quick_prepare_end:NNNnw }
+ \s_@@_stop
+ }
+ }
+ }
+\cs_new:Npn \@@_quick_prepare:Nnnn #1#2#3#4
+ {
+ \prg_break: #4 \prg_break_point:
+ \@@_quick_prepare:Nnnn #1 { #2 #3 } { #1 {#4} }
+ }
+\cs_new:Npn \@@_quick_prepare_end:NNNnw #1#2#3#4#5 \s_@@_stop
+ {
+ \@@_quick_split:NnNn #4 \@@_quick_end:nnTFNn { }
+ \s_@@_mark { \@@_quick_cleanup:w \exp_stop_f: }
+ \s_@@_mark \s_@@_stop
+ }
+\cs_new:Npn \@@_quick_cleanup:w #1 \s_@@_mark \s_@@_stop {#1}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_quick_split:NnNn,
+% \@@_quick_only_i:NnnnnNn,
+% \@@_quick_only_ii:NnnnnNn,
+% \@@_quick_split_i:NnnnnNn,
+% \@@_quick_split_ii:NnnnnNn
+% }
+% The \texttt{only_i}, \texttt{only_ii}, \texttt{split_i} and
+% \texttt{split_ii} auxiliaries receive a useless first argument, the
+% new item~|#2| (that they append to either one of the next two
+% arguments), the list~|#3| of items less than the pivot, bigger
+% items~|#4|, the pivot~|#5|, a \meta{function}~|#6|, and an
+% item~|#7|. The \meta{function} is the user's \meta{conditional}
+% except at the end of the list where it is
+% \cs{@@_quick_end:nnTFNn}. The comparison is applied to the
+% \meta{pivot} and the \meta{item}, and calls the \texttt{only_i} or
+% \texttt{split_i} auxiliaries if the \meta{item} is smaller, and the
+% \texttt{only_ii} or \texttt{split_ii} auxiliaries otherwise. In
+% both cases, the next auxiliary goes to work right away, with no
+% intermediate expansion that would slow down operations. Note that
+% the argument~|#2| left for the next call has the form
+% \meta{conditional} \Arg{item}, so that the lists~|#3| and~|#4| keep
+% the right form to be fed to the next sorting function.
+% The \texttt{split} auxiliary differs from these in that it is
+% missing three of the arguments, which would be empty, and its first
+% argument is always the user's \meta{conditional} rather than an
+% ending function.
+% \begin{macrocode}
+\cs_new:Npn \@@_quick_split:NnNn #1#2#3#4
+ {
+ #3 {#2} {#4} \@@_quick_only_ii:NnnnnNn
+ \@@_quick_only_i:NnnnnNn
+ \@@_quick_single_end:nnnwnw
+ { #3 {#4} } { } { } {#2}
+ }
+\cs_new:Npn \@@_quick_only_i:NnnnnNn #1#2#3#4#5#6#7
+ {
+ #6 {#5} {#7} \@@_quick_split_ii:NnnnnNn
+ \@@_quick_only_i:NnnnnNn
+ \@@_quick_only_i_end:nnnwnw
+ { #6 {#7} } { #3 #2 } { } {#5}
+ }
+\cs_new:Npn \@@_quick_only_ii:NnnnnNn #1#2#3#4#5#6#7
+ {
+ #6 {#5} {#7} \@@_quick_only_ii:NnnnnNn
+ \@@_quick_split_i:NnnnnNn
+ \@@_quick_only_ii_end:nnnwnw
+ { #6 {#7} } { } { #4 #2 } {#5}
+ }
+\cs_new:Npn \@@_quick_split_i:NnnnnNn #1#2#3#4#5#6#7
+ {
+ #6 {#5} {#7} \@@_quick_split_ii:NnnnnNn
+ \@@_quick_split_i:NnnnnNn
+ \@@_quick_split_end:nnnwnw
+ { #6 {#7} } { #3 #2 } {#4} {#5}
+ }
+\cs_new:Npn \@@_quick_split_ii:NnnnnNn #1#2#3#4#5#6#7
+ {
+ #6 {#5} {#7} \@@_quick_split_ii:NnnnnNn
+ \@@_quick_split_i:NnnnnNn
+ \@@_quick_split_end:nnnwnw
+ { #6 {#7} } {#3} { #4 #2 } {#5}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_quick_end:nnTFNn,
+% \@@_quick_single_end:nnnwnw,
+% \@@_quick_only_i_end:nnnwnw,
+% \@@_quick_only_ii_end:nnnwnw,
+% \@@_quick_split_end:nnnwnw,
+% }
+% The \cs{@@_quick_end:nnTFNn} appears instead of the user's
+% conditional, and receives as its arguments the pivot~|#1|, a fake
+% item~|#2|, a \texttt{true} and a \texttt{false} branches |#3|
+% and~|#4|, followed by an ending function~|#5| (one of the four
+% auxiliaries here) and another copy~|#6| of the fake item. All those
+% are discarded except the function~|#5|. This function receives
+% lists~|#1| and~|#2| of items less than or greater than the
+% pivot~|#3|, then a continuation code~|#5| just after \cs{s_@@_mark}.
+% To avoid a memory problem described earlier, all of the ending
+% functions read~|#6| until \cs{s_@@_stop} and place~|#6| back into the
+% input stream. When the lists |#1| and~|#2| are empty, the
+% \texttt{single} auxiliary simply places the continuation~|#5| before
+% the pivot~|{#3}|. When |#2|~is empty, |#1|~is sorted and placed
+% before the pivot~|{#3}|, taking care to feed the continuation~|#5|
+% as a continuation for the function sorting~|#1|. When |#1|~is
+% empty, |#2|~is sorted, and the continuation argument is used to
+% place the continuation~|#5| and the pivot~|{#3}| before the sorted
+% result. Finally, when both lists are non-empty, items larger than
+% the pivot are sorted, then items less than the pivot, and the
+% continuations are done in such a way to place the pivot in between.
+% \begin{macrocode}
+\cs_new:Npn \@@_quick_end:nnTFNn #1#2#3#4#5#6 {#5}
+\cs_new:Npn \@@_quick_single_end:nnnwnw #1#2#3#4 \s_@@_mark #5#6 \s_@@_stop
+ { #5 {#3} #6 \s_@@_stop }
+\cs_new:Npn \@@_quick_only_i_end:nnnwnw #1#2#3#4 \s_@@_mark #5#6 \s_@@_stop
+ {
+ \@@_quick_split:NnNn #1
+ \@@_quick_end:nnTFNn { } \s_@@_mark {#5}
+ {#3}
+ #6 \s_@@_stop
+ }
+\cs_new:Npn \@@_quick_only_ii_end:nnnwnw #1#2#3#4 \s_@@_mark #5#6 \s_@@_stop
+ {
+ \@@_quick_split:NnNn #2
+ \@@_quick_end:nnTFNn { } \s_@@_mark { #5 {#3} }
+ #6 \s_@@_stop
+ }
+\cs_new:Npn \@@_quick_split_end:nnnwnw #1#2#3#4 \s_@@_mark #5#6 \s_@@_stop
+ {
+ \@@_quick_split:NnNn #2 \@@_quick_end:nnTFNn { } \s_@@_mark
+ {
+ \@@_quick_split:NnNn #1
+ \@@_quick_end:nnTFNn { } \s_@@_mark {#5}
+ {#3}
+ }
+ #6 \s_@@_stop
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+% \begin{macro}{\@@_error:}
+% Bailing out of the sorting code is a bit tricky. It may not be safe
+% to use a delimited argument, so instead we redefine many
+% \pkg{l3sort} commands to be trivial, with \cs{@@_level:} jumping to
+% the break point. This error recovery won't work in a group.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_error:
+ {
+ \cs_set_eq:NN \@@_merge_blocks_aux: \prg_do_nothing:
+ \cs_set_eq:NN \@@_merge_blocks: \prg_do_nothing:
+ \cs_set_protected:Npn \@@_level: { \group_end: \prg_break: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_disable_toksdef:, \@@_disabled_toksdef:n}
+% While sorting, \tn{toksdef} is locally disabled to prevent users
+% from using \tn{newtoks} or similar commands in their comparison
+% code: the \tn{toks} registers that would be assigned are in use by
+% \pkg{l3sort}. In format mode, none of this is needed since there is
+% no \tn{toks} allocator.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_disable_toksdef:
+ { \cs_set_eq:NN \toksdef \@@_disabled_toksdef:n }
+\cs_new_protected:Npn \@@_disabled_toksdef:n #1
+ {
+ \msg_error:nne { sort } { toksdef }
+ { \token_to_str:N #1 }
+ \@@_error:
+ \tex_toksdef:D #1
+ }
+\msg_new:nnnn { sort } { toksdef }
+ { Allocation~of~\iow_char:N\\toks~registers~impossible~while~sorting. }
+ {
+ The~comparison~code~used~for~sorting~a~list~has~attempted~to~
+ define~#1~as~a~new~\iow_char:N\\toks~register~using~
+ \iow_char:N\\newtoks~
+ or~a~similar~command.~The~list~will~not~be~sorted.
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_too_long_error:NNw}
+% When there are too many items in a sequence, this is an error, and
+% we clean up properly the mapping over items in the list: break using
+% the type-specific breaking function |#1|.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_too_long_error:NNw #1#2 \fi:
+ {
+ \fi:
+ \msg_error:nneee { sort } { too-large }
+ { \token_to_str:N #2 }
+ { \int_eval:n { \l_@@_true_max_int - \l_@@_min_int } }
+ { \int_eval:n { \l_@@_top_int - \l_@@_min_int } }
+ #1 \@@_error:
+ }
+\msg_new:nnnn { sort } { too-large }
+ { The~list~#1~is~too~long~to~be~sorted~by~TeX. }
+ {
+ TeX~has~#2~toks~registers~still~available:~
+ this~only~allows~to~sort~with~up~to~#3~
+ items.~The~list~will~not~be~sorted.
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+\msg_new:nnnn { sort } { return-none }
+ { The~comparison~code~did~not~return. }
+ {
+ When~sorting~a~list,~the~code~to~compare~items~#1~and~#2~
+ did~not~call~
+ \iow_char:N\\sort_return_same: ~nor~
+ \iow_char:N\\sort_return_swapped: .~
+ Exactly~one~of~these~should~be~called.
+ }
+\msg_new:nnnn { sort } { return-two }
+ { The~comparison~code~returned~multiple~times. }
+ {
+ When~sorting~a~list,~the~code~to~compare~items~#1~and~#2~called~
+ \iow_char:N\\sort_return_same: ~or~
+ \iow_char:N\\sort_return_swapped: ~multiple~times.~
+ Exactly~one~of~these~should~be~called.
+ }
+\prop_gput:Nnn \g_msg_module_name_prop { sort } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { sort } { }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3str-convert.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3str-convert.dtx
new file mode 100644
index 00000000000..75dbdd095b8
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3str-convert.dtx
@@ -0,0 +1,3566 @@
+% \iffalse meta-comment
+%
+%% File: l3str-convert.dtx
+%
+% Copyright (C) 2013-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+%
+% \title{^^A
+% The \pkg{l3str-convert} module\\ String encoding conversions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{Encoding and escaping schemes}
+%
+% Traditionally, string encodings only specify how strings of characters
+% should be stored as bytes. However, the resulting lists of bytes are
+% often to be used in contexts where only a restricted subset of bytes
+% are permitted (\emph{e.g.}, \textsc{pdf} string objects,
+% \textsc{url}s). Hence, storing a string of characters is done in two
+% steps.
+% \begin{itemize}
+% \item The code points (\enquote{character codes}) are expressed as
+% bytes following a given \enquote{encoding}. This can be
+% \textsc{utf-16}, \textsc{iso 8859-1}, \emph{etc.} See
+% Table~\ref{tab:encodings} for a list of supported
+% encodings.\footnote{Encodings and escapings will be added as they
+% are requested.}
+% \item Bytes are translated to \TeX{} tokens through a given
+% \enquote{escaping}. Those are defined for the most part by the
+% \texttt{pdf} file format. See Table~\ref{tab:escapings} for a
+% list of escaping methods supported.\footnotemark[\csname c@footnote\endcsname]
+% \end{itemize}
+%
+% \begin{table}\centering
+% \caption{\label{tab:encodings}Supported encodings.
+% Non-alphanumeric characters are ignored,
+% and capital letters are lower-cased
+% before searching for the encoding in this list.}
+% \begin{tabular}{cc}
+% \toprule
+% \meta{Encoding} & description \\
+% \midrule
+% \texttt{utf8} & \textsc{utf-8} \\
+% \texttt{utf16} & \textsc{utf-16}, with byte-order mark \\
+% \texttt{utf16be} & \textsc{utf-16}, big-endian \\
+% \texttt{utf16le} & \textsc{utf-16}, little-endian \\
+% \texttt{utf32} & \textsc{utf-32}, with byte-order mark \\
+% \texttt{utf32be} & \textsc{utf-32}, big-endian \\
+% \texttt{utf32le} & \textsc{utf-32}, little-endian \\
+% \midrule
+% \texttt{iso88591}, \texttt{latin1} & \textsc{iso 8859-1} \\
+% \texttt{iso88592}, \texttt{latin2} & \textsc{iso 8859-2} \\
+% \texttt{iso88593}, \texttt{latin3} & \textsc{iso 8859-3} \\
+% \texttt{iso88594}, \texttt{latin4} & \textsc{iso 8859-4} \\
+% \texttt{iso88595} & \textsc{iso 8859-5} \\
+% \texttt{iso88596} & \textsc{iso 8859-6} \\
+% \texttt{iso88597} & \textsc{iso 8859-7} \\
+% \texttt{iso88598} & \textsc{iso 8859-8} \\
+% \texttt{iso88599}, \texttt{latin5} & \textsc{iso 8859-9} \\
+% \texttt{iso885910}, \texttt{latin6} & \textsc{iso 8859-10} \\
+% \texttt{iso885911} & \textsc{iso 8859-11} \\
+% \texttt{iso885913}, \texttt{latin7} & \textsc{iso 8859-13} \\
+% \texttt{iso885914}, \texttt{latin8} & \textsc{iso 8859-14} \\
+% \texttt{iso885915}, \texttt{latin9} & \textsc{iso 8859-15} \\
+% \texttt{iso885916}, \texttt{latin10} & \textsc{iso 8859-16} \\
+% \midrule
+% \texttt{clist} & comma-list of integers \\
+% \meta{empty} & native (Unicode) string \\
+% \texttt{default} & like \texttt{utf8} with 8-bit engines,
+% and like native with unicode-engines \\
+% \bottomrule
+% \end{tabular}
+% \end{table}
+%
+% \begin{table}\centering
+% \caption{\label{tab:escapings}Supported escapings.
+% Non-alphanumeric characters are ignored,
+% and capital letters are lower-cased
+% before searching for the escaping in this list.}
+% \begin{tabular}{cc}
+% \toprule
+% \meta{Escaping} & description \\
+% \midrule
+% \texttt{bytes}, or empty
+% & arbitrary bytes \\
+% \texttt{hex}, \texttt{hexadecimal}
+% & byte $=$ two hexadecimal digits \\
+% \texttt{name}
+% & see \tn{pdfescapename} \\
+% \texttt{string}
+% & see \tn{pdfescapestring} \\
+% \texttt{url}
+% & encoding used in \textsc{url}s \\
+% \bottomrule
+% \end{tabular}
+% \end{table}
+%
+% \section{Conversion functions}
+%
+% \begin{function}{\str_set_convert:Nnnn, \str_gset_convert:Nnnn}
+% \begin{syntax}
+% \cs{str_set_convert:Nnnn} \meta{str~var} \Arg{string} \Arg{name~1} \Arg{name~2}
+% \end{syntax}
+% This function converts the \meta{string} from the encoding given by
+% \meta{name~1} to the encoding given by \meta{name~2}, and stores the
+% result in the \meta{str~var}. Each \meta{name} can have the form
+% \meta{encoding} or \meta{encoding}\texttt{/}\meta{escaping}, where
+% the possible values of \meta{encoding} and \meta{escaping} are given
+% in Tables~\ref{tab:encodings} and~\ref{tab:escapings}, respectively.
+% The default escaping is to input and output bytes directly. The
+% special case of an empty \meta{name} indicates the use of
+% \enquote{native} strings, 8-bit for \pdfTeX{}, and Unicode strings
+% for the other two engines.
+%
+% For example,
+% \begin{verbatim}
+% \str_set_convert:Nnnn \l_foo_str { Hello! } { } { utf16/hex }
+% \end{verbatim}
+% results in the variable \cs[no-index]{l_foo_str} holding the string
+% \texttt{FEFF00480065006C006C006F0021}. This is obtained by
+% converting each character in the (native) string \texttt{Hello!} to
+% the \textsc{utf-16} encoding, and expressing each byte as a pair of
+% hexadecimal digits. Note the presence of a (big-endian) byte order
+% mark \hexnum{FEFF}, which can be avoided by specifying the encoding
+% \texttt{utf16be/hex}.
+%
+% An error is raised if the \meta{string} is not valid according to
+% the \meta{escaping~1} and \meta{encoding~1}, or if it cannot be
+% reencoded in the \meta{encoding~2} and \meta{escaping~2} (for
+% instance, if a character does not exist in the \meta{encoding~2}).
+% Erroneous input is replaced by the Unicode replacement character
+% \hexnum{FFFD}, and characters which cannot be reencoded are replaced
+% by either the replacement character \hexnum{FFFD} if it exists in
+% the \meta{encoding~2}, or an encoding-specific replacement
+% character, or the question mark character.
+% \end{function}
+%
+% \begin{function}[TF]{\str_set_convert:Nnnn, \str_gset_convert:Nnnn}
+% \begin{syntax}
+% \cs{str_set_convert:NnnnTF} \meta{str~var} \Arg{string} \Arg{name~1} \Arg{name~2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% As \cs{str_set_convert:Nnnn}, converts the \meta{string} from the
+% encoding given by \meta{name~1} to the encoding given by
+% \meta{name~2}, and assigns the result to \meta{str~var}. Contrarily
+% to \cs{str_set_convert:Nnnn}, the conditional variant does not raise
+% errors in case the \meta{string} is not valid according to the
+% \meta{name~1} encoding, or cannot be expressed in the \meta{name~2}
+% encoding. Instead, the \meta{false code} is performed.
+% \end{function}
+%
+% \section{Conversion by expansion (for PDF contexts)}
+%
+% A small number of expandable functions are provided for use in PDF string/name
+% contexts. These \emph{assume UTF-8} and \emph{no escaping} in the input.
+%
+% \begin{function}[EXP]{\str_convert_pdfname:n}
+% \begin{syntax}
+% \cs{str_convert_pdfname:n} \meta{string}
+% \end{syntax}
+% As \cs{str_set_convert:Nnnn}, converts the \meta{string} on a byte-by-byte
+% basis with non-ASCII codepoints escaped using hashes.
+% \end{function}
+%
+% \section{Possibilities, and things to do}
+%
+% Encoding/escaping-related tasks.
+% \begin{itemize}
+% \item In \XeTeX{}/\LuaTeX{}, would it be better to use the
+% |^^^^....| approach to build a string from a given list of
+% character codes? Namely, within a group, assign |0-9a-f| and all
+% characters we want to category ``other'', then assign~|^| the
+% category superscript, and use \tn{scantokens}.
+% \item Change \cs{str_set_convert:Nnnn} to expand its last two
+% arguments.
+% \item Describe the internal format in the code comments. Refuse code
+% points in $[\hexnum{D800}, \hexnum{DFFF}]$ in the internal
+% representation?
+% \item Add documentation about each encoding and escaping method, and
+% add examples.
+% \item The \texttt{hex} unescaping should raise an error for
+% odd-token count strings.
+% \item Decide what bytes should be escaped in the \texttt{url}
+% escaping. Perhaps the characters |!'()*-./0123456789_| are safe,
+% and all other characters should be escaped?
+% \item Automate generation of 8-bit mapping files.
+% \item Change the framework for 8-bit encodings: for decoding from
+% 8-bit to Unicode, use $256$ integer registers; for encoding, use a
+% tree-box.
+% \item More encodings (see Heiko's \pkg{stringenc}). CESU?
+% \item More escapings: \textsc{ascii85}, shell escapes, lua escapes,
+% \emph{etc.}?
+% \end{itemize}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3str-convert} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=str>
+% \end{macrocode}
+%
+% \subsection{Helpers}
+%
+% \subsubsection{Variables and constants}
+%
+% \begin{macro}{\@@_tmp:w}
+% \begin{variable}{\l_@@_internal_tl}
+% Internal scratch space for some functions.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_tmp:w { }
+\tl_new:N \l_@@_internal_tl
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \begin{variable}{\g_@@_result_tl}
+% The \cs{g_@@_result_tl} variable is used to hold the result of
+% various internal string operations (mostly conversions) which are
+% typically performed in a group. The variable is global so that it
+% remains defined outside the group, to be assigned to a user-provided
+% variable.
+% \begin{macrocode}
+\tl_new:N \g_@@_result_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_replacement_char_int}
+% When converting, invalid bytes are replaced by the Unicode
+% replacement character \hexnum{FFFD}.
+% \begin{macrocode}
+\int_const:Nn \c_@@_replacement_char_int { "FFFD }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_max_byte_int}
+% The maximal byte number.
+% \begin{macrocode}
+\int_const:Nn \c_@@_max_byte_int { 255 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\s_@@}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\q_@@_nil}
+% Internal quarks.
+% \begin{macrocode}
+\quark_new:N \q_@@_nil
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_alias_prop}
+% To avoid needing one file per encoding/escaping alias, we keep track
+% of those in a property list.
+% \begin{macrocode}
+\prop_new:N \g_@@_alias_prop
+\prop_gput:Nnn \g_@@_alias_prop { latin1 } { iso88591 }
+\prop_gput:Nnn \g_@@_alias_prop { latin2 } { iso88592 }
+\prop_gput:Nnn \g_@@_alias_prop { latin3 } { iso88593 }
+\prop_gput:Nnn \g_@@_alias_prop { latin4 } { iso88594 }
+\prop_gput:Nnn \g_@@_alias_prop { latin5 } { iso88599 }
+\prop_gput:Nnn \g_@@_alias_prop { latin6 } { iso885910 }
+\prop_gput:Nnn \g_@@_alias_prop { latin7 } { iso885913 }
+\prop_gput:Nnn \g_@@_alias_prop { latin8 } { iso885914 }
+\prop_gput:Nnn \g_@@_alias_prop { latin9 } { iso885915 }
+\prop_gput:Nnn \g_@@_alias_prop { latin10 } { iso885916 }
+\prop_gput:Nnn \g_@@_alias_prop { utf16le } { utf16 }
+\prop_gput:Nnn \g_@@_alias_prop { utf16be } { utf16 }
+\prop_gput:Nnn \g_@@_alias_prop { utf32le } { utf32 }
+\prop_gput:Nnn \g_@@_alias_prop { utf32be } { utf32 }
+\prop_gput:Nnn \g_@@_alias_prop { hexadecimal } { hex }
+\bool_lazy_any:nTF
+ {
+ \sys_if_engine_luatex_p:
+ \sys_if_engine_xetex_p:
+ }
+ {
+ \prop_gput:Nnn \g_@@_alias_prop { default } { }
+ }
+ {
+ \prop_gput:Nnn \g_@@_alias_prop { default } { utf8 }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_error_bool}
+% In conversion functions with a built-in conditional, errors are not
+% reported directly to the user, but the information is collected in
+% this boolean, used at the end to decide on which branch of the
+% conditional to take.
+% \begin{macrocode}
+\bool_new:N \g_@@_error_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_byte_flag, \l_@@_error_flag}
+% Conversions from one \meta{encoding}/\meta{escaping} pair to another
+% are done within \texttt{e}-expanding assignments. Errors are
+% signalled by raising the relevant flag.
+% \begin{macrocode}
+\flag_new:N \l_@@_byte_flag
+\flag_new:N \l_@@_error_flag
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{String conditionals}
+%
+% \begin{macro}[EXP]{\@@_if_contains_char:NnT, \@@_if_contains_char:NnTF}
+% \begin{macro}[EXP]{\@@_if_contains_char:nnTF}
+% \begin{macro}[EXP]{\@@_if_contains_char_aux:nn,\@@_if_contains_char_auxi:nN}
+% \begin{macro}[EXP]{\@@_if_contains_char_true:}
+% \begin{syntax}
+% \cs{@@_if_contains_char:nnTF} \Arg{token list} \meta{char}
+% \end{syntax}
+% Expects the \meta{token list} to be an \meta{other string}: the
+% caller is responsible for ensuring that no (too-)special catcodes
+% remain.
+% Loop over the characters of the string, comparing character codes.
+% The loop is broken if character codes match. Otherwise we return
+% \enquote{false}.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_if_contains_char:Nn #1#2 { T , TF }
+ {
+ \exp_after:wN \@@_if_contains_char_aux:nn \exp_after:wN {#1} {#2}
+ { \prg_break:n { ? \fi: } }
+ \prg_break_point:
+ \prg_return_false:
+ }
+\cs_new:Npn \@@_if_contains_char_aux:nn #1#2
+ { \@@_if_contains_char_auxi:nN {#2} #1 }
+\prg_new_conditional:Npnn \@@_if_contains_char:nn #1#2 { TF }
+ {
+ \@@_if_contains_char_auxi:nN {#2} #1 { \prg_break:n { ? \fi: } }
+ \prg_break_point:
+ \prg_return_false:
+ }
+\cs_new:Npn \@@_if_contains_char_auxi:nN #1#2
+ {
+ \if_charcode:w #1 #2
+ \exp_after:wN \@@_if_contains_char_true:
+ \fi:
+ \@@_if_contains_char_auxi:nN {#1}
+ }
+\cs_new:Npn \@@_if_contains_char_true:
+ { \prg_break:n { \prg_return_true: \use_none:n } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_octal_use:NTF}
+% \begin{syntax}
+% \cs{@@_octal_use:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{token} is an octal digit, it is left in the input
+% stream, \emph{followed} by the \meta{true code}. Otherwise, the
+% \meta{false code} is left in the input stream.
+% \begin{texnote}
+% This function will fail if the escape character is an octal
+% digit. We are thus careful to set the escape character to a known
+% value before using it.
+% \end{texnote}
+% \TeX{} dutifully detects octal digits for us: if |#1| is an octal
+% digit, then the right-hand side of the comparison is |'1#1|, greater
+% than $1$. Otherwise, the right-hand side stops as |'1|, and the
+% conditional takes the \texttt{false} branch.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_octal_use:N #1 { TF }
+ {
+ \if_int_compare:w 1 < '1 \token_to_str:N #1 \exp_stop_f:
+ #1 \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_hexadecimal_use:NTF}
+% \TeX{} detects uppercase hexadecimal digits for us (see
+% \cs{@@_octal_use:NTF}), but not the lowercase letters, which we
+% need to detect and replace by their uppercase counterpart.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_hexadecimal_use:N #1 { TF }
+ {
+ \if_int_compare:w 1 < "1 \token_to_str:N #1 \exp_stop_f:
+ #1 \prg_return_true:
+ \else:
+ \if_case:w \int_eval:n { \exp_after:wN ` \token_to_str:N #1 - `a }
+ A
+ \or: B
+ \or: C
+ \or: D
+ \or: E
+ \or: F
+ \else:
+ \prg_return_false:
+ \exp_after:wN \use_none:n
+ \fi:
+ \prg_return_true:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Conversions}
+%
+% \subsubsection{Producing one byte or character}
+%
+% \begin{variable}{\c_@@_byte_0_tl, \c_@@_byte_1_tl, \c_@@_byte_255_tl}
+% \begin{variable}{\c_@@_byte_-1_tl}
+% For each integer $N$ in the range $[0,255]$, we create a constant
+% token list which holds three character tokens with category code
+% other: the character with character code $N$, followed by the
+% representation of $N$ as two hexadecimal digits. The value $-1$ is
+% given a default token list which ensures that later functions give
+% an empty result for the input $-1$.
+% \begin{macrocode}
+\group_begin:
+ \__kernel_tl_set:Nx \l_@@_internal_tl { \tl_to_str:n { 0123456789ABCDEF } }
+ \tl_map_inline:Nn \l_@@_internal_tl
+ {
+ \tl_map_inline:Nn \l_@@_internal_tl
+ {
+ \tl_const:ce { c_@@_byte_ \int_eval:n {"#1##1} _tl }
+ { \char_generate:nn { "#1##1 } { 12 } #1 ##1 }
+ }
+ }
+\group_end:
+\tl_const:cn { c_@@_byte_-1_tl } { { } \use_none:n { } }
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_output_byte:n}
+% \begin{macro}[EXP]{\@@_output_byte:w}
+% \begin{macro}[EXP]{\@@_output_hexadecimal:n}
+% \begin{macro}[EXP]{\@@_output_end:}
+% Those functions must be used carefully: feeding them a value outside
+% the range $[-1,255]$ will attempt to use the undefined token list
+% variable \cs{c_@@_byte_\meta{number}_tl}. Assuming that the
+% argument is in the right range, we expand the corresponding token
+% list, and pick either the byte (first token) or the hexadecimal
+% representations (second and third tokens). The value $-1$ produces
+% an empty result in both cases.
+% \begin{macrocode}
+\cs_new:Npn \@@_output_byte:n #1
+ { \@@_output_byte:w #1 \@@_output_end: }
+\cs_new:Npn \@@_output_byte:w
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \use_i:nnn
+ \cs:w c_@@_byte_ \int_eval:w
+ }
+\cs_new:Npn \@@_output_hexadecimal:n #1
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \use_none:n
+ \cs:w c_@@_byte_ \int_eval:n {#1} _tl \cs_end:
+ }
+\cs_new:Npn \@@_output_end:
+ { \scan_stop: _tl \cs_end: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_output_byte_pair_be:n}
+% \begin{macro}[rEXP]{\@@_output_byte_pair_le:n}
+% \begin{macro}[rEXP]{\@@_output_byte_pair:nnN}
+% Convert a number in the range $[0,65535]$ to a pair of bytes, either
+% big-endian or little-endian.
+% \begin{macrocode}
+\cs_new:Npn \@@_output_byte_pair_be:n #1
+ {
+ \exp_args:Nf \@@_output_byte_pair:nnN
+ { \int_div_truncate:nn { #1 } { "100 } } {#1} \use:nn
+ }
+\cs_new:Npn \@@_output_byte_pair_le:n #1
+ {
+ \exp_args:Nf \@@_output_byte_pair:nnN
+ { \int_div_truncate:nn { #1 } { "100 } } {#1} \use_ii_i:nn
+ }
+\cs_new:Npn \@@_output_byte_pair:nnN #1#2#3
+ {
+ #3
+ { \@@_output_byte:n { #1 } }
+ { \@@_output_byte:n { #2 - #1 * "100 } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Mapping functions for conversions}
+%
+% \begin{macro}{\@@_convert_gmap:N}
+% \begin{macro}[rEXP]{\@@_convert_gmap_loop:NN}
+% This maps the function |#1| over all characters in
+% \cs{g_@@_result_tl}, which should be a byte string in most cases,
+% sometimes a native string.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_convert_gmap:N #1
+ {
+ \__kernel_tl_gset:Nx \g_@@_result_tl
+ {
+ \exp_after:wN \@@_convert_gmap_loop:NN
+ \exp_after:wN #1
+ \g_@@_result_tl { ? \prg_break: }
+ \prg_break_point:
+ }
+ }
+\cs_new:Npn \@@_convert_gmap_loop:NN #1#2
+ {
+ \use_none:n #2
+ #1#2
+ \@@_convert_gmap_loop:NN #1
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_convert_gmap_internal:N}
+% \begin{macro}[rEXP]{\@@_convert_gmap_internal_loop:Nw}
+% This maps the function |#1| over all character codes in
+% \cs{g_@@_result_tl}, which must be in the internal representation.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_convert_gmap_internal:N #1
+ {
+ \__kernel_tl_gset:Nx \g_@@_result_tl
+ {
+ \exp_after:wN \@@_convert_gmap_internal_loop:Nww
+ \exp_after:wN #1
+ \g_@@_result_tl \s_@@ \s_@@_stop \prg_break: \s_@@
+ \prg_break_point:
+ }
+ }
+\cs_new:Npn \@@_convert_gmap_internal_loop:Nww #1 #2 \s_@@ #3 \s_@@
+ {
+ \@@_use_none_delimit_by_s_stop:w #3 \s_@@_stop
+ #1 {#3}
+ \@@_convert_gmap_internal_loop:Nww #1
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Error-reporting during conversion}
+%
+% \begin{macro}{\@@_if_flag_error:Nne}
+% \begin{macro}{\@@_if_flag_no_error:Nne}
+% When converting using the function \cs{str_set_convert:Nnnn}, errors
+% should be reported to the user after each step in the
+% conversion. Errors are signalled by raising some flag (typically
+% \texttt{@@_error}), so here we test that flag: if it is raised,
+% give the user an error, otherwise remove the arguments. On the other
+% hand, in the conditional functions \cs{str_set_convert:NnnnTF},
+% errors should be suppressed. This is done by changing
+% \cs{@@_if_flag_error:Nne} into \cs{@@_if_flag_no_error:Nne}
+% locally.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_if_flag_error:Nne #1
+ {
+ \flag_if_raised:NTF #1
+ { \msg_error:nne { str } }
+ { \use_none:nn }
+ }
+\cs_new_protected:Npn \@@_if_flag_no_error:Nne #1#2#3
+ { \flag_if_raised:NT #1 { \bool_gset_true:N \g_@@_error_bool } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_if_flag_times:NT}
+% At the end of each conversion step, we raise all relevant errors as
+% one error message, built on the fly. The height of each flag
+% indicates how many times a given error was encountered. This
+% function prints |#2| followed by the number of occurrences of an
+% error if it occurred, nothing otherwise.
+% \begin{macrocode}
+\cs_new:Npn \@@_if_flag_times:NT #1#2
+ { \flag_if_raised:NT #1 { #2~(x \flag_height:N #1 ) } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Framework for conversions}
+%
+% Most functions in this module expect to be working with
+% \enquote{native} strings. Strings can also be stored as bytes, in one
+% of many encodings, for instance \textsc{utf8}. The bytes themselves
+% can be expressed in various ways in terms of \TeX{} tokens, for
+% instance as pairs of hexadecimal digits. The questions of going from
+% arbitrary Unicode code points to bytes, and from bytes to tokens are
+% mostly independent.
+%
+% Conversions are done in four steps:
+% \begin{itemize}
+% \item \enquote{unescape} produces a string of bytes;
+% \item \enquote{decode} takes in a string of bytes, and converts it
+% to a list of Unicode characters in an internal representation,
+% with items of the form
+% \begin{quote}
+% \meta{bytes} \cs{s_@@} \meta{Unicode code point} \cs{s_@@}
+% \end{quote}
+% where we have collected the \meta{bytes} which combined to form
+% this particular Unicode character, and the \meta{Unicode code
+% point} is in the range $[0,\hexnum{10FFFF}]$.
+% \item \enquote{encode} encodes the internal list of code points as a
+% byte string in the new encoding;
+% \item \enquote{escape} escapes bytes as requested.
+% \end{itemize}
+% The process is modified in case one of the encoding is empty (or the
+% conversion function has been set equal to the empty encoding because
+% it was not found): then the unescape or escape step is ignored, and
+% the decode or encode steps work on tokens instead of bytes. Otherwise,
+% each step must ensure that it passes a correct byte string or internal
+% string to the next step.
+%
+% \begin{macro}{\str_set_convert:Nnnn, \str_gset_convert:Nnnn}
+% \begin{macro}[TF]{\str_set_convert:Nnnn, \str_gset_convert:Nnnn}
+% \begin{macro}{\@@_convert:nNNnnn}
+% The input string is stored in \cs{g_@@_result_tl}, then we:
+% unescape and decode; encode and escape; exit the group and store the
+% result in the user's variable. The various conversion functions all
+% act on \cs{g_@@_result_tl}. Errors are silenced for the conditional
+% functions by redefining \cs{@@_if_flag_error:Nne} locally.
+% \begin{macrocode}
+\cs_new_protected:Npn \str_set_convert:Nnnn
+ { \@@_convert:nNNnnn { } \tl_set_eq:NN }
+\cs_new_protected:Npn \str_gset_convert:Nnnn
+ { \@@_convert:nNNnnn { } \tl_gset_eq:NN }
+\prg_new_protected_conditional:Npnn
+ \str_set_convert:Nnnn #1#2#3#4 { T , F , TF }
+ {
+ \bool_gset_false:N \g_@@_error_bool
+ \@@_convert:nNNnnn
+ { \cs_set_eq:NN \@@_if_flag_error:Nne \@@_if_flag_no_error:Nne }
+ \tl_set_eq:NN #1 {#2} {#3} {#4}
+ \bool_if:NTF \g_@@_error_bool \prg_return_false: \prg_return_true:
+ }
+\prg_new_protected_conditional:Npnn
+ \str_gset_convert:Nnnn #1#2#3#4 { T , F , TF }
+ {
+ \bool_gset_false:N \g_@@_error_bool
+ \@@_convert:nNNnnn
+ { \cs_set_eq:NN \@@_if_flag_error:Nne \@@_if_flag_no_error:Nne }
+ \tl_gset_eq:NN #1 {#2} {#3} {#4}
+ \bool_if:NTF \g_@@_error_bool \prg_return_false: \prg_return_true:
+ }
+\cs_new_protected:Npn \@@_convert:nNNnnn #1#2#3#4#5#6
+ {
+ \group_begin:
+ #1
+ \__kernel_tl_gset:Nx \g_@@_result_tl { \__kernel_str_to_other_fast:n {#4} }
+ \exp_after:wN \@@_convert:wwwnn
+ \tl_to_str:n {#5} /// \s_@@_stop
+ { decode } { unescape }
+ \prg_do_nothing:
+ \@@_convert_decode_:
+ \exp_after:wN \@@_convert:wwwnn
+ \tl_to_str:n {#6} /// \s_@@_stop
+ { encode } { escape }
+ \use_ii_i:nn
+ \@@_convert_encode_:
+ \__kernel_tl_gset:Nx \g_@@_result_tl
+ { \tl_to_str:V \g_@@_result_tl }
+ \group_end:
+ #2 #3 \g_@@_result_tl
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_convert:wwwnn}
+% \begin{macro}{\@@_convert:NNnNN}
+% The task of \cs{@@_convert:wwwnn} is to split
+% \meta{encoding}/\meta{escaping} pairs into their components, |#1|
+% and |#2|. Calls to \cs{@@_convert:nnn} ensure that the
+% corresponding conversion functions are defined. The third auxiliary
+% does the main work.
+% \begin{itemize}
+% \item |#1| is the encoding conversion function;
+% \item |#2| is the escaping function;
+% \item |#3| is the escaping name for use in an error message;
+% \item |#4| is \cs{prg_do_nothing:} for unescaping/decoding, and
+% \cs{use_ii_i:nn} for encoding/escaping;
+% \item |#5| is the default encoding function (either
+% \enquote{decode} or \enquote{encode}), for which there should be
+% no escaping.
+% \end{itemize}
+% Let us ignore the native encoding for a second. In the
+% unescaping/decoding phase, we want to do |#2#1| in this order, and
+% in the encoding/escaping phase, the order should be reversed:
+% |#4#2#1| does exactly that. If one of the encodings is the default
+% (native), then the escaping should be ignored, with an error if any
+% was given, and only the encoding, |#1|, should be performed.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_convert:wwwnn
+ #1 / #2 // #3 \s_@@_stop #4#5
+ {
+ \@@_convert:nnn {enc} {#4} {#1}
+ \@@_convert:nnn {esc} {#5} {#2}
+ \exp_args:Ncc \@@_convert:NNnNN
+ { @@_convert_#4_#1: } { @@_convert_#5_#2: } {#2}
+ }
+\cs_new_protected:Npn \@@_convert:NNnNN #1#2#3#4#5
+ {
+ \if_meaning:w #1 #5
+ \tl_if_empty:nF {#3}
+ { \msg_error:nne { str } { native-escaping } {#3} }
+ #1
+ \else:
+ #4 #2 #1
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_convert:nnn}
+% \begin{macro}{\@@_convert:nnnn}
+% The arguments of \cs{@@_convert:nnn} are: \texttt{enc} or
+% \texttt{esc}, used to build filenames, the type of the conversion
+% (unescape, decode, encode, escape), and the encoding or escaping
+% name. If the function is already defined, no need to do anything.
+% Otherwise, filter out all non-alphanumerics in the name, and
+% lowercase it. Feed that, and the same three arguments, to
+% \cs{@@_convert:nnnn}. The task is then to make sure that the
+% conversion function |#3_#1| corresponding to the type |#3| and
+% filtered name |#1| is defined, then set our initial conversion
+% function |#3_#4| equal to that.
+%
+% How do we get the |#3_#1| conversion to be defined if it isn't?
+% Two main cases.
+%
+% First, if |#1| is a key in \cs{g_@@_alias_prop}, then the value
+% \cs{l_@@_internal_tl} tells us what file to load. Loading is
+% skipped if the file was already read, \emph{i.e.}, if the conversion
+% command based on \cs{l_@@_internal_tl} already exists. Otherwise,
+% try to load the file; if that fails, there is an error, use the
+% default empty name instead.
+%
+% Second, |#1| may be absent from the property list. The
+% \cs{cs_if_exist:cF} test is automatically false, and we search for a
+% file defining the encoding or escaping |#1| (this should allow
+% third-party \texttt{.def} files). If the file is not found, there is
+% an error, use the default empty name instead.
+%
+% In all cases, the conversion based on \cs{l_@@_internal_tl} is
+% defined, so we can set the |#3_#1| function equal to that. In some
+% cases (\emph{e.g.}, \texttt{utf16be}), the |#3_#1| function is
+% actually defined within the file we just loaded, and it is different
+% from the \cs{l_@@_internal_tl}-based function: we mustn't clobber
+% that different definition.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_convert:nnn #1#2#3
+ {
+ \cs_if_exist:cF { @@_convert_#2_#3: }
+ {
+ \exp_args:Ne \@@_convert:nnnn
+ { \@@_convert_lowercase_alphanum:n {#3} }
+ {#1} {#2} {#3}
+ }
+ }
+\cs_new_protected:Npn \@@_convert:nnnn #1#2#3#4
+ {
+ \cs_if_exist:cF { @@_convert_#3_#1: }
+ {
+ \prop_get:NnNF \g_@@_alias_prop {#1} \l_@@_internal_tl
+ { \tl_set:Nn \l_@@_internal_tl {#1} }
+ \cs_if_exist:cF { @@_convert_#3_ \l_@@_internal_tl : }
+ {
+ \file_if_exist:nTF { l3str-#2- \l_@@_internal_tl .def }
+ {
+ \group_begin:
+ \cctab_select:N \c_code_cctab
+ \file_input:n { l3str-#2- \l_@@_internal_tl .def }
+ \group_end:
+ }
+ {
+ \tl_clear:N \l_@@_internal_tl
+ \msg_error:nnee { str } { unknown-#2 } {#4} {#1}
+ }
+ }
+ \cs_if_exist:cF { @@_convert_#3_#1: }
+ {
+ \cs_gset_eq:cc { @@_convert_#3_#1: }
+ { @@_convert_#3_ \l_@@_internal_tl : }
+ }
+ }
+ \cs_gset_eq:cc { @@_convert_#3_#4: } { @@_convert_#3_#1: }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_convert_lowercase_alphanum:n}
+% \begin{macro}[rEXP]{\@@_convert_lowercase_alphanum_loop:N}
+% This function keeps only letters and digits, with upper case letters
+% converted to lower case.
+% \begin{macrocode}
+\cs_new:Npn \@@_convert_lowercase_alphanum:n #1
+ {
+ \exp_after:wN \@@_convert_lowercase_alphanum_loop:N
+ \tl_to_str:n {#1} { ? \prg_break: }
+ \prg_break_point:
+ }
+\cs_new:Npn \@@_convert_lowercase_alphanum_loop:N #1
+ {
+ \use_none:n #1
+ \if_int_compare:w `#1 > `Z \exp_stop_f:
+ \if_int_compare:w `#1 > `z \exp_stop_f: \else:
+ \if_int_compare:w `#1 < `a \exp_stop_f: \else:
+ #1
+ \fi:
+ \fi:
+ \else:
+ \if_int_compare:w `#1 < `A \exp_stop_f:
+ \if_int_compare:w 1 < 1#1 \exp_stop_f:
+ #1
+ \fi:
+ \else:
+ \@@_output_byte:n { `#1 + `a - `A }
+ \fi:
+ \fi:
+ \@@_convert_lowercase_alphanum_loop:N
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Byte unescape and escape}
+%
+% Strings of bytes may need to be stored in auxiliary files in safe
+% \enquote{escaping} formats. Each such escaping is only loaded as
+% needed. By default, on input any non-byte is filtered out, while the
+% output simply consists in letting bytes through.
+%
+% \begin{macro}[rEXP]{\@@_filter_bytes:n}
+% \begin{macro}[rEXP]{\@@_filter_bytes_aux:N}
+% In the case of 8-bit engines, every character is a byte. For
+% Unicode-aware engines, test the character code; non-bytes cause us
+% to raise the flag \cs{l_@@_byte_flag}. Spaces have already been given
+% the correct category code when this function is called.
+% \begin{macrocode}
+\bool_lazy_any:nTF
+ {
+ \sys_if_engine_luatex_p:
+ \sys_if_engine_xetex_p:
+ }
+ {
+ \cs_new:Npn \@@_filter_bytes:n #1
+ {
+ \@@_filter_bytes_aux:N #1
+ { ? \prg_break: }
+ \prg_break_point:
+ }
+ \cs_new:Npn \@@_filter_bytes_aux:N #1
+ {
+ \use_none:n #1
+ \if_int_compare:w `#1 < 256 \exp_stop_f:
+ #1
+ \else:
+ \flag_raise:N \l_@@_byte_flag
+ \fi:
+ \@@_filter_bytes_aux:N
+ }
+ }
+ { \cs_new_eq:NN \@@_filter_bytes:n \use:n }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_convert_unescape_:}
+% \begin{macro}{\@@_convert_unescape_bytes:}
+% The simplest unescaping method removes non-bytes from
+% \cs{g_@@_result_tl}.
+% \begin{macrocode}
+\bool_lazy_any:nTF
+ {
+ \sys_if_engine_luatex_p:
+ \sys_if_engine_xetex_p:
+ }
+ {
+ \cs_new_protected:Npn \@@_convert_unescape_:
+ {
+ \flag_clear:N \l_@@_byte_flag
+ \__kernel_tl_gset:Nx \g_@@_result_tl
+ { \exp_args:No \@@_filter_bytes:n \g_@@_result_tl }
+ \@@_if_flag_error:Nne \l_@@_byte_flag { non-byte } { bytes }
+ }
+ }
+ { \cs_new_protected:Npn \@@_convert_unescape_: { } }
+\cs_new_eq:NN \@@_convert_unescape_bytes: \@@_convert_unescape_:
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_convert_escape_:}
+% \begin{macro}{\@@_convert_escape_bytes:}
+% The simplest form of escape leaves the bytes from the previous step
+% of the conversion unchanged.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_convert_escape_: { }
+\cs_new_eq:NN \@@_convert_escape_bytes: \@@_convert_escape_:
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Native strings}
+%
+% \begin{macro}{\@@_convert_decode_:}
+% \begin{macro}[rEXP]{\@@_decode_native_char:N}
+% Convert each character to its character code, one at a time.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_convert_decode_:
+ { \@@_convert_gmap:N \@@_decode_native_char:N }
+\cs_new:Npn \@@_decode_native_char:N #1
+ { #1 \s_@@ \int_value:w `#1 \s_@@ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_convert_encode_:}
+% \begin{macro}[EXP]{\@@_encode_native_char:n}
+% The conversion from an internal string to native character tokens
+% basically maps \cs{char_generate:nn} through the code-points, but in
+% non-Unicode-aware engines we use a fall-back character |?| rather
+% than nothing when given a character code outside $[0,255]$. We
+% detect the presence of bad characters using a flag and only produce
+% a single error after the \texttt{e}-expanding assignment.
+% \begin{macrocode}
+\bool_lazy_any:nTF
+ {
+ \sys_if_engine_luatex_p:
+ \sys_if_engine_xetex_p:
+ }
+ {
+ \cs_new_protected:Npn \@@_convert_encode_:
+ { \@@_convert_gmap_internal:N \@@_encode_native_char:n }
+ \cs_new:Npn \@@_encode_native_char:n #1
+ { \char_generate:nn {#1} {12} }
+ }
+ {
+ \cs_new_protected:Npn \@@_convert_encode_:
+ {
+ \flag_clear:N \l_@@_error_flag
+ \@@_convert_gmap_internal:N \@@_encode_native_char:n
+ \@@_if_flag_error:Nne \l_@@_error_flag
+ { native-overflow } { }
+ }
+ \cs_new:Npn \@@_encode_native_char:n #1
+ {
+ \if_int_compare:w #1 > \c_@@_max_byte_int
+ \flag_raise:N \l_@@_error_flag
+ ?
+ \else:
+ \char_generate:nn {#1} {12}
+ \fi:
+ }
+ \msg_new:nnnn { str } { native-overflow }
+ { Character~code~too~large~for~this~engine. }
+ {
+ This~engine~only~support~8-bit~characters:~
+ valid~character~codes~are~in~the~range~[0,255].~
+ To~manipulate~arbitrary~Unicode,~use~LuaTeX~or~XeTeX.
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{\texttt{clist}}
+%
+% \begin{macro}{\@@_convert_decode_clist:}
+% \begin{macro}[rEXP]{\@@_decode_clist_char:n}
+% Convert each integer to the internal form. We first turn
+% \cs{g_@@_result_tl} into a clist variable, as this avoids problems
+% with leading or trailing commas.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_convert_decode_clist:
+ {
+ \clist_gset:No \g_@@_result_tl \g_@@_result_tl
+ \__kernel_tl_gset:Nx \g_@@_result_tl
+ {
+ \exp_args:No \clist_map_function:nN
+ \g_@@_result_tl \@@_decode_clist_char:n
+ }
+ }
+\cs_new:Npn \@@_decode_clist_char:n #1
+ { #1 \s_@@ \int_eval:n {#1} \s_@@ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_convert_encode_clist:}
+% \begin{macro}[rEXP]{\@@_encode_clist_char:n}
+% Convert the internal list of character codes to a comma-list of
+% character codes. The first line produces a comma-list with a
+% leading comma, removed in the next step (this also works in the
+% empty case, since \cs{tl_tail:N} does not trigger an error in this
+% case).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_convert_encode_clist:
+ {
+ \@@_convert_gmap_internal:N \@@_encode_clist_char:n
+ \__kernel_tl_gset:Nx \g_@@_result_tl { \tl_tail:N \g_@@_result_tl }
+ }
+\cs_new:Npn \@@_encode_clist_char:n #1 { , #1 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{8-bit encodings}
+%
+% It is not clear in what situations 8-bit encodings are used, hence it
+% is not clear what should be optimized. The current approach is
+% reasonably efficient to convert long strings, and it scales well when
+% using many different encodings.
+%
+% The data needed to support a given 8-bit encoding is stored in a file
+% that consists of a single function call
+% \begin{quote}\ttfamily
+% \cs{@@_declare_eight_bit_encoding:nnnn} \Arg{name} \Arg{modulo}
+% \Arg{mapping} \Arg{missing}
+% \end{quote}
+% This declares the encoding \meta{name} to map bytes to Unicode
+% characters according to the \meta{mapping}, and map those bytes which
+% are not mentioned in the \meta{mapping} either to the replacement
+% character (if they appear in \meta{missing}), or to themselves. The
+% \meta{mapping} argument is a token list of pairs \Arg{byte}
+% \Arg{Unicode} expressed in uppercase hexadecimal notation. The
+% \meta{missing} argument is a token list of \Arg{byte}. Every
+% \meta{byte} which does not appear in the \meta{mapping} nor the
+% \meta{missing} lists maps to itself in Unicode, so for instance the
+% \texttt{latin1} encoding has empty \meta{mapping} and \meta{missing}
+% lists. The \meta{modulo} is a (decimal) integer between $256$ and
+% $558$ inclusive, modulo which all Unicode code points supported by the
+% encodings must be different.
+%
+% We use two integer arrays per encoding. When decoding we only use the
+% \texttt{decode} integer array, with entry $n+1$ (offset needed because
+% integer array indices start at~$1$) equal to the Unicode code point
+% that corresponds to the $n$-th byte in the encoding under
+% consideration, or $-1$ if the given byte is invalid in this encoding.
+% When encoding we use both arrays: upon seeing a code point~$n$, we
+% look up the entry ($1$~plus) $n$ modulo some number $M$ in the
+% \texttt{encode} array, which tells us the byte that might encode the
+% given Unicode code point, then we check in the \texttt{decode} array
+% that indeed this byte encodes the Unicode code point we want. Here,
+% $M$ is an encoding-dependent integer between $256$ and $558$ (it turns
+% out), chosen so that among the Unicode code points that can be validly
+% represented in the given encoding, no pair of code points have the
+% same value modulo~$M$.
+%
+% \begin{macro}
+% {
+% \@@_declare_eight_bit_encoding:nnnn,
+% \@@_declare_eight_bit_aux:NNnnn,
+% \@@_declare_eight_bit_loop:Nnn,
+% \@@_declare_eight_bit_loop:Nn
+% }
+% Loop through both lists of bytes to fill in the \texttt{decode}
+% integer array, then fill the \texttt{encode} array accordingly.
+% For bytes that are invalid in the given encoding, store $-1$ in the
+% \texttt{decode} array.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_declare_eight_bit_encoding:nnnn #1
+ {
+ \tl_set:Nn \l_@@_internal_tl {#1}
+ \cs_new_protected:cpn { @@_convert_decode_#1: }
+ { \@@_convert_decode_eight_bit:n {#1} }
+ \cs_new_protected:cpn { @@_convert_encode_#1: }
+ { \@@_convert_encode_eight_bit:n {#1} }
+ \exp_args:Ncc \@@_declare_eight_bit_aux:NNnnn
+ { g_@@_decode_#1_intarray } { g_@@_encode_#1_intarray }
+ }
+\cs_new_protected:Npn \@@_declare_eight_bit_aux:NNnnn #1#2#3#4#5
+ {
+ \intarray_new:Nn #1 { 256 }
+ \int_step_inline:nnn { 0 } { 255 }
+ { \intarray_gset:Nnn #1 { 1 + ##1 } {##1} }
+ \@@_declare_eight_bit_loop:Nnn #1
+ #4 { \s_@@_stop \prg_break: } { }
+ \prg_break_point:
+ \@@_declare_eight_bit_loop:Nn #1
+ #5 { \s_@@_stop \prg_break: }
+ \prg_break_point:
+ \intarray_new:Nn #2 {#3}
+ \int_step_inline:nnn { 0 } { 255 }
+ {
+ \int_compare:nNnF { \intarray_item:Nn #1 { 1 + ##1 } } = { -1 }
+ {
+ \intarray_gset:Nnn #2
+ {
+ 1 +
+ \int_mod:nn { \intarray_item:Nn #1 { 1 + ##1 } }
+ { \intarray_count:N #2 }
+ }
+ {##1}
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_declare_eight_bit_loop:Nnn #1#2#3
+ {
+ \@@_use_none_delimit_by_s_stop:w #2 \s_@@_stop
+ \intarray_gset:Nnn #1 { 1 + "#2 } { "#3 }
+ \@@_declare_eight_bit_loop:Nnn #1
+ }
+\cs_new_protected:Npn \@@_declare_eight_bit_loop:Nn #1#2
+ {
+ \@@_use_none_delimit_by_s_stop:w #2 \s_@@_stop
+ \intarray_gset:Nnn #1 { 1 + "#2 } { -1 }
+ \@@_declare_eight_bit_loop:Nn #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_convert_decode_eight_bit:n}
+% \begin{macro}[rEXP]{\@@_decode_eight_bit_aux:n, \@@_decode_eight_bit_aux:Nn}
+% The map from bytes to Unicode code points is in the \texttt{decode}
+% array corresponding to the given encoding. Define \cs{@@_tmp:w} and
+% pass it successively all bytes in the string. It produces an
+% internal representation with suitable \cs{s_@@} inserted, and the
+% corresponding code point is obtained by looking it up in the integer
+% array. If the entry is $-1$ then issue a replacement character and
+% raise the flag indicating that there was an error.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_convert_decode_eight_bit:n #1
+ {
+ \cs_set:Npe \@@_tmp:w
+ {
+ \exp_not:N \@@_decode_eight_bit_aux:Nn
+ \exp_not:c { g_@@_decode_#1_intarray }
+ }
+ \flag_clear:N \l_@@_error_flag
+ \@@_convert_gmap:N \@@_tmp:w
+ \@@_if_flag_error:Nne \l_@@_error_flag { decode-8-bit } {#1}
+ }
+\cs_new:Npn \@@_decode_eight_bit_aux:Nn #1#2
+ {
+ #2 \s_@@
+ \exp_args:Nf \@@_decode_eight_bit_aux:n
+ { \intarray_item:Nn #1 { 1 + `#2 } }
+ \s_@@
+ }
+\cs_new:Npn \@@_decode_eight_bit_aux:n #1
+ {
+ \if_int_compare:w #1 < \c_zero_int
+ \flag_raise:N \l_@@_error_flag
+ \int_value:w \c_@@_replacement_char_int
+ \else:
+ #1
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_convert_encode_eight_bit:n}
+% \begin{macro}[rEXP]{\@@_encode_eight_bit_aux:nnN, \@@_encode_eight_bit_aux:NNn}
+% It is not practical to make an integer array with indices in the
+% full Unicode range, so we work modulo some number, which is simply
+% the size of the \texttt{encode} integer array for the given
+% encoding. This gives us a candidate byte for representing a given
+% Unicode code point. Of course taking the modulo leads to collisions
+% so we check in the \texttt{decode} array that the byte we got is
+% indeed correct. Otherwise the Unicode code point we started from is
+% simply not representable in the given encoding.
+% \begin{macrocode}
+\int_new:N \l_@@_modulo_int
+\cs_new_protected:Npn \@@_convert_encode_eight_bit:n #1
+ {
+ \cs_set:Npe \@@_tmp:w
+ {
+ \exp_not:N \@@_encode_eight_bit_aux:NNn
+ \exp_not:c { g_@@_encode_#1_intarray }
+ \exp_not:c { g_@@_decode_#1_intarray }
+ }
+ \flag_clear:N \l_@@_error_flag
+ \@@_convert_gmap_internal:N \@@_tmp:w
+ \@@_if_flag_error:Nne \l_@@_error_flag { encode-8-bit } {#1}
+ }
+\cs_new:Npn \@@_encode_eight_bit_aux:NNn #1#2#3
+ {
+ \exp_args:Nf \@@_encode_eight_bit_aux:nnN
+ {
+ \intarray_item:Nn #1
+ { 1 + \int_mod:nn {#3} { \intarray_count:N #1 } }
+ }
+ {#3}
+ #2
+ }
+\cs_new:Npn \@@_encode_eight_bit_aux:nnN #1#2#3
+ {
+ \int_compare:nNnTF { \intarray_item:Nn #3 { 1 + #1 } } = {#2}
+ { \@@_output_byte:n {#1} }
+ { \flag_raise:N \l_@@_error_flag }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+% General messages, and messages for the encodings and escapings loaded
+% by default (\enquote{native}, and \enquote{bytes}).
+% \begin{macrocode}
+\msg_new:nnn { str } { unknown-esc }
+ { Escaping~scheme~'#1'~(filtered:~'#2')~unknown. }
+\msg_new:nnn { str } { unknown-enc }
+ { Encoding~scheme~'#1'~(filtered:~'#2')~unknown. }
+\msg_new:nnnn { str } { native-escaping }
+ { The~'native'~encoding~scheme~does~not~support~any~escaping. }
+ {
+ Since~native~strings~do~not~consist~in~bytes,~
+ none~of~the~escaping~methods~make~sense.~
+ The~specified~escaping,~'#1',~will~be~ignored.
+ }
+\msg_new:nnn { str } { file-not-found }
+ { File~'l3str-#1.def'~not~found. }
+% \end{macrocode}
+%
+% Message used when the \enquote{bytes} unescaping fails because the
+% string given to \cs{str_set_convert:Nnnn} contains a non-byte. This
+% cannot happen for the -8-bit engines.
+% Messages used for other escapings and
+% encodings are defined in each definition file.
+% \begin{macrocode}
+\bool_lazy_any:nT
+ {
+ \sys_if_engine_luatex_p:
+ \sys_if_engine_xetex_p:
+ }
+ {
+ \msg_new:nnnn { str } { non-byte }
+ { String~invalid~in~escaping~'#1':~it~may~only~contain~bytes. }
+ {
+ Some~characters~in~the~string~you~asked~to~convert~are~not~
+ 8-bit~characters.~Perhaps~the~string~is~a~'native'~Unicode~string?~
+ If~it~is,~try~using\\
+ \\
+ \iow_indent:n
+ {
+ \iow_char:N\\str_set_convert:Nnnn \\
+ \ \ <str~var>~\{~<string>~\}~\{~native~\}~\{~<target~encoding>~\}
+ }
+ }
+ }
+% \end{macrocode}
+%
+% Those messages are used when converting to and from 8-bit encodings.
+% \begin{macrocode}
+\msg_new:nnnn { str } { decode-8-bit }
+ { Invalid~string~in~encoding~'#1'. }
+ {
+ LaTeX~came~across~a~byte~which~is~not~defined~to~represent~
+ any~character~in~the~encoding~'#1'.
+ }
+\msg_new:nnnn { str } { encode-8-bit }
+ { Unicode~string~cannot~be~converted~to~encoding~'#1'. }
+ {
+ The~encoding~'#1'~only~contains~a~subset~of~all~Unicode~characters.~
+ LaTeX~was~asked~to~convert~a~string~to~that~encoding,~but~that~
+ string~contains~a~character~that~'#1'~does~not~support.
+ }
+% \end{macrocode}
+%
+% \subsection{Escaping definitions}
+%
+% Several of those encodings are defined by the pdf file format. The
+% following byte storage methods are defined:
+% \begin{itemize}
+% \item \texttt{bytes} (default), non-bytes are filtered out, and
+% bytes are left untouched (this is defined by default);
+% \item \texttt{hex} or \texttt{hexadecimal}, as per the \pdfTeX{}
+% primitive \tn{pdfescapehex}
+% \item \texttt{name}, as per the \pdfTeX{} primitive
+% \tn{pdfescapename}
+% \item \texttt{string}, as per the \pdfTeX{} primitive
+% \tn{pdfescapestring}
+% \item \texttt{url}, as per the percent encoding of urls.
+% \end{itemize}
+%
+% \subsubsection{Unescape methods}
+%
+% \begin{macro}{\@@_convert_unescape_hex:}
+% \begin{macro}[rEXP]{\@@_unescape_hex_auxi:N}
+% \begin{macro}[rEXP]{\@@_unescape_hex_auxii:N}
+% Take chars two by two, and interpret each pair as the hexadecimal
+% code for a byte. Anything else than hexadecimal digits is ignored,
+% raising the flag. A string which contains an odd number of
+% hexadecimal digits gets |0| appended to it: this is equivalent to
+% appending a |0| in all cases, and dropping it if it is alone.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_convert_unescape_hex:
+ {
+ \group_begin:
+ \flag_clear:N \l_@@_error_flag
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \__kernel_tl_gset:Nx \g_@@_result_tl
+ {
+ \@@_output_byte:w "
+ \exp_last_unbraced:Nf \@@_unescape_hex_auxi:N
+ { \tl_to_str:N \g_@@_result_tl }
+ 0 { ? 0 - 1 \prg_break: }
+ \prg_break_point:
+ \@@_output_end:
+ }
+ \@@_if_flag_error:Nne \l_@@_error_flag { unescape-hex } { }
+ \group_end:
+ }
+\cs_new:Npn \@@_unescape_hex_auxi:N #1
+ {
+ \use_none:n #1
+ \@@_hexadecimal_use:NTF #1
+ { \@@_unescape_hex_auxii:N }
+ {
+ \flag_raise:N \l_@@_error_flag
+ \@@_unescape_hex_auxi:N
+ }
+ }
+\cs_new:Npn \@@_unescape_hex_auxii:N #1
+ {
+ \use_none:n #1
+ \@@_hexadecimal_use:NTF #1
+ {
+ \@@_output_end:
+ \@@_output_byte:w " \@@_unescape_hex_auxi:N
+ }
+ {
+ \flag_raise:N \l_@@_error_flag
+ \@@_unescape_hex_auxii:N
+ }
+ }
+\msg_new:nnnn { str } { unescape-hex }
+ { String~invalid~in~escaping~'hex':~only~hexadecimal~digits~allowed. }
+ {
+ Some~characters~in~the~string~you~asked~to~convert~are~not~
+ hexadecimal~digits~(0-9,~A-F,~a-f)~nor~spaces.
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_convert_unescape_name:}
+% \begin{macro}[rEXP]{\@@_unescape_name_loop:wNN}
+% \begin{macro}{\@@_convert_unescape_url:}
+% \begin{macro}[rEXP]{\@@_unescape_url_loop:wNN}
+% The \cs{@@_convert_unescape_name:} function replaces each
+% occurrence of |#| followed by two hexadecimal digits in
+% \cs{g_@@_result_tl} by the corresponding byte. The \texttt{url}
+% function is identical, with escape character |%| instead of |#|.
+% Thus we define the two together. The arguments of \cs{@@_tmp:w} are
+% the character code of |#| or |%| in hexadecimal, the name of the
+% main function to define, and the name of the auxiliary which
+% performs the loop.
+%
+% The looping auxiliary |#3| finds the next escape character, reads
+% the following two characters, and tests them. The test
+% \cs{@@_hexadecimal_use:NTF} leaves the upper-case digit in the
+% input stream, hence we surround the test with
+% \cs{@@_output_byte:w}~|"| and \cs{@@_output_end:}. If both
+% characters are hexadecimal digits, they should be removed before
+% looping: this is done by \cs{use_i:nnn}. If one of the characters
+% is not a hexadecimal digit, then feed |"#1| to
+% \cs{@@_output_byte:w} to produce the escape character, raise the
+% flag, and call the looping function followed by the two characters
+% (remove \cs{use_i:nnn}).
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3
+ {
+ \cs_new_protected:cpn { @@_convert_unescape_#2: }
+ {
+ \group_begin:
+ \flag_clear:N \l_@@_byte_flag
+ \flag_clear:N \l_@@_error_flag
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \__kernel_tl_gset:Nx \g_@@_result_tl
+ {
+ \exp_after:wN #3 \g_@@_result_tl
+ #1 ? { ? \prg_break: }
+ \prg_break_point:
+ }
+ \@@_if_flag_error:Nne \l_@@_byte_flag { non-byte } { #2 }
+ \@@_if_flag_error:Nne \l_@@_error_flag { unescape-#2 } { }
+ \group_end:
+ }
+ \cs_new:Npn #3 ##1#1##2##3
+ {
+ \@@_filter_bytes:n {##1}
+ \use_none:n ##3
+ \@@_output_byte:w "
+ \@@_hexadecimal_use:NTF ##2
+ {
+ \@@_hexadecimal_use:NTF ##3
+ { }
+ {
+ \flag_raise:N \l_@@_error_flag
+ * 0 + `#1 \use_i:nn
+ }
+ }
+ {
+ \flag_raise:N \l_@@_error_flag
+ 0 + `#1 \use_i:nn
+ }
+ \@@_output_end:
+ \use_i:nnn #3 ##2##3
+ }
+ \msg_new:nnnn { str } { unescape-#2 }
+ { String~invalid~in~escaping~'#2'. }
+ {
+ LaTeX~came~across~the~escape~character~'#1'~not~followed~by~
+ two~hexadecimal~digits.~This~is~invalid~in~the~escaping~'#2'.
+ }
+ }
+\exp_after:wN \@@_tmp:w \c_hash_str { name }
+ \@@_unescape_name_loop:wNN
+\exp_after:wN \@@_tmp:w \c_percent_str { url }
+ \@@_unescape_url_loop:wNN
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_convert_unescape_string:}
+% \begin{macro}[rEXP]{\@@_unescape_string_newlines:wN}
+% \begin{macro}[rEXP]{\@@_unescape_string_loop:wNNN}
+% \begin{macro}[rEXP]{\@@_unescape_string_repeat:NNNNNN}
+% The \texttt{string} escaping is somewhat similar to the
+% \texttt{name} and \texttt{url} escapings, with escape character |\|.
+% The first step is to convert all three line endings, |^^J|, |^^M|,
+% and |^^M^^J| to the common |^^J|, as per the \textsc{pdf}
+% specification. This step cannot raise the flag.
+%
+% Then the following escape sequences are decoded.
+% \begin{itemize}\def\makelabel#1{\hss\llap{\ttfamily\string#1}}
+% \item[\n] Line feed ($10$)
+% \item[\r] Carriage return ($13$)
+% \item[\t] Horizontal tab ($9$)
+% \item[\b] Backspace ($8$)
+% \item[\f] Form feed ($12$)
+% \item[\(] Left parenthesis
+% \item[\)] Right parenthesis
+% \item[\\] Backslash
+% \item[\ddd] (backslash followed by $1$ to $3$ octal digits) Byte
+% \texttt{ddd} (octal), subtracting $256$ in case of overflow.
+% \end{itemize}
+% If followed by an end-of-line character, the backslash and the
+% end-of-line are ignored. If followed by anything else, the backslash
+% is ignored, raising the error flag.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_other:N \^^J
+ \char_set_catcode_other:N \^^M
+ \cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_new_protected:Npn \@@_convert_unescape_string:
+ {
+ \group_begin:
+ \flag_clear:N \l_@@_byte_flag
+ \flag_clear:N \l_@@_error_flag
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \__kernel_tl_gset:Nx \g_@@_result_tl
+ {
+ \exp_after:wN \@@_unescape_string_newlines:wN
+ \g_@@_result_tl \prg_break: ^^M ?
+ \prg_break_point:
+ }
+ \__kernel_tl_gset:Nx \g_@@_result_tl
+ {
+ \exp_after:wN \@@_unescape_string_loop:wNNN
+ \g_@@_result_tl #1 ?? { ? \prg_break: }
+ \prg_break_point:
+ }
+ \@@_if_flag_error:Nne \l_@@_byte_flag { non-byte } { string }
+ \@@_if_flag_error:Nne \l_@@_error_flag { unescape-string } { }
+ \group_end:
+ }
+ }
+ \exp_args:No \@@_tmp:w { \c_backslash_str }
+ \exp_last_unbraced:NNNNo
+ \cs_new:Npn \@@_unescape_string_loop:wNNN #1 \c_backslash_str #2#3#4
+ {
+ \@@_filter_bytes:n {#1}
+ \use_none:n #4
+ \@@_output_byte:w '
+ \@@_octal_use:NTF #2
+ {
+ \@@_octal_use:NTF #3
+ {
+ \@@_octal_use:NTF #4
+ {
+ \if_int_compare:w #2 > 3 \exp_stop_f:
+ - 256
+ \fi:
+ \@@_unescape_string_repeat:NNNNNN
+ }
+ { \@@_unescape_string_repeat:NNNNNN ? }
+ }
+ { \@@_unescape_string_repeat:NNNNNN ?? }
+ }
+ {
+ \str_case_e:nnF {#2}
+ {
+ { \c_backslash_str } { 134 }
+ { ( } { 50 }
+ { ) } { 51 }
+ { r } { 15 }
+ { f } { 14 }
+ { n } { 12 }
+ { t } { 11 }
+ { b } { 10 }
+ { ^^J } { 0 - 1 }
+ }
+ {
+ \flag_raise:N \l_@@_error_flag
+ 0 - 1 \use_i:nn
+ }
+ }
+ \@@_output_end:
+ \use_i:nn \@@_unescape_string_loop:wNNN #2#3#4
+ }
+ \cs_new:Npn \@@_unescape_string_repeat:NNNNNN #1#2#3#4#5#6
+ { \@@_output_end: \@@_unescape_string_loop:wNNN }
+ \cs_new:Npn \@@_unescape_string_newlines:wN #1 ^^M #2
+ {
+ #1
+ \if_charcode:w ^^J #2 \else: ^^J \fi:
+ \@@_unescape_string_newlines:wN #2
+ }
+ \msg_new:nnnn { str } { unescape-string }
+ { String~invalid~in~escaping~'string'. }
+ {
+ LaTeX~came~across~an~escape~character~'\c_backslash_str'~
+ not~followed~by~any~of:~'n',~'r',~'t',~'b',~'f',~'(',~')',~
+ '\c_backslash_str',~one~to~three~octal~digits,~or~the~end~
+ of~a~line.
+ }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Escape methods}
+%
+% Currently, none of the escape methods can lead to errors, assuming
+% that their input is made out of bytes.
+%
+% \begin{macro}{\@@_convert_escape_hex:}
+% \begin{macro}[rEXP]{\@@_escape_hex_char:N}
+% Loop and convert each byte to hexadecimal.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_convert_escape_hex:
+ { \@@_convert_gmap:N \@@_escape_hex_char:N }
+\cs_new:Npn \@@_escape_hex_char:N #1
+ { \@@_output_hexadecimal:n { `#1 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_convert_escape_name:}
+% \begin{macro}[rEXP]{\@@_escape_name_char:n}
+% \begin{macro}[rEXP]{\@@_if_escape_name:nTF}
+% \begin{variable}{\c_@@_escape_name_str}
+% \begin{variable}{\c_@@_escape_name_not_str}
+% For each byte, test whether it should be output as is, or be
+% \enquote{hash-encoded}. Roughly, bytes outside the range
+% $[\hexnum{2A},\hexnum{7E}]$ are hash-encoded. We keep two lists of
+% exceptions: characters in \cs{c_@@_escape_name_not_str} are not
+% hash-encoded, and characters in the \cs{c_@@_escape_name_str} are
+% encoded.
+% \begin{macrocode}
+\str_const:Nn \c_@@_escape_name_not_str { ! " $ & ' } %$
+\str_const:Nn \c_@@_escape_name_str { {}/<>[] }
+\cs_new_protected:Npn \@@_convert_escape_name:
+ { \@@_convert_gmap:N \@@_escape_name_char:n }
+\cs_new:Npn \@@_escape_name_char:n #1
+ {
+ \@@_if_escape_name:nTF {#1} {#1}
+ { \c_hash_str \@@_output_hexadecimal:n {`#1} }
+ }
+\prg_new_conditional:Npnn \@@_if_escape_name:n #1 { TF }
+ {
+ \if_int_compare:w `#1 < "2A \exp_stop_f:
+ \@@_if_contains_char:NnTF \c_@@_escape_name_not_str {#1}
+ \prg_return_true: \prg_return_false:
+ \else:
+ \if_int_compare:w `#1 > "7E \exp_stop_f:
+ \prg_return_false:
+ \else:
+ \@@_if_contains_char:NnTF \c_@@_escape_name_str {#1}
+ \prg_return_false: \prg_return_true:
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_convert_escape_string:}
+% \begin{macro}[rEXP]{\@@_escape_string_char:N}
+% \begin{macro}[rEXP]{\@@_if_escape_string:NTF}
+% \begin{variable}{\c_@@_escape_string_str}
+% Any character below (and including) space, and any character above
+% (and including) \texttt{del}, are converted to octal. One backslash
+% is added before each parenthesis and backslash.
+% \begin{macrocode}
+\str_const:Ne \c_@@_escape_string_str
+ { \c_backslash_str ( ) }
+\cs_new_protected:Npn \@@_convert_escape_string:
+ { \@@_convert_gmap:N \@@_escape_string_char:N }
+\cs_new:Npn \@@_escape_string_char:N #1
+ {
+ \@@_if_escape_string:NTF #1
+ {
+ \@@_if_contains_char:NnT
+ \c_@@_escape_string_str {#1}
+ { \c_backslash_str }
+ #1
+ }
+ {
+ \c_backslash_str
+ \int_div_truncate:nn {`#1} {64}
+ \int_mod:nn { \int_div_truncate:nn {`#1} { 8 } } { 8 }
+ \int_mod:nn {`#1} { 8 }
+ }
+ }
+\prg_new_conditional:Npnn \@@_if_escape_string:N #1 { TF }
+ {
+ \if_int_compare:w `#1 < "27 \exp_stop_f:
+ \prg_return_false:
+ \else:
+ \if_int_compare:w `#1 > "7A \exp_stop_f:
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_convert_escape_url:}
+% \begin{macro}[rEXP]{\@@_escape_url_char:n}
+% \begin{macro}[rEXP]{\@@_if_escape_url:nTF}
+% This function is similar to \cs{@@_convert_escape_name:}, escaping
+% different characters.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_convert_escape_url:
+ { \@@_convert_gmap:N \@@_escape_url_char:n }
+\cs_new:Npn \@@_escape_url_char:n #1
+ {
+ \@@_if_escape_url:nTF {#1} {#1}
+ { \c_percent_str \@@_output_hexadecimal:n { `#1 } }
+ }
+\prg_new_conditional:Npnn \@@_if_escape_url:n #1 { TF }
+ {
+ \if_int_compare:w `#1 < "30 \exp_stop_f:
+ \@@_if_contains_char:nnTF { "-. } {#1}
+ \prg_return_true: \prg_return_false:
+ \else:
+ \if_int_compare:w `#1 > "7E \exp_stop_f:
+ \prg_return_false:
+ \else:
+ \@@_if_contains_char:nnTF { : ; = ? @ [ ] } {#1}
+ \prg_return_false: \prg_return_true:
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Encoding definitions}
+%
+% The \texttt{native} encoding is automatically defined. Other encodings
+% are loaded as needed. The following encodings are supported:
+% \begin{itemize}
+% \item \textsc{utf-8};
+% \item \textsc{utf-16}, big-, little-endian, or with byte order mark;
+% \item \textsc{utf-32}, big-, little-endian, or with byte order mark;
+% \item the \textsc{iso 8859} code pages, numbered from $1$ to $16$,
+% skipping the inexistent \textsc{iso 8859-12}.
+% \end{itemize}
+%
+% \subsubsection{\textsc{utf-8} support}
+%
+% \begin{macro}{\@@_convert_encode_utf8:}
+% \begin{macro}[rEXP]{\@@_encode_utf_viii_char:n}
+% \begin{macro}[rEXP]{\@@_encode_utf_viii_loop:wwnnw}
+% Loop through the internal string, and convert each character to its
+% \textsc{utf-8} representation. The representation is built from the
+% right-most (least significant) byte to the left-most (most
+% significant) byte. Continuation bytes are in the range $[128,191]$,
+% taking $64$ different values, hence we roughly want to express the
+% character code in base $64$, shifting the first digit in the
+% representation by some number depending on how many continuation
+% bytes there are. In the range $[0,127]$, output the corresponding
+% byte directly. In the range $[128,2047]$, output the remainder
+% modulo $64$, plus $128$ as a continuation byte, then output the
+% quotient (which is in the range $[0,31]$), shifted by $192$. In the
+% next range, $[2048,65535]$, split the character code into residue
+% and quotient modulo $64$, output the residue as a first continuation
+% byte, then repeat; this leaves us with a quotient in the range
+% $[0,15]$, which we output shifted by $224$. The last range,
+% $[65536,1114111]$, follows the same pattern: once we realize that
+% dividing twice by $64$ leaves us with a number larger than $15$, we
+% repeat, producing a last continuation byte, and offset the quotient
+% by $240$ for the leading byte.
+%
+% How is that implemented? \cs{@@_encode_utf_vii_loop:wwnnw} takes
+% successive quotients as its first argument, the quotient from the
+% previous step as its second argument (except in step~$1$), the bound
+% for quotients that trigger one more step or not, and finally the
+% offset used if this step should produce the leading byte. Leading
+% bytes can be in the ranges $[0,127]$, $[192,223]$, $[224,239]$, and
+% $[240,247]$ (really, that last limit should be $244$ because Unicode
+% stops at the code point $1114111$). At each step, if the quotient
+% |#1| is less than the limit |#3| for that range, output the leading
+% byte (|#1| shifted by |#4|) and stop. Otherwise, we need one more
+% step: use the quotient of |#1| by $64$, and |#1| as arguments for
+% the looping auxiliary, and output the continuation byte
+% corresponding to the remainder $|#2|-64|#1|+128$. The bizarre
+% construction |- 1 + 0 *| removes the spurious initial
+% continuation byte (better methods welcome).
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_convert_encode_utf8: }
+ { \@@_convert_gmap_internal:N \@@_encode_utf_viii_char:n }
+\cs_new:Npn \@@_encode_utf_viii_char:n #1
+ {
+ \@@_encode_utf_viii_loop:wwnnw #1 ; - 1 + 0 * ;
+ { 128 } { 0 }
+ { 32 } { 192 }
+ { 16 } { 224 }
+ { 8 } { 240 }
+ \s_@@_stop
+ }
+\cs_new:Npn \@@_encode_utf_viii_loop:wwnnw #1; #2; #3#4 #5 \s_@@_stop
+ {
+ \if_int_compare:w #1 < #3 \exp_stop_f:
+ \@@_output_byte:n { #1 + #4 }
+ \exp_after:wN \@@_use_none_delimit_by_s_stop:w
+ \fi:
+ \exp_after:wN \@@_encode_utf_viii_loop:wwnnw
+ \int_value:w \int_div_truncate:nn {#1} {64} ; #1 ;
+ #5 \s_@@_stop
+ \@@_output_byte:n { #2 - 64 * ( #1 - 2 ) }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}
+% {
+% @@_missing ,
+% @@_extra ,
+% @@_overlong ,
+% @@_overflow ,
+% }
+% When decoding a string that is purportedly in the \textsc{utf-8}
+% encoding, four different errors can occur, signalled by a specific
+% flag for each (we define those flags using \cs{flag_clear_new:N}
+% rather than \cs{flag_new:N}, because they are shared with other
+% encoding definition files).
+% \begin{itemize}
+% \item \enquote{Missing continuation byte}: a leading byte is not
+% followed by the right number of continuation bytes.
+% \item \enquote{Extra continuation byte}: a continuation byte
+% appears where it was not expected, \emph{i.e.}, not after an
+% appropriate leading byte.
+% \item \enquote{Overlong}: a Unicode character is expressed using
+% more bytes than necessary, for instance, \hexnum{C0}\hexnum{80}
+% for the code point $0$, instead of a single null byte.
+% \item \enquote{Overflow}: this occurs when decoding produces
+% Unicode code points greater than $1114111$.
+% \end{itemize}
+% We only raise one \LaTeX3 error message, combining all the errors
+% which occurred. In the short message, the leading comma must be
+% removed to get a grammatically correct sentence. In the long text,
+% first remind the user what a correct \textsc{utf-8} string should
+% look like, then add error-specific information.
+% \begin{macrocode}
+\flag_clear_new:N \l_@@_missing_flag
+\flag_clear_new:N \l_@@_extra_flag
+\flag_clear_new:N \l_@@_overlong_flag
+\flag_clear_new:N \l_@@_overflow_flag
+\msg_new:nnnn { str } { utf8-decode }
+ {
+ Invalid~UTF-8~string:
+ \exp_last_unbraced:Nf \use_none:n
+ {
+ \@@_if_flag_times:NT \l_@@_missing_flag { ,~missing~continuation~byte }
+ \@@_if_flag_times:NT \l_@@_extra_flag { ,~extra~continuation~byte }
+ \@@_if_flag_times:NT \l_@@_overlong_flag { ,~overlong~form }
+ \@@_if_flag_times:NT \l_@@_overflow_flag { ,~code~point~too~large }
+ }
+ .
+ }
+ {
+ In~the~UTF-8~encoding,~each~Unicode~character~consists~in~
+ 1~to~4~bytes,~with~the~following~bit~pattern: \\
+ \iow_indent:n
+ {
+ Code~point~\ \ \ \ <~128:~0xxxxxxx \\
+ Code~point~\ \ \ <~2048:~110xxxxx~10xxxxxx \\
+ Code~point~\ \ <~65536:~1110xxxx~10xxxxxx~10xxxxxx \\
+ Code~point~ <~1114112:~11110xxx~10xxxxxx~10xxxxxx~10xxxxxx \\
+ }
+ Bytes~of~the~form~10xxxxxx~are~called~continuation~bytes.
+ \flag_if_raised:NT \l_@@_missing_flag
+ {
+ \\\\
+ A~leading~byte~(in~the~range~[192,255])~was~not~followed~by~
+ the~appropriate~number~of~continuation~bytes.
+ }
+ \flag_if_raised:NT \l_@@_extra_flag
+ {
+ \\\\
+ LaTeX~came~across~a~continuation~byte~when~it~was~not~expected.
+ }
+ \flag_if_raised:NT \l_@@_overlong_flag
+ {
+ \\\\
+ Every~Unicode~code~point~must~be~expressed~in~the~shortest~
+ possible~form.~For~instance,~'0xC0'~'0x83'~is~not~a~valid~
+ representation~for~the~code~point~3.
+ }
+ \flag_if_raised:NT \l_@@_overflow_flag
+ {
+ \\\\
+ Unicode~limits~code~points~to~the~range~[0,1114111].
+ }
+ }
+\prop_gput:Nnn \g_msg_module_name_prop { str } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { str } { }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_convert_decode_utf8:}
+% \begin{macro}[rEXP]
+% {
+% \@@_decode_utf_viii_start:N,
+% \@@_decode_utf_viii_continuation:wwN,
+% \@@_decode_utf_viii_aux:wNnnwN
+% }
+% \begin{macro}[rEXP]
+% {\@@_decode_utf_viii_overflow:w, \@@_decode_utf_viii_end:}
+% Decoding is significantly harder than encoding. As before, lower
+% some flags, which are tested at the end (in bulk, to trigger at most
+% one \LaTeX3 error, as explained above). We expect successive
+% multi-byte sequences of the form \meta{start byte}
+% \meta{continuation bytes}. The \texttt{_start} auxiliary tests the
+% first byte:
+% \begin{itemize}
+% \item $[0,\hexnum{7F}]$: the byte stands alone, and is converted
+% to its own character code;
+% \item $[\hexnum{80}, \hexnum{BF}]$: unexpected continuation byte,
+% raise the appropriate flag, and convert that byte to the
+% replacement character \hexnum{FFFD};
+% \item $[\hexnum{C0}, \hexnum{FF}]$: this byte should be followed
+% by some continuation byte(s).
+% \end{itemize}
+% In the first two cases, \cs{use_none_delimit_by_q_stop:w} removes
+% data that only the third case requires, namely the limits of ranges
+% of Unicode characters which can be expressed with $1$, $2$, $3$, or
+% $4$ bytes.
+%
+% We can now concentrate on the multi-byte case and the
+% \texttt{_continuation} auxiliary. We expect |#3| to be in the range
+% $[\hexnum{80}, \hexnum{BF}]$. The test for this goes as follows: if
+% the character code is less than \hexnum{80}, we compare it to
+% $-\hexnum{C0}$, yielding \texttt{false}; otherwise to \hexnum{C0},
+% yielding \texttt{true} in the range $[\hexnum{80}, \hexnum{BF}]$ and
+% \texttt{false} otherwise. If we find that the byte is not a
+% continuation range, stop the current slew of bytes, output the
+% replacement character, and continue parsing with the \texttt{_start}
+% auxiliary, starting at the byte we just tested. Once we know that
+% the byte is a continuation byte, leave it behind us in the input
+% stream, compute what code point the bytes read so far would produce,
+% and feed that number to the \texttt{_aux} function.
+%
+% The \texttt{_aux} function tests whether we should look for more
+% continuation bytes or not. If the number it receives as |#1| is less
+% than the maximum |#4| for the current range, then we are done: check
+% for an overlong representation by comparing |#1| with the maximum
+% |#3| for the previous range. Otherwise, we call the
+% \texttt{_continuation} auxiliary again, after shifting the
+% \enquote{current code point} by |#4| (maximum from the range we just
+% checked).
+%
+% Two additional tests are needed: if we reach the end of the list of
+% range maxima and we are still not done, then we are faced with an
+% overflow. Clean up, and again insert the code point \hexnum{FFFD}
+% for the replacement character. Also, every time we read a byte, we
+% need to check whether we reached the end of the string. In a correct
+% \textsc{utf-8} string, this happens automatically when the
+% \texttt{_start} auxiliary leaves its first argument in the input
+% stream: the end-marker begins with \cs{prg_break:}, which ends
+% the loop. On the other hand, if the end is reached when looking for
+% a continuation byte, the \cs{use_none:n} |#3| construction removes
+% the first token from the end-marker, and leaves the \texttt{_end}
+% auxiliary, which raises the appropriate error flag before ending the
+% mapping.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_convert_decode_utf8: }
+ {
+ \flag_clear:N \l_@@_error_flag
+ \flag_clear:N \l_@@_missing_flag
+ \flag_clear:N \l_@@_extra_flag
+ \flag_clear:N \l_@@_overlong_flag
+ \flag_clear:N \l_@@_overflow_flag
+ \__kernel_tl_gset:Nx \g_@@_result_tl
+ {
+ \exp_after:wN \@@_decode_utf_viii_start:N \g_@@_result_tl
+ { \prg_break: \@@_decode_utf_viii_end: }
+ \prg_break_point:
+ }
+ \@@_if_flag_error:Nne \l_@@_error_flag { utf8-decode } { }
+ }
+\cs_new:Npn \@@_decode_utf_viii_start:N #1
+ {
+ #1
+ \if_int_compare:w `#1 < "C0 \exp_stop_f:
+ \s_@@
+ \if_int_compare:w `#1 < "80 \exp_stop_f:
+ \int_value:w `#1
+ \else:
+ \flag_raise:N \l_@@_extra_flag
+ \flag_raise:N \l_@@_error_flag
+ \int_use:N \c_@@_replacement_char_int
+ \fi:
+ \else:
+ \exp_after:wN \@@_decode_utf_viii_continuation:wwN
+ \int_value:w \int_eval:n { `#1 - "C0 } \exp_after:wN
+ \fi:
+ \s_@@
+ \@@_use_none_delimit_by_s_stop:w {"80} {"800} {"10000} {"110000} \s_@@_stop
+ \@@_decode_utf_viii_start:N
+ }
+\cs_new:Npn \@@_decode_utf_viii_continuation:wwN
+ #1 \s_@@ #2 \@@_decode_utf_viii_start:N #3
+ {
+ \use_none:n #3
+ \if_int_compare:w `#3 <
+ \if_int_compare:w `#3 < "80 \exp_stop_f: - \fi:
+ "C0 \exp_stop_f:
+ #3
+ \exp_after:wN \@@_decode_utf_viii_aux:wNnnwN
+ \int_value:w \int_eval:n { #1 * "40 + `#3 - "80 } \exp_after:wN
+ \else:
+ \s_@@
+ \flag_raise:N \l_@@_missing_flag
+ \flag_raise:N \l_@@_error_flag
+ \int_use:N \c_@@_replacement_char_int
+ \fi:
+ \s_@@
+ #2
+ \@@_decode_utf_viii_start:N #3
+ }
+\cs_new:Npn \@@_decode_utf_viii_aux:wNnnwN
+ #1 \s_@@ #2#3#4 #5 \@@_decode_utf_viii_start:N #6
+ {
+ \if_int_compare:w #1 < #4 \exp_stop_f:
+ \s_@@
+ \if_int_compare:w #1 < #3 \exp_stop_f:
+ \flag_raise:N \l_@@_overlong_flag
+ \flag_raise:N \l_@@_error_flag
+ \int_use:N \c_@@_replacement_char_int
+ \else:
+ #1
+ \fi:
+ \else:
+ \if_meaning:w \s_@@_stop #5
+ \@@_decode_utf_viii_overflow:w #1
+ \fi:
+ \exp_after:wN \@@_decode_utf_viii_continuation:wwN
+ \int_value:w \int_eval:n { #1 - #4 } \exp_after:wN
+ \fi:
+ \s_@@
+ #2 {#4} #5
+ \@@_decode_utf_viii_start:N
+ }
+\cs_new:Npn \@@_decode_utf_viii_overflow:w #1 \fi: #2 \fi:
+ {
+ \fi: \fi:
+ \flag_raise:N \l_@@_overflow_flag
+ \flag_raise:N \l_@@_error_flag
+ \int_use:N \c_@@_replacement_char_int
+ }
+\cs_new:Npn \@@_decode_utf_viii_end:
+ {
+ \s_@@
+ \flag_raise:N \l_@@_missing_flag
+ \flag_raise:N \l_@@_error_flag
+ \int_use:N \c_@@_replacement_char_int \s_@@
+ \prg_break:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{\textsc{utf-16} support}
+%
+% The definitions are done in a category code regime where the bytes
+% $254$ and $255$ used by the byte order mark have catcode~$12$.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_other:N \^^fe
+ \char_set_catcode_other:N \^^ff
+% \end{macrocode}
+%
+% \begin{macro}
+% {
+% \@@_convert_encode_utf16: ,
+% \@@_convert_encode_utf16be: ,
+% \@@_convert_encode_utf16le: ,
+% }
+% \begin{macro}[rEXP]
+% {
+% \@@_encode_utf_xvi_aux:N ,
+% \@@_encode_utf_xvi_char:n ,
+% }
+% When the endianness is not specified, it is big-endian by default,
+% and we add a byte-order mark. Convert characters one by one in a
+% loop, with different behaviours depending on the character code.
+% \begin{itemize}
+% \item $[0, \hexnum{D7FF}]$: converted to two bytes;
+% \item $[\hexnum{D800}, \hexnum{DFFF}]$ are used as surrogates:
+% they cannot be converted and are replaced by the replacement
+% character;
+% \item $[\hexnum{E000}, \hexnum{FFFF}]$: converted to two bytes;
+% \item $[\hexnum{10000}, \hexnum{10FFFF}]$: converted to a pair of
+% surrogates, each two bytes. The magic \hexnum{D7C0} is
+% $\hexnum{D800}-\hexnum{10000}/\hexnum{400}$.
+% \end{itemize}
+% For the duration of this operation, \cs{@@_tmp:w} is defined as a
+% function to convert a number in the range $[0, \hexnum{FFFF}]$ to a
+% pair of bytes (either big endian or little endian), by feeding the
+% quotient of the division of |#1| by \hexnum{100}, followed by |#1|
+% to \cs{@@_encode_utf_xvi_be:nn} or its \texttt{le} analog: those
+% compute the remainder, and output two bytes for the quotient and
+% remainder.
+% \begin{macrocode}
+ \cs_new_protected:cpn { @@_convert_encode_utf16: }
+ {
+ \@@_encode_utf_xvi_aux:N \@@_output_byte_pair_be:n
+ \tl_gput_left:Ne \g_@@_result_tl { ^^fe ^^ff }
+ }
+ \cs_new_protected:cpn { @@_convert_encode_utf16be: }
+ { \@@_encode_utf_xvi_aux:N \@@_output_byte_pair_be:n }
+ \cs_new_protected:cpn { @@_convert_encode_utf16le: }
+ { \@@_encode_utf_xvi_aux:N \@@_output_byte_pair_le:n }
+ \cs_new_protected:Npn \@@_encode_utf_xvi_aux:N #1
+ {
+ \flag_clear:N \l_@@_error_flag
+ \cs_set_eq:NN \@@_tmp:w #1
+ \@@_convert_gmap_internal:N \@@_encode_utf_xvi_char:n
+ \@@_if_flag_error:Nne \l_@@_error_flag { utf16-encode } { }
+ }
+ \cs_new:Npn \@@_encode_utf_xvi_char:n #1
+ {
+ \if_int_compare:w #1 < "D800 \exp_stop_f:
+ \@@_tmp:w {#1}
+ \else:
+ \if_int_compare:w #1 < "10000 \exp_stop_f:
+ \if_int_compare:w #1 < "E000 \exp_stop_f:
+ \flag_raise:N \l_@@_error_flag
+ \@@_tmp:w { \c_@@_replacement_char_int }
+ \else:
+ \@@_tmp:w {#1}
+ \fi:
+ \else:
+ \exp_args:Nf \@@_tmp:w { \int_div_truncate:nn {#1} {"400} + "D7C0 }
+ \exp_args:Nf \@@_tmp:w { \int_mod:nn {#1} {"400} + "DC00 }
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}
+% {
+% @@_missing ,
+% @@_extra ,
+% @@_end ,
+% }
+% When encoding a Unicode string to \textsc{utf-16}, only one error
+% can occur: code points in the range $[\hexnum{D800},
+% \hexnum{DFFF}]$, corresponding to surrogates, cannot be encoded. We
+% use the all-purpose flag \texttt{@@_error} to signal that error.
+%
+% When decoding a Unicode string which is purportedly in
+% \textsc{utf-16}, three errors can occur: a missing trail surrogate,
+% an unexpected trail surrogate, and a string containing an odd number
+% of bytes.
+% \begin{macrocode}
+ \flag_clear_new:N \l_@@_missing_flag
+ \flag_clear_new:N \l_@@_extra_flag
+ \flag_clear_new:N \l_@@_end_flag
+ \msg_new:nnnn { str } { utf16-encode }
+ { Unicode~string~cannot~be~expressed~in~UTF-16:~surrogate. }
+ {
+ Surrogate~code~points~(in~the~range~[U+D800,~U+DFFF])~
+ can~be~expressed~in~the~UTF-8~and~UTF-32~encodings,~
+ but~not~in~the~UTF-16~encoding.
+ }
+ \msg_new:nnnn { str } { utf16-decode }
+ {
+ Invalid~UTF-16~string:
+ \exp_last_unbraced:Nf \use_none:n
+ {
+ \@@_if_flag_times:NT \l_@@_missing_flag { ,~missing~trail~surrogate }
+ \@@_if_flag_times:NT \l_@@_extra_flag { ,~extra~trail~surrogate }
+ \@@_if_flag_times:NT \l_@@_end_flag { ,~odd~number~of~bytes }
+ }
+ .
+ }
+ {
+ In~the~UTF-16~encoding,~each~Unicode~character~is~encoded~as~
+ 2~or~4~bytes: \\
+ \iow_indent:n
+ {
+ Code~point~in~[U+0000,~U+D7FF]:~two~bytes \\
+ Code~point~in~[U+D800,~U+DFFF]:~illegal \\
+ Code~point~in~[U+E000,~U+FFFF]:~two~bytes \\
+ Code~point~in~[U+10000,~U+10FFFF]:~
+ a~lead~surrogate~and~a~trail~surrogate \\
+ }
+ Lead~surrogates~are~pairs~of~bytes~in~the~range~[0xD800,~0xDBFF],~
+ and~trail~surrogates~are~in~the~range~[0xDC00,~0xDFFF].
+ \flag_if_raised:NT \l_@@_missing_flag
+ {
+ \\\\
+ A~lead~surrogate~was~not~followed~by~a~trail~surrogate.
+ }
+ \flag_if_raised:NT \l_@@_extra_flag
+ {
+ \\\\
+ LaTeX~came~across~a~trail~surrogate~when~it~was~not~expected.
+ }
+ \flag_if_raised:NT \l_@@_end_flag
+ {
+ \\\\
+ The~string~contained~an~odd~number~of~bytes.~This~is~invalid:~
+ the~basic~code~unit~for~UTF-16~is~16~bits~(2~bytes).
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}
+% {
+% \@@_convert_decode_utf16: ,
+% \@@_convert_decode_utf16be: ,
+% \@@_convert_decode_utf16le: ,
+% }
+% \begin{macro}{\@@_decode_utf_xvi_bom:NN, \@@_decode_utf_xvi:Nw}
+% As for \textsc{utf-8}, decoding \textsc{utf-16} is harder than
+% encoding it. If the endianness is unknown, check the first two
+% bytes: if those are \hexnum{FE} and \hexnum{FF} in either order,
+% remove them and use the corresponding endianness, otherwise assume
+% big-endianness. The three endianness cases are based on a common
+% auxiliary whose first argument is $1$ for big-endian and $2$ for
+% little-endian, and whose second argument, delimited by the scan mark
+% \cs{s_@@_stop}, is expanded once (the string may be long; passing
+% \cs{g_@@_result_tl} as an argument before expansion is cheaper).
+%
+% The \cs{@@_decode_utf_xvi:Nw} function defines \cs{@@_tmp:w} to
+% take two arguments and return the character code of the first one if
+% the string is big-endian, and the second one if the string is
+% little-endian, then loops over the string using
+% \cs{@@_decode_utf_xvi_pair:NN} described below.
+% \begin{macrocode}
+ \cs_new_protected:cpn { @@_convert_decode_utf16be: }
+ { \@@_decode_utf_xvi:Nw 1 \g_@@_result_tl \s_@@_stop }
+ \cs_new_protected:cpn { @@_convert_decode_utf16le: }
+ { \@@_decode_utf_xvi:Nw 2 \g_@@_result_tl \s_@@_stop }
+ \cs_new_protected:cpn { @@_convert_decode_utf16: }
+ {
+ \exp_after:wN \@@_decode_utf_xvi_bom:NN
+ \g_@@_result_tl \s_@@_stop \s_@@_stop \s_@@_stop
+ }
+ \cs_new_protected:Npn \@@_decode_utf_xvi_bom:NN #1#2
+ {
+ \str_if_eq:nnTF { #1#2 } { ^^ff ^^fe }
+ { \@@_decode_utf_xvi:Nw 2 }
+ {
+ \str_if_eq:nnTF { #1#2 } { ^^fe ^^ff }
+ { \@@_decode_utf_xvi:Nw 1 }
+ { \@@_decode_utf_xvi:Nw 1 #1#2 }
+ }
+ }
+ \cs_new_protected:Npn \@@_decode_utf_xvi:Nw #1#2 \s_@@_stop
+ {
+ \flag_clear:N \l_@@_error_flag
+ \flag_clear:N \l_@@_missing_flag
+ \flag_clear:N \l_@@_extra_flag
+ \flag_clear:N \l_@@_end_flag
+ \cs_set:Npn \@@_tmp:w ##1 ##2 { ` ## #1 }
+ \__kernel_tl_gset:Nx \g_@@_result_tl
+ {
+ \exp_after:wN \@@_decode_utf_xvi_pair:NN
+ #2 \q_@@_nil \q_@@_nil
+ \prg_break_point:
+ }
+ \@@_if_flag_error:Nne \l_@@_error_flag { utf16-decode } { }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_decode_utf_xvi_pair:NN ,
+% \@@_decode_utf_xvi_quad:NNwNN ,
+% \@@_decode_utf_xvi_pair_end:Nw ,
+% }
+% \begin{macro}[rEXP]
+% {
+% \@@_decode_utf_xvi_error:nNN ,
+% \@@_decode_utf_xvi_extra:NNw ,
+% }
+% Bytes are read two at a time. At this stage, |\@@_tmp:w #1#2|
+% expands to the character code of the most significant byte, and we
+% distinguish cases depending on which range it lies in:
+% \begin{itemize}
+% \item $[\hexnum{D8}, \hexnum{DB}]$ signals a lead surrogate, and
+% the integer expression yields $1$ (\eTeX{} rounds ties away from
+% zero);
+% \item $[\hexnum{DC}, \hexnum{DF}]$ signals a trail surrogate,
+% unexpected here, and the integer expression yields $2$;
+% \item any other value signals a code point in the Basic
+% Multilingual Plane, which stands for itself, and the
+% \cs{if_case:w} construction expands to nothing (cases other than
+% $1$ or $2$), leaving the relevant material in the input stream,
+% followed by another call to the \texttt{_pair} auxiliary.
+% \end{itemize}
+% The case of a lead surrogate is treated by the \texttt{_quad}
+% auxiliary, whose arguments |#1|, |#2|, |#4| and |#5| are the four
+% bytes. We expect the most significant byte of |#4#5| to be in the
+% range $[\hexnum{DC}, \hexnum{DF}]$ (trail surrogate). The test is
+% similar to the test used for continuation bytes in the
+% \textsc{utf-8} decoding functions. In the case where |#4#5| is
+% indeed a trail surrogate, leave |#1#2#4#5| \cs{s_@@}
+% \meta{code~point} \cs{s_@@}, and remove the pair |#4#5| before
+% looping with \cs{@@_decode_utf_xvi_pair:NN}. Otherwise, of course,
+% complain about the missing surrogate.
+%
+% The magic number \hexnum{D7F7} is such that
+% $\hexnum{D7F7}*\hexnum{400} = \hexnum{D800}*\hexnum{400} +
+% \hexnum{DC00} - \hexnum{10000}$.
+%
+% Every time we read a pair of bytes, we test for the end-marker
+% \cs{q_@@_nil}. When reaching the end, we additionally check that the
+% string had an even length. Also, if the end is reached when
+% expecting a trail surrogate, we treat that as a missing surrogate.
+% \begin{macrocode}
+ \cs_new:Npn \@@_decode_utf_xvi_pair:NN #1#2
+ {
+ \if_meaning:w \q_@@_nil #2
+ \@@_decode_utf_xvi_pair_end:Nw #1
+ \fi:
+ \if_case:w
+ \int_eval:n { ( \@@_tmp:w #1#2 - "D6 ) / 4 } \scan_stop:
+ \or: \exp_after:wN \@@_decode_utf_xvi_quad:NNwNN
+ \or: \exp_after:wN \@@_decode_utf_xvi_extra:NNw
+ \fi:
+ #1#2 \s_@@
+ \int_eval:n { "100 * \@@_tmp:w #1#2 + \@@_tmp:w #2#1 } \s_@@
+ \@@_decode_utf_xvi_pair:NN
+ }
+ \cs_new:Npn \@@_decode_utf_xvi_quad:NNwNN
+ #1#2 #3 \@@_decode_utf_xvi_pair:NN #4#5
+ {
+ \if_meaning:w \q_@@_nil #5
+ \@@_decode_utf_xvi_error:nNN { missing } #1#2
+ \@@_decode_utf_xvi_pair_end:Nw #4
+ \fi:
+ \if_int_compare:w
+ \if_int_compare:w \@@_tmp:w #4#5 < "DC \exp_stop_f:
+ 0 = 1
+ \else:
+ \@@_tmp:w #4#5 < "E0
+ \fi:
+ \exp_stop_f:
+ #1 #2 #4 #5 \s_@@
+ \int_eval:n
+ {
+ ( "100 * \@@_tmp:w #1#2 + \@@_tmp:w #2#1 - "D7F7 ) * "400
+ + "100 * \@@_tmp:w #4#5 + \@@_tmp:w #5#4
+ }
+ \s_@@
+ \exp_after:wN \use_i:nnn
+ \else:
+ \@@_decode_utf_xvi_error:nNN { missing } #1#2
+ \fi:
+ \@@_decode_utf_xvi_pair:NN #4#5
+ }
+ \cs_new:Npn \@@_decode_utf_xvi_pair_end:Nw #1 \fi:
+ {
+ \fi:
+ \if_meaning:w \q_@@_nil #1
+ \else:
+ \@@_decode_utf_xvi_error:nNN { end } #1 \prg_do_nothing:
+ \fi:
+ \prg_break:
+ }
+ \cs_new:Npn \@@_decode_utf_xvi_extra:NNw #1#2 \s_@@ #3 \s_@@
+ { \@@_decode_utf_xvi_error:nNN { extra } #1#2 }
+ \cs_new:Npn \@@_decode_utf_xvi_error:nNN #1#2#3
+ {
+ \flag_raise:N \l_@@_error_flag
+ \flag_raise:c { l_@@_#1_flag }
+ #2 #3 \s_@@
+ \int_use:N \c_@@_replacement_char_int \s_@@
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% Restore the original catcodes of bytes $254$ and $255$.
+% \begin{macrocode}
+\group_end:
+% \end{macrocode}
+%
+% \subsubsection{\textsc{utf-32} support}
+%
+% The definitions are done in a category code regime where the bytes
+% $0$, $254$ and $255$ used by the byte order mark have catcode
+% \enquote{other}.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_other:N \^^00
+ \char_set_catcode_other:N \^^fe
+ \char_set_catcode_other:N \^^ff
+% \end{macrocode}
+%
+% \begin{macro}
+% {
+% \@@_convert_encode_utf32: ,
+% \@@_convert_encode_utf32be: ,
+% \@@_convert_encode_utf32le: ,
+% }
+% \begin{macro}[rEXP]
+% {
+% \@@_encode_utf_xxxii_be:n ,
+% \@@_encode_utf_xxxii_be_aux:nn ,
+% \@@_encode_utf_xxxii_le:n ,
+% \@@_encode_utf_xxxii_le_aux:nn ,
+% }
+% Convert each integer in the comma-list \cs{g_@@_result_tl} to a
+% sequence of four bytes. The functions for big-endian and
+% little-endian encodings are very similar, but the
+% \cs{@@_output_byte:n} instructions are reversed.
+% \begin{macrocode}
+ \cs_new_protected:cpn { @@_convert_encode_utf32: }
+ {
+ \@@_convert_gmap_internal:N \@@_encode_utf_xxxii_be:n
+ \tl_gput_left:Ne \g_@@_result_tl { ^^00 ^^00 ^^fe ^^ff }
+ }
+ \cs_new_protected:cpn { @@_convert_encode_utf32be: }
+ { \@@_convert_gmap_internal:N \@@_encode_utf_xxxii_be:n }
+ \cs_new_protected:cpn { @@_convert_encode_utf32le: }
+ { \@@_convert_gmap_internal:N \@@_encode_utf_xxxii_le:n }
+ \cs_new:Npn \@@_encode_utf_xxxii_be:n #1
+ {
+ \exp_args:Nf \@@_encode_utf_xxxii_be_aux:nn
+ { \int_div_truncate:nn {#1} { "100 } } {#1}
+ }
+ \cs_new:Npn \@@_encode_utf_xxxii_be_aux:nn #1#2
+ {
+ ^^00
+ \@@_output_byte_pair_be:n {#1}
+ \@@_output_byte:n { #2 - #1 * "100 }
+ }
+ \cs_new:Npn \@@_encode_utf_xxxii_le:n #1
+ {
+ \exp_args:Nf \@@_encode_utf_xxxii_le_aux:nn
+ { \int_div_truncate:nn {#1} { "100 } } {#1}
+ }
+ \cs_new:Npn \@@_encode_utf_xxxii_le_aux:nn #1#2
+ {
+ \@@_output_byte:n { #2 - #1 * "100 }
+ \@@_output_byte_pair_le:n {#1}
+ ^^00
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{@@_overflow, @@_end}
+% There can be no error when encoding in \textsc{utf-32}. When
+% decoding, the string may not have length $4n$, or it may contain
+% code points larger than \hexnum{10FFFF}. The latter case often
+% happens if the encoding was in fact not \textsc{utf-32}, because
+% most arbitrary strings are not valid in \textsc{utf-32}.
+% \begin{macrocode}
+ \flag_clear_new:N \l_@@_overflow_flag
+ \flag_clear_new:N \l_@@_end_flag
+ \msg_new:nnnn { str } { utf32-decode }
+ {
+ Invalid~UTF-32~string:
+ \exp_last_unbraced:Nf \use_none:n
+ {
+ \@@_if_flag_times:NT \l_@@_overflow_flag { ,~code~point~too~large }
+ \@@_if_flag_times:NT \l_@@_end_flag { ,~truncated~string }
+ }
+ .
+ }
+ {
+ In~the~UTF-32~encoding,~every~Unicode~character~
+ (in~the~range~[U+0000,~U+10FFFF])~is~encoded~as~4~bytes.
+ \flag_if_raised:NT \l_@@_overflow_flag
+ {
+ \\\\
+ LaTeX~came~across~a~code~point~larger~than~1114111,~
+ the~maximum~code~point~defined~by~Unicode.~
+ Perhaps~the~string~was~not~encoded~in~the~UTF-32~encoding?
+ }
+ \flag_if_raised:NT \l_@@_end_flag
+ {
+ \\\\
+ The~length~of~the~string~is~not~a~multiple~of~4.~
+ Perhaps~the~string~was~truncated?
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}
+% {
+% \@@_convert_decode_utf32: ,
+% \@@_convert_decode_utf32be: ,
+% \@@_convert_decode_utf32le: ,
+% }
+% \begin{macro}
+% {\@@_decode_utf_xxxii_bom:NNNN, \@@_decode_utf_xxxii:Nw}
+% \begin{macro}[rEXP]
+% {\@@_decode_utf_xxxii_loop:NNNN, \@@_decode_utf_xxxii_end:w}
+%
+% The structure is similar to \textsc{utf-16} decoding functions. If
+% the endianness is not given, test the first $4$ bytes of the string
+% (possibly \cs{s_@@_stop} if the string is too short) for the presence
+% of a byte-order mark. If there is a byte-order mark, use that
+% endianness, and remove the $4$ bytes, otherwise default to
+% big-endian, and leave the $4$ bytes in place. The
+% \cs{@@_decode_utf_xxxii:Nw} auxiliary receives $1$ or $2$ as its
+% first argument indicating endianness, and the string to convert as
+% its second argument (expanded or not). It sets \cs{@@_tmp:w} to
+% expand to the character code of either of its two arguments
+% depending on endianness, then triggers the \texttt{_loop} auxiliary
+% inside an \texttt{e}-expanding assignment to \cs{g_@@_result_tl}.
+%
+% The \texttt{_loop} auxiliary first checks for the end-of-string
+% marker \cs{s_@@_stop}, calling the \texttt{_end} auxiliary if
+% appropriate. Otherwise, leave the \meta{4~bytes} \cs{s_@@} behind,
+% then check that the code point is not overflowing: the leading byte
+% must be $0$, and the following byte at most $16$.
+%
+% In the ending code, we check that there remains no byte: there
+% should be nothing left until the first \cs{s_@@_stop}. Break the map.
+% \begin{macrocode}
+ \cs_new_protected:cpn { @@_convert_decode_utf32be: }
+ { \@@_decode_utf_xxxii:Nw 1 \g_@@_result_tl \s_@@_stop }
+ \cs_new_protected:cpn { @@_convert_decode_utf32le: }
+ { \@@_decode_utf_xxxii:Nw 2 \g_@@_result_tl \s_@@_stop }
+ \cs_new_protected:cpn { @@_convert_decode_utf32: }
+ {
+ \exp_after:wN \@@_decode_utf_xxxii_bom:NNNN \g_@@_result_tl
+ \s_@@_stop \s_@@_stop \s_@@_stop \s_@@_stop \s_@@_stop
+ }
+ \cs_new_protected:Npn \@@_decode_utf_xxxii_bom:NNNN #1#2#3#4
+ {
+ \str_if_eq:nnTF { #1#2#3#4 } { ^^ff ^^fe ^^00 ^^00 }
+ { \@@_decode_utf_xxxii:Nw 2 }
+ {
+ \str_if_eq:nnTF { #1#2#3#4 } { ^^00 ^^00 ^^fe ^^ff }
+ { \@@_decode_utf_xxxii:Nw 1 }
+ { \@@_decode_utf_xxxii:Nw 1 #1#2#3#4 }
+ }
+ }
+ \cs_new_protected:Npn \@@_decode_utf_xxxii:Nw #1#2 \s_@@_stop
+ {
+ \flag_clear:N \l_@@_overflow_flag
+ \flag_clear:N \l_@@_end_flag
+ \flag_clear:N \l_@@_error_flag
+ \cs_set:Npn \@@_tmp:w ##1 ##2 { ` ## #1 }
+ \__kernel_tl_gset:Nx \g_@@_result_tl
+ {
+ \exp_after:wN \@@_decode_utf_xxxii_loop:NNNN
+ #2 \s_@@_stop \s_@@_stop \s_@@_stop \s_@@_stop
+ \prg_break_point:
+ }
+ \@@_if_flag_error:Nne \l_@@_error_flag { utf32-decode } { }
+ }
+ \cs_new:Npn \@@_decode_utf_xxxii_loop:NNNN #1#2#3#4
+ {
+ \if_meaning:w \s_@@_stop #4
+ \exp_after:wN \@@_decode_utf_xxxii_end:w
+ \fi:
+ #1#2#3#4 \s_@@
+ \if_int_compare:w \@@_tmp:w #1#4 > \c_zero_int
+ \flag_raise:N \l_@@_overflow_flag
+ \flag_raise:N \l_@@_error_flag
+ \int_use:N \c_@@_replacement_char_int
+ \else:
+ \if_int_compare:w \@@_tmp:w #2#3 > 16 \exp_stop_f:
+ \flag_raise:N \l_@@_overflow_flag
+ \flag_raise:N \l_@@_error_flag
+ \int_use:N \c_@@_replacement_char_int
+ \else:
+ \int_eval:n
+ { \@@_tmp:w #2#3*"10000 + \@@_tmp:w #3#2*"100 + \@@_tmp:w #4#1 }
+ \fi:
+ \fi:
+ \s_@@
+ \@@_decode_utf_xxxii_loop:NNNN
+ }
+ \cs_new:Npn \@@_decode_utf_xxxii_end:w #1 \s_@@_stop
+ {
+ \tl_if_empty:nF {#1}
+ {
+ \flag_raise:N \l_@@_end_flag
+ \flag_raise:N \l_@@_error_flag
+ #1 \s_@@
+ \int_use:N \c_@@_replacement_char_int \s_@@
+ }
+ \prg_break:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% Restore the original catcodes of bytes $0$, $254$ and $255$.
+% \begin{macrocode}
+\group_end:
+% \end{macrocode}
+%
+% \subsection{PDF names and strings by expansion}
+%
+% \begin{macro}[EXP]{\str_convert_pdfname:n}
+% \begin{macro}[EXP]{\@@_convert_pdfname:n}
+% \begin{macro}[EXP]
+% {\@@_convert_pdfname_bytes:n, \@@_convert_pdfname_bytes_aux:n}
+% \begin{macro}[EXP]{\@@_convert_pdfname_bytes_aux:nnn}
+% To convert to PDF names by expansion, we work purely on UTF-8 input. The
+% first step is to make a string with \enquote{other} spaces,
+% after which we use a simple token-by-token approach. In Unicode
+% engines, we break down everything before one-byte codepoints, but for
+% $8$-bit engines there is no need to worry. Actual escaping is covered
+% by the same code as used in the non-expandable route.
+% \begin{macrocode}
+\cs_new:Npn \str_convert_pdfname:n #1
+ {
+ \exp_args:Ne \tl_to_str:n
+ { \str_map_function:nN {#1} \@@_convert_pdfname:n }
+ }
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_new:Npn \@@_convert_pdfname:n #1
+ {
+ \int_compare:nNnTF { `#1 } > { "7F }
+ { \@@_convert_pdfname_bytes:n {#1} }
+ { \@@_escape_name_char:n {#1} }
+ }
+ \cs_new:Npn \@@_convert_pdfname_bytes:n #1
+ {
+ \exp_args:Ne \@@_convert_pdfname_bytes_aux:n
+ { \__kernel_codepoint_to_bytes:n {`#1} }
+ }
+ \cs_new:Npn \@@_convert_pdfname_bytes_aux:n #1
+ { \@@_convert_pdfname_bytes_aux:nnnn #1 }
+ \cs_new:Npe \@@_convert_pdfname_bytes_aux:nnnn #1#2#3#4
+ {
+ \c_hash_str \exp_not:N \@@_output_hexadecimal:n {#1}
+ \c_hash_str \exp_not:N \@@_output_hexadecimal:n {#2}
+ \exp_not:N \tl_if_blank:nF {#3}
+ {
+ \c_hash_str \exp_not:N \@@_output_hexadecimal:n {#3}
+ \exp_not:N \tl_if_blank:nF {#4}
+ {
+ \c_hash_str \exp_not:N \@@_output_hexadecimal:n {#4}
+ }
+ }
+ }
+ }
+ { \cs_new_eq:NN \@@_convert_pdfname:n \@@_escape_name_char:n }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \subsubsection{\textsc{iso 8859} support}
+%
+% The \textsc{iso-8859-1} encoding exactly matches with the $256$ first
+% Unicode characters. For other 8-bit encodings of the \textsc{iso-8859}
+% family, we keep track only of differences, and of unassigned bytes.
+% \begin{macrocode}
+%<*iso88591>
+\@@_declare_eight_bit_encoding:nnnn { iso88591 } { 256 }
+ {
+ }
+ {
+ }
+%</iso88591>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso88592>
+\@@_declare_eight_bit_encoding:nnnn { iso88592 } { 399 }
+ {
+ { A1 } { 0104 }
+ { A2 } { 02D8 }
+ { A3 } { 0141 }
+ { A5 } { 013D }
+ { A6 } { 015A }
+ { A9 } { 0160 }
+ { AA } { 015E }
+ { AB } { 0164 }
+ { AC } { 0179 }
+ { AE } { 017D }
+ { AF } { 017B }
+ { B1 } { 0105 }
+ { B2 } { 02DB }
+ { B3 } { 0142 }
+ { B5 } { 013E }
+ { B6 } { 015B }
+ { B7 } { 02C7 }
+ { B9 } { 0161 }
+ { BA } { 015F }
+ { BB } { 0165 }
+ { BC } { 017A }
+ { BD } { 02DD }
+ { BE } { 017E }
+ { BF } { 017C }
+ { C0 } { 0154 }
+ { C3 } { 0102 }
+ { C5 } { 0139 }
+ { C6 } { 0106 }
+ { C8 } { 010C }
+ { CA } { 0118 }
+ { CC } { 011A }
+ { CF } { 010E }
+ { D0 } { 0110 }
+ { D1 } { 0143 }
+ { D2 } { 0147 }
+ { D5 } { 0150 }
+ { D8 } { 0158 }
+ { D9 } { 016E }
+ { DB } { 0170 }
+ { DE } { 0162 }
+ { E0 } { 0155 }
+ { E3 } { 0103 }
+ { E5 } { 013A }
+ { E6 } { 0107 }
+ { E8 } { 010D }
+ { EA } { 0119 }
+ { EC } { 011B }
+ { EF } { 010F }
+ { F0 } { 0111 }
+ { F1 } { 0144 }
+ { F2 } { 0148 }
+ { F5 } { 0151 }
+ { F8 } { 0159 }
+ { F9 } { 016F }
+ { FB } { 0171 }
+ { FE } { 0163 }
+ { FF } { 02D9 }
+ }
+ {
+ }
+%</iso88592>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso88593>
+\@@_declare_eight_bit_encoding:nnnn { iso88593 } { 384 }
+ {
+ { A1 } { 0126 }
+ { A2 } { 02D8 }
+ { A6 } { 0124 }
+ { A9 } { 0130 }
+ { AA } { 015E }
+ { AB } { 011E }
+ { AC } { 0134 }
+ { AF } { 017B }
+ { B1 } { 0127 }
+ { B6 } { 0125 }
+ { B9 } { 0131 }
+ { BA } { 015F }
+ { BB } { 011F }
+ { BC } { 0135 }
+ { BF } { 017C }
+ { C5 } { 010A }
+ { C6 } { 0108 }
+ { D5 } { 0120 }
+ { D8 } { 011C }
+ { DD } { 016C }
+ { DE } { 015C }
+ { E5 } { 010B }
+ { E6 } { 0109 }
+ { F5 } { 0121 }
+ { F8 } { 011D }
+ { FD } { 016D }
+ { FE } { 015D }
+ { FF } { 02D9 }
+ }
+ {
+ { A5 }
+ { AE }
+ { BE }
+ { C3 }
+ { D0 }
+ { E3 }
+ { F0 }
+ }
+%</iso88593>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso88594>
+\@@_declare_eight_bit_encoding:nnnn { iso88594 } { 383 }
+ {
+ { A1 } { 0104 }
+ { A2 } { 0138 }
+ { A3 } { 0156 }
+ { A5 } { 0128 }
+ { A6 } { 013B }
+ { A9 } { 0160 }
+ { AA } { 0112 }
+ { AB } { 0122 }
+ { AC } { 0166 }
+ { AE } { 017D }
+ { B1 } { 0105 }
+ { B2 } { 02DB }
+ { B3 } { 0157 }
+ { B5 } { 0129 }
+ { B6 } { 013C }
+ { B7 } { 02C7 }
+ { B9 } { 0161 }
+ { BA } { 0113 }
+ { BB } { 0123 }
+ { BC } { 0167 }
+ { BD } { 014A }
+ { BE } { 017E }
+ { BF } { 014B }
+ { C0 } { 0100 }
+ { C7 } { 012E }
+ { C8 } { 010C }
+ { CA } { 0118 }
+ { CC } { 0116 }
+ { CF } { 012A }
+ { D0 } { 0110 }
+ { D1 } { 0145 }
+ { D2 } { 014C }
+ { D3 } { 0136 }
+ { D9 } { 0172 }
+ { DD } { 0168 }
+ { DE } { 016A }
+ { E0 } { 0101 }
+ { E7 } { 012F }
+ { E8 } { 010D }
+ { EA } { 0119 }
+ { EC } { 0117 }
+ { EF } { 012B }
+ { F0 } { 0111 }
+ { F1 } { 0146 }
+ { F2 } { 014D }
+ { F3 } { 0137 }
+ { F9 } { 0173 }
+ { FD } { 0169 }
+ { FE } { 016B }
+ { FF } { 02D9 }
+ }
+ {
+ }
+%</iso88594>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso88595>
+\@@_declare_eight_bit_encoding:nnnn { iso88595 } { 374 }
+ {
+ { A1 } { 0401 }
+ { A2 } { 0402 }
+ { A3 } { 0403 }
+ { A4 } { 0404 }
+ { A5 } { 0405 }
+ { A6 } { 0406 }
+ { A7 } { 0407 }
+ { A8 } { 0408 }
+ { A9 } { 0409 }
+ { AA } { 040A }
+ { AB } { 040B }
+ { AC } { 040C }
+ { AE } { 040E }
+ { AF } { 040F }
+ { B0 } { 0410 }
+ { B1 } { 0411 }
+ { B2 } { 0412 }
+ { B3 } { 0413 }
+ { B4 } { 0414 }
+ { B5 } { 0415 }
+ { B6 } { 0416 }
+ { B7 } { 0417 }
+ { B8 } { 0418 }
+ { B9 } { 0419 }
+ { BA } { 041A }
+ { BB } { 041B }
+ { BC } { 041C }
+ { BD } { 041D }
+ { BE } { 041E }
+ { BF } { 041F }
+ { C0 } { 0420 }
+ { C1 } { 0421 }
+ { C2 } { 0422 }
+ { C3 } { 0423 }
+ { C4 } { 0424 }
+ { C5 } { 0425 }
+ { C6 } { 0426 }
+ { C7 } { 0427 }
+ { C8 } { 0428 }
+ { C9 } { 0429 }
+ { CA } { 042A }
+ { CB } { 042B }
+ { CC } { 042C }
+ { CD } { 042D }
+ { CE } { 042E }
+ { CF } { 042F }
+ { D0 } { 0430 }
+ { D1 } { 0431 }
+ { D2 } { 0432 }
+ { D3 } { 0433 }
+ { D4 } { 0434 }
+ { D5 } { 0435 }
+ { D6 } { 0436 }
+ { D7 } { 0437 }
+ { D8 } { 0438 }
+ { D9 } { 0439 }
+ { DA } { 043A }
+ { DB } { 043B }
+ { DC } { 043C }
+ { DD } { 043D }
+ { DE } { 043E }
+ { DF } { 043F }
+ { E0 } { 0440 }
+ { E1 } { 0441 }
+ { E2 } { 0442 }
+ { E3 } { 0443 }
+ { E4 } { 0444 }
+ { E5 } { 0445 }
+ { E6 } { 0446 }
+ { E7 } { 0447 }
+ { E8 } { 0448 }
+ { E9 } { 0449 }
+ { EA } { 044A }
+ { EB } { 044B }
+ { EC } { 044C }
+ { ED } { 044D }
+ { EE } { 044E }
+ { EF } { 044F }
+ { F0 } { 2116 }
+ { F1 } { 0451 }
+ { F2 } { 0452 }
+ { F3 } { 0453 }
+ { F4 } { 0454 }
+ { F5 } { 0455 }
+ { F6 } { 0456 }
+ { F7 } { 0457 }
+ { F8 } { 0458 }
+ { F9 } { 0459 }
+ { FA } { 045A }
+ { FB } { 045B }
+ { FC } { 045C }
+ { FD } { 00A7 }
+ { FE } { 045E }
+ { FF } { 045F }
+ }
+ {
+ }
+%</iso88595>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso88596>
+\@@_declare_eight_bit_encoding:nnnn { iso88596 } { 344 }
+ {
+ { AC } { 060C }
+ { BB } { 061B }
+ { BF } { 061F }
+ { C1 } { 0621 }
+ { C2 } { 0622 }
+ { C3 } { 0623 }
+ { C4 } { 0624 }
+ { C5 } { 0625 }
+ { C6 } { 0626 }
+ { C7 } { 0627 }
+ { C8 } { 0628 }
+ { C9 } { 0629 }
+ { CA } { 062A }
+ { CB } { 062B }
+ { CC } { 062C }
+ { CD } { 062D }
+ { CE } { 062E }
+ { CF } { 062F }
+ { D0 } { 0630 }
+ { D1 } { 0631 }
+ { D2 } { 0632 }
+ { D3 } { 0633 }
+ { D4 } { 0634 }
+ { D5 } { 0635 }
+ { D6 } { 0636 }
+ { D7 } { 0637 }
+ { D8 } { 0638 }
+ { D9 } { 0639 }
+ { DA } { 063A }
+ { E0 } { 0640 }
+ { E1 } { 0641 }
+ { E2 } { 0642 }
+ { E3 } { 0643 }
+ { E4 } { 0644 }
+ { E5 } { 0645 }
+ { E6 } { 0646 }
+ { E7 } { 0647 }
+ { E8 } { 0648 }
+ { E9 } { 0649 }
+ { EA } { 064A }
+ { EB } { 064B }
+ { EC } { 064C }
+ { ED } { 064D }
+ { EE } { 064E }
+ { EF } { 064F }
+ { F0 } { 0650 }
+ { F1 } { 0651 }
+ { F2 } { 0652 }
+ }
+ {
+ { A1 }
+ { A2 }
+ { A3 }
+ { A5 }
+ { A6 }
+ { A7 }
+ { A8 }
+ { A9 }
+ { AA }
+ { AB }
+ { AE }
+ { AF }
+ { B0 }
+ { B1 }
+ { B2 }
+ { B3 }
+ { B4 }
+ { B5 }
+ { B6 }
+ { B7 }
+ { B8 }
+ { B9 }
+ { BA }
+ { BC }
+ { BD }
+ { BE }
+ { C0 }
+ { DB }
+ { DC }
+ { DD }
+ { DE }
+ { DF }
+ }
+%</iso88596>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso88597>
+\@@_declare_eight_bit_encoding:nnnn { iso88597 } { 498 }
+ {
+ { A1 } { 2018 }
+ { A2 } { 2019 }
+ { A4 } { 20AC }
+ { A5 } { 20AF }
+ { AA } { 037A }
+ { AF } { 2015 }
+ { B4 } { 0384 }
+ { B5 } { 0385 }
+ { B6 } { 0386 }
+ { B8 } { 0388 }
+ { B9 } { 0389 }
+ { BA } { 038A }
+ { BC } { 038C }
+ { BE } { 038E }
+ { BF } { 038F }
+ { C0 } { 0390 }
+ { C1 } { 0391 }
+ { C2 } { 0392 }
+ { C3 } { 0393 }
+ { C4 } { 0394 }
+ { C5 } { 0395 }
+ { C6 } { 0396 }
+ { C7 } { 0397 }
+ { C8 } { 0398 }
+ { C9 } { 0399 }
+ { CA } { 039A }
+ { CB } { 039B }
+ { CC } { 039C }
+ { CD } { 039D }
+ { CE } { 039E }
+ { CF } { 039F }
+ { D0 } { 03A0 }
+ { D1 } { 03A1 }
+ { D3 } { 03A3 }
+ { D4 } { 03A4 }
+ { D5 } { 03A5 }
+ { D6 } { 03A6 }
+ { D7 } { 03A7 }
+ { D8 } { 03A8 }
+ { D9 } { 03A9 }
+ { DA } { 03AA }
+ { DB } { 03AB }
+ { DC } { 03AC }
+ { DD } { 03AD }
+ { DE } { 03AE }
+ { DF } { 03AF }
+ { E0 } { 03B0 }
+ { E1 } { 03B1 }
+ { E2 } { 03B2 }
+ { E3 } { 03B3 }
+ { E4 } { 03B4 }
+ { E5 } { 03B5 }
+ { E6 } { 03B6 }
+ { E7 } { 03B7 }
+ { E8 } { 03B8 }
+ { E9 } { 03B9 }
+ { EA } { 03BA }
+ { EB } { 03BB }
+ { EC } { 03BC }
+ { ED } { 03BD }
+ { EE } { 03BE }
+ { EF } { 03BF }
+ { F0 } { 03C0 }
+ { F1 } { 03C1 }
+ { F2 } { 03C2 }
+ { F3 } { 03C3 }
+ { F4 } { 03C4 }
+ { F5 } { 03C5 }
+ { F6 } { 03C6 }
+ { F7 } { 03C7 }
+ { F8 } { 03C8 }
+ { F9 } { 03C9 }
+ { FA } { 03CA }
+ { FB } { 03CB }
+ { FC } { 03CC }
+ { FD } { 03CD }
+ { FE } { 03CE }
+ }
+ {
+ { AE }
+ { D2 }
+ }
+%</iso88597>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso88598>
+\@@_declare_eight_bit_encoding:nnnn { iso88598 } { 308 }
+ {
+ { AA } { 00D7 }
+ { BA } { 00F7 }
+ { DF } { 2017 }
+ { E0 } { 05D0 }
+ { E1 } { 05D1 }
+ { E2 } { 05D2 }
+ { E3 } { 05D3 }
+ { E4 } { 05D4 }
+ { E5 } { 05D5 }
+ { E6 } { 05D6 }
+ { E7 } { 05D7 }
+ { E8 } { 05D8 }
+ { E9 } { 05D9 }
+ { EA } { 05DA }
+ { EB } { 05DB }
+ { EC } { 05DC }
+ { ED } { 05DD }
+ { EE } { 05DE }
+ { EF } { 05DF }
+ { F0 } { 05E0 }
+ { F1 } { 05E1 }
+ { F2 } { 05E2 }
+ { F3 } { 05E3 }
+ { F4 } { 05E4 }
+ { F5 } { 05E5 }
+ { F6 } { 05E6 }
+ { F7 } { 05E7 }
+ { F8 } { 05E8 }
+ { F9 } { 05E9 }
+ { FA } { 05EA }
+ { FD } { 200E }
+ { FE } { 200F }
+ }
+ {
+ { A1 }
+ { BF }
+ { C0 }
+ { C1 }
+ { C2 }
+ { C3 }
+ { C4 }
+ { C5 }
+ { C6 }
+ { C7 }
+ { C8 }
+ { C9 }
+ { CA }
+ { CB }
+ { CC }
+ { CD }
+ { CE }
+ { CF }
+ { D0 }
+ { D1 }
+ { D2 }
+ { D3 }
+ { D4 }
+ { D5 }
+ { D6 }
+ { D7 }
+ { D8 }
+ { D9 }
+ { DA }
+ { DB }
+ { DC }
+ { DD }
+ { DE }
+ { FB }
+ { FC }
+ }
+%</iso88598>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso88599>
+\@@_declare_eight_bit_encoding:nnnn { iso88599 } { 352 }
+ {
+ { D0 } { 011E }
+ { DD } { 0130 }
+ { DE } { 015E }
+ { F0 } { 011F }
+ { FD } { 0131 }
+ { FE } { 015F }
+ }
+ {
+ }
+%</iso88599>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso885910>
+\@@_declare_eight_bit_encoding:nnnn { iso885910 } { 383 }
+ {
+ { A1 } { 0104 }
+ { A2 } { 0112 }
+ { A3 } { 0122 }
+ { A4 } { 012A }
+ { A5 } { 0128 }
+ { A6 } { 0136 }
+ { A8 } { 013B }
+ { A9 } { 0110 }
+ { AA } { 0160 }
+ { AB } { 0166 }
+ { AC } { 017D }
+ { AE } { 016A }
+ { AF } { 014A }
+ { B1 } { 0105 }
+ { B2 } { 0113 }
+ { B3 } { 0123 }
+ { B4 } { 012B }
+ { B5 } { 0129 }
+ { B6 } { 0137 }
+ { B8 } { 013C }
+ { B9 } { 0111 }
+ { BA } { 0161 }
+ { BB } { 0167 }
+ { BC } { 017E }
+ { BD } { 2015 }
+ { BE } { 016B }
+ { BF } { 014B }
+ { C0 } { 0100 }
+ { C7 } { 012E }
+ { C8 } { 010C }
+ { CA } { 0118 }
+ { CC } { 0116 }
+ { D1 } { 0145 }
+ { D2 } { 014C }
+ { D7 } { 0168 }
+ { D9 } { 0172 }
+ { E0 } { 0101 }
+ { E7 } { 012F }
+ { E8 } { 010D }
+ { EA } { 0119 }
+ { EC } { 0117 }
+ { F1 } { 0146 }
+ { F2 } { 014D }
+ { F7 } { 0169 }
+ { F9 } { 0173 }
+ { FF } { 0138 }
+ }
+ {
+ }
+%</iso885910>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso885911>
+\@@_declare_eight_bit_encoding:nnnn { iso885911 } { 369 }
+ {
+ { A1 } { 0E01 }
+ { A2 } { 0E02 }
+ { A3 } { 0E03 }
+ { A4 } { 0E04 }
+ { A5 } { 0E05 }
+ { A6 } { 0E06 }
+ { A7 } { 0E07 }
+ { A8 } { 0E08 }
+ { A9 } { 0E09 }
+ { AA } { 0E0A }
+ { AB } { 0E0B }
+ { AC } { 0E0C }
+ { AD } { 0E0D }
+ { AE } { 0E0E }
+ { AF } { 0E0F }
+ { B0 } { 0E10 }
+ { B1 } { 0E11 }
+ { B2 } { 0E12 }
+ { B3 } { 0E13 }
+ { B4 } { 0E14 }
+ { B5 } { 0E15 }
+ { B6 } { 0E16 }
+ { B7 } { 0E17 }
+ { B8 } { 0E18 }
+ { B9 } { 0E19 }
+ { BA } { 0E1A }
+ { BB } { 0E1B }
+ { BC } { 0E1C }
+ { BD } { 0E1D }
+ { BE } { 0E1E }
+ { BF } { 0E1F }
+ { C0 } { 0E20 }
+ { C1 } { 0E21 }
+ { C2 } { 0E22 }
+ { C3 } { 0E23 }
+ { C4 } { 0E24 }
+ { C5 } { 0E25 }
+ { C6 } { 0E26 }
+ { C7 } { 0E27 }
+ { C8 } { 0E28 }
+ { C9 } { 0E29 }
+ { CA } { 0E2A }
+ { CB } { 0E2B }
+ { CC } { 0E2C }
+ { CD } { 0E2D }
+ { CE } { 0E2E }
+ { CF } { 0E2F }
+ { D0 } { 0E30 }
+ { D1 } { 0E31 }
+ { D2 } { 0E32 }
+ { D3 } { 0E33 }
+ { D4 } { 0E34 }
+ { D5 } { 0E35 }
+ { D6 } { 0E36 }
+ { D7 } { 0E37 }
+ { D8 } { 0E38 }
+ { D9 } { 0E39 }
+ { DA } { 0E3A }
+ { DF } { 0E3F }
+ { E0 } { 0E40 }
+ { E1 } { 0E41 }
+ { E2 } { 0E42 }
+ { E3 } { 0E43 }
+ { E4 } { 0E44 }
+ { E5 } { 0E45 }
+ { E6 } { 0E46 }
+ { E7 } { 0E47 }
+ { E8 } { 0E48 }
+ { E9 } { 0E49 }
+ { EA } { 0E4A }
+ { EB } { 0E4B }
+ { EC } { 0E4C }
+ { ED } { 0E4D }
+ { EE } { 0E4E }
+ { EF } { 0E4F }
+ { F0 } { 0E50 }
+ { F1 } { 0E51 }
+ { F2 } { 0E52 }
+ { F3 } { 0E53 }
+ { F4 } { 0E54 }
+ { F5 } { 0E55 }
+ { F6 } { 0E56 }
+ { F7 } { 0E57 }
+ { F8 } { 0E58 }
+ { F9 } { 0E59 }
+ { FA } { 0E5A }
+ { FB } { 0E5B }
+ }
+ {
+ { DB }
+ { DC }
+ { DD }
+ { DE }
+ }
+%</iso885911>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso885913>
+\@@_declare_eight_bit_encoding:nnnn { iso885913 } { 399 }
+ {
+ { A1 } { 201D }
+ { A5 } { 201E }
+ { A8 } { 00D8 }
+ { AA } { 0156 }
+ { AF } { 00C6 }
+ { B4 } { 201C }
+ { B8 } { 00F8 }
+ { BA } { 0157 }
+ { BF } { 00E6 }
+ { C0 } { 0104 }
+ { C1 } { 012E }
+ { C2 } { 0100 }
+ { C3 } { 0106 }
+ { C6 } { 0118 }
+ { C7 } { 0112 }
+ { C8 } { 010C }
+ { CA } { 0179 }
+ { CB } { 0116 }
+ { CC } { 0122 }
+ { CD } { 0136 }
+ { CE } { 012A }
+ { CF } { 013B }
+ { D0 } { 0160 }
+ { D1 } { 0143 }
+ { D2 } { 0145 }
+ { D4 } { 014C }
+ { D8 } { 0172 }
+ { D9 } { 0141 }
+ { DA } { 015A }
+ { DB } { 016A }
+ { DD } { 017B }
+ { DE } { 017D }
+ { E0 } { 0105 }
+ { E1 } { 012F }
+ { E2 } { 0101 }
+ { E3 } { 0107 }
+ { E6 } { 0119 }
+ { E7 } { 0113 }
+ { E8 } { 010D }
+ { EA } { 017A }
+ { EB } { 0117 }
+ { EC } { 0123 }
+ { ED } { 0137 }
+ { EE } { 012B }
+ { EF } { 013C }
+ { F0 } { 0161 }
+ { F1 } { 0144 }
+ { F2 } { 0146 }
+ { F4 } { 014D }
+ { F8 } { 0173 }
+ { F9 } { 0142 }
+ { FA } { 015B }
+ { FB } { 016B }
+ { FD } { 017C }
+ { FE } { 017E }
+ { FF } { 2019 }
+ }
+ {
+ }
+%</iso885913>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso885914>
+\@@_declare_eight_bit_encoding:nnnn { iso885914 } { 529 }
+ {
+ { A1 } { 1E02 }
+ { A2 } { 1E03 }
+ { A4 } { 010A }
+ { A5 } { 010B }
+ { A6 } { 1E0A }
+ { A8 } { 1E80 }
+ { AA } { 1E82 }
+ { AB } { 1E0B }
+ { AC } { 1EF2 }
+ { AF } { 0178 }
+ { B0 } { 1E1E }
+ { B1 } { 1E1F }
+ { B2 } { 0120 }
+ { B3 } { 0121 }
+ { B4 } { 1E40 }
+ { B5 } { 1E41 }
+ { B7 } { 1E56 }
+ { B8 } { 1E81 }
+ { B9 } { 1E57 }
+ { BA } { 1E83 }
+ { BB } { 1E60 }
+ { BC } { 1EF3 }
+ { BD } { 1E84 }
+ { BE } { 1E85 }
+ { BF } { 1E61 }
+ { D0 } { 0174 }
+ { D7 } { 1E6A }
+ { DE } { 0176 }
+ { F0 } { 0175 }
+ { F7 } { 1E6B }
+ { FE } { 0177 }
+ }
+ {
+ }
+%</iso885914>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso885915>
+\@@_declare_eight_bit_encoding:nnnn { iso885915 } { 383 }
+ {
+ { A4 } { 20AC }
+ { A6 } { 0160 }
+ { A8 } { 0161 }
+ { B4 } { 017D }
+ { B8 } { 017E }
+ { BC } { 0152 }
+ { BD } { 0153 }
+ { BE } { 0178 }
+ }
+ {
+ }
+%</iso885915>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso885916>
+\@@_declare_eight_bit_encoding:nnnn { iso885916 } { 558 }
+ {
+ { A1 } { 0104 }
+ { A2 } { 0105 }
+ { A3 } { 0141 }
+ { A4 } { 20AC }
+ { A5 } { 201E }
+ { A6 } { 0160 }
+ { A8 } { 0161 }
+ { AA } { 0218 }
+ { AC } { 0179 }
+ { AE } { 017A }
+ { AF } { 017B }
+ { B2 } { 010C }
+ { B3 } { 0142 }
+ { B4 } { 017D }
+ { B5 } { 201D }
+ { B8 } { 017E }
+ { B9 } { 010D }
+ { BA } { 0219 }
+ { BC } { 0152 }
+ { BD } { 0153 }
+ { BE } { 0178 }
+ { BF } { 017C }
+ { C3 } { 0102 }
+ { C5 } { 0106 }
+ { D0 } { 0110 }
+ { D1 } { 0143 }
+ { D5 } { 0150 }
+ { D7 } { 015A }
+ { D8 } { 0170 }
+ { DD } { 0118 }
+ { DE } { 021A }
+ { E3 } { 0103 }
+ { E5 } { 0107 }
+ { F0 } { 0111 }
+ { F1 } { 0144 }
+ { F5 } { 0151 }
+ { F7 } { 015B }
+ { F8 } { 0171 }
+ { FD } { 0119 }
+ { FE } { 021B }
+ }
+ {
+ }
+%</iso885916>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3str.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3str.dtx
new file mode 100644
index 00000000000..efc24fe8eae
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3str.dtx
@@ -0,0 +1,2226 @@
+% \iffalse meta-comment
+%
+%% File: l3str.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3str} module\\ Strings^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \TeX{} associates each character with a category code: as such, there is no
+% concept of a \enquote{string} as commonly understood in many other
+% programming languages. However, there are places where we wish to manipulate
+% token lists while in some sense \enquote{ignoring} category codes: this is
+% done by treating token lists as strings in a \TeX{} sense.
+%
+% A \TeX{} string (and thus an \pkg{expl3} string) is a series of characters
+% which have category code $12$ (\enquote{other}) with the exception of
+% space characters which have category code $10$ (\enquote{space}). Thus
+% at a technical level, a \TeX{} string is a token list with the appropriate
+% category codes. In this documentation, these are simply referred to as
+% strings.
+%
+% String variables are simply specialised token lists, but by convention
+% should be named with the suffix \texttt{\ldots{}str}. Such variables
+% should contain characters with category code $12$ (other), except
+% spaces, which have category code $10$ (blank space). All the
+% functions in this module which accept a token list argument first
+% convert it to a string using \cs{tl_to_str:n} for internal processing,
+% and do not treat a token list or the corresponding string
+% representation differently.
+%
+% As a string is a subset of the more general token list, it is sometimes unclear
+% when one should be used over the other.
+% Use a string variable for data that isn't primarily intended for typesetting
+% and for which a level of protection from unwanted expansion is suitable.
+% This data type simplifies comparison of variables since there are no concerns
+% about expansion of their contents.
+%
+% The functions \cs{cs_to_str:N}, \cs{tl_to_str:n}, \cs{tl_to_str:N} and
+% \cs{token_to_str:N} (and variants) generate strings from the appropriate
+% input: these are documented in \pkg{l3basics}, \pkg{l3tl} and \pkg{l3token},
+% respectively.
+%
+% Most expandable functions in this module come in three flavours:
+% \begin{itemize}
+% \item \cs[no-index]{str_\ldots{}:N}, which expect a token list or string
+% variable as their argument;
+% \item \cs[no-index]{str_\ldots{}:n}, taking any token list (or string) as an
+% argument;
+% \item \cs[no-index]{str_\ldots{}_ignore_spaces:n}, which ignores any space
+% encountered during the operation: these functions are typically
+% faster than those which take care of escaping spaces
+% appropriately.
+% \end{itemize}
+%
+% \section{Creating and initialising string variables}
+%
+% \begin{function}[added = 2015-09-18]{\str_new:N, \str_new:c}
+% \begin{syntax}
+% \cs{str_new:N} \meta{str~var}
+% \end{syntax}
+% Creates a new \meta{str~var} or raises an error if the name is
+% already taken. The declaration is global. The \meta{str~var} is
+% initially empty.
+% \end{function}
+%
+% \begin{function}[added = 2015-09-18, updated = 2018-07-28]
+% {
+% \str_const:Nn, \str_const:NV, \str_const:Ne,
+% \str_const:cn, \str_const:cV, \str_const:ce
+% }
+% \begin{syntax}
+% \cs{str_const:Nn} \meta{str~var} \Arg{token list}
+% \end{syntax}
+% Creates a new constant \meta{str~var} or raises an error if the name
+% is already taken. The value of the \meta{str~var} is set
+% globally to the \meta{token list}, converted to a string.
+% \end{function}
+%
+% \begin{function}[added = 2015-09-18]
+% {\str_clear:N, \str_clear:c, \str_gclear:N, \str_gclear:c}
+% \begin{syntax}
+% \cs{str_clear:N} \meta{str~var}
+% \end{syntax}
+% Clears the content of the \meta{str~var}.
+% \end{function}
+%
+% \begin{function}[added = 2015-09-18]
+% {
+% \str_clear_new:N, \str_clear_new:c,
+% \str_gclear_new:N, \str_gclear_new:c
+% }
+% \begin{syntax}
+% \cs{str_clear_new:N} \meta{str~var}
+% \end{syntax}
+% Ensures that the \meta{str~var} exists globally by applying
+% \cs{str_new:N} if necessary, then applies
+% \cs[index=str_clear:N]{str_(g)clear:N} to leave
+% the \meta{str~var} empty.
+% \end{function}
+%
+% \begin{function}[added = 2015-09-18]
+% {
+% \str_set_eq:NN, \str_set_eq:cN, \str_set_eq:Nc, \str_set_eq:cc,
+% \str_gset_eq:NN, \str_gset_eq:cN, \str_gset_eq:Nc, \str_gset_eq:cc
+% }
+% \begin{syntax}
+% \cs{str_set_eq:NN} \meta{str~var_1} \meta{str~var_2}
+% \end{syntax}
+% Sets the content of \meta{str~var_1} equal to that of
+% \meta{str~var_2}.
+% \end{function}
+%
+% \begin{function}[added = 2017-10-08]
+% {
+% \str_concat:NNN, \str_concat:ccc,
+% \str_gconcat:NNN, \str_gconcat:ccc
+% }
+% \begin{syntax}
+% \cs{str_concat:NNN} \meta{str~var_1} \meta{str~var_2} \meta{str~var_3}
+% \end{syntax}
+% Concatenates the content of \meta{str~var_2} and \meta{str~var_3}
+% together and saves the result in \meta{str~var_1}. The \meta{str~var_2}
+% is placed at the left side of the new string variable.
+% The \meta{str~var_2} and \meta{str~var_3} must indeed be strings, as
+% this function does not convert their contents to a string.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added = 2015-09-18]
+% {\str_if_exist:N, \str_if_exist:c}
+% \begin{syntax}
+% \cs{str_if_exist_p:N} \meta{str~var}
+% \cs{str_if_exist:NTF} \meta{str~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{str~var} is currently defined. This does not
+% check that the \meta{str~var} really is a string.
+% \end{function}
+%
+% \section{Adding data to string variables}
+%
+% \begin{function}[added = 2015-09-18, updated = 2018-07-28]
+% {
+% \str_set:Nn, \str_set:NV, \str_set:Ne,
+% \str_set:cn, \str_set:cV, \str_set:ce,
+% \str_gset:Nn, \str_gset:NV, \str_gset:Ne,
+% \str_gset:cn, \str_gset:cV, \str_gset:ce
+% }
+% \begin{syntax}
+% \cs{str_set:Nn} \meta{str var} \Arg{token list}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string}, and stores the
+% result in \meta{str var}.
+% \end{function}
+%
+% \begin{function}[added = 2015-09-18, updated = 2018-07-28]
+% {
+% \str_put_left:Nn, \str_put_left:NV, \str_put_left:Ne,
+% \str_put_left:cn, \str_put_left:cV, \str_put_left:ce,
+% \str_gput_left:Nn, \str_gput_left:NV, \str_gput_left:Ne,
+% \str_gput_left:cn, \str_gput_left:cV, \str_gput_left:ce
+% }
+% \begin{syntax}
+% \cs{str_put_left:Nn} \meta{str var} \Arg{token list}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string}, and prepends the
+% result to \meta{str var}. The current contents of the \meta{str
+% var} are not automatically converted to a string.
+% \end{function}
+%
+% \begin{function}[added = 2015-09-18, updated = 2018-07-28]
+% {
+% \str_put_right:Nn, \str_put_right:NV, \str_put_right:Ne,
+% \str_put_right:cn, \str_put_right:cV, \str_put_right:Ne,
+% \str_gput_right:Nn, \str_gput_right:NV, \str_gput_right:Ne,
+% \str_gput_right:cn, \str_gput_right:cV, \str_gput_right:ce
+% }
+% \begin{syntax}
+% \cs{str_put_right:Nn} \meta{str var} \Arg{token list}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string}, and appends the
+% result to \meta{str var}. The current contents of the \meta{str
+% var} are not automatically converted to a string.
+% \end{function}
+%
+% \section{String conditionals}
+%
+% \begin{function}[EXP,pTF, added = 2015-09-18, updated = 2022-03-21]
+% {\str_if_empty:N, \str_if_empty:c, \str_if_empty:n}
+% \begin{syntax}
+% \cs{str_if_empty_p:N} \meta{str~var}
+% \cs{str_if_empty:NTF} \meta{str~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{string variable} is entirely empty
+% (\emph{i.e.}~contains no characters at all).
+% \end{function}
+%
+% \begin{function}[EXP,pTF, added = 2015-09-18]
+% {\str_if_eq:NN, \str_if_eq:Nc, \str_if_eq:cN, \str_if_eq:cc}
+% \begin{syntax}
+% \cs{str_if_eq_p:NN} \meta{str~var_1} \meta{str~var_2}
+% \cs{str_if_eq:NNTF} \meta{str~var_1} \meta{str~var_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Compares the content of two \meta{str variables} and
+% is logically \texttt{true} if the two contain the same characters
+% in the same order. See \cs{tl_if_eq:NNTF} to compare tokens
+% (including their category codes) rather than characters.
+% \end{function}
+%
+% \begin{function}[EXP,pTF, updated = 2018-06-18]
+% {
+% \str_if_eq:nn, \str_if_eq:Vn, \str_if_eq:on, \str_if_eq:no,
+% \str_if_eq:nV, \str_if_eq:VV, \str_if_eq:vn, \str_if_eq:nv,
+% \str_if_eq:ee
+% }
+% \begin{syntax}
+% \cs{str_if_eq_p:nn} \Arg{tl_1} \Arg{tl_2}
+% \cs{str_if_eq:nnTF} \Arg{tl_1} \Arg{tl_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Compares the two \meta{token lists} on a character by character
+% basis (namely after converting them to strings),
+% and is \texttt{true} if the two \meta{strings} contain the same
+% characters in the same order. Thus for example
+% \begin{verbatim}
+% \str_if_eq_p:no { abc } { \tl_to_str:n { abc } }
+% \end{verbatim}
+% is logically \texttt{true}. See \cs{tl_if_eq:nnTF} to compare
+% tokens (including their category codes) rather than characters.
+% \end{function}
+%
+% \begin{function}[TF, added = 2017-10-08]{\str_if_in:Nn, \str_if_in:cn}
+% \begin{syntax}
+% \cs{str_if_in:NnTF} \meta{str~var} \Arg{token list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string} and
+% tests if that \meta{string} is found in the content of the
+% \meta{str~var}.
+% \end{function}
+%
+% \begin{function}[TF, added = 2017-10-08]{\str_if_in:nn}
+% \begin{syntax}
+% \cs{str_if_in:nnTF} \Arg{tl_1} \Arg{tl_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Converts both \meta{token lists} to \meta{strings} and
+% tests whether \meta{string_2} is found inside \meta{string_1}.
+% \end{function}
+%
+% \begin{function}[added = 2013-07-24, updated = 2022-03-21, EXP, noTF]
+% {
+% \str_case:nn, \str_case:Vn, \str_case:Nn, \str_case:on, \str_case:en,
+% \str_case:nV, \str_case:nv
+% }
+% \begin{syntax}
+% \cs{str_case:nnTF} \Arg{test string} \\
+% ~~|{| \\
+% ~~~~\Arg{string case_1} \Arg{code case_1} \\
+% ~~~~\Arg{string case_2} \Arg{code case_2} \\
+% ~~~~\ldots \\
+% ~~~~\Arg{string case_n} \Arg{code case_n} \\
+% ~~|}| \\
+% ~~\Arg{true code}
+% ~~\Arg{false code}
+% \end{syntax}
+% Compares the \meta{test string} in turn with each
+% of the \meta{string case}s (all token lists are converted to strings).
+% If the two are equal (as described for
+% \cs{str_if_eq:nnTF}) 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 function
+% \cs{str_case:nn}, which does nothing if there is no match, is also
+% available.
+%
+% This set of functions performs no expansion on each
+% \meta{string~case} argument, so any variable in there will be
+% compared as a string. If expansion is needed in the
+% \meta{string~case}s, then \cs[no-index]{str_case_e:nn(TF)} should
+% be used instead.
+% \end{function}
+%
+% \begin{function}[added = 2018-06-19, EXP, noTF]
+% {\str_case_e:nn, \str_case_e:en}
+% \begin{syntax}
+% \cs{str_case_e:nnTF} \Arg{test string} \\
+% ~~|{| \\
+% ~~~~\Arg{string case_1} \Arg{code case_1} \\
+% ~~~~\Arg{string case_2} \Arg{code case_2} \\
+% ~~~~\ldots \\
+% ~~~~\Arg{string case_n} \Arg{code case_n} \\
+% ~~|}| \\
+% ~~\Arg{true code}
+% ~~\Arg{false code}
+% \end{syntax}
+% Compares the full expansion of the \meta{test string}
+% in turn with the full expansion of the \meta{string case}s
+% (all token lists are converted to strings). If the two
+% full expansions are equal (as described for \cs{str_if_eq:eeTF}) 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 function
+% \cs{str_case_e:nn}, which does nothing if there is no match, is also
+% available.
+% In \cs[index=str_case_e:nnTF]{str_case_e:nn(TF)}, the \meta{test string}
+% is expanded in each comparison, and must always yield the same result:
+% for example, random numbers must not be used within this string.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added = 2021-05-17]{\str_compare:nNn, \str_compare:eNe}
+% \begin{syntax}
+% \cs{str_compare_p:nNn} \Arg{tl_1} \meta{relation} \Arg{tl_2}
+% \cs{str_compare:nNnTF} \Arg{tl_1} \meta{relation} \Arg{tl_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Compares the two \meta{token lists} on a character by character
+% basis (namely after converting them to strings) in a lexicographic
+% order according to the character codes of the characters. The
+% \meta{relation} can be |<|, |=|, or~|>| and the test is
+% \texttt{true} under the following conditions:
+% \begin{itemize}
+% \item for |<|, if the first string is earlier than the second in lexicographic order;
+% \item for |=|, if the two strings have exactly the same characters;
+% \item for |>|, if the first string is later than the second in lexicographic order.
+% \end{itemize}
+% Thus for example the following is logically \texttt{true}:
+% \begin{verbatim}
+% \str_compare_p:nNn { ab } < { abc }
+% \end{verbatim}
+% \begin{texnote}
+% This is a wrapper around the \TeX{} primitive
+% \cs[index=pdfstrcmp]{(pdf)strcmp}. It is meant for programming
+% and not for sorting textual contents, as it simply considers
+% character codes and not more elaborate considerations of grapheme
+% clusters, locale, etc.
+% \end{texnote}
+% \end{function}
+%
+% \section{Mapping over strings}
+%
+% All mappings are done at the current group level, \emph{i.e.}~any
+% local assignments made by the \meta{function} or \meta{code} discussed
+% below remain in effect after the loop.
+%
+% \begin{function}[added = 2017-11-14, rEXP]
+% {\str_map_function:nN, \str_map_function:NN, \str_map_function:cN}
+% \begin{syntax}
+% \cs{str_map_function:nN} \Arg{token list} \meta{function}
+% \cs{str_map_function:NN} \meta{str~var} \meta{function}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string} then
+% applies \meta{function} to every \meta{character} in the
+% \meta{string} including spaces.
+% \end{function}
+%
+% \begin{function}[added = 2017-11-14]
+% {\str_map_inline:nn, \str_map_inline:Nn, \str_map_inline:cn}
+% \begin{syntax}
+% \cs{str_map_inline:nn} \Arg{token list} \Arg{inline function}
+% \cs{str_map_inline:Nn} \meta{str~var} \Arg{inline function}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string} then
+% applies the \meta{inline function} to every \meta{character} in the
+% \meta{str~var} including spaces.
+% The \meta{inline function} should consist of code which
+% receives the \meta{character} as |#1|.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2021-05-05]
+% {\str_map_tokens:nn, \str_map_tokens:Nn, \str_map_tokens:cn}
+% \begin{syntax}
+% \cs{str_map_tokens:nn} \Arg{token list} \Arg{code}
+% \cs{str_map_tokens:Nn} \meta{str~var} \Arg{code}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string} then applies
+% \meta{code} to every \meta{character} in the \meta{string} including
+% spaces. The \meta{code} receives each character as a trailing brace
+% group. This is equivalent to \cs{str_map_function:nN} if the
+% \meta{code} consists of a single function.
+% \end{function}
+%
+% \begin{function}[added = 2017-11-14]
+% {\str_map_variable:nNn, \str_map_variable:NNn, \str_map_variable:cNn}
+% \begin{syntax}
+% \cs{str_map_variable:nNn} \Arg{token list} \meta{variable} \Arg{code}
+% \cs{str_map_variable:NNn} \meta{str~var} \meta{variable} \Arg{code}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string} then stores each
+% \meta{character} in the \meta{string} (including spaces) in turn in
+% the (string or token list) \meta{variable} and applies the
+% \meta{code}. The \meta{code} will usually make use of the
+% \meta{variable}, but this is not enforced. The assignments to the
+% \meta{variable} are local. Its value after the loop is the last
+% \meta{character} in the \meta{string}, or its original value if the
+% \meta{string} is empty. See also \cs{str_map_inline:Nn}.
+% \end{function}
+%
+% \begin{function}[added = 2017-10-08, rEXP]{\str_map_break:}
+% \begin{syntax}
+% \cs{str_map_break:}
+% \end{syntax}
+% Used to terminate a \cs[no-index]{str_map_\ldots} function before all
+% characters in the \meta{string} have been processed. This
+% normally takes place within a conditional statement, for example
+% \begin{verbatim}
+% \str_map_inline:Nn \l_my_str
+% {
+% \str_if_eq:nnT { #1 } { bingo } { \str_map_break: }
+% % Do something useful
+% }
+% \end{verbatim}
+% See also \cs{str_map_break:n}.
+% Use outside of a \cs[no-index]{str_map_\ldots} scenario leads to low
+% level \TeX{} errors.
+% \begin{texnote}
+% When the mapping is broken, additional tokens may be inserted
+% before continuing with the
+% code that follows the loop.
+% This depends on the design of the mapping function.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2017-10-08, rEXP]{\str_map_break:n}
+% \begin{syntax}
+% \cs{str_map_break:n} \Arg{code}
+% \end{syntax}
+% Used to terminate a \cs[no-index]{str_map_\ldots} function before all
+% characters in the \meta{string} have been processed, inserting
+% the \meta{code} after the mapping has ended. This
+% normally takes place within a conditional statement, for example
+% \begin{verbatim}
+% \str_map_inline:Nn \l_my_str
+% {
+% \str_if_eq:nnT { #1 } { bingo }
+% { \str_map_break:n { <code> } }
+% % Do something useful
+% }
+% \end{verbatim}
+% Use outside of a \cs[no-index]{str_map_\ldots} scenario leads to low
+% level \TeX{} errors.
+% \begin{texnote}
+% When the mapping is broken, additional tokens may be inserted
+% before the \meta{code} is
+% inserted into the input stream.
+% This depends on the design of the mapping function.
+% \end{texnote}
+% \end{function}
+%
+% \section{Working with the content of strings}
+%
+% \begin{function}[EXP, added = 2015-09-18]{\str_use:N, \str_use:c}
+% \begin{syntax}
+% \cs{str_use:N} \meta{str~var}
+% \end{syntax}
+% Recovers the content of a \meta{str~var} and places it
+% directly in the input stream. An error is raised if the variable
+% does not exist or if it is invalid. Note that it is possible to use
+% a \meta{str} directly without an accessor function.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2015-09-18]
+% {\str_count:N, \str_count:c, \str_count:n, \str_count_ignore_spaces:n}
+% \begin{syntax}
+% \cs{str_count:n} \Arg{token list}
+% \end{syntax}
+% Leaves in the input stream the number of characters in the string
+% representation of \meta{token list}, as an integer denotation. The
+% functions differ in their treatment of spaces. In the case of
+% \cs{str_count:N} and \cs{str_count:n}, all characters including
+% spaces are counted. The \cs{str_count_ignore_spaces:n} function
+% leaves the number of non-space characters in the input stream.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2015-09-18]
+% {\str_count_spaces:N, \str_count_spaces:c, \str_count_spaces:n}
+% \begin{syntax}
+% \cs{str_count_spaces:n} \Arg{token list}
+% \end{syntax}
+% Leaves in the input stream the number of space characters in the
+% string representation of \meta{token list}, as an integer
+% denotation. Of course, this function has no \texttt{_ignore_spaces}
+% variant.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2015-09-18]
+% {\str_head:N, \str_head:c, \str_head:n, \str_head_ignore_spaces:n}
+% \begin{syntax}
+% \cs{str_head:n} \Arg{token list}
+% \end{syntax}
+% Converts the \meta{token list} into a \meta{string}. The first
+% character in the \meta{string} is then left in the input stream,
+% with category code \enquote{other}. The functions differ if the
+% first character is a space: \cs{str_head:N} and \cs{str_head:n}
+% return a space token with category code~$10$ (blank space), while
+% the \cs{str_head_ignore_spaces:n} function ignores this space
+% character and leaves the first non-space character in the input
+% stream. If the \meta{string} is empty (or only contains spaces in
+% the case of the \texttt{_ignore_spaces} function), then nothing is
+% left on the input stream.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2015-09-18]
+% {\str_tail:N, \str_tail:c, \str_tail:n, \str_tail_ignore_spaces:n}
+% \begin{syntax}
+% \cs{str_tail:n} \Arg{token list}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string}, removes the first
+% character, and leaves the remaining characters (if any) in the input
+% stream, with category codes $12$ and $10$ (for spaces). The
+% functions differ in the case where the first character is a space:
+% \cs{str_tail:N} and \cs{str_tail:n} only trim that space, while
+% \cs{str_tail_ignore_spaces:n} removes the first non-space character
+% and any space before it. If the \meta{token list} is empty (or
+% blank in the case of the \texttt{_ignore_spaces} variant), then
+% nothing is left on the input stream.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2015-09-18]
+% {\str_item:Nn, \str_item:nn, \str_item_ignore_spaces:nn}
+% \begin{syntax}
+% \cs{str_item:nn} \Arg{token list} \Arg{integer expression}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string}, and leaves in the
+% input stream the character in position \meta{integer expression} of
+% the \meta{string}, starting at $1$ for the first (left-most)
+% character. In the case of \cs{str_item:Nn} and \cs{str_item:nn},
+% all characters including spaces are taken into account. The
+% \cs{str_item_ignore_spaces:nn} function skips spaces when counting
+% characters. If the \meta{integer expression} is negative,
+% characters are counted from the end of the \meta{string}. Hence,
+% $-1$ is the right-most character, \emph{etc.}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2015-09-18]
+% {
+% \str_range:Nnn, \str_range:cnn, \str_range:nnn,
+% \str_range_ignore_spaces:nnn
+% }
+% \begin{syntax}
+% \cs{str_range:nnn} \Arg{token list} \Arg{start index} \Arg{end index}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string}, and leaves in the
+% input stream the characters from the \meta{start index} to the
+% \meta{end index} inclusive. Spaces are preserved and counted as items
+% (contrast this with \cs{tl_range:nnn} where spaces are not counted as
+% items and are possibly discarded from the output).
+%
+% Here \meta{start index} and \meta{end index} should be integer denotations.
+% For describing in detail the functions' behavior, let $m$ and $n$ be the start
+% and end index respectively. If either is $0$, the result is empty. A positive
+% index means `start counting from the left end', a negative index means
+% `start counting from the right end'. Let $l$ be the count of the token list.
+%
+% The \emph{actual start point} is determined as $M=m$ if~$m>0$ and as $M=l+m+1$
+% if~$m<0$. Similarly the \emph{actual end point} is $N=n$ if~$n>0$ and $N=l+n+1$
+% if~$n<0$. If $M>N$, the result is empty. Otherwise it consists of all items from
+% position $M$ to position $N$ inclusive; for the purpose of this rule, we can
+% imagine that the token list extends at infinity on either side, with void items
+% at positions $s$ for $s\le0$ or $s>l$.
+% For instance,
+% \begin{verbatim}
+% \iow_term:e { \str_range:nnn { abcdef } { 2 } { 5 } }
+% \iow_term:e { \str_range:nnn { abcdef } { -4 } { -1 } }
+% \iow_term:e { \str_range:nnn { abcdef } { -2 } { -1 } }
+% \iow_term:e { \str_range:nnn { abcdef } { 0 } { -1 } }
+% \end{verbatim}
+% prints \texttt{bcde}, \texttt{cdef}, \texttt{ef}, and an empty
+% line to the terminal. The \meta{start index} must always be smaller than
+% or equal to the \meta{end index}: if this is not the case then no output
+% is generated. Thus
+% \begin{verbatim}
+% \iow_term:e { \str_range:nnn { abcdef } { 5 } { 2 } }
+% \iow_term:e { \str_range:nnn { abcdef } { -1 } { -4 } }
+% \end{verbatim}
+% both yield empty strings.
+% \end{function}
+%
+% ^^A If this stays in the same {function} environment, we get a really
+% ^^A awful page break. Perhaps we should add a way to allow a page break
+% ^^A in a function environment...
+% The behavior of \cs{str_range_ignore_spaces:nnn} is similar, but spaces
+% are removed before starting the job. The input
+% \begin{verbatim}
+% \iow_term:e { \str_range:nnn { abcdefg } { 2 } { 5 } }
+% \iow_term:e { \str_range:nnn { abcdefg } { 2 } { -3 } }
+% \iow_term:e { \str_range:nnn { abcdefg } { -6 } { 5 } }
+% \iow_term:e { \str_range:nnn { abcdefg } { -6 } { -3 } }
+%
+% \iow_term:e { \str_range:nnn { abc~efg } { 2 } { 5 } }
+% \iow_term:e { \str_range:nnn { abc~efg } { 2 } { -3 } }
+% \iow_term:e { \str_range:nnn { abc~efg } { -6 } { 5 } }
+% \iow_term:e { \str_range:nnn { abc~efg } { -6 } { -3 } }
+%
+% \iow_term:e { \str_range_ignore_spaces:nnn { abcdefg } { 2 } { 5 } }
+% \iow_term:e { \str_range_ignore_spaces:nnn { abcdefg } { 2 } { -3 } }
+% \iow_term:e { \str_range_ignore_spaces:nnn { abcdefg } { -6 } { 5 } }
+% \iow_term:e { \str_range_ignore_spaces:nnn { abcdefg } { -6 } { -3 } }
+%
+% \iow_term:e { \str_range_ignore_spaces:nnn { abcd~efg } { 2 } { 5 } }
+% \iow_term:e { \str_range_ignore_spaces:nnn { abcd~efg } { 2 } { -3 } }
+% \iow_term:e { \str_range_ignore_spaces:nnn { abcd~efg } { -6 } { 5 } }
+% \iow_term:e { \str_range_ignore_spaces:nnn { abcd~efg } { -6 } { -3 } }
+% \end{verbatim}
+% will print four instances of |bcde|, four instances of |bc e| and eight
+% instances of |bcde|.
+% ^^A\end{function}
+%
+% \section{Modifying string variables}
+%
+% \begin{function}[added = 2017-10-08]
+% {
+% \str_replace_once:Nnn, \str_replace_once:cnn,
+% \str_greplace_once:Nnn, \str_greplace_once:cnn
+% }
+% \begin{syntax}
+% \cs{str_replace_once:Nnn} \meta{str~var} \Arg{old} \Arg{new}
+% \end{syntax}
+% Converts the \meta{old} and \meta{new} token lists to strings, then
+% replaces the first (leftmost) occurrence of \meta{old string} in the
+% \meta{str~var} with \meta{new string}.
+% \end{function}
+%
+% \begin{function}[added = 2017-10-08]
+% {
+% \str_replace_all:Nnn, \str_replace_all:cnn,
+% \str_greplace_all:Nnn, \str_greplace_all:cnn
+% }
+% \begin{syntax}
+% \cs{str_replace_all:Nnn} \meta{str~var} \Arg{old} \Arg{new}
+% \end{syntax}
+% Converts the \meta{old} and \meta{new} token lists to strings, then
+% replaces all occurrences of \meta{old string} in the
+% \meta{str~var} with \meta{new string}.
+% As this function
+% operates from left to right, the pattern \meta{old string}
+% may remain after the replacement (see \cs{str_remove_all:Nn}
+% for an example).
+% \end{function}
+%
+% \begin{function}[added = 2017-10-08]
+% {
+% \str_remove_once:Nn, \str_remove_once:cn,
+% \str_gremove_once:Nn, \str_gremove_once:cn
+% }
+% \begin{syntax}
+% \cs{str_remove_once:Nn} \meta{str~var} \Arg{token list}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string} then
+% removes the first (leftmost) occurrence of \meta{string} from the
+% \meta{str~var}.
+% \end{function}
+%
+% \begin{function}[added = 2017-10-08]
+% {
+% \str_remove_all:Nn, \str_remove_all:cn,
+% \str_gremove_all:Nn, \str_gremove_all:cn
+% }
+% \begin{syntax}
+% \cs{str_remove_all:Nn} \meta{str~var} \Arg{token list}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string} then
+% removes all occurrences of \meta{string} from the
+% \meta{str~var}.
+% As this function
+% operates from left to right, the pattern \meta{string}
+% may remain after the removal, for instance,
+% \begin{quote}
+% \cs{str_set:Nn} \cs{l_tmpa_str} |{abbccd}|
+% \cs{str_remove_all:Nn} \cs{l_tmpa_str} |{bc}|
+% \end{quote}
+% results in \cs{l_tmpa_str} containing \texttt{abcd}.
+% \end{function}
+%
+% \section{String manipulation}
+%
+% \begin{function}[EXP, added = 2019-11-26]
+% {
+% \str_lowercase:n, \str_lowercase:f,
+% \str_uppercase:n, \str_uppercase:f
+% }
+% \begin{syntax}
+% \cs{str_lowercase:n} \Arg{tokens}
+% \cs{str_uppercase:n} \Arg{tokens}
+% \end{syntax}
+% Converts the input \meta{tokens} to their string representation, as
+% described for \cs{tl_to_str:n}, and then to the lower or upper
+% case representation using a one-to-one mapping as described by the
+% Unicode Consortium file |UnicodeData.txt|.
+%
+% These functions are intended for case changing programmatic data in
+% places where upper/lower case distinctions are meaningful. One example
+% would be automatically generating a function name from user input where
+% some case changing is needed. In this situation the input is programmatic,
+% not textual, case does have meaning and a language-independent one-to-one
+% mapping is appropriate. For example
+% \begin{verbatim}
+% \cs_new_protected:Npn \myfunc:nn #1#2
+% {
+% \cs_set_protected:cpn
+% {
+% user
+% \str_uppercase:f { \tl_head:n {#1} }
+% \str_lowercase:f { \tl_tail:n {#1} }
+% }
+% { #2 }
+% }
+% \end{verbatim}
+% would be used to generate a function with an auto-generated name consisting
+% of the upper case equivalent of the supplied name followed by the lower
+% case equivalent of the rest of the input.
+%
+% These functions should \emph{not} be used for
+% \begin{itemize}
+% \item Caseless comparisons: use \cs{str_casefold:n} for this
+% situation (case folding is distinct from lower casing).
+% \item Case changing text for typesetting: see the
+% \cs[index=text_lowercase:n]{text_lowercase:n(n)},
+% \cs[index=text_uppercase:n]{text_uppercase:n(n)} and
+% \cs[index=text_titlecase_all:n]{text_titlecase_(all|once):n(n)} functions which
+% correctly deal with context-dependence and other factors appropriate
+% to text case changing.
+% \end{itemize}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2022-10-16]
+% {\str_casefold:n, \str_casefold:V}
+% \begin{syntax}
+% \cs{str_casefold:n} \Arg{tokens}
+% \end{syntax}
+% Converts the input \meta{tokens} to their string representation, as
+% described for \cs{tl_to_str:n}, and then folds the case of the resulting
+% \meta{string} to remove case information. The result of this process is
+% left in the input stream.
+%
+% String folding is a process used for material such as identifiers rather
+% than for \enquote{text}. The folding provided by \cs{str_casefold:n}
+% follows the mappings provided by the \href{http://www.unicode.org}^^A
+% {Unicode Consortium}, who
+% \href{http://www.unicode.org/faq/casemap_charprop.html#2}{state}:
+% \begin{quote}
+% Case folding is primarily used for caseless comparison of text, such
+% as identifiers in a computer program, rather than actual text
+% transformation. Case folding in Unicode is based on the lowercase
+% mapping, but includes additional changes to the source text to help make
+% it language-insensitive and consistent. As a result, case-folded text
+% should be used solely for internal processing and generally should not be
+% stored or displayed to the end user.
+% \end{quote}
+% The folding approach implemented by \cs{str_casefold:n} follows the
+% \enquote{full} scheme defined by the Unicode Consortium
+% (\emph{e.g.}~\SS folds to \texttt{SS}). As case-folding is
+% a language-insensitive process, there is no special treatment of
+% Turkic input (\emph{i.e.}~\texttt{I} always folds to \texttt{i} and
+% not to \texttt{\i}).
+% \end{function}
+%
+% \begin{function}[added = 2023-05-19, EXP]{\str_mdfive_hash:n, \str_mdfive_hash:e}
+% \begin{syntax}
+% \cs{str_mdfive_hash:n} \Arg{tl}
+% \end{syntax}
+% Expands to the MD5 sum generated from the \meta{tl}, which is converted
+% to a \meta{string} as described for \cs{tl_to_str:n}.
+% \end{function}
+%
+% \section{Viewing strings}
+%
+% \begin{function}[added = 2015-09-18, updated = 2021-04-29]
+% {\str_show:N, \str_show:c, \str_show:n}
+% \begin{syntax}
+% \cs{str_show:N} \meta{str~var}
+% \end{syntax}
+% Displays the content of the \meta{str~var} on the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2019-02-15, updated = 2021-04-29]
+% {\str_log:N, \str_log:c, \str_log:n}
+% \begin{syntax}
+% \cs{str_log:N} \meta{str~var}
+% \end{syntax}
+% Writes the content of the \meta{str~var} in the log file.
+% \end{function}
+%
+% \section{Constant strings}
+%
+% \begin{variable}[added = 2015-09-19, updated = 2020-12-22, module = str]
+% {
+% \c_ampersand_str,
+% \c_atsign_str,
+% \c_backslash_str,
+% \c_left_brace_str,
+% \c_right_brace_str,
+% \c_circumflex_str,
+% \c_colon_str,
+% \c_dollar_str,
+% \c_hash_str,
+% \c_percent_str,
+% \c_tilde_str,
+% \c_underscore_str,
+% \c_zero_str
+% }
+% Constant strings, containing a single character token, with category
+% code $12$.
+% \end{variable}
+%
+% \begin{variable}[added = 2023-12-07]{\c_empty_str}
+% Constant that is always empty.
+% \end{variable}
+%
+% \section{Scratch strings}
+%
+% \begin{variable}{\l_tmpa_str, \l_tmpb_str}
+% Scratch strings for local assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \begin{variable}{\g_tmpa_str, \g_tmpb_str}
+% Scratch strings for global assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3str} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=str>
+% \end{macrocode}
+%
+% \subsection{Internal auxiliaries}
+%
+% \begin{variable}{\s_@@_mark,\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{
+% \@@_use_none_delimit_by_s_stop:w,
+% \@@_use_i_delimit_by_s_stop:nw
+% }
+% Functions to gobble up to a scan mark.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_none_delimit_by_s_stop:w #1 \s_@@_stop { }
+\cs_new:Npn \@@_use_i_delimit_by_s_stop:nw #1 #2 \s_@@_stop {#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\q_@@_recursion_tail,\q_@@_recursion_stop}
+% Internal recursion quarks.
+% \begin{macrocode}
+\quark_new:N \q_@@_recursion_tail
+\quark_new:N \q_@@_recursion_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{
+% \@@_if_recursion_tail_break:NN,
+% \@@_if_recursion_tail_stop_do:Nn
+% }
+% Functions to query recursion quarks.
+% \begin{macrocode}
+\__kernel_quark_new_test:N \@@_if_recursion_tail_break:NN
+\__kernel_quark_new_test:N \@@_if_recursion_tail_stop_do:Nn
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Creating and setting string variables}
+%
+% \begin{macro}
+% {
+% \str_new:N, \str_new:c,
+% \str_use:N, \str_use:c,
+% \str_clear:N, \str_clear:c,
+% \str_gclear:N,\str_gclear:c,
+% \str_clear_new:N, \str_clear_new:c,
+% \str_gclear_new:N, \str_gclear_new:c
+% }
+% \begin{macro}
+% {
+% \str_set_eq:NN, \str_set_eq:cN, \str_set_eq:Nc, \str_set_eq:cc,
+% \str_gset_eq:NN, \str_gset_eq:cN, \str_gset_eq:Nc, \str_gset_eq:cc
+% }
+% \begin{macro}
+% {\str_concat:NNN, \str_concat:ccc, \str_gconcat:NNN, \str_gconcat:ccc}
+% A string is simply a token list. The full mapping system isn't set up
+% yet so do things by hand.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \@@_tmp:n #1
+ {
+ \tl_if_blank:nF {#1}
+ {
+ \cs_new_eq:cc { str_ #1 :N } { tl_ #1 :N }
+ \exp_args:Nc \cs_generate_variant:Nn { str_ #1 :N } { c }
+ \@@_tmp:n
+ }
+ }
+ \@@_tmp:n
+ { new }
+ { use }
+ { clear }
+ { gclear }
+ { clear_new }
+ { gclear_new }
+ { }
+\group_end:
+\cs_new_eq:NN \str_set_eq:NN \tl_set_eq:NN
+\cs_new_eq:NN \str_gset_eq:NN \tl_gset_eq:NN
+\cs_generate_variant:Nn \str_set_eq:NN { c , Nc , cc }
+\cs_generate_variant:Nn \str_gset_eq:NN { c , Nc , cc }
+\cs_new_eq:NN \str_concat:NNN \tl_concat:NNN
+\cs_new_eq:NN \str_gconcat:NNN \tl_gconcat:NNN
+\cs_generate_variant:Nn \str_concat:NNN { ccc }
+\cs_generate_variant:Nn \str_gconcat:NNN { ccc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \str_set:Nn, \str_set:NV, \str_set:Ne, \str_set:Nx,
+% \str_set:cn, \str_set:cV, \str_set:ce, \str_set:cx,
+% \str_gset:Nn, \str_gset:NV, \str_gset:Ne, \str_gset:Nx,
+% \str_gset:cn, \str_gset:cV, \str_gset:ce, \str_gset:cx,
+% \str_const:Nn, \str_const:NV, \str_const:Ne, \str_const:Nx,
+% \str_const:cn, \str_const:cV, \str_const:ce, \str_const:cx,
+% \str_put_left:Nn, \str_put_left:NV, \str_put_left:Ne, \str_put_left:Nx,
+% \str_put_left:cn, \str_put_left:cV, \str_put_left:ce, \str_put_left:cx,
+% \str_gput_left:Nn, \str_gput_left:NV, \str_gput_left:Ne, \str_gput_left:Nx,
+% \str_gput_left:cn, \str_gput_left:cV, \str_gput_left:ce, \str_gput_left:cx,
+% \str_put_right:Nn, \str_put_right:NV, \str_put_right:Ne, \str_put_right:Nx,
+% \str_put_right:cn, \str_put_right:cV, \str_put_right:ce, \str_put_right:cx,
+% \str_gput_right:Nn, \str_gput_right:NV, \str_gput_right:Ne, \str_gput_right:Nx,
+% \str_gput_right:cn, \str_gput_right:cV, \str_gput_right:ce, \str_gput_right:cx
+% }
+% Similar to corresponding \pkg{l3tl} base functions, except that
+% \cs{__kernel_exp_not:w} is replaced with \cs{__kernel_tl_to_str:w}.
+% Just like token list, string constants use \cs{cs_gset_nopar:Npe}
+% instead of \cs{__kernel_tl_gset:Nx} so that the scope checking for
+% |c| is applied when \pkg{l3debug} is used.
+% To maintain backward compatibility, in
+% \cs[index=str_put_left:Nn]{str_(g)put_left:Nn} and
+% \cs[index=str_put_right:Nn]{str_(g)put_right:Nn},
+% contents of string variables are wrapped in \cs{__kernel_exp_not:w}
+% to prevent further expansion.
+% \begin{macrocode}
+\cs_new_protected:Npn \str_set:Nn #1#2
+ { \__kernel_tl_set:Nx #1 { \__kernel_tl_to_str:w {#2} } }
+\cs_gset_protected:Npn \str_gset:Nn #1#2
+ { \__kernel_tl_gset:Nx #1 { \__kernel_tl_to_str:w {#2} } }
+\cs_new_protected:Npn \str_const:Nn #1#2
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_nopar:Npe #1 { \__kernel_tl_to_str:w {#2} }
+ }
+\cs_new_protected:Npn \str_put_left:Nn #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \__kernel_tl_to_str:w {#2} \__kernel_exp_not:w \exp_after:wN {#1} }
+ }
+\cs_new_protected:Npn \str_gput_left:Nn #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \__kernel_tl_to_str:w {#2} \__kernel_exp_not:w \exp_after:wN {#1} }
+ }
+\cs_new_protected:Npn \str_put_right:Nn #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \__kernel_exp_not:w \exp_after:wN {#1} \__kernel_tl_to_str:w {#2} }
+ }
+\cs_new_protected:Npn \str_gput_right:Nn #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \__kernel_exp_not:w \exp_after:wN {#1} \__kernel_tl_to_str:w {#2} }
+ }
+\cs_generate_variant:Nn \str_set:Nn { NV , Ne , Nx , c , cV , ce , cx }
+\cs_generate_variant:Nn \str_gset:Nn { NV , Ne , Nx , c , cV , ce , cx }
+\cs_generate_variant:Nn \str_const:Nn { NV , Ne , Nx , c , cV , ce , cx }
+\cs_generate_variant:Nn \str_put_left:Nn { NV , Ne , Nx , c , cV , ce , cx }
+\cs_generate_variant:Nn \str_gput_left:Nn { NV , Ne , Nx , c , cV , ce , cx }
+\cs_generate_variant:Nn \str_put_right:Nn { NV , Ne , Nx , c , cV , ce , cx }
+\cs_generate_variant:Nn \str_gput_right:Nn { NV , Ne , Nx , c , cV , ce , cx }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Modifying string variables}
+%
+% \begin{macro}
+% {
+% \str_replace_all:Nnn, \str_replace_all:cnn,
+% \str_greplace_all:Nnn, \str_greplace_all:cnn,
+% \str_replace_once:Nnn, \str_replace_once:cnn,
+% \str_greplace_once:Nnn, \str_greplace_once:cnn
+% }
+% \begin{macro}{\@@_replace:NNNnn}
+% \begin{macro}{\@@_replace_aux:NNNnnn}
+% \begin{macro}{\@@_replace_next:w}
+% Start by applying \cs{tl_to_str:n} to convert the old and new token
+% lists to strings, and also apply \cs{tl_to_str:N} to avoid any
+% issues if we are fed a token list variable. Then the code is a much
+% simplified version of the token list code because neither the
+% delimiter nor the replacement can contain macro parameters or
+% braces. The delimiter \cs{s_@@_mark} cannot appear in the string to
+% edit so it is used in all cases. Some |e|-expansion is unnecessary.
+% There is no need to avoid losing braces nor to protect against
+% expansion. The ending code is much simplified and does not need to
+% hide in braces.
+% \begin{macrocode}
+\cs_new_protected:Npn \str_replace_once:Nnn
+ { \@@_replace:NNNnn \prg_do_nothing: \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \str_greplace_once:Nnn
+ { \@@_replace:NNNnn \prg_do_nothing: \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \str_replace_all:Nnn
+ { \@@_replace:NNNnn \@@_replace_next:w \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \str_greplace_all:Nnn
+ { \@@_replace:NNNnn \@@_replace_next:w \__kernel_tl_gset:Nx }
+\cs_generate_variant:Nn \str_replace_once:Nnn { c }
+\cs_generate_variant:Nn \str_greplace_once:Nnn { c }
+\cs_generate_variant:Nn \str_replace_all:Nnn { c }
+\cs_generate_variant:Nn \str_greplace_all:Nnn { c }
+\cs_new_protected:Npn \@@_replace:NNNnn #1#2#3#4#5
+ {
+ \tl_if_empty:nTF {#4}
+ {
+ \msg_error:nne { kernel } { empty-search-pattern } {#5}
+ }
+ {
+ \use:e
+ {
+ \exp_not:n { \@@_replace_aux:NNNnnn #1 #2 #3 }
+ { \tl_to_str:N #3 }
+ { \tl_to_str:n {#4} } { \tl_to_str:n {#5} }
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_replace_aux:NNNnnn #1#2#3#4#5#6
+ {
+ \cs_set:Npn \@@_replace_next:w ##1 #5 { ##1 #6 #1 }
+ #2 #3
+ {
+ \@@_replace_next:w
+ #4
+ \@@_use_none_delimit_by_s_stop:w
+ #5
+ \s_@@_stop
+ }
+ }
+\cs_new_eq:NN \@@_replace_next:w ?
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\str_remove_once:Nn, \str_remove_once:cn}
+% \begin{macro}{\str_gremove_once:Nn, \str_gremove_once:cn}
+% Removal is just a special case of replacement.
+% \begin{macrocode}
+\cs_new_protected:Npn \str_remove_once:Nn #1#2
+ { \str_replace_once:Nnn #1 {#2} { } }
+\cs_new_protected:Npn \str_gremove_once:Nn #1#2
+ { \str_greplace_once:Nnn #1 {#2} { } }
+\cs_generate_variant:Nn \str_remove_once:Nn { c }
+\cs_generate_variant:Nn \str_gremove_once:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\str_remove_all:Nn, \str_remove_all:cn}
+% \begin{macro}{\str_gremove_all:Nn, \str_gremove_all:cn}
+% Removal is just a special case of replacement.
+% \begin{macrocode}
+\cs_new_protected:Npn \str_remove_all:Nn #1#2
+ { \str_replace_all:Nnn #1 {#2} { } }
+\cs_new_protected:Npn \str_gremove_all:Nn #1#2
+ { \str_greplace_all:Nnn #1 {#2} { } }
+\cs_generate_variant:Nn \str_remove_all:Nn { c }
+\cs_generate_variant:Nn \str_gremove_all:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{String comparisons}
+%
+% \begin{macro}[pTF, EXP]
+% {
+% \str_if_empty:N, \str_if_empty:c, \str_if_empty:n,
+% \str_if_exist:N, \str_if_exist:c
+% }
+% More copy-paste!
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \str_if_exist:N \tl_if_exist:N
+ { p , T , F , TF }
+\prg_new_eq_conditional:NNn \str_if_exist:c \tl_if_exist:c
+ { p , T , F , TF }
+\prg_new_eq_conditional:NNn \str_if_empty:N \tl_if_empty:N
+ { p , T , F , TF }
+\prg_new_eq_conditional:NNn \str_if_empty:c \tl_if_empty:c
+ { p , T , F , TF }
+\prg_new_eq_conditional:NNn \str_if_empty:n \tl_if_empty:n
+ { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_if_eq:nn}
+% String comparisons rely on the primitive \tn[index=pdfstrcmp]{(pdf)strcmp},
+% so we define a new name for it.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_if_eq:nn \tex_strcmp:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF, EXP]{\str_compare:nNn, \str_compare:eNe}
+% Simply rely on \cs{@@_if_eq:nn}, which expands to |-1|, |0|
+% or~|1|. The |ee| version is created directly because it is more efficient.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \str_compare:nNn #1#2#3 { p , T , F , TF }
+ {
+ \if_int_compare:w
+ \@@_if_eq:nn { \exp_not:n {#1} } { \exp_not:n {#3} }
+ #2 \c_zero_int
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_new_conditional:Npnn \str_compare:eNe #1#2#3 { p , T , F , TF }
+ {
+ \if_int_compare:w \@@_if_eq:nn {#1} {#3} #2 \c_zero_int
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF, EXP]
+% {
+% \str_if_eq:nn, \str_if_eq:Vn, \str_if_eq:on, \str_if_eq:nV,
+% \str_if_eq:no, \str_if_eq:VV,
+% \str_if_eq:ee
+% }
+% Modern engines provide a direct way of comparing two token lists,
+% but returning a number. This set of conditionals therefore makes life
+% a bit clearer. The \texttt{nn} and \texttt{ee} versions are created
+% directly as this is most efficient. Since \cs{@@_if_eq:nn} will expand to
+% |0| as an explicit character with category 12 if the two lists match (and
+% either |-1| or |1| if they don't) we can use \cs{if:w} here which is faster
+% than using \cs{if_int_compare:w}.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \str_if_eq:nn #1#2 { p , T , F , TF }
+ {
+ \if:w 0 \@@_if_eq:nn { \exp_not:n {#1} } { \exp_not:n {#2} }
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_generate_conditional_variant:Nnn \str_if_eq:nn
+ { V , v , o , nV , no , VV , nv } { p , T , F , TF }
+\prg_new_conditional:Npnn \str_if_eq:ee #1#2 { p , T , F , TF }
+ {
+ \if:w 0 \@@_if_eq:nn {#1} {#2}
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, pTF]
+% {\str_if_eq:NN, \str_if_eq:Nc, \str_if_eq:cN, \str_if_eq:cc}
+% Note that \cs{str_if_eq:NNTF} is different from
+% \cs{tl_if_eq:NNTF} because it needs to ignore category codes.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \str_if_eq:NN #1#2 { p , TF , T , F }
+ {
+ \if:w 0 \@@_if_eq:nn { \tl_to_str:N #1 } { \tl_to_str:N #2 }
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_generate_conditional_variant:Nnn \str_if_eq:NN
+ { c , Nc , cc } { T , F , TF , p }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[TF]{\str_if_in:Nn, \str_if_in:cn, \str_if_in:nn}
+% Everything here needs to be detokenized but beyond that it is a
+% simple token list test. It would be faster to fine-tune the |T|,
+% |F|, |TF| variants by calling the appropriate variant of
+% \cs{tl_if_in:nnTF} directly but that takes more code.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \str_if_in:Nn #1#2 { T , F , TF }
+ {
+ \use:e
+ { \tl_if_in:nnTF { \tl_to_str:N #1 } { \tl_to_str:n {#2} } }
+ { \prg_return_true: } { \prg_return_false: }
+ }
+\prg_generate_conditional_variant:Nnn \str_if_in:Nn
+ { c } { T , F , TF }
+\prg_new_protected_conditional:Npnn \str_if_in:nn #1#2 { T , F , TF }
+ {
+ \use:e
+ { \tl_if_in:nnTF { \tl_to_str:n {#1} } { \tl_to_str:n {#2} } }
+ { \prg_return_true: } { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, noTF]
+% {
+% \str_case:nn, \str_case:Vn, \str_case:Nn, \str_case:on, \str_case:en, \str_case:nV, \str_case:nv,
+% \str_case_e:nn, \str_case_e:en
+% }
+% \begin{macro}[EXP]{\@@_case:nnTF, \@@_case_e:nnTF}
+% \begin{macro}[EXP]
+% {\@@_case:nw, \@@_case_e:nw, \@@_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 \str_case:nn #1#2
+ {
+ \exp:w
+ \@@_case:nnTF {#1} {#2} { } { }
+ }
+\cs_new:Npn \str_case:nnT #1#2#3
+ {
+ \exp:w
+ \@@_case:nnTF {#1} {#2} {#3} { }
+ }
+\cs_new:Npn \str_case:nnF #1#2
+ {
+ \exp:w
+ \@@_case:nnTF {#1} {#2} { }
+ }
+\cs_new:Npn \str_case:nnTF #1#2
+ {
+ \exp:w
+ \@@_case:nnTF {#1} {#2}
+ }
+\cs_new:Npn \@@_case:nnTF #1#2#3#4
+ { \@@_case:nw {#1} #2 {#1} { } \s_@@_mark {#3} \s_@@_mark {#4} \s_@@_stop }
+\cs_generate_variant:Nn \str_case:nn { V , o , e , nV , nv }
+\prg_generate_conditional_variant:Nnn \str_case:nn
+ { V , o , e , nV , nv } { T , F , TF }
+\cs_new_eq:NN \str_case:Nn \str_case:Vn
+\cs_new_eq:NN \str_case:NnT \str_case:VnT
+\cs_new_eq:NN \str_case:NnF \str_case:VnF
+\cs_new_eq:NN \str_case:NnTF \str_case:VnTF
+\cs_new:Npn \@@_case:nw #1#2#3
+ {
+ \str_if_eq:nnTF {#1} {#2}
+ { \@@_case_end:nw {#3} }
+ { \@@_case:nw {#1} }
+ }
+\cs_new:Npn \str_case_e:nn #1#2
+ {
+ \exp:w
+ \@@_case_e:nnTF {#1} {#2} { } { }
+ }
+\cs_new:Npn \str_case_e:nnT #1#2#3
+ {
+ \exp:w
+ \@@_case_e:nnTF {#1} {#2} {#3} { }
+ }
+\cs_new:Npn \str_case_e:nnF #1#2
+ {
+ \exp:w
+ \@@_case_e:nnTF {#1} {#2} { }
+ }
+\cs_new:Npn \str_case_e:nnTF #1#2
+ {
+ \exp:w
+ \@@_case_e:nnTF {#1} {#2}
+ }
+\cs_new:Npn \@@_case_e:nnTF #1#2#3#4
+ { \@@_case_e:nw {#1} #2 {#1} { } \s_@@_mark {#3} \s_@@_mark {#4} \s_@@_stop }
+\cs_generate_variant:Nn \str_case_e:nn { e }
+\prg_generate_conditional_variant:Nnn \str_case_e:nn { e } { T , F , TF }
+\cs_new:Npn \@@_case_e:nw #1#2#3
+ {
+ \str_if_eq:eeTF {#1} {#2}
+ { \@@_case_end:nw {#3} }
+ { \@@_case_e:nw {#1} }
+ }
+% \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}
+% \end{macro}
+%
+% \subsection{Mapping over strings}
+%
+% \begin{macro}[rEXP]{\str_map_function:NN, \str_map_function:cN}
+% \begin{macro}[rEXP]{\str_map_function:nN}
+% \begin{macro}{\str_map_inline:Nn, \str_map_inline:cn}
+% \begin{macro}{\str_map_inline:nn}
+% \begin{macro}{\str_map_variable:NNn, \str_map_variable:cNn}
+% \begin{macro}{\str_map_variable:nNn}
+% \begin{macro}{\str_map_break:}
+% \begin{macro}{\str_map_break:n}
+% \begin{macro}[rEXP]{\@@_map_function:w, \@@_map_function:nn}
+% \begin{macro}{\@@_map_inline:NN, \@@_map_variable:NnN}
+% The inline and variable mappings are similar to the usual token list
+% mappings but start out by turning the argument to an ``other
+% string''. Doing the same for the expandable function mapping would
+% require \cs{__kernel_str_to_other:n}, quadratic in the string length. To deal
+% with spaces in that case, \cs{@@_map_function:w} replaces the
+% following space by a braced space and a further call to itself.
+% These are received by \cs{@@_map_function:nn}, which passes
+% the space to |#1| and calls \cs{@@_map_function:w} to deal with the
+% next space. The space before the braced space allows to optimize
+% the \cs{q_@@_recursion_tail} test. Of course we need to include a
+% trailing space (the question mark is needed to avoid losing the
+% space when \TeX{} tokenizes the line).
+% At the cost of about three more auxiliaries this code could get a $9$
+% times speed up by testing only every $9$-th character for whether it
+% is \cs{q_@@_recursion_tail} (also by converting $9$ spaces at a time in
+% the \cs{str_map_function:nN} case).
+%
+% For the \texttt{map_variable} functions we use a string assignment
+% to store each character because spaces are made catcode~$12$ before
+% the loop.
+% \begin{macrocode}
+\cs_new:Npn \str_map_function:nN #1#2
+ {
+ \exp_after:wN \@@_map_function:w
+ \exp_after:wN \@@_map_function:nn \exp_after:wN #2
+ \__kernel_tl_to_str:w {#1}
+ \q_@@_recursion_tail ? ~
+ \prg_break_point:Nn \str_map_break: { }
+ }
+\cs_new:Npn \str_map_function:NN
+ { \exp_args:No \str_map_function:nN }
+\cs_new:Npn \@@_map_function:w #1 ~
+ { #1 { ~ { ~ } \@@_map_function:w } }
+\cs_new:Npn \@@_map_function:nn #1#2
+ {
+ \if_meaning:w \q_@@_recursion_tail #2
+ \exp_after:wN \str_map_break:
+ \fi:
+ #1 #2 \@@_map_function:nn {#1}
+ }
+\cs_generate_variant:Nn \str_map_function:NN { c }
+\cs_new_protected:Npn \str_map_inline:nn #1#2
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \cs_gset_protected:cpn
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w } ##1 {#2}
+ \use:e
+ {
+ \exp_not:N \@@_map_inline:NN
+ \exp_not:c { @@_map_ \int_use:N \g__kernel_prg_map_int :w }
+ \__kernel_str_to_other_fast:n {#1}
+ }
+ \q_@@_recursion_tail
+ \prg_break_point:Nn \str_map_break:
+ { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+\cs_new_protected:Npn \str_map_inline:Nn
+ { \exp_args:No \str_map_inline:nn }
+\cs_generate_variant:Nn \str_map_inline:Nn { c }
+\cs_new:Npn \@@_map_inline:NN #1#2
+ {
+ \@@_if_recursion_tail_break:NN #2 \str_map_break:
+ \exp_args:No #1 { \token_to_str:N #2 }
+ \@@_map_inline:NN #1
+ }
+\cs_new_protected:Npn \str_map_variable:nNn #1#2#3
+ {
+ \use:e
+ {
+ \exp_not:n { \@@_map_variable:NnN #2 {#3} }
+ \__kernel_str_to_other_fast:n {#1}
+ }
+ \q_@@_recursion_tail
+ \prg_break_point:Nn \str_map_break: { }
+ }
+\cs_new_protected:Npn \str_map_variable:NNn
+ { \exp_args:No \str_map_variable:nNn }
+\cs_new_protected:Npn \@@_map_variable:NnN #1#2#3
+ {
+ \@@_if_recursion_tail_break:NN #3 \str_map_break:
+ \str_set:Nn #1 {#3}
+ \use:n {#2}
+ \@@_map_variable:NnN #1 {#2}
+ }
+\cs_generate_variant:Nn \str_map_variable:NNn { c }
+\cs_new:Npn \str_map_break:
+ { \prg_map_break:Nn \str_map_break: { } }
+\cs_new:Npn \str_map_break:n
+ { \prg_map_break:Nn \str_map_break: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\str_map_tokens:Nn, \str_map_tokens:cn}
+% \begin{macro}[rEXP]{\str_map_tokens:nn}
+% Uses an auxiliary of \cs{str_map_function:NN}.
+% \begin{macrocode}
+\cs_new:Npn \str_map_tokens:nn #1#2
+ {
+ \exp_args:Nno \use:nn
+ { \@@_map_function:w \@@_map_function:nn {#2} }
+ { \__kernel_tl_to_str:w {#1} }
+ \q_@@_recursion_tail ? ~
+ \prg_break_point:Nn \str_map_break: { }
+ }
+\cs_new:Npn \str_map_tokens:Nn { \exp_args:No \str_map_tokens:nn }
+\cs_generate_variant:Nn \str_map_tokens:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Accessing specific characters in a string}
+%
+% \begin{macro}[EXP]{\__kernel_str_to_other:n}
+% \begin{macro}[EXP]{\@@_to_other_loop:w, \@@_to_other_end:w}
+% First apply \cs{tl_to_str:n}, then replace all spaces by
+% \enquote{other} spaces, $8$ at a time, storing the converted part of
+% the string between the \cs{s_@@_mark} and \cs{s_@@_stop} markers. The end
+% is detected when \cs{@@_to_other_loop:w} finds one of the trailing
+% |A|, distinguished from any contents of the initial token list by
+% their category. Then \cs{@@_to_other_end:w} is called, and finds
+% the result between \cs{s_@@_mark} and the first |A| (well, there is
+% also the need to remove a space).
+% \begin{macrocode}
+\cs_new:Npn \__kernel_str_to_other:n #1
+ {
+ \exp_after:wN \@@_to_other_loop:w
+ \tl_to_str:n {#1} ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ \s_@@_mark \s_@@_stop
+ }
+\group_begin:
+\tex_lccode:D `\* = `\ %
+\tex_lccode:D `\A = `\A %
+\tex_lowercase:D
+ {
+ \group_end:
+ \cs_new:Npn \@@_to_other_loop:w
+ #1 ~ #2 ~ #3 ~ #4 ~ #5 ~ #6 ~ #7 ~ #8 ~ #9 \s_@@_stop
+ {
+ \if_meaning:w A #8
+ \@@_to_other_end:w
+ \fi:
+ \@@_to_other_loop:w
+ #9 #1 * #2 * #3 * #4 * #5 * #6 * #7 * #8 * \s_@@_stop
+ }
+ \cs_new:Npn \@@_to_other_end:w \fi: #1 \s_@@_mark #2 * A #3 \s_@@_stop
+ { \fi: #2 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\__kernel_str_to_other_fast:n}
+% \begin{macro}[rEXP]{\__kernel_str_to_other_fast_loop:w, \@@_to_other_fast_end:w}
+% The difference with \cs{__kernel_str_to_other:n} is that the converted part is
+% left in the input stream, making these commands only
+% restricted-expandable.
+% \begin{macrocode}
+\cs_new:Npn \__kernel_str_to_other_fast:n #1
+ {
+ \exp_after:wN \@@_to_other_fast_loop:w \tl_to_str:n {#1} ~
+ A ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ \s_@@_stop
+ }
+\group_begin:
+\tex_lccode:D `\* = `\ %
+\tex_lccode:D `\A = `\A %
+\tex_lowercase:D
+ {
+ \group_end:
+ \cs_new:Npn \@@_to_other_fast_loop:w
+ #1 ~ #2 ~ #3 ~ #4 ~ #5 ~ #6 ~ #7 ~ #8 ~ #9 ~
+ {
+ \if_meaning:w A #9
+ \@@_to_other_fast_end:w
+ \fi:
+ #1 * #2 * #3 * #4 * #5 * #6 * #7 * #8 * #9
+ \@@_to_other_fast_loop:w *
+ }
+ \cs_new:Npn \@@_to_other_fast_end:w #1 * A #2 \s_@@_stop {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\str_item:Nn, \str_item:cn, \str_item:nn, \str_item_ignore_spaces:nn}
+% \begin{macro}[EXP]{\@@_item:nn, \@@_item:w}
+% The \cs{str_item:nn} hands its argument with spaces escaped to
+% \cs{@@_item:nn}, and makes sure to turn the result back into
+% a proper string (with category code~$10$ spaces) eventually. The
+% \cs{str_item_ignore_spaces:nn} function does not escape spaces,
+% which are thus ignored by \cs{@@_item:nn} since
+% everything else is done with undelimited arguments.
+% Evaluate the \meta{index} argument~|#2| and count characters in
+% the string, passing those two numbers to \cs{@@_item:w} for
+% further analysis. If the \meta{index} is negative, shift it by
+% the \meta{count} to know the how many character to discard, and if
+% that is still negative give an empty result. If the \meta{index}
+% is larger than the \meta{count}, give an empty result, and
+% otherwise discard $\meta{index}-1$ characters before returning the
+% following one. The shift by $-1$ is obtained by inserting an empty
+% brace group before the string in that case: that brace group also
+% covers the case where the \meta{index} is zero.
+% \begin{macrocode}
+\cs_new:Npn \str_item:Nn { \exp_args:No \str_item:nn }
+\cs_generate_variant:Nn \str_item:Nn { c }
+\cs_new:Npn \str_item:nn #1#2
+ {
+ \exp_args:Nf \tl_to_str:n
+ {
+ \exp_args:Nf \@@_item:nn
+ { \__kernel_str_to_other:n {#1} } {#2}
+ }
+ }
+\cs_new:Npn \str_item_ignore_spaces:nn #1
+ { \exp_args:No \@@_item:nn { \tl_to_str:n {#1} } }
+\cs_new:Npn \@@_item:nn #1#2
+ {
+ \exp_after:wN \@@_item:w
+ \int_value:w \int_eval:n {#2} \exp_after:wN ;
+ \int_value:w \@@_count:n {#1} ;
+ #1 \s_@@_stop
+ }
+\cs_new:Npn \@@_item:w #1; #2;
+ {
+ \int_compare:nNnTF {#1} < 0
+ {
+ \int_compare:nNnTF {#1} < {-#2}
+ { \@@_use_none_delimit_by_s_stop:w }
+ {
+ \exp_after:wN \@@_use_i_delimit_by_s_stop:nw
+ \exp:w \exp_after:wN \@@_skip_exp_end:w
+ \int_value:w \int_eval:n { #1 + #2 } ;
+ }
+ }
+ {
+ \int_compare:nNnTF {#1} > {#2}
+ { \@@_use_none_delimit_by_s_stop:w }
+ {
+ \exp_after:wN \@@_use_i_delimit_by_s_stop:nw
+ \exp:w \@@_skip_exp_end:w #1 ; { }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_skip_exp_end:w}
+% \begin{macro}[EXP]
+% {\@@_skip_loop:wNNNNNNNN, \@@_skip_end:w, \@@_skip_end:NNNNNNNN}
+% Removes |max(#1,0)| characters from the input stream, and then
+% leaves \cs{exp_end:}. This should be expanded using
+% \cs{exp:w}. We remove characters $8$ at a time until
+% there are at most $8$ to remove. Then we do a dirty trick: the
+% \cs{if_case:w} construction leaves between $0$ and $8$ times the
+% \cs{or:} control sequence, and those \cs{or:} become arguments of
+% \cs{@@_skip_end:NNNNNNNN}. If the number of characters to remove
+% is $6$, say, then there are two \cs{or:} left, and the $8$ arguments
+% of \cs{@@_skip_end:NNNNNNNN} are the two \cs{or:}, and $6$
+% characters from the input stream, exactly what we wanted to
+% remove. Then close the \cs{if_case:w} conditional with \cs{fi:}, and
+% stop the initial expansion with \cs{exp_end:} (see places where
+% \cs{@@_skip_exp_end:w} is called).
+% \begin{macrocode}
+\cs_new:Npn \@@_skip_exp_end:w #1;
+ {
+ \if_int_compare:w #1 > 8 \exp_stop_f:
+ \exp_after:wN \@@_skip_loop:wNNNNNNNN
+ \else:
+ \exp_after:wN \@@_skip_end:w
+ \int_value:w \int_eval:w
+ \fi:
+ #1 ;
+ }
+\cs_new:Npn \@@_skip_loop:wNNNNNNNN #1; #2#3#4#5#6#7#8#9
+ {
+ \exp_after:wN \@@_skip_exp_end:w
+ \int_value:w \int_eval:n { #1 - 8 } ;
+ }
+\cs_new:Npn \@@_skip_end:w #1 ;
+ {
+ \exp_after:wN \@@_skip_end:NNNNNNNN
+ \if_case:w #1 \exp_stop_f: \or: \or: \or: \or: \or: \or: \or: \or:
+ }
+\cs_new:Npn \@@_skip_end:NNNNNNNN #1#2#3#4#5#6#7#8 { \fi: \exp_end: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\str_range:Nnn, \str_range:nnn, \str_range_ignore_spaces:nnn}
+% \begin{macro}[EXP]{\@@_range:nnn}
+% \begin{macro}[EXP]{\@@_range:w, \@@_range:nnw}
+% Sanitize the string. Then evaluate the arguments. At this stage we
+% also decrement the \meta{start index}, since our goal is to know how
+% many characters should be removed. Then limit the range to be
+% non-negative and at most the length of the string (this avoids
+% needing to check for the end of the string when grabbing
+% characters), shifting negative numbers by the appropriate amount.
+% Afterwards, skip characters, then keep some more, and finally drop
+% the end of the string.
+% \begin{macrocode}
+\cs_new:Npn \str_range:Nnn { \exp_args:No \str_range:nnn }
+\cs_generate_variant:Nn \str_range:Nnn { c }
+\cs_new:Npn \str_range:nnn #1#2#3
+ {
+ \exp_args:Nf \tl_to_str:n
+ {
+ \exp_args:Nf \@@_range:nnn
+ { \__kernel_str_to_other:n {#1} } {#2} {#3}
+ }
+ }
+\cs_new:Npn \str_range_ignore_spaces:nnn #1
+ { \exp_args:No \@@_range:nnn { \tl_to_str:n {#1} } }
+\cs_new:Npn \@@_range:nnn #1#2#3
+ {
+ \exp_after:wN \@@_range:w
+ \int_value:w \@@_count:n {#1} \exp_after:wN ;
+ \int_value:w \int_eval:n { (#2) - 1 } \exp_after:wN ;
+ \int_value:w \int_eval:n {#3} ;
+ #1 \s_@@_stop
+ }
+\cs_new:Npn \@@_range:w #1; #2; #3;
+ {
+ \exp_args:Nf \@@_range:nnw
+ { \@@_range_normalize:nn {#2} {#1} }
+ { \@@_range_normalize:nn {#3} {#1} }
+ }
+\cs_new:Npn \@@_range:nnw #1#2
+ {
+ \exp_after:wN \@@_collect_delimit_by_q_stop:w
+ \int_value:w \int_eval:n { #2 - #1 } \exp_after:wN ;
+ \exp:w \@@_skip_exp_end:w #1 ;
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{macro}[EXP]{\@@_range_normalize:nn}
+% This function converts an \meta{index} argument into an explicit
+% position in the string (a result of $0$ denoting \enquote{out of
+% bounds}). Expects two explicit integer arguments: the
+% \meta{index} |#1| and the string count~|#2|. If |#1| is negative,
+% replace it by $|#1| + |#2| + 1$, then limit to the range $[0,
+% |#2|]$.
+% \begin{macrocode}
+\cs_new:Npn \@@_range_normalize:nn #1#2
+ {
+ \int_eval:n
+ {
+ \if_int_compare:w #1 < \c_zero_int
+ \if_int_compare:w #1 < -#2 \exp_stop_f:
+ 0
+ \else:
+ #1 + #2 + 1
+ \fi:
+ \else:
+ \if_int_compare:w #1 < #2 \exp_stop_f:
+ #1
+ \else:
+ #2
+ \fi:
+ \fi:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[EXP]{\@@_collect_delimit_by_q_stop:w}
+% \begin{macro}[EXP]
+% {
+% \@@_collect_loop:wn, \@@_collect_loop:wnNNNNNNN,
+% \@@_collect_end:wn, \@@_collect_end:nnnnnnnnw
+% }
+% Collects |max(#1,0)| characters, and removes everything else until
+% \cs{s_@@_stop}. This is somewhat similar to \cs{@@_skip_exp_end:w}, but
+% accepts integer expression arguments. This time we can only grab
+% $7$ characters at a time. At the end, we use an \cs{if_case:w}
+% trick again, so that the $8$ first arguments of
+% \cs{@@_collect_end:nnnnnnnnw} are some \cs{or:}, followed by an
+% \cs{fi:}, followed by |#1| characters from the input stream. Simply
+% leaving this in the input stream closes the conditional properly
+% and the \cs{or:} disappear.
+% \begin{macrocode}
+\cs_new:Npn \@@_collect_delimit_by_q_stop:w #1;
+ { \@@_collect_loop:wn #1 ; { } }
+\cs_new:Npn \@@_collect_loop:wn #1 ;
+ {
+ \if_int_compare:w #1 > 7 \exp_stop_f:
+ \exp_after:wN \@@_collect_loop:wnNNNNNNN
+ \else:
+ \exp_after:wN \@@_collect_end:wn
+ \fi:
+ #1 ;
+ }
+\cs_new:Npn \@@_collect_loop:wnNNNNNNN #1; #2 #3#4#5#6#7#8#9
+ {
+ \exp_after:wN \@@_collect_loop:wn
+ \int_value:w \int_eval:n { #1 - 7 } ;
+ { #2 #3#4#5#6#7#8#9 }
+ }
+\cs_new:Npn \@@_collect_end:wn #1 ;
+ {
+ \exp_after:wN \@@_collect_end:nnnnnnnnw
+ \if_case:w \if_int_compare:w #1 > \c_zero_int
+ #1 \else: 0 \fi: \exp_stop_f:
+ \or: \or: \or: \or: \or: \or: \fi:
+ }
+\cs_new:Npn \@@_collect_end:nnnnnnnnw #1#2#3#4#5#6#7#8 #9 \s_@@_stop
+ { #1#2#3#4#5#6#7#8 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Counting characters}
+%
+% \begin{macro}[EXP]
+% {\str_count_spaces:N, \str_count_spaces:c, \str_count_spaces:n}
+% \begin{macro}[EXP]{\@@_count_spaces_loop:w}
+% To speed up this function, we grab and discard $9$ space-delimited
+% arguments in each iteration of the loop. The loop stops when the
+% last argument is one of the trailing |X|\meta{number}, and that
+% \meta{number} is added to the sum of $9$ that precedes, to adjust
+% the result.
+% \begin{macrocode}
+\cs_new:Npn \str_count_spaces:N
+ { \exp_args:No \str_count_spaces:n }
+\cs_generate_variant:Nn \str_count_spaces:N { c }
+\cs_new:Npn \str_count_spaces:n #1
+ {
+ \int_eval:n
+ {
+ \exp_after:wN \@@_count_spaces_loop:w
+ \tl_to_str:n {#1} ~
+ X 7 ~ X 6 ~ X 5 ~ X 4 ~ X 3 ~ X 2 ~ X 1 ~ X 0 ~ X -1 ~
+ \s_@@_stop
+ }
+ }
+\cs_new:Npn \@@_count_spaces_loop:w #1~#2~#3~#4~#5~#6~#7~#8~#9~
+ {
+ \if_meaning:w X #9
+ \@@_use_i_delimit_by_s_stop:nw
+ \fi:
+ 9 + \@@_count_spaces_loop:w
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\str_count:N, \str_count:c, \str_count:n, \str_count_ignore_spaces:n}
+% \begin{macro}[EXP]{\@@_count:n}
+% \begin{macro}[EXP]{\@@_count_aux:n, \@@_count_loop:NNNNNNNNN}
+% To count characters in a string we could first escape all spaces
+% using \cs{__kernel_str_to_other:n}, then pass the result to \cs{tl_count:n}.
+% However, the escaping step would be quadratic in the number of
+% characters in the string, and we can do better. Namely, sum the
+% number of spaces (\cs{str_count_spaces:n}) and the result of
+% \cs{tl_count:n}, which ignores spaces. Since strings tend to be
+% longer than token lists, we use specialized functions to count
+% characters ignoring spaces. Namely, loop, grabbing $9$ non-space
+% characters at each step, and end as soon as we reach one of the $9$
+% trailing items. The internal function \cs{@@_count:n}, used in
+% \cs{str_item:nn} and \cs{str_range:nnn}, is similar to
+% \cs{str_count_ignore_spaces:n} but expects its argument to already
+% be a string or a string with spaces escaped.
+% \begin{macrocode}
+\cs_new:Npn \str_count:N { \exp_args:No \str_count:n }
+\cs_generate_variant:Nn \str_count:N { c }
+\cs_new:Npn \str_count:n #1
+ {
+ \@@_count_aux:n
+ {
+ \str_count_spaces:n {#1}
+ + \exp_after:wN \@@_count_loop:NNNNNNNNN \tl_to_str:n {#1}
+ }
+ }
+\cs_new:Npn \@@_count:n #1
+ {
+ \@@_count_aux:n
+ { \@@_count_loop:NNNNNNNNN #1 }
+ }
+\cs_new:Npn \str_count_ignore_spaces:n #1
+ {
+ \@@_count_aux:n
+ { \exp_after:wN \@@_count_loop:NNNNNNNNN \tl_to_str:n {#1} }
+ }
+\cs_new:Npn \@@_count_aux:n #1
+ {
+ \int_eval:n
+ {
+ #1
+ { X 8 } { X 7 } { X 6 }
+ { X 5 } { X 4 } { X 3 }
+ { X 2 } { X 1 } { X 0 }
+ \s_@@_stop
+ }
+ }
+\cs_new:Npn \@@_count_loop:NNNNNNNNN #1#2#3#4#5#6#7#8#9
+ {
+ \if_meaning:w X #9
+ \exp_after:wN \@@_use_none_delimit_by_s_stop:w
+ \fi:
+ 9 + \@@_count_loop:NNNNNNNNN
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{The first character in a string}
+%
+% \begin{macro}[EXP]
+% {\str_head:N, \str_head:c, \str_head:n, \str_head_ignore_spaces:n}
+% \begin{macro}[EXP]{\@@_head:w}
+% The \texttt{_ignore_spaces} variant applies \cs{tl_to_str:n} then
+% grabs the first item, thus skipping spaces.
+% As usual, \cs{str_head:N} expands its argument and
+% hands it to \cs{str_head:n}. To circumvent the fact that \TeX{}
+% skips spaces when grabbing undelimited macro parameters,
+% \cs{@@_head:w} takes an argument delimited by a space. If |#1|
+% starts with a non-space character, \cs{@@_use_i_delimit_by_s_stop:nw}
+% leaves that in the input stream. On the other hand, if |#1| starts
+% with a space, the \cs{@@_head:w} takes an empty argument, and the
+% single (initially braced) space in the definition of \cs{@@_head:w}
+% makes its way to the output. Finally, for an empty argument, the
+% (braced) empty brace group in the definition of \cs{str_head:n}
+% gives an empty result after passing through
+% \cs{@@_use_i_delimit_by_s_stop:nw}.
+% \begin{macrocode}
+\cs_new:Npn \str_head:N { \exp_args:No \str_head:n }
+\cs_generate_variant:Nn \str_head:N { c }
+\cs_new:Npn \str_head:n #1
+ {
+ \exp_after:wN \@@_head:w
+ \tl_to_str:n {#1}
+ { { } } ~ \s_@@_stop
+ }
+\cs_new:Npn \@@_head:w #1 ~ %
+ { \@@_use_i_delimit_by_s_stop:nw #1 { ~ } }
+\cs_new:Npn \str_head_ignore_spaces:n #1
+ {
+ \exp_after:wN \@@_use_i_delimit_by_s_stop:nw
+ \tl_to_str:n {#1} { } \s_@@_stop
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\str_tail:N, \str_tail:c, \str_tail:n, \str_tail_ignore_spaces:n}
+% \begin{macro}[EXP]{\@@_tail_auxi:w, \@@_tail_auxii:w}
+% Getting the tail is a little bit more convoluted than the head of a
+% string. We hit the front of the string with \cs{reverse_if:N}
+% \cs{if_charcode:w} \cs{scan_stop:}. This removes the first
+% character, and necessarily makes the test true, since the character
+% cannot match \cs{scan_stop:}. The auxiliary function then inserts
+% the required \cs{fi:} to close the conditional, and leaves the tail
+% of the string in the input stream. The details are such that an
+% empty string has an empty tail (this requires in particular that the
+% end-marker |X| be unexpandable and not a control sequence). The
+% \texttt{_ignore_spaces} is rather simpler: after converting the
+% input to a string, \cs{@@_tail_auxii:w} removes one undelimited
+% argument and leaves everything else until an end-marker \cs{s_@@_mark}.
+% One can check that an empty (or blank) string yields an empty
+% tail.
+% \begin{macrocode}
+\cs_new:Npn \str_tail:N { \exp_args:No \str_tail:n }
+\cs_generate_variant:Nn \str_tail:N { c }
+\cs_new:Npn \str_tail:n #1
+ {
+ \exp_after:wN \@@_tail_auxi:w
+ \reverse_if:N \if_charcode:w
+ \scan_stop: \tl_to_str:n {#1} X X \s_@@_stop
+ }
+\cs_new:Npn \@@_tail_auxi:w #1 X #2 \s_@@_stop { \fi: #1 }
+\cs_new:Npn \str_tail_ignore_spaces:n #1
+ {
+ \exp_after:wN \@@_tail_auxii:w
+ \tl_to_str:n {#1} \s_@@_mark \s_@@_mark \s_@@_stop
+ }
+\cs_new:Npn \@@_tail_auxii:w #1 #2 \s_@@_mark #3 \s_@@_stop { #2 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{String manipulation}
+%
+% \begin{macro}[EXP]
+% {
+% \str_casefold:n, \str_casefold:V,
+% \str_lowercase:n, \str_lowercase:f,
+% \str_uppercase:n, \str_uppercase:f
+% }
+% \begin{macro}[EXP]{\@@_change_case:nn}
+% \begin{macro}[EXP]{\@@_change_case_aux:nn}
+% \begin{macro}[EXP]{\@@_change_case_result:n}
+% \begin{macro}[EXP]{\@@_change_case_output:nw, \@@_change_case_output:fw}
+% \begin{macro}[EXP]{\@@_change_case_end:nw}
+% \begin{macro}[EXP]{\@@_change_case_loop:nw}
+% \begin{macro}[EXP]{\@@_change_case_space:n}
+% \begin{macro}[EXP]
+% {\@@_change_case_char:nN, \@@_change_case_char_auxi:nN, \@@_change_case_char_auxii:nN}
+% \begin{macro}[EXP]{\@@_change_case_codepoint:nN}
+% \begin{macro}[EXP]{\@@_change_case_codepoint:nNN}
+% \begin{macro}[EXP]{\@@_change_case_codepoint:nNNN}
+% \begin{macro}[EXP]{\@@_change_case_codepoint:nNNNN}
+% \begin{macro}[EXP]{\@@_change_case_char:nnn, \@@_change_case_char_aux:nnn}
+% \begin{macro}[EXP]{\@@_change_case_char:nnnnn}
+% Case changing for programmatic reasons is done by first detokenizing
+% input then doing a simple loop that only has to worry about spaces
+% and everything else. The output is detokenized to allow data sharing
+% with text-based case changing. Similarly, for $8$-bit engines the
+% multi-byte information is shared.
+% \begin{macrocode}
+\cs_new:Npn \str_casefold:n #1 { \@@_change_case:nn {#1} { casefold } }
+\cs_new:Npn \str_lowercase:n #1 { \@@_change_case:nn {#1} { lowercase } }
+\cs_new:Npn \str_uppercase:n #1 { \@@_change_case:nn {#1} { uppercase } }
+\cs_generate_variant:Nn \str_casefold:n { V }
+\cs_generate_variant:Nn \str_lowercase:n { f }
+\cs_generate_variant:Nn \str_uppercase:n { f }
+\cs_new:Npn \@@_change_case:nn #1
+ {
+ \exp_after:wN \@@_change_case_aux:nn \exp_after:wN
+ { \tl_to_str:n {#1} }
+ }
+\cs_new:Npn \@@_change_case_aux:nn #1#2
+ {
+ \@@_change_case_loop:nw {#2} #1 \q_@@_recursion_tail \q_@@_recursion_stop
+ \@@_change_case_result:n { }
+ }
+\cs_new:Npn \@@_change_case_output:nw #1#2 \@@_change_case_result:n #3
+ { #2 \@@_change_case_result:n { #3 #1 } }
+\cs_generate_variant:Nn \@@_change_case_output:nw { f }
+\cs_new:Npn \@@_change_case_end:wn #1 \@@_change_case_result:n #2
+ { \tl_to_str:n {#2} }
+\cs_new:Npn \@@_change_case_loop:nw #1#2 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_space:nTF {#2}
+ { \@@_change_case_space:n }
+ { \@@_change_case_char:nN }
+ {#1} #2 \q_@@_recursion_stop
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new:Npn \@@_change_case_space:n #1 \c_space_tl
+ {
+ \@@_change_case_output:nw { ~ }
+ \@@_change_case_loop:nw {#1}
+ }
+\cs_new:Npn \@@_change_case_char:nN #1#2
+ {
+ \@@_if_recursion_tail_stop_do:Nn #2
+ { \@@_change_case_end:wn }
+ \@@_change_case_codepoint:nN {#1} #2
+ }
+\if_int_compare:w 0
+ \cs_if_exist:NT \tex_XeTeXversion:D { 1 }
+ \cs_if_exist:NT \tex_luatexversion:D { 1 }
+ > 0 \exp_stop_f:
+ \cs_new:Npn \@@_change_case_codepoint:nN #1#2
+ { \@@_change_case_char:fnn { \int_eval:n {`#2} } {#1} {#2} }
+\else:
+ \cs_new:Npe \@@_change_case_codepoint:nN #1#2
+ {
+ \exp_not:N \int_compare:nNnTF {`#2} > { "80 }
+ {
+ \cs_if_exist:NTF \tex_pdftexversion:D
+ { \exp_not:N \@@_change_case_char_auxi:nN }
+ {
+ \exp_not:N \int_compare:nNnTF {`#2} > { "FF }
+ { \exp_not:N \@@_change_case_char_auxii:nN }
+ { \exp_not:N \@@_change_case_char_auxi:nN }
+ }
+ }
+ { \exp_not:N \@@_change_case_char_auxii:nN }
+ {#1} #2
+ }
+ \cs_new:Npn \@@_change_case_char_auxi:nN #1#2
+ {
+ \int_compare:nNnTF {`#2} < { "E0 }
+ { \@@_change_case_codepoint:nNN }
+ {
+ \int_compare:nNnTF {`#2} < { "F0 }
+ { \@@_change_case_codepoint:nNNN }
+ { \@@_change_case_codepoint:nNNNNN }
+ }
+ {#1} #2
+ }
+ \cs_new:Npn \@@_change_case_char_auxii:nN #1#2
+ { \@@_change_case_char:fnn { \int_eval:n {`#2} } {#1} {#2} }
+ \cs_new:Npn \@@_change_case_codepoint:nNN #1#2#3
+ {
+ \@@_change_case_char:fnn
+ { \int_eval:n { (`#2 - "C0) * "40 + `#3 - "80 } }
+ {#1} {#2#3}
+ }
+ \cs_new:Npn \@@_change_case_codepoint:nNNN #1#2#3#4
+ {
+ \@@_change_case_char:fnn
+ {
+ \int_eval:n
+ { (`#2 - "E0) * "1000 + (`#3 - "80) * "40 + `#4 - "80 }
+ }
+ {#1} {#2#3#4}
+ }
+ \cs_new:Npn \@@_change_case_codepoint:nNNNN #1#2#3#4#5
+ {
+ \@@_change_case_char:fnn
+ {
+ \int_eval:n
+ {
+ (`#2 - "F0) * "40000
+ + (`#3 - "80) * "1000
+ + (`#4 - "80) * "40
+ + `#5 - "80
+ }
+ }
+ {#1} {#2#3#4#5}
+ }
+\fi:
+\cs_new:Npn \@@_change_case_char:nnn #1#2#3
+ {
+ \@@_change_case_output:fw
+ {
+ \exp_args:Ne \@@_change_case_char_aux:nnn
+ { \__kernel_codepoint_case:nn {#2} {#1} } {#1} {#3}
+ }
+ \@@_change_case_loop:nw {#2}
+ }
+\cs_generate_variant:Nn \@@_change_case_char:nnn { f }
+\cs_new:Npn \@@_change_case_char_aux:nnn #1#2#3
+ {
+ \use:e { \@@_change_case_char:nnnnn #1 {#2} {#3} }
+ }
+\cs_new:Npn \@@_change_case_char:nnnnn #1#2#3#4#5
+ {
+ \int_compare:nNnTF {#1} = {#4}
+ { \tl_to_str:n {#5} }
+ {
+ \codepoint_str_generate:n {#1}
+ \tl_if_blank:nF {#2}
+ {
+ \codepoint_str_generate:n {#2}
+ \tl_if_blank:nF {#3}
+ { \codepoint_str_generate:n {#3} }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\str_mdfive_hash:n, \str_mdfive_hash:e}
+% \begin{macrocode}
+\cs_new:Npn \str_mdfive_hash:n #1 { \tex_mdfivesum:D { \tl_to_str:n {#1} } }
+\cs_new:Npn \str_mdfive_hash:e #1 { \tex_mdfivesum:D {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}
+% {
+% \c_ampersand_str,
+% \c_atsign_str,
+% \c_backslash_str,
+% \c_left_brace_str,
+% \c_right_brace_str,
+% \c_circumflex_str,
+% \c_colon_str,
+% \c_dollar_str,
+% \c_hash_str,
+% \c_percent_str,
+% \c_tilde_str,
+% \c_underscore_str,
+% \c_zero_str
+% }
+% For all of those strings, use \cs{cs_to_str:N} to get characters with
+% the correct category code without worries
+% \begin{macrocode}
+\str_const:Ne \c_ampersand_str { \cs_to_str:N \& }
+\str_const:Ne \c_atsign_str { \cs_to_str:N \@ }
+\str_const:Ne \c_backslash_str { \cs_to_str:N \\ }
+\str_const:Ne \c_left_brace_str { \cs_to_str:N \{ }
+\str_const:Ne \c_right_brace_str { \cs_to_str:N \} }
+\str_const:Ne \c_circumflex_str { \cs_to_str:N \^ }
+\str_const:Ne \c_colon_str { \cs_to_str:N \: }
+\str_const:Ne \c_dollar_str { \cs_to_str:N \$ }
+\str_const:Ne \c_hash_str { \cs_to_str:N \# }
+\str_const:Ne \c_percent_str { \cs_to_str:N \% }
+\str_const:Ne \c_tilde_str { \cs_to_str:N \~ }
+\str_const:Ne \c_underscore_str { \cs_to_str:N \_ }
+\str_const:Ne \c_zero_str { 0 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_empty_str}
+% An empty string is simply an empty token list.
+% \begin{macrocode}
+\cs_new_eq:NN \c_empty_str \c_empty_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_tmpa_str, \l_tmpb_str, \g_tmpa_str, \g_tmpb_str}
+% Scratch strings.
+% \begin{macrocode}
+\str_new:N \l_tmpa_str
+\str_new:N \l_tmpb_str
+\str_new:N \g_tmpa_str
+\str_new:N \g_tmpb_str
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Viewing strings}
+%
+% \begin{macro}{\str_show:n, \str_show:N, \str_show:c}
+% \begin{macro}{\str_log:n, \str_log:N, \str_log:c}
+% Displays a string on the terminal.
+% \begin{macrocode}
+\cs_new_eq:NN \str_show:n \tl_show:n
+\cs_new_protected:Npn \str_show:N #1
+ {
+ \__kernel_chk_tl_type:NnnT #1 { str } { \tl_to_str:N #1 }
+ { \tl_show:N #1 }
+ }
+\cs_generate_variant:Nn \str_show:N { c }
+\cs_new_eq:NN \str_log:n \tl_log:n
+\cs_new_protected:Npn \str_log:N #1
+ {
+ \__kernel_chk_tl_type:NnnT #1 { str } { \tl_to_str:N #1 }
+ { \tl_log:N #1 }
+ }
+\cs_generate_variant:Nn \str_log:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3sys.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3sys.dtx
new file mode 100644
index 00000000000..085b5f2c979
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3sys.dtx
@@ -0,0 +1,1383 @@
+% \iffalse meta-comment
+%
+%% File: l3sys.dtx
+%
+% Copyright (C) 2015-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3sys} module\\ System/runtime functions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{The name of the job}
+%
+% \begin{variable}[added = 2015-09-19, updated = 2019-10-27]{\c_sys_jobname_str}
+% Constant that gets the \enquote{job name} assigned when \TeX{} starts.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{jobname}. For technical
+% reasons, the string here is not of the same internal form as other,
+% but may be manipulated using normal string functions.
+% \end{texnote}
+% \end{variable}
+%
+% \section{Date and time}
+%
+% \begin{variable}[added = 2015-09-22]
+% {
+% \c_sys_minute_int,
+% \c_sys_hour_int,
+% \c_sys_day_int,
+% \c_sys_month_int,
+% \c_sys_year_int,
+% }
+% The date and time at which the current job was started: these are
+% all reported as integers.
+% \begin{texnote}
+% Whilst the underlying \TeX{} primitives \tn{time}, \tn{day}, \tn{month},
+% and~\tn{year} can be altered by the user, this
+% interface to the time and date is intended to be the \enquote{real}
+% values.
+% \end{texnote}
+% \end{variable}
+%
+% \begin{variable}[added = 2023-08-27]{\c_sys_timestamp_str}
+% The timestamp for the current job: the format is as described for
+% \cs{file_timestamp:n}.
+% \end{variable}
+%
+% \section{Engine}
+%
+% \begin{function}[added = 2015-09-07, EXP, pTF]
+% {
+% \sys_if_engine_luatex:,
+% \sys_if_engine_pdftex:,
+% \sys_if_engine_ptex: ,
+% \sys_if_engine_uptex: ,
+% \sys_if_engine_xetex:
+% }
+% \begin{syntax}
+% \cs{sys_if_engine_pdftex_p:}
+% \cs{sys_if_engine_pdftex:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Conditionals which allow engine-specific code to be used. The names
+% follow naturally from those of the engine binaries: note that the
+% |(u)ptex| tests are for \epTeX{} and \eupTeX{} as \pkg{expl3} requires
+% the \eTeX{} extensions. Each conditional is true for
+% \emph{exactly one} supported engine. In particular,
+% |\sys_if_engine_ptex_p:| is true for \epTeX{} but false for \eupTeX{}.
+% \end{function}
+%
+% \begin{variable}[added = 2015-09-19]{\c_sys_engine_str}
+% The current engine given as a lower case string: one of
+% |luatex|, |pdftex|, |ptex|, |uptex| or |xetex|.
+% \end{variable}
+%
+% \begin{variable}[added = 2020-08-20]{\c_sys_engine_exec_str}
+% The name of the standard executable for the current \TeX{} engine given
+% as a lower case string: one of |luatex|,
+% |luahbtex|, |pdftex|, |eptex|, |euptex| or |xetex|.
+% \end{variable}
+%
+% \begin{variable}[added = 2020-08-20]{\c_sys_engine_format_str}
+% The name of the preloaded format for the current \TeX{} run given
+% as a lower case string: one of
+% |lualatex| (or |dvilualatex|),
+% |pdflatex| (or |latex|), |platex|, |uplatex| or |xelatex| for \LaTeX{},
+% similar names for plain \TeX{} (except \pdfTeX{} in DVI mode yields
+% |etex|), and |cont-en| for Con\TeX{}t (i.e.~the
+% \tn{fmtname}).
+% \end{variable}
+%
+% \begin{variable}[added = 2018-05-02]{\c_sys_engine_version_str}
+% The version string of the current engine, in the same form as
+% given in the banner issued when running a job. For \pdfTeX{}
+% and \LuaTeX{} this is of the form
+% \begin{quote}
+% \meta{major}.\meta{minor}.\meta{revision}
+% \end{quote}
+% For \XeTeX{}, the form is
+% \begin{quote}
+% \meta{major}.\meta{minor}
+% \end{quote}
+% For \pTeX{} and \upTeX{}, only releases since \TeX{} Live 2018
+% make the data available, and the form is more complex, as it comprises
+% the \pTeX{} version, the \upTeX{} version and the e-\pTeX{} version.
+% \begin{quote}
+% p\meta{major}.\meta{minor}.\meta{revision}-u\meta{major}.\meta{minor}^^A
+% -\meta{epTeX}
+% \end{quote}
+% where the |u| part is only present for \upTeX{}.
+% \end{variable}
+%
+% \begin{function}[added = 2021-05-12, EXP]{\sys_timer:}
+% \begin{syntax}
+% \cs{sys_timer:}
+% \end{syntax}
+% Expands to the current value of the engine's timer clock, a
+% non-negative integer. This function is only defined for engines with
+% timer support. This command measures not just CPU time but
+% real time (including time waiting for user input). The unit are
+% scaled seconds ($2^{-16}$ seconds).
+% \end{function}
+%
+% \begin{function}[added = 2021-05-12, EXP, pTF]{\sys_if_timer_exist:}
+% \begin{syntax}
+% \cs{sys_if_timer_exist_p:}
+% \cs{sys_if_timer_exist:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether current engine has timer support.
+% \end{function}
+%
+% \section{Output format}
+%
+% \begin{function}[added = 2015-09-19, EXP, pTF]
+% {
+% \sys_if_output_dvi:,
+% \sys_if_output_pdf:
+% }
+% \begin{syntax}
+% \cs{sys_if_output_dvi_p:}
+% \cs{sys_if_output_dvi:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Conditionals which give the current output mode the \TeX{} run is
+% operating in. This is always one of two outcomes, DVI mode or
+% PDF mode. The two sets of conditionals are thus complementary and
+% are both provided to allow the programmer to emphasise the most
+% appropriate case.
+% \end{function}
+%
+% \begin{variable}[added = 2015-09-19]{\c_sys_output_str}
+% The current output mode given as a lower case string: one of
+% |dvi| or |pdf|.
+% \end{variable}
+%
+% \section{Platform}
+%
+% \begin{function}[added = 2018-07-27, EXP, pTF]
+% {
+% \sys_if_platform_unix:,
+% \sys_if_platform_windows:
+% }
+% \begin{syntax}
+% \cs{sys_if_platform_unix_p:}
+% \cs{sys_if_platform_unix:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Conditionals which allow platform-specific code to be used. The names
+% follow the \Lua{} |os.type()| function, \emph{i.e.}~all Unix-like systems
+% are |unix| (including Linux and MacOS).
+% \end{function}
+%
+% \begin{variable}[added = 2018-07-27]{\c_sys_platform_str}
+% The current platform given as a lower case string: one of
+% |unix|, |windows| or |unknown|.
+% \end{variable}
+%
+% \section{Random numbers}
+%
+% \begin{function}[added = 2017-05-27, EXP]{\sys_rand_seed:}
+% \begin{syntax}
+% \cs{sys_rand_seed:}
+% \end{syntax}
+% Expands to the current value of the engine's random seed, a
+% non-negative integer. In engines without random number support this
+% expands to $0$.
+% \end{function}
+%
+% \begin{function}[added = 2017-05-27]{\sys_gset_rand_seed:n}
+% \begin{syntax}
+% \cs{sys_gset_rand_seed:n} \Arg{int expr}
+% \end{syntax}
+% Globally sets the seed for the engine's pseudo-random number
+% generator to the \meta{integer expression}. This random seed
+% affects all \cs[no-index]{\ldots{}_rand} functions (such as
+% \cs{int_rand:nn} or \cs{clist_rand_item:n}) as well as other
+% packages relying on the engine's random number generator. In
+% engines without random number support this produces an error.
+% \begin{texnote}
+% While a $32$-bit (signed) integer can be given as a seed, only the
+% absolute value is used and any number beyond $2^{28}$ is divided
+% by an appropriate power of~$2$. We recommend using an integer in
+% $[0,2^{28}-1]$.
+% \end{texnote}
+% \end{function}
+%
+% \section{Access to the shell}
+%
+% \begin{function}[noTF, added = 2019-09-20]
+% {\sys_get_shell:nnN}
+% \begin{syntax}
+% \cs{sys_get_shell:nnN} \Arg{shell~command} \Arg{setup} \meta{tl~var}
+% \cs{sys_get_shell:nnNTF} \Arg{shell~command} \Arg{setup} \meta{tl~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Defines \meta{tl~var} to the text returned by the \meta{shell command}.
+% The \meta{shell command} is converted to a string using
+% \cs{tl_to_str:n}. Category codes may need to be set appropriately
+% via the \meta{setup} argument, which is run just before running the
+% \meta{shell command} (in a group).
+% If shell escape is disabled, the \meta{tl~var} will be set to
+% \cs{q_no_value} in the non-branching version.
+% Note that quote characters (|"|) \emph{cannot} be used inside the
+% \meta{shell command}. The \cs{sys_get_shell:nnNTF} conditional
+% inserts the \meta{true code} if the shell is available and no quote is
+% detected, and the \meta{false code} otherwise.
+%
+% \emph{Note}: It is not possible to tell from \TeX{} if a command is allowed
+% in restricted shell escape. If restricted escape is enabled, the
+% \texttt{true} branch is taken: if the command is forbidden at this stage, a
+% low-level \TeX{} error will arise.
+% \end{function}
+%
+% \begin{variable}[added = 2017-05-27]{\c_sys_shell_escape_int}
+% This variable exposes the internal triple of the shell escape
+% status. The possible values are
+% \begin{description}
+% \item[0] Shell escape is disabled
+% \item[1] Unrestricted shell escape is enabled
+% \item[2] Restricted shell escape is enabled
+% \end{description}
+% \end{variable}
+%
+% \begin{function}[added = 2017-05-27, EXP, pTF]{\sys_if_shell:}
+% \begin{syntax}
+% \cs{sys_if_shell_p:}
+% \cs{sys_if_shell:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Performs a check for whether shell escape is enabled. This
+% returns true if either of restricted or unrestricted shell escape
+% is enabled.
+% \end{function}
+%
+% \begin{function}[added = 2017-05-27, EXP, pTF]{\sys_if_shell_unrestricted:}
+% \begin{syntax}
+% \cs{sys_if_shell_unrestricted_p:}
+% \cs{sys_if_shell_unrestricted:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Performs a check for whether \emph{unrestricted} shell escape is
+% enabled.
+% \end{function}
+%
+% \begin{function}[added = 2017-05-27, EXP, pTF]{\sys_if_shell_restricted:}
+% \begin{syntax}
+% \cs{sys_if_shell_restricted_p:}
+% \cs{sys_if_shell_restricted:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Performs a check for whether \emph{restricted} shell escape is
+% enabled. This returns false if unrestricted shell escape is
+% enabled. Unrestricted shell escape is not considered a superset
+% of restricted shell escape in this case. To find whether any
+% shell escape is enabled use \cs{sys_if_shell:TF}.
+% \end{function}
+%
+% \begin{function}[added = 2017-05-27]
+% {\sys_shell_now:n, \sys_shell_now:e}
+% \begin{syntax}
+% \cs{sys_shell_now:n} \Arg{tokens}
+% \end{syntax}
+% Execute \meta{tokens} through shell escape immediately.
+% \end{function}
+%
+% \begin{function}[added = 2017-05-27]
+% {\sys_shell_shipout:n, \sys_shell_shipout:e}
+% \begin{syntax}
+% \cs{sys_shell_shipout:n} \Arg{tokens}
+% \end{syntax}
+% Execute \meta{tokens} through shell escape at shipout.
+% \end{function}
+%
+% \section{System queries}
+%
+% Some queries can be made about the file system, etc., without needing to
+% use unrestricted shell escape. This is carried out using the script
+% \texttt{l3sys-query}, which is documented separately. The wrappers here
+% use this script, if available, to obtain system information that is
+% not directly available within the \TeX{} run. Note that if restricted
+% shell escape is disabled, no results can be obtained.
+%
+% \begin{function}[added = 2024-03-08, updated = 2024-04-08]
+% {\sys_get_query:nN, \sys_get_query:nnN, \sys_get_query:nnnN}
+% \begin{syntax}
+% \cs{sys_get_query:nN} \Arg{cmd} \Arg{tl var}
+% \cs{sys_get_query:nnN} \Arg{cmd} \Arg{spec} \Arg{tl var}
+% \cs{sys_get_query:nnnN} \Arg{cmd} \Arg{options} \Arg{spec} \Arg{tl var}
+% \end{syntax}
+% Sets the \meta{tl var} to the information returned by the
+% \texttt{l3sys-query} \meta{cmd}, potentially supplying the \meta{options}
+% and \meta{spec} to the query call. The valid \meta{cmd} names are at present
+% \begin{itemize}
+% \item \texttt{pwd} Returns the present working directory
+% \item \texttt{ls} Returns a directory listing, using the \meta{spec} to
+% select files and applying the \meta{options} if given
+% \end{itemize}
+% The \meta{spec} is likely to contain the wildcards |*| or |?|,
+% and will automatically be passed to
+% the script without shell expansion. In a glob is needed within the
+% \meta{options}, this will need to be protected from shell expansion
+% using |'| tokens.
+%
+% The \meta{spec} and \meta{options}, if given, are expanded fully
+% before passing to the underlying script.
+%
+% Spaces in the output are stored as active tokens, allowing them to be
+% replaced by for example a visible space easily. Other non-letter
+% characters in the ASCII range are set to category code~12. The category
+% codes for characters out of the ASCII range are left unchanged: typically
+% this will mean that with an 8-bit engine, accented values can be typeset
+% directly whilst in Unicode engines, standard category code setup will
+% apply.
+%
+% If more than one line of text is returned by the \meta{cmd}, these will be
+% separated by character~13 (|^^M|) tokens of category code~12. In most
+% cases, \cs{sys_split_query:nnnN} should be preferred when multi-line
+% output is expected.
+% \end{function}
+%
+% \begin{function}[added = 2024-03-08]
+% {\sys_split_query:nN, \sys_split_query:nnN, \sys_split_query:nnnN}
+% \begin{syntax}
+% \cs{sys_split_query:nN} \Arg{cmd} \Arg{seq}
+% \cs{sys_split_query:nnN} \Arg{cmd} \Arg{spec} \Arg{seq}
+% \cs{sys_split_query:nnnN} \Arg{cmd} \Arg{options} \Arg{spec} \Arg{seq}
+% \end{syntax}
+% Works as described for \cs{sys_split_query:nnnN}, but sets the \meta{seq}
+% to contain one entry for each line returned by \texttt{l3sys-query}.
+% This function should therefore be preferred where multi-line return is
+% expected, e.g.~for the \texttt{ls} command.
+% \end{function}
+%
+% \section{Loading configuration data}
+%
+% \begin{function}[added = 2019-09-12]{\sys_load_backend:n}
+% \begin{syntax}
+% \cs{sys_load_backend:n} \Arg{backend}
+% \end{syntax}
+% Loads the additional configuration file needed for backend support.
+% If the \meta{backend} is empty, the standard backend for the engine in
+% use will be loaded. This command may only be used once.
+% \end{function}
+%
+% \begin{function}[added = 2022-07-29]{\sys_ensure_backend:}
+% \begin{syntax}
+% \cs{sys_ensure_backend:}
+% \end{syntax}
+% Ensures that a backend has been loaded by calling \cs{sys_load_backend:n}
+% if required.
+% \end{function}
+%
+% \begin{variable}{\c_sys_backend_str}
+% Set to the name of the backend in use by \cs{sys_load_backend:n} when
+% issued. Possible values are
+% \begin{itemize}
+% \item \texttt{pdftex}
+% \item \texttt{luatex}
+% \item \texttt{xetex}
+% \item \texttt{dvips}
+% \item \texttt{dvipdfmx}
+% \item \texttt{dvisvgm}
+% \end{itemize}
+% \end{variable}
+%
+% \begin{function}[added = 2019-09-12]{\sys_load_debug:}
+% \begin{syntax}
+% \cs{sys_load_debug:}
+% \end{syntax}
+% Load the additional configuration file for debugging support.
+% \end{function}
+%
+% \subsection{Final settings}
+%
+% \begin{function}[added = 2019-10-06]{\sys_finalise:}
+% \begin{syntax}
+% \cs{sys_finalise:}
+% \end{syntax}
+% Finalises all system-dependent functionality: required before loading
+% a backend.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3sys} implementation}
+%
+% \begin{macrocode}
+%<@@=sys>
+% \end{macrocode}
+%
+% \subsection{Kernel code}
+%
+% \begin{macrocode}
+%<*package>
+%<*tex>
+% \end{macrocode}
+%
+% \begin{macro}{\l_@@_tmp_tl}
+% \begin{macrocode}
+\tl_new:N \l_@@_tmp_tl
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Detecting the engine}
+%
+% \begin{macro}{\@@_const:nn}
+% Set the |T|, |F|, |TF|, |p| forms of |#1| to be constants equal to
+% the result of evaluating the boolean expression~|#2|.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_const:nn #1#2
+ {
+ \bool_if:nTF {#2}
+ {
+ \cs_new_eq:cN { #1 :T } \use:n
+ \cs_new_eq:cN { #1 :F } \use_none:n
+ \cs_new_eq:cN { #1 :TF } \use_i:nn
+ \cs_new_eq:cN { #1 _p: } \c_true_bool
+ }
+ {
+ \cs_new_eq:cN { #1 :T } \use_none:n
+ \cs_new_eq:cN { #1 :F } \use:n
+ \cs_new_eq:cN { #1 :TF } \use_ii:nn
+ \cs_new_eq:cN { #1 _p: } \c_false_bool
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF, EXP]
+% {
+% \sys_if_engine_luatex:,
+% \sys_if_engine_pdftex:,
+% \sys_if_engine_ptex:,
+% \sys_if_engine_uptex:,
+% \sys_if_engine_xetex:
+% }
+% \begin{variable}{\c_sys_engine_str}
+% Set up the engine tests on the basis exactly one test should be true.
+% Mainly a case of looking for the appropriate marker primitive.
+% \begin{macrocode}
+\str_const:Ne \c_sys_engine_str
+ {
+ \cs_if_exist:NT \tex_luatexversion:D { luatex }
+ \cs_if_exist:NT \tex_pdftexversion:D { pdftex }
+ \cs_if_exist:NT \tex_kanjiskip:D
+ {
+ \cs_if_exist:NTF \tex_enablecjktoken:D
+ { uptex }
+ { ptex }
+ }
+ \cs_if_exist:NT \tex_XeTeXversion:D { xetex }
+ }
+\tl_map_inline:nn { { luatex } { pdftex } { ptex } { uptex } { xetex } }
+ {
+ \@@_const:nn { sys_if_engine_ #1 }
+ { \str_if_eq_p:Vn \c_sys_engine_str {#1} }
+ }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \begin{variable}{\c_sys_engine_exec_str,\c_sys_engine_format_str}
+% Take the functions defined above, and set up the engine and format
+% names. \cs{c_sys_engine_exec_str} differs from \cs{c_sys_engine_str}
+% as it is the \emph{actual} engine name, not a \enquote{filtered}
+% version. It differs for |ptex| and |uptex|, which have a leading
+% |e|, and for |luatex|, because \LaTeX{} uses the \Lua HB\TeX{}
+% engine.
+%
+% \cs{c_sys_engine_format_str} is quite similar to
+% \cs{c_sys_engine_str}, except that it differentiates |pdflatex| from
+% |latex| (which is \pdfTeX{} in DVI mode). This differentiation,
+% however, is reliable only if the user doesn't change
+% \cs{tex_pdfoutput:D} before loading this code.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_eq:NN \lua_now:e \tex_directlua:D
+ \str_const:Ne \c_sys_engine_exec_str
+ {
+ \sys_if_engine_pdftex:T { pdf }
+ \sys_if_engine_xetex:T { xe }
+ \sys_if_engine_ptex:T { ep }
+ \sys_if_engine_uptex:T { eup }
+ \sys_if_engine_luatex:T
+ {
+ lua \lua_now:e
+ {
+ if (pcall(require, 'luaharfbuzz')) then ~
+ tex.print("hb") ~
+ end
+ }
+ }
+ tex
+ }
+\group_end:
+\str_const:Ne \c_sys_engine_format_str
+ {
+ \cs_if_exist:NTF \fmtname
+ {
+ \bool_lazy_or:nnTF
+ { \str_if_eq_p:Vn \fmtname { plain } }
+ { \str_if_eq_p:Vn \fmtname { LaTeX2e } }
+ {
+ \sys_if_engine_pdftex:T
+ { \int_compare:nNnT { \tex_pdfoutput:D } = { 1 } { pdf } }
+ \sys_if_engine_xetex:T { xe }
+ \sys_if_engine_ptex:T { p }
+ \sys_if_engine_uptex:T { up }
+ \sys_if_engine_luatex:T
+ {
+ \int_compare:nNnT { \tex_pdfoutput:D } = { 0 } { dvi }
+ lua
+ }
+ \str_if_eq:VnTF \fmtname { LaTeX2e }
+ { latex }
+ {
+ \bool_lazy_and:nnT
+ { \sys_if_engine_pdftex_p: }
+ { \int_compare_p:nNn { \tex_pdfoutput:D } = { 0 } }
+ { e }
+ tex
+ }
+ }
+ { \fmtname }
+ }
+ { unknown }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_sys_engine_version_str}
+% Various different engines, various different ways to extract the
+% data!
+% \begin{macrocode}
+\str_const:Ne \c_sys_engine_version_str
+ {
+ \str_case:on \c_sys_engine_str
+ {
+ { pdftex }
+ {
+ \int_div_truncate:nn { \tex_pdftexversion:D } { 100 }
+ .
+ \int_mod:nn { \tex_pdftexversion:D } { 100 }
+ .
+ \tex_pdftexrevision:D
+ }
+ { ptex }
+ {
+ \cs_if_exist:NT \tex_ptexversion:D
+ {
+ p
+ \int_use:N \tex_ptexversion:D
+ .
+ \int_use:N \tex_ptexminorversion:D
+ \tex_ptexrevision:D
+ -
+ \int_use:N \tex_epTeXversion:D
+ }
+ }
+ { luatex }
+ {
+ \int_div_truncate:nn { \tex_luatexversion:D } { 100 }
+ .
+ \int_mod:nn { \tex_luatexversion:D } { 100 }
+ .
+ \tex_luatexrevision:D
+ }
+ { uptex }
+ {
+ \cs_if_exist:NT \tex_ptexversion:D
+ {
+ p
+ \int_use:N \tex_ptexversion:D
+ .
+ \int_use:N \tex_ptexminorversion:D
+ \tex_ptexrevision:D
+ -
+ u
+ \int_use:N \tex_uptexversion:D
+ \tex_uptexrevision:D
+ -
+ \int_use:N \tex_epTeXversion:D
+ }
+ }
+ { xetex }
+ {
+ \int_use:N \tex_XeTeXversion:D
+ \tex_XeTeXrevision:D
+ }
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Platform}
+%
+% \begin{macro}[pTF]{\sys_if_platform_unix:, \sys_if_platform_windows:}
+% \begin{variable}{\c_sys_platform_str}
+% Setting these up requires the file module (file lookup), so is actually
+% implemented there.
+% \end{variable}
+% \end{macro}
+%
+% \subsubsection{Configurations}
+%
+% \begin{macro}{\sys_load_backend:n}
+% \begin{macro}{\@@_load_backend_check:N}
+% \begin{variable}{\c_sys_backend_str}
+% Loading the backend code is pretty simply: check that the backend is valid,
+% then load it up.
+% \begin{macrocode}
+\cs_new_protected:Npn \sys_load_backend:n #1
+ {
+ \sys_finalise:
+ \str_if_exist:NTF \c_sys_backend_str
+ {
+ \str_if_eq:VnF \c_sys_backend_str {#1}
+ { \msg_error:nn { sys } { backend-set } }
+ }
+ {
+ \tl_if_blank:nF {#1}
+ { \tl_gset:Nn \g_@@_backend_tl {#1} }
+ \@@_load_backend_check:N \g_@@_backend_tl
+ \str_const:Ne \c_sys_backend_str { \g_@@_backend_tl }
+ \__kernel_sys_configuration_load:n
+ { l3backend- \c_sys_backend_str }
+ }
+ }
+\cs_new_protected:Npn \@@_load_backend_check:N #1
+ {
+ \sys_if_engine_xetex:TF
+ {
+ \str_case:VnF #1
+ {
+ { dvisvgm } { }
+ { xdvipdfmx } { \tl_gset:Nn #1 { xetex } }
+ { xetex } { }
+ }
+ {
+ \msg_error:nnee { sys } { wrong-backend }
+ #1 { xetex }
+ \tl_gset:Nn #1 { xetex }
+ }
+ }
+ {
+ \sys_if_output_pdf:TF
+ {
+ \str_if_eq:VnTF #1 { pdfmode }
+ {
+ \sys_if_engine_luatex:TF
+ { \tl_gset:Nn #1 { luatex } }
+ { \tl_gset:Nn #1 { pdftex } }
+ }
+ {
+ \bool_lazy_or:nnF
+ { \str_if_eq_p:Vn #1 { luatex } }
+ { \str_if_eq_p:Vn #1 { pdftex } }
+ {
+ \msg_error:nnee { sys } { wrong-backend }
+ #1 { \sys_if_engine_luatex:TF { luatex } { pdftex } }
+ \sys_if_engine_luatex:TF
+ { \tl_gset:Nn #1 { luatex } }
+ { \tl_gset:Nn #1 { pdftex } }
+ }
+ }
+ }
+ {
+ \str_case:VnF #1
+ {
+ { dvipdfmx } { }
+ { dvips } { }
+ { dvisvgm } { }
+ }
+ {
+ \msg_error:nnee { sys } { wrong-backend }
+ #1 { dvips }
+ \tl_gset:Nn #1 { dvips }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\sys_ensure_backend:}
+% A simple wrapper.
+% \begin{macrocode}
+\cs_new_protected:Npn \sys_ensure_backend:
+ {
+ \str_if_exist:NF \c_sys_backend_str
+ { \sys_load_backend:n { } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\g_@@_debug_bool}
+% \begin{macrocode}
+\bool_new:N \g_@@_debug_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\sys_load_debug:}
+% The most complicated thing here is that we can only use
+% \cs{__kernel_sys_configuration_load:n} in the preamble in \LaTeX{}.
+% \begin{macrocode}
+\cs_new_protected:Npn \sys_load_debug:
+ {
+ \bool_if:NF \g_@@_debug_bool
+ { \__kernel_sys_configuration_load:n { l3debug } }
+ \bool_gset_true:N \g_@@_debug_bool
+ }
+\cs_if_exist:NT \@expl@finalise@setup@@@@
+ {
+ \tl_gput_right:Nn \@expl@finalise@setup@@@@
+ {
+ \tl_gput_right:Nn \@kernel@after@begindocument
+ {
+ \cs_gset_protected:Npn \sys_load_debug:
+ { \msg_error:nn { sys } { load-debug-in-preamble } }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Access to the shell}
+%
+% \begin{variable}{\l_@@_internal_tl}
+% \begin{macrocode}
+\tl_new:N \l_@@_internal_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_marker_tl}
+% The same idea as the marker for rescanning token lists.
+% \begin{macrocode}
+\tl_const:Ne \c_@@_marker_tl { : \token_to_str:N : }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[TF]{\sys_get_shell:nnN}
+% \begin{macro}{\sys_get_shell:nnN,\@@_get:nnN,\@@_get_do:Nw}
+% Setting using a shell is at this level just a slightly specialised file
+% operation, with an additional check for quotes, as these are not supported.
+% \begin{macrocode}
+\cs_new_protected:Npn \sys_get_shell:nnN #1#2#3
+ {
+ \sys_get_shell:nnNF {#1} {#2} #3
+ { \tl_set:Nn #3 { \q_no_value } }
+ }
+\prg_new_protected_conditional:Npnn \sys_get_shell:nnN #1#2#3 { T , F , TF }
+ {
+ \sys_if_shell:TF
+ { \exp_args:No \@@_get:nnN { \tl_to_str:n {#1} } {#2} #3 }
+ { \prg_return_false: }
+ }
+\cs_new_protected:Npn \@@_get:nnN #1#2#3
+ {
+ \tl_if_in:nnTF {#1} { " }
+ {
+ \msg_error:nne
+ { kernel } { quote-in-shell } {#1}
+ \prg_return_false:
+ }
+ {
+ \group_begin:
+ \if_false: { \fi:
+ \int_set_eq:NN \tex_tracingnesting:D \c_zero_int
+ \exp_args:No \tex_everyeof:D { \c_@@_marker_tl }
+ #2 \scan_stop:
+ \exp_after:wN \@@_get_do:Nw
+ \exp_after:wN #3
+ \exp_after:wN \prg_do_nothing:
+ \tex_input:D | "#1" \scan_stop:
+ \if_false: } \fi:
+ \prg_return_true:
+ }
+ }
+\exp_args:Nno \use:nn
+ { \cs_new_protected:Npn \@@_get_do:Nw #1#2 }
+ { \c_@@_marker_tl }
+ {
+ \group_end:
+ \tl_set:No #1 {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\c_@@_shell_stream_int}
+% This is not needed for \LuaTeX{}: shell escape there isn't done using
+% a \TeX{} interface.
+% \begin{macrocode}
+\sys_if_engine_luatex:F
+ { \int_const:Nn \c_@@_shell_stream_int { 18 } }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\sys_shell_now:n, \sys_shell_now:e, \sys_shell_now:x}
+% \begin{macro}{\@@_shell_now:e}
+% Execute commands through shell escape immediately.
+%
+% For \LuaTeX{}, we use a pseudo-primitive to do the actual work.
+% \begin{macrocode}
+%</tex>
+%<*lua>
+do
+ local os_exec = os.execute
+
+ local function shellescape(cmd)
+ local status,msg = os_exec(cmd)
+ if status == nil then
+ write_nl("log","runsystem(" .. cmd .. ")...(" .. msg .. ")\n")
+ elseif status == 0 then
+ write_nl("log","runsystem(" .. cmd .. ")...executed\n")
+ else
+ write_nl("log","runsystem(" .. cmd .. ")...failed " .. (msg or "") .. "\n")
+ end
+ end
+ luacmd("@@_shell_now:e", function()
+ shellescape(scan_string())
+ end, "global", "protected")
+%</lua>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*tex>
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \sys_shell_now:n #1
+ { \@@_shell_now:e { \exp_not:n {#1} } }
+ }
+ {
+ \cs_new_protected:Npn \sys_shell_now:n #1
+ { \iow_now:Nn \c_@@_shell_stream_int {#1} }
+ }
+\cs_generate_variant:Nn \sys_shell_now:n { e, x }
+%</tex>
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\sys_shell_shipout:n, \sys_shell_shipout:e, \sys_shell_shipout:x}
+% \begin{macro}{\@@_shell_shipout:e}
+% Execute commands through shell escape at shipout.
+%
+% For \LuaTeX, we use the same helper as above but delayed using a |late_lua| whatsit.
+% Creating a |late_lua| whatsit works a bit different if we are running under Con\TeX{}t.
+% \begin{macrocode}
+%<*lua>
+ local new_latelua = nodes and nodes.nuts and nodes.nuts.pool and nodes.nuts.pool.latelua or (function()
+ local whatsit_id = node.id'whatsit'
+ local latelua_sub = node.subtype'late_lua'
+ local node_new = node.direct.new
+ local setfield = node.direct.setwhatsitfield or node.direct.setfield
+ return function(f)
+ local n = node_new(whatsit_id, latelua_sub)
+ setfield(n, 'data', f)
+ return n
+ end
+ end)()
+ local node_write = node.direct.write
+
+ luacmd("@@_shell_shipout:e", function()
+ local cmd = scan_string()
+ node_write(new_latelua(function() shellescape(cmd) end))
+ end, "global", "protected")
+end
+%</lua>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*tex>
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \sys_shell_shipout:n #1
+ { \@@_shell_shipout:e { \exp_not:n {#1} } }
+ }
+ {
+ \cs_new_protected:Npn \sys_shell_shipout:n #1
+ { \iow_shipout:Nn \c_@@_shell_stream_int {#1} }
+ }
+\cs_generate_variant:Nn \sys_shell_shipout:n { e , x }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Dynamic (every job) code}
+%
+% \begin{macro}{\__kernel_sys_everyjob:}
+% \begin{macro}{\@@_everyjob:n}
+% \begin{variable}{\g_@@_everyjob_tl}
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_sys_everyjob:
+ {
+ \tl_use:N \g_@@_everyjob_tl
+ \tl_gclear:N \g_@@_everyjob_tl
+ }
+\cs_new_protected:Npn \@@_everyjob:n #1
+ { \tl_gput_right:Nn \g_@@_everyjob_tl {#1} }
+\tl_new:N \g_@@_everyjob_tl
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{The name of the job}
+%
+% \begin{variable}{\c_sys_jobname_str}
+% Inherited from the \LaTeX3 name for the primitive. This \emph{has} to be
+% the primitive as it's set in \tn{everyjob}. If the user does
+% \begin{verbatim}
+% pdflatex \input some-file-name
+% \end{verbatim}
+% then \tn{everyjob} is inserted \emph{before} \tn{jobname} is changed form
+% |texput|, and thus we would have the wrong result.
+% \begin{macrocode}
+\@@_everyjob:n
+ { \cs_new_eq:NN \c_sys_jobname_str \tex_jobname:D }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Time and date}
+%
+% \begin{variable}
+% {
+% \c_sys_minute_int,
+% \c_sys_hour_int,
+% \c_sys_day_int,
+% \c_sys_month_int,
+% \c_sys_year_int,
+% }
+% Copies of the information provided by \TeX{}. There is a lot of defensive
+% code in package mode: someone may have moved the primitives, and they can
+% only be recovered if we have \tn{primitive} and it is working correctly.
+% For Ini\TeX{} of course that is all redundant but does no harm.
+% \begin{macrocode}
+\@@_everyjob:n
+ {
+ \group_begin:
+ \cs_set:Npn \@@_tmp:w #1
+ {
+ \str_if_eq:eeTF { \cs_meaning:N #1 } { \token_to_str:N #1 }
+ { #1 }
+ {
+ \cs_if_exist:NTF \tex_primitive:D
+ {
+ \bool_lazy_and:nnTF
+ { \sys_if_engine_xetex_p: }
+ {
+ \int_compare_p:nNn
+ { \exp_after:wN \use_none:n \tex_XeTeXrevision:D }
+ < { 99999 }
+ }
+ { 0 }
+ { \tex_primitive:D #1 }
+ }
+ { 0 }
+ }
+ }
+ \int_const:Nn \c_sys_minute_int
+ { \int_mod:nn { \@@_tmp:w \time } { 60 } }
+ \int_const:Nn \c_sys_hour_int
+ { \int_div_truncate:nn { \@@_tmp:w \time } { 60 } }
+ \int_const:Nn \c_sys_day_int { \@@_tmp:w \day }
+ \int_const:Nn \c_sys_month_int { \@@_tmp:w \month }
+ \int_const:Nn \c_sys_year_int { \@@_tmp:w \year }
+ \group_end:
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_sys_timestamp_str}
+% A simple expansion: Lua\TeX{} chokes if we use \tn{pdffeedback} here,
+% hence the direct use of Lua. Notice that the function there is in the
+% \texttt{pdf} library but isn actually tied to PDF.
+% \begin{macrocode}
+\@@_everyjob:n
+ {
+ \str_const:Ne \c_sys_timestamp_str
+ {
+ \cs_if_exist:NTF \tex_directlua:D
+ { \tex_directlua:D { tex.print(pdf.getcreationdate()) } }
+ { \tex_creationdate:D }
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Random numbers}
+%
+% \begin{macro}[EXP]{\sys_rand_seed:}
+% Unpack the primitive.
+% \begin{macrocode}
+\@@_everyjob:n
+ {
+ \cs_new:Npn \sys_rand_seed: { \tex_the:D \tex_randomseed:D }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\sys_gset_rand_seed:n}
+% The primitive always assigns the seed globally.
+% \begin{macrocode}
+\@@_everyjob:n
+ {
+ \cs_new_protected:Npn \sys_gset_rand_seed:n #1
+ { \tex_setrandomseed:D \int_eval:n {#1} \exp_stop_f: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\sys_timer:, \@@_elapsedtime:}
+% \begin{macro}[EXP, pTF]{\sys_if_timer_exist:}
+% In \LuaTeX{}, create a pseudo-primitve, otherwise try to
+% locate the real primitive. The elapsed time will be
+% available if this succeeds.
+% \begin{macrocode}
+%</tex>
+%<*lua>
+ local gettimeofday = os.gettimeofday
+ local epoch = gettimeofday() - os.clock()
+ local write = tex.write
+ local tointeger = math.tointeger
+ luacmd('@@_elapsedtime:', function()
+ write(tointeger((gettimeofday() - epoch)*65536 // 1))
+ end, 'global')
+%</lua>
+%<*tex>
+\sys_if_engine_luatex:TF
+ {
+ \cs_new:Npn \sys_timer:
+ { \@@_elapsedtime: }
+ }
+ {
+ \cs_if_exist:NTF \tex_elapsedtime:D
+ {
+ \cs_new:Npn \sys_timer:
+ { \int_value:w \tex_elapsedtime:D }
+ }
+ {
+ \cs_new:Npn \sys_timer:
+ {
+ \int_value:w
+ \msg_expandable_error:nnn { kernel } { no-elapsed-time }
+ { \sys_timer: }
+ \c_zero_int
+ }
+ }
+ }
+\@@_const:nn { sys_if_timer_exist }
+ { \cs_if_exist_p:N \tex_elapsedtime:D || \cs_if_exist_p:N \@@_elapsedtime: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Access to the shell}
+%
+% \begin{variable}{\c_sys_shell_escape_int}
+% Expose the engine's shell escape status to the user.
+% \begin{macrocode}
+\@@_everyjob:n
+ {
+ \int_const:Nn \c_sys_shell_escape_int
+ {
+ \sys_if_engine_luatex:TF
+ {
+ \tex_directlua:D
+ { tex.sprint(status.shell_escape~or~os.execute()) }
+ }
+ { \tex_shellescape:D }
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP, pTF]{\sys_if_shell:, \sys_if_shell_unrestricted:, \sys_if_shell_restricted:}
+% Performs a check for whether shell escape is enabled. The first set
+% of functions returns true if either of restricted or unrestricted
+% shell escape is enabled, while the other two sets of functions
+% return true in only one of these two cases.
+% \begin{macrocode}
+\@@_everyjob:n
+ {
+ \@@_const:nn { sys_if_shell }
+ { \int_compare_p:nNn \c_sys_shell_escape_int > 0 }
+ \@@_const:nn { sys_if_shell_unrestricted }
+ { \int_compare_p:nNn \c_sys_shell_escape_int = 1 }
+ \@@_const:nn { sys_if_shell_restricted }
+ { \int_compare_p:nNn \c_sys_shell_escape_int = 2 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{System queries}
+%
+% \begin{macro}{\sys_get_query:nN}
+% \begin{macro}{\sys_get_query:nnN}
+% \begin{macro}
+% {
+% \sys_get_query:nnnN,
+% \@@_get_query_auxi:nnnN, \@@_get_query_auxi:neeN,
+% \@@_get_query_auxii:nnnN, \@@_get_query_auxii:neeN
+% }
+% Calling the query system is quite straight-forward: most of the effort is
+% in making the read-back catcode-safe. We also want to trim off the trailing
+% |^^M| from the last line.
+% \begin{macrocode}
+\cs_new_protected:Npn \sys_get_query:nN #1#2
+ { \sys_get_query:nnnN {#1} { } { } #2 }
+\cs_new_protected:Npn \sys_get_query:nnN #1#2#3
+ { \sys_get_query:nnnN {#1} { } {#2} #3 }
+\cs_new_protected:Npn \sys_get_query:nnnN #1#2#3#4
+ {
+ \tl_clear:N #4
+ \@@_get_query_auxi:neeN {#1} {#2} {#3} #4
+ }
+\cs_new:Npn \@@_get_query_auxi:nnnN #1#2#3#4
+ {
+ \@@_get_query_auxii:neeN {#1}
+ { \tl_if_blank:nF {#2} { \tl_to_str:n { ~ #2 } } }
+ {
+ \tl_if_blank:nF {#3}
+ {
+ \c_space_tl
+ \sys_if_shell_restricted:F '
+ \tl_to_str:n {#3}
+ \sys_if_shell_restricted:F '
+ }
+ }
+ #4
+ }
+\cs_generate_variant:Nn \@@_get_query_auxi:nnnN { nee }
+\cs_new_protected:Npn \@@_get_query_auxii:nnnN #1#2#3#4
+ {
+ \sys_if_shell:T
+ {
+ \sys_get_shell:nnN
+ { l3sys-query~#1 #2 #3 }
+ {
+ \int_step_inline:nnn { 0 } { `A - 1 }
+ { \char_set_catcode_other:n {##1} }
+ \int_step_inline:nnn { `Z + 1 } { `a - 1 }
+ { \char_set_catcode_other:n {##1} }
+ \int_step_inline:nnn { `z + 1 } { 127 }
+ { \char_set_catcode_other:n {##1} }
+ \char_set_catcode_active:n { `\ }
+ \tex_endlinechar:D 13 \scan_stop:
+ }
+ \l_@@_tmp_tl
+ \tl_if_empty:NF \l_@@_tmp_tl
+ {
+ \exp_after:wN \@@_get_query:Nw \exp_after:wN #4
+ \l_@@_tmp_tl \q_stop
+ }
+ }
+ }
+\cs_generate_variant:Nn \@@_get_query_auxii:nnnN { nee }
+\group_begin:
+ \tex_lccode:D `\* = 13 \scan_stop:
+ \tex_lowercase:D
+ {
+ \group_end:
+ \cs_new_protected:Npn \@@_get_query:Nw #1#2 * \q_stop
+ }
+ { \tl_set:Nn #1 {#2} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\sys_split_query:nN}
+% \begin{macro}{\sys_split_query:nnN}
+% \begin{macro}{\sys_split_query:nnnN}
+% A wrapper for convenience.
+% \begin{macrocode}
+\cs_new_protected:Npn \sys_split_query:nN #1#2
+ { \sys_split_query:nnnN {#1} { } { } #2 }
+\cs_new_protected:Npn \sys_split_query:nnN #1#2#3
+ { \sys_split_query:nnnN {#1} { } {#2} #3 }
+\group_begin:
+ \tex_lccode:D `\* = 13 \scan_stop:
+ \tex_lowercase:D
+ {
+ \group_end:
+ \cs_new_protected:Npn \sys_split_query:nnnN #1#2#3#4
+ {
+ \seq_clear:N #4
+ \sys_get_query:nnnN {#1} {#2} {#3} \l_@@_tmp_tl
+ \tl_if_empty:NF \l_@@_tmp_tl
+ { \seq_set_split:NnV #4 * \l_@@_tmp_tl }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Held over from \pkg{l3file}}
+%
+% \begin{variable}{\g_file_curr_name_str}
+% See comments about \cs{c_sys_jobname_str}: here, as soon as there is
+% file input/output, things get \enquote{tided up}.
+% \begin{macrocode}
+\@@_everyjob:n
+ { \cs_gset_eq:NN \g_file_curr_name_str \tex_jobname:D }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Last-minute code}
+%
+% \begin{macro}{\sys_finalise:}
+% \begin{macro}{\@@_finalise:n}
+% \begin{variable}{\g_@@_finalise_tl}
+% A simple hook to finalise the system-dependent layer. This is forced by
+% the backend loader, which is forced by the main loader, so we do not need
+% to include that here.
+% \begin{macrocode}
+\cs_new_protected:Npn \sys_finalise:
+ {
+ \__kernel_sys_everyjob:
+ \tl_use:N \g_@@_finalise_tl
+ \tl_gclear:N \g_@@_finalise_tl
+ }
+\cs_new_protected:Npn \@@_finalise:n #1
+ { \tl_gput_right:Nn \g_@@_finalise_tl {#1} }
+\tl_new:N \g_@@_finalise_tl
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Detecting the output}
+%
+% \begin{macro}[pTF, EXP]
+% {
+% \sys_if_output_dvi:,
+% \sys_if_output_pdf:
+% }
+% \begin{variable}{\c_sys_output_str}
+% This is a simple enough concept: the two views here are complementary.
+% \begin{macrocode}
+\@@_finalise:n
+ {
+ \str_const:Ne \c_sys_output_str
+ {
+ \int_compare:nNnTF
+ { \cs_if_exist_use:NF \tex_pdfoutput:D { 0 } } > { 0 }
+ { pdf }
+ { dvi }
+ }
+ \@@_const:nn { sys_if_output_dvi }
+ { \str_if_eq_p:Vn \c_sys_output_str { dvi } }
+ \@@_const:nn { sys_if_output_pdf }
+ { \str_if_eq_p:Vn \c_sys_output_str { pdf } }
+ }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \subsubsection{Configurations}
+%
+% \begin{variable}{\g_@@_backend_tl}
+% As the backend has to be checked and possibly adjusted, the approach here
+% is to create a variable and use that in a one-shot to set a constant.
+% \begin{macrocode}
+\tl_new:N \g_@@_backend_tl
+\@@_finalise:n
+ {
+ \__kernel_tl_gset:Nx \g_@@_backend_tl
+ {
+ \sys_if_engine_xetex:TF
+ { xetex }
+ {
+ \sys_if_output_pdf:TF
+ {
+ \sys_if_engine_pdftex:TF
+ { pdftex }
+ { luatex }
+ }
+ { dvips }
+ }
+ }
+ }
+% \end{macrocode}
+% If there is a class option set, and recognised, we pick it up: these
+% will over-ride anything set automatically but will themselves be
+% over-written if there is a package option.
+% \begin{macrocode}
+\@@_finalise:n
+ {
+ \cs_if_exist:NT \@classoptionslist
+ {
+ \cs_if_eq:NNF \@classoptionslist \scan_stop:
+ {
+ \clist_map_inline:Nn \@classoptionslist
+ {
+ \str_case:nnT {#1}
+ {
+ { dvipdfmx }
+ { \tl_gset:Nn \g_@@_backend_tl { dvipdfmx } }
+ { dvips }
+ { \tl_gset:Nn \g_@@_backend_tl { dvips } }
+ { dvisvgm }
+ { \tl_gset:Nn \g_@@_backend_tl { dvisvgm } }
+ { pdftex }
+ { \tl_gset:Nn \g_@@_backend_tl { pdfmode } }
+ { xetex }
+ { \tl_gset:Nn \g_@@_backend_tl { xdvipdfmx } }
+ }
+ { \clist_remove_all:Nn \@unusedoptionlist {#1} }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macrocode}
+%</tex>
+%</package>
+% \end{macrocode}
+%
+%\end{implementation}
+%
+%\PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3text-case.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3text-case.dtx
new file mode 100644
index 00000000000..5010114fb4b
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3text-case.dtx
@@ -0,0 +1,2336 @@
+% \iffalse meta-comment
+%
+%% File: l3text-case.dtx
+%
+% Copyright (C) 2020-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3text-case} module\\ Text processing (case changing)^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3text-case} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=text>
+% \end{macrocode}
+%
+% \subsection{Case changing}
+%
+% \begin{variable}{\l_text_titlecase_check_letter_bool}
+% Needed to determine the route used in titlecasing.
+% \begin{macrocode}
+\bool_new:N \l_text_titlecase_check_letter_bool
+\bool_set_true:N \l_text_titlecase_check_letter_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]
+% {
+% \text_lowercase:n,
+% \text_uppercase:n,
+% \text_titlecase_all:n,
+% \text_titlecase_first:n
+% }
+% \begin{macro}[EXP]
+% {
+% \text_lowercase:nn,
+% \text_uppercase:nn,
+% \text_titlecase_all:nn,
+% \text_titlecase_first:nn
+% }
+% \begin{macro}[EXP]{\@@_change_case:nnn}
+% The user level functions here are all wrappers around the internal
+% functions for case changing.
+% \begin{macrocode}
+\cs_new:Npn \text_lowercase:n #1
+ { \@@_change_case:nnn { lower } { } {#1} }
+\cs_new:Npn \text_uppercase:n #1
+ { \@@_change_case:nnn { upper } { } {#1} }
+\cs_new:Npn \text_titlecase_all:n #1
+ { \@@_change_case:nnn { title } { } {#1} }
+\cs_new:Npn \text_titlecase_first:n #1
+ { \@@_change_case:nnnn { title } { break } { } {#1} }
+\cs_new:Npn \text_lowercase:nn #1#2
+ { \@@_change_case:nnn { lower } {#1} {#2} }
+\cs_new:Npn \text_uppercase:nn #1#2
+ { \@@_change_case:nnn { upper } {#1} {#2} }
+\cs_new:Npn \text_titlecase_all:nn #1#2
+ { \@@_change_case:nnn { title } {#1} {#2} }
+\cs_new:Npn \text_titlecase_first:nn #1#2
+ { \@@_change_case:nnnn { title } { break } {#1} {#2} }
+\cs_new:Npn \@@_change_case:nnn #1#2#3
+ { \@@_change_case:nnnn {#1} {#1} {#2} {#3} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_change_case:nnnn ,
+% \@@_change_case_auxi:nnnn ,
+% \@@_change_case_auxii:nnnn
+% }
+% \begin{macro}[EXP]{\@@_change_case_BCP:nnnn}
+% \begin{macro}[EXP]{\@@_change_case_BCP:nnnw}
+% \begin{macro}[EXP]{\@@_change_case_BCP:nnnnnw}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_store:n, \@@_change_case_store:o,
+% \@@_change_case_store:V, \@@_change_case_store:v,
+% \@@_change_case_store:e
+% }
+% \begin{macro}[EXP]{\@@_change_case_store:nw}
+% \begin{macro}[EXP]{\@@_change_case_result:n}
+% \begin{macro}[EXP]{\@@_change_case_end:w}
+% \begin{macro}[EXP]{\@@_change_case_loop:nnnw}
+% \begin{macro}[EXP]{\@@_change_case_break:w, \@@_change_case_break_aux:w}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_group_lower:nnnn ,
+% \@@_change_case_group_upper:nnnn ,
+% \@@_change_case_group_title:nnnn
+% }
+% \begin{macro}[EXP]
+% {\@@_change_case_space:nnnw, \@@_change_case_space_break:nnnw}
+% \begin{macro}[EXP]
+% {\@@_change_case_N_type:nnnN, \@@_change_case_N_type_aux:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_N_type:nnnnN}
+% \begin{macro}[EXP]{\@@_change_case_math_search:nnnNNN}
+% \begin{macro}[EXP]{\@@_change_case_math_loop:nnnNw}
+% \begin{macro}[EXP]{\@@_change_case_math_N_type:nnnNN}
+% \begin{macro}[EXP]{\@@_change_case_math_group:nnnNn}
+% \begin{macro}[EXP]{\@@_change_case_math_space:nnnNw}
+% \begin{macro}[EXP]{\@@_change_case_cs_check:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_exclude:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_exclude:nnnnN}
+% \begin{macro}[EXP]{\@@_change_case_exclude:nnnNN}
+% \begin{macro}[EXP]{\@@_change_case_exclude:nnnNw}
+% \begin{macro}[EXP]{\@@_change_case_exclude:nnnNnn}
+% \begin{macro}[EXP]{\@@_change_case_replace:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_replace:nnnn, \@@_change_case_replace:vnnn}
+% \begin{macro}[EXP]{\@@_change_case_switch:nnnN}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_switch_lower:nnnNnnnn ,
+% \@@_change_case_switch_upper:nnnNnnnn ,
+% \@@_change_case_switch_title:nnnNnnnn
+% }
+% \begin{macro}[EXP]{\@@_change_case_skip:nnw}
+% \begin{macro}[EXP]{\@@_change_case_skip_N_type:nnN}
+% \begin{macro}[EXP]{\@@_change_case_skip_group:nnn}
+% \begin{macro}[EXP]{\@@_change_case_skip_space:nnw}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_letterlike_lower:nnnN ,
+% \@@_change_case_letterlike_upper:nnnN ,
+% \@@_change_case_letterlike_title:nnnN
+% }
+% \begin{macro}[EXP]{\@@_change_case_letterlike:nnnnnN}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_custom_lower:nnnn ,
+% \@@_change_case_custom_title:nnnn ,
+% \@@_change_case_custom_upper:nnnn
+% }
+% \begin{macro}[EXP]{\@@_change_case_custom:nnnnn}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_codepoint_lower:nnnn ,
+% \@@_change_case_codepoint_upper:nnnn ,
+% \@@_change_case_codepoint_title:nnnn
+% }
+% \begin{macro}[EXP]{\@@_change_case_lower_sigma:nnnnn}
+% \begin{macro}[EXP]{\@@_change_case_lower_sigma:nnnnw}
+% \begin{macro}[EXP]{\@@_change_case_lower_sigma:nnnnN}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_codepoint_title_auxi:nnnn ,
+% \@@_change_case_codepoint_title_auxii:nnnn
+% }
+% \begin{macro}[EXP]{\@@_change_case_codepoint_title:nnn}
+% \begin{macro}[EXP]{\@@_change_case_codepoint:nnnnn}
+% \begin{macro}[EXP]{\@@_change_case_codepoint:nn}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_codepoint:nnn ,
+% \@@_change_case_codepoint:fnn ,
+% \@@_change_case_codepoint_aux:nnn
+% }
+% \begin{macro}[EXP]{\@@_change_case_codepoint_aux:nnn}
+% \begin{macro}[EXP]{\@@_change_case_codepoint_aux:nn}
+% \begin{macro}[EXP]{\@@_change_case_catcode:nn}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_next_lower:nnn ,
+% \@@_change_case_next_upper:nnn ,
+% \@@_change_case_next_title:nnn ,
+% \@@_change_case_next_end:nnn
+% }
+% As for the expansion code, the business end of case changing is the
+% handling of \texttt{N}-type tokens. First, we expand the input fully
+% (so the loops here don't need to worry about awkward look-aheads and the
+% like). Then we split into the different paths.
+%
+% The code here needs to be \texttt{f}-type expandable to deal with the
+% situation where case changing is applied in running text. There, we
+% might have case changing as a document command and the text containing
+% other non-expandable document commands.
+% \begin{verbatim}
+% \cs_set_eq:NN \MakeLowercase \text_lowercase
+% ...
+% \MakeLowercase{\enquote*{A} text}
+% \end{verbatim}
+% If we use an \texttt{e}-type expansion and wrap each token in
+% \cs{exp_not:n}, that would explode: the document command grabs
+% \cs{exp_not:n} as an argument, and things go badly wrong. So we have to
+% wrap the entire result in exactly one \cs{exp_not:n}, or rather in the
+% kernel version.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case:nnnn #1#2#3#4
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \exp:w
+ \exp_args:Ne \@@_change_case_auxi:nnnn
+ { \text_expand:n {#4} }
+ {#1} {#2} {#3}
+ }
+ }
+\cs_new:Npn \@@_change_case_auxi:nnnn #1#2#3#4
+ {
+ \exp_args:No \@@_change_case_BCP:nnnn
+ { \tl_to_str:n {#4} } {#1} {#2} {#3}
+ }
+\cs_new:Npe \@@_change_case_BCP:nnnn #1#2#3#4
+ {
+ \exp_not:N \@@_change_case_BCP:nnnw
+ {#2} {#3} {#4} #1 \tl_to_str:n { -x- -x- } \exp_not:N \q_@@_stop
+ }
+\use:e
+ {
+ \cs_new:Npn \exp_not:N \@@_change_case_BCP:nnnw
+ #1#2#3#4 \tl_to_str:n { -x- } #5 \tl_to_str:n { -x- } #6
+ \exp_not:N \q_@@_stop
+ }
+ { \@@_change_case_BCP:nnnnnw {#1} {#2} {#3} {#5} {#4} #4 - \q_@@_stop }
+\cs_new:Npn \@@_change_case_BCP:nnnnnw #1#2#3#4#5#6 - #7 \q_@@_stop
+ {
+ \bool_lazy_or:nnTF
+ { \cs_if_exist_p:c { @@_change_case_ #2 _ #6 -x- #4 :nnnnn } }
+ { \tl_if_exist_p:c { l_@@_ #2 case_special_ #6 -x- #4 _tl } }
+ { \@@_change_case_auxii:nnnn {#1} {#2} {#3} { #6 -x- #4 } }
+ {
+ \cs_if_exist:cTF { @@_change_case_ #2 _ #6 :nnnnn }
+ { \@@_change_case_auxii:nnnn {#1} {#2} {#3} {#6} }
+ { \@@_change_case_auxii:nnnn {#1} {#2} {#3} {#5} }
+ }
+ }
+\cs_new:Npn \@@_change_case_auxii:nnnn #1#2#3#4
+ {
+ \group_align_safe_begin:
+ \cs_if_exist_use:c { @@_change_case_boundary_ #2 _ #4 :Nnnnw }
+ \@@_change_case_loop:nnnw {#2} {#3} {#4} #1
+ \q_@@_recursion_tail \q_@@_recursion_stop
+ \@@_change_case_result:n { }
+ }
+% \end{macrocode}
+% As for expansion, collect up the tokens for future use.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_store:n #1
+ { \@@_change_case_store:nw {#1} }
+\cs_generate_variant:Nn \@@_change_case_store:n { o , e , V , v }
+\cs_new:Npn \@@_change_case_store:nw #1#2 \@@_change_case_result:n #3
+ { #2 \@@_change_case_result:n { #3 #1 } }
+\cs_new:Npn \@@_change_case_end:w #1 \@@_change_case_result:n #2
+ {
+ \group_align_safe_end:
+ \exp_end:
+ #2
+ }
+% \end{macrocode}
+% The main loop is the standard \texttt{tl action} type.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_loop:nnnw #1#2#3#4 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#4}
+ { \@@_change_case_N_type:nnnN }
+ {
+ \tl_if_head_is_group:nTF {#4}
+ { \use:c { @@_change_case_group_ #1 :nnnn } }
+ { \@@_change_case_space:nnnw }
+ }
+ {#1} {#2} {#3} #4 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_break:w
+ { \@@_change_case_break_aux:w \prg_do_nothing: }
+\cs_new:Npn \@@_change_case_break_aux:w
+ #1 \q_@@_recursion_tail \q_@@_recursion_stop
+ {
+ \@@_change_case_store:o {#1}
+ \@@_change_case_end:w
+ }
+% \end{macrocode}
+% For a group, we \emph{could} worry about whether this contains a character
+% or not. However, that would make life very complex for little gain: exactly
+% what a first character is is rather weakly-defined anyway. So if there is
+% a group, we simply assume that a character has been seen, and for title
+% case we switch to the \enquote{rest of the tokens} situation. To avoid
+% having too much testing, we use a two-step process here to allow the
+% titlecase functions to be separate.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_group_lower:nnnn #1#2#3#4
+ {
+ \@@_change_case_store:o
+ {
+ \exp_after:wN
+ {
+ \exp:w
+ \@@_change_case_auxii:nnnn {#4} {#1} {#2} {#3}
+ }
+ }
+ \@@_change_case_loop:nnnw {#1} {#2} {#3}
+ }
+\cs_new_eq:NN \@@_change_case_group_upper:nnnn
+ \@@_change_case_group_lower:nnnn
+\cs_new:Npn \@@_change_case_group_title:nnnn #1#2#3#4
+ {
+ \@@_change_case_store:o
+ {
+ \exp_after:wN
+ {
+ \exp:w
+ \@@_change_case_auxii:nnnn {#4} {#1} {#2} {#3}
+ }
+ }
+ \@@_change_case_skip:nnw {#2} {#3}
+ }
+\use:e
+ {
+ \cs_new:Npn \exp_not:N \@@_change_case_space:nnnw #1#2#3 \c_space_tl
+ }
+ {
+ \@@_change_case_store:n { ~ }
+ \cs_if_exist_use:cF { @@_change_case_space_ #2 :nnn }
+ {
+ \cs_if_exist_use:c { @@_change_case_boundary_ #1 _ #3 :Nnnnw }
+ \@@_change_case_loop:nnnw
+ }
+ {#2} {#2} {#3}
+ }
+\cs_new:Npn \@@_change_case_space_break:nnn #1#2#3
+ { \@@_change_case_break:w }
+% \end{macrocode}
+% The first step of handling \texttt{N}-type tokens is to filter out the
+% end-of-loop. That has to be done separately from the first real step
+% as otherwise we pick up the wrong delimiter. The loop here is the same
+% as the \texttt{expand} one, just passing the additional data long. If no
+% close-math token is found then the final clean-up is forced
+% (i.e.~there is no assumption of \enquote{well-behaved} input in terms of
+% math mode).
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_N_type:nnnN #1#2#3#4
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #4
+ { \@@_change_case_end:w }
+ \@@_change_case_N_type_aux:nnnN {#1} {#2} {#3} #4
+ }
+\cs_new:Npn \@@_change_case_N_type_aux:nnnN #1#2#3#4
+ {
+ \exp_args:NV \@@_change_case_N_type:nnnnN
+ \l_text_math_delims_tl {#1} {#2} {#3} #4
+ }
+\cs_new:Npn \@@_change_case_N_type:nnnnN #1#2#3#4#5
+ {
+ \@@_change_case_math_search:nnnNNN {#2} {#3} {#4} #5 #1
+ \q_@@_recursion_tail \q_@@_recursion_tail
+ \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_math_search:nnnNNN #1#2#3#4#5#6
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #5
+ { \@@_change_case_cs_check:nnnN {#1} {#2} {#3} #4 }
+ \token_if_eq_meaning:NNTF #4 #5
+ {
+ \@@_use_i_delimit_by_q_recursion_stop:nw
+ {
+ \@@_change_case_store:n {#4}
+ \@@_change_case_math_loop:nnnNw {#1} {#2} {#3} #6
+ }
+ }
+ { \@@_change_case_math_search:nnnNNN {#1} {#2} {#3} #4 }
+ }
+\cs_new:Npn \@@_change_case_math_loop:nnnNw #1#2#3#4#5 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#5}
+ { \@@_change_case_math_N_type:nnnNN }
+ {
+ \tl_if_head_is_group:nTF {#5}
+ { \@@_change_case_math_group:nnnNn }
+ { \@@_change_case_math_space:nnnNw }
+ }
+ {#1} {#2} {#3} #4 #5 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_math_N_type:nnnNN #1#2#3#4#5
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #5
+ { \@@_change_case_end:w }
+ \@@_change_case_store:n {#5}
+ \token_if_eq_meaning:NNTF #5 #4
+ { \@@_change_case_loop:nnnw {#1} {#2} {#3} }
+ { \@@_change_case_math_loop:nnnNw {#1} {#2} {#3} #4 }
+ }
+\cs_new:Npn \@@_change_case_math_group:nnnNn #1#2#3#4#5
+ {
+ \@@_change_case_store:n { {#5} }
+ \@@_change_case_math_loop:nnnNw {#1} {#2} {#3} #4
+ }
+\use:e
+ {
+ \cs_new:Npn \exp_not:N \@@_change_case_math_space:nnnNw #1#2#3#4
+ \c_space_tl
+ }
+ {
+ \@@_change_case_store:n { ~ }
+ \@@_change_case_math_loop:nnnNw {#1} {#2} {#3} #4
+ }
+% \end{macrocode}
+% Once potential math-mode cases are filtered out the next stage is to
+% test if the token grabbed is a control sequence: the two routes the code
+% may take are then very different.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_cs_check:nnnN #1#2#3#4
+ {
+ \token_if_cs:NTF #4
+ { \@@_change_case_exclude:nnnN {#1} {#2} {#3} }
+ {
+ \@@_codepoint_process:nN
+ { \use:c { @@_change_case_custom_ #1 :nnnn } {#1} {#2} {#3} }
+ }
+ #4
+ }
+% \end{macrocode}
+% To deal with a control sequence there is first a need to test if it is
+% on the list which indicate that case changing should be skipped. That's
+% done using a loop as for the other special cases. If a hit is found then
+% the argument is grabbed and passed through as-is.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_exclude:nnnN #1#2#3#4
+ {
+ \exp_args:Ne \@@_change_case_exclude:nnnnN
+ {
+ \exp_not:V \l_text_math_arg_tl
+ \exp_not:V \l_text_case_exclude_arg_tl
+ }
+ {#1} {#2} {#3} #4
+ }
+\cs_new:Npn \@@_change_case_exclude:nnnnN #1#2#3#4#5
+ {
+ \@@_change_case_exclude:nnnNN {#2} {#3} {#4} #5 #1
+ \q_@@_recursion_tail \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_exclude:nnnNN #1#2#3#4#5
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #5
+ { \@@_change_case_replace:nnnN {#1} {#2} {#3} #4 }
+ \str_if_eq:nnTF {#4} {#5}
+ {
+ \@@_use_i_delimit_by_q_recursion_stop:nw
+ { \@@_change_case_exclude:nnnNw {#1} {#2} {#3} #4 }
+ }
+ { \@@_change_case_exclude:nnnNN {#1} {#2} {#3} #4 }
+ }
+\cs_new:Npn \@@_change_case_exclude:nnnNw #1#2#3#4#5#
+ { \@@_change_case_exclude:nnnNnn {#1} {#2} {#3} {#4} {#5} }
+\cs_new:Npn \@@_change_case_exclude:nnnNnn #1#2#3#4#5#6
+ {
+ \tl_if_blank:nTF {#5}
+ { \@@_change_case_store:n { #4 {#6} } }
+ {
+ \@@_change_case_store:o
+ {
+ \exp_after:wN #4
+ \exp:w \@@_change_case_auxii:nnnn {#5} {#1} {#2} {#3}
+ {#6}
+ }
+ }
+ \@@_change_case_loop:nnnw {#1} {#2} {#3}
+ }
+% \end{macrocode}
+% Deal with any specialist replacement for case changing.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_replace:nnnN #1#2#3#4
+ {
+ \cs_if_exist:cTF { l_@@_case_ \token_to_str:N #4 _tl }
+ {
+ \@@_change_case_replace:vnnn
+ { l_@@_case_ \token_to_str:N #4 _tl } {#1} {#2} {#3}
+ }
+ { \@@_change_case_switch:nnnN {#1} {#2} {#3} #4 }
+ }
+\cs_new:Npn \@@_change_case_replace:nnnn #1#2#3#4
+ { \@@_change_case_loop:nnnw {#2} {#3} {#4} #1 }
+\cs_generate_variant:Nn \@@_change_case_replace:nnnn { v }
+% \end{macrocode}
+% Allow for manually-controlled case switching.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_switch:nnnN #1#2#3#4
+ {
+ \cs_if_eq:NNTF #4 \text_case_switch:nnnn
+ { \use:c { @@_change_case_switch_ #1 :nnnNnnnn } }
+ { \use:c { @@_change_case_letterlike_ #1 :nnnN } }
+ {#1} {#2} {#3} #4
+ }
+\cs_new:Npn \@@_change_case_switch_lower:nnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \@@_change_case_store:n {#7}
+ \@@_change_case_loop:nnnw {#1} {#2} {#3}
+ }
+\cs_new:Npn \@@_change_case_switch_upper:nnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \@@_change_case_store:n {#6}
+ \@@_change_case_loop:nnnw {#1} {#2} {#3}
+ }
+\cs_new:Npn \@@_change_case_switch_title:nnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \@@_change_case_store:n {#8}
+ \@@_change_case_skip:nnw {#2} {#3}
+ }
+% \end{macrocode}
+% Skip over material quickly after titlecase-first-only initials
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_skip:nnw #1#2#3 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#3}
+ { \@@_change_case_skip_N_type:nnN }
+ {
+ \tl_if_head_is_group:nTF {#3}
+ { \@@_change_case_skip_group:nnn }
+ { \@@_change_case_skip_space:nnw }
+ }
+ {#1} {#2} #3 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_skip_N_type:nnN #1#2#3
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #3
+ { \@@_change_case_end:w }
+ \@@_change_case_store:n {#3}
+ \@@_change_case_skip:nnw {#1} {#2}
+ }
+\cs_new:Npn \@@_change_case_skip_group:nnn #1#2#3
+ {
+ \@@_change_case_store:n { {#3} }
+ \@@_change_case_skip:nnw {#1} {#2}
+ }
+\cs_new:Npn \@@_change_case_skip_space:nnw #1#2
+ { \@@_change_case_space:nnnw {#1} {#1} {#2} }
+% \end{macrocode}
+% Letter-like commands may still be present: they are set up using a simple
+% lookup approach, so can easily be handled with no loop. If there is no
+% hit, we are at the end of the process: we loop around. Letter-like chars
+% are all available only in upper- and lowercase, so titlecasing maps to the
+% uppercase version.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_letterlike_lower:nnnN #1#2#3#4
+ { \@@_change_case_letterlike:nnnnnN {#1} {#1} {#1} {#2} {#3} #4 }
+\cs_new_eq:NN \@@_change_case_letterlike_upper:nnnN
+ \@@_change_case_letterlike_lower:nnnN
+\cs_new:Npn \@@_change_case_letterlike_title:nnnN #1#2#3#4
+ { \@@_change_case_letterlike:nnnnnN { upper } { end } {#1} {#2} {#3} #4 }
+\cs_new:Npn \@@_change_case_letterlike:nnnnnN #1#2#3#4#5#6
+ {
+ \cs_if_exist:cTF { c_@@_ #1 case_ \token_to_str:N #6 _tl }
+ {
+ \@@_change_case_store:v
+ { c_@@_ #1 case_ \token_to_str:N #6 _tl }
+ \use:c { @@_change_case_next_ #2 :nnn } {#2} {#4} {#5}
+ }
+ {
+ \@@_change_case_store:n {#6}
+ \cs_if_exist:cTF
+ {
+ c_@@_
+ \str_if_eq:nnTF {#1} { lower } { upper } { lower }
+ case_ \token_to_str:N #6 _tl
+ }
+ { \use:c { @@_change_case_next_ #2 :nnn } {#2} {#4} {#5} }
+ { \@@_change_case_loop:nnnw {#3} {#4} {#5} }
+ }
+ }
+% \end{macrocode}
+% Check for a customised codepoint result.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_custom_lower:nnnn #1#2#3#4
+ {
+ \@@_change_case_custom:nnnnnn {#1} {#1} {#2} {#3} {#4}
+ { \use:c { @@_change_case_codepoint_ #1 :nnnn } {#1} {#2} {#3} {#4} }
+ }
+\cs_new_eq:NN \@@_change_case_custom_upper:nnnn
+ \@@_change_case_custom_lower:nnnn
+\cs_new:Npn \@@_change_case_custom_title:nnnn #1#2#3#4
+ {
+ \@@_change_case_custom:nnnnnn { title } {#1} {#2} {#3} {#4}
+ {
+ \@@_change_case_custom:nnnnnn { upper } {#1} {#2} {#3} {#4}
+ { \use:c { @@_change_case_codepoint_ #1 :nnnn } {#1} {#2} {#3} {#4} }
+ }
+ }
+\cs_new:Npn \@@_change_case_custom:nnnnnn #1#2#3#4#5#6
+ {
+ \tl_if_exist:cTF { l_@@_ #1 case _ \tl_to_str:n {#5} _ #4 _tl }
+ {
+ \@@_change_case_replace:vnnn
+ { l_@@_ #1 case _ \tl_to_str:n {#5} _ #4 _tl } {#2} {#3} {#4}
+ }
+ {
+ \tl_if_exist:cTF { l_@@_ #1 case _ \tl_to_str:n {#5} _tl }
+ {
+ \@@_change_case_replace:vnnn
+ { l_@@_ #1 case _ \tl_to_str:n {#5} _tl } {#2} {#3} {#4}
+ }
+ {#6}
+ }
+ }
+% \end{macrocode}
+% For upper- and lowercase changes, once we get to this stage there are only
+% a couple of questions remaining: is there a language-specific mapping and
+% is there the special case of a terminal sigma. If not, then we pass to
+% a simple codepoint mapping.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_codepoint_lower:nnnn #1#2#3#4
+ {
+ \cs_if_exist_use:cF { @@_change_case_lower_ #3 :nnnnn }
+ { \@@_change_case_lower_sigma:nnnnn }
+ {#1} {#1} {#2} {#3} {#4}
+ }
+\cs_new:Npn \@@_change_case_codepoint_upper:nnnn #1#2#3#4
+ {
+ \cs_if_exist_use:cF { @@_change_case_upper_ #3 :nnnnn }
+ { \@@_change_case_codepoint:nnnnn }
+ {#1} {#1} {#2} {#3} {#4}
+ }
+% \end{macrocode}
+% If the current character is an uppercase sigma, the a check is made on the
+% next item in the input. If it is \texttt{N}-type and not a control sequence
+% then there is a look-ahead phase: the logic here is simply based on letters
+% or actives (to cover $8$-bit engines).
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_lower_sigma:nnnnn #1#2#3#4#5
+ {
+ \@@_codepoint_compare:nNnTF {#5} = { "03A3 }
+ { \@@_change_case_lower_sigma:nnnnw {#2} }
+ { \@@_change_case_codepoint:nnnnn {#1} {#2} }
+ {#3} {#4} {#5}
+ }
+\cs_new:Npn \@@_change_case_lower_sigma:nnnnw #1#2#3#4#5 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#5}
+ { \@@_change_case_lower_sigma:nnnnN {#4} }
+ {
+ \@@_change_case_store:e
+ { \codepoint_generate:nn { "03C2 } { \@@_char_catcode:N #4 } }
+ \@@_change_case_loop:nnnw
+ }
+ {#1} {#2} {#3} #5 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_lower_sigma:nnnnN #1#2#3#4#5
+ {
+ \@@_change_case_store:e
+ {
+ \bool_lazy_or:nnTF
+ { \token_if_letter_p:N #5 }
+ {
+ \bool_lazy_and_p:nn
+ { \token_if_active_p:N #5 }
+ { \int_compare_p:nNn {`#5} > { "80 } }
+ }
+ { \codepoint_generate:nn { "03C3 } { \@@_char_catcode:N #1 } }
+ { \codepoint_generate:nn { "03C2 } { \@@_char_catcode:N #1 } }
+ }
+ \@@_change_case_loop:nnnw {#2} {#3} {#4} #5
+ }
+% \end{macrocode}
+% For titlecasing, we need to obtain the general category of the current
+% codepoint.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_codepoint_title:nnnn #1#2#3#4
+ {
+ \bool_if:NTF \l_text_titlecase_check_letter_bool
+ {
+ \exp_args:Ne \@@_change_case_codepoint_title_auxi:nnnn
+ {
+ \codepoint_to_category:n
+ { \@@_codepoint_from_chars:Nw #4 }
+ }
+ }
+ { \@@_change_case_codepoint_title:nnn }
+ {#2} {#3} {#4}
+ }
+\cs_new:Npn \@@_change_case_codepoint_title_auxi:nnnn #1#2#3#4
+ {
+ \tl_if_head_eq_charcode:nNTF {#1} { L }
+ { \@@_change_case_codepoint_title:nnn }
+ { \@@_change_case_codepoint_title_auxii:nnnn { title } }
+ {#2} {#3} {#4}
+ }
+\cs_new:Npn \@@_change_case_codepoint_title:nnn #1#2#3
+ { \@@_change_case_codepoint_title_auxii:nnnn { end } {#1} {#2} {#3} }
+\cs_new:Npn \@@_change_case_codepoint_title_auxii:nnnn #1#2#3#4
+ {
+ \cs_if_exist_use:cF { @@_change_case_title_ #3 :nnnnn }
+ {
+ \cs_if_exist_use:cF { @@_change_case_upper_ #3 :nnnnn }
+ { \@@_change_case_codepoint:nnnnn }
+ }
+ { title } {#1} {#2} {#3} {#4}
+ }
+\cs_new:Npn \@@_change_case_codepoint:nnnnn #1#2#3#4#5
+ {
+ \bool_lazy_and:nnTF
+ { \tl_if_single_p:n {#5} }
+ { \token_if_active_p:N #5 }
+ { \@@_change_case_store:n {#5} }
+ {
+ \@@_change_case_store:e
+ { \@@_change_case_codepoint:nn {#1} {#5} }
+ }
+ \use:c { @@_change_case_next_ #2 :nnn } {#2} {#3} {#4}
+ }
+\cs_new:Npn \@@_change_case_codepoint:nn #1#2
+ {
+ \@@_change_case_codepoint:fnn
+ { \int_eval:n { \@@_codepoint_from_chars:Nw #2 } } {#1} {#2}
+ }
+\cs_new:Npn \@@_change_case_codepoint:nnn #1#2#3
+ {
+ \exp_args:Ne \@@_change_case_codepoint_aux:nn
+ { \__kernel_codepoint_case:nn { #2 case } {#1} } {#3}
+ }
+\cs_generate_variant:Nn \@@_change_case_codepoint:nnn { f }
+% \end{macrocode}
+% Avoid high chars with p\TeX{}.
+% \begin{macrocode}
+\sys_if_engine_ptex:T
+ {
+ \cs_new_eq:NN \@@_change_case_codepoint_aux:nnn
+ \@@_change_case_codepoint:nnn
+ \cs_gset:Npn \@@_change_case_codepoint:nnn #1#2#3
+ {
+ \int_compare:nNnTF {#1} = { -1 }
+ { \exp_not:n {#3} }
+ { \@@_change_case_codepoint_aux:nnn {#1} {#2} {#3} }
+ }
+ }
+\cs_new:Npn \@@_change_case_codepoint_aux:nn #1#2
+ {
+ \use:e { \@@_change_case_codepoint_aux:nnnn #1 {#2} }
+ }
+\cs_new:Npn \@@_change_case_codepoint_aux:nnnn #1#2#3#4
+ {
+ \@@_codepoint_compare:nNnTF {#4} = {#1}
+ { \exp_not:n {#4} }
+ {
+ \codepoint_generate:nn {#1}
+ { \@@_change_case_catcode:nn {#4} {#1} }
+ \tl_if_blank:nF {#2}
+ {
+ \codepoint_generate:nn {#2}
+ { \char_value_catcode:n {#2} }
+ \tl_if_blank:nF {#3}
+ {
+ \codepoint_generate:nn {#3}
+ { \char_value_catcode:n {#3} }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% We need to ensure that only valid catcode-extraction is attempted. That's
+% fine with Unicode engines but needs a bit of work with 8-bit ones. The
+% logic is that if the original codepoint was in the ASCII range, we keep
+% the catcode. Otherwise, if the target is in the ASCII range, we use
+% the standard catcode. If neither are true, we set as 13 on the grounds that
+% this will be what is used anyway!
+% \begin{macrocode}
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_new:Npn \@@_change_case_catcode:nn #1#2
+ { \@@_char_catcode:N #1 }
+ }
+ {
+ \cs_new:Npn \@@_change_case_catcode:nn #1#2
+ {
+ \@@_codepoint_compare:nNnTF {#1} < { "80 }
+ { \@@_char_catcode:N #1 }
+ {
+ \int_compare:nNnTF {#2} < { "80 }
+ { \char_value_catcode:n {#2} }
+ { 13 }
+ }
+ }
+ }
+\cs_new:Npn \@@_change_case_next_lower:nnn #1#2#3
+ { \@@_change_case_loop:nnnw {#1} {#2} {#3} }
+\cs_new_eq:NN \@@_change_case_next_upper:nnn
+ \@@_change_case_next_lower:nnn
+\cs_new_eq:NN \@@_change_case_next_title:nnn
+ \@@_change_case_next_lower:nnn
+\cs_new:Npn \@@_change_case_next_end:nnn #1#2#3
+ { \@@_change_case_skip:nnw {#2} {#3} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\text_declare_case_equivalent:Nn}
+% Create equivalents to allow replacement.
+% \begin{macrocode}
+\cs_new_protected:Npn \text_declare_case_equivalent:Nn #1#2
+ {
+ \tl_clear_new:c { l_@@_case_ \token_to_str:N #1 _tl }
+ \tl_set:cn { l_@@_case_ \token_to_str:N #1 _tl } {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \text_declare_lowercase_mapping:nn ,
+% \text_declare_titlecase_mapping:nn ,
+% \text_declare_uppercase_mapping:nn
+% }
+% \begin{macro}
+% {\@@_declare_case_mapping:nnn, \@@_declare_case_mapping_aux:nnn}
+% \begin{macro}
+% {
+% \text_declare_lowercase_mapping:nnn ,
+% \text_declare_titlecase_mapping:nnn ,
+% \text_declare_uppercase_mapping:nnn
+% }
+% \begin{macro}
+% {\@@_declare_case_mapping:nnnn, \@@_declare_case_mapping_aux:nnnn}
+% Codepoint customisation.
+% \begin{macrocode}
+\cs_new_protected:Npn \text_declare_lowercase_mapping:nn #1#2
+ { \@@_declare_case_mapping:nnn { lower } {#1} {#2} }
+\cs_new_protected:Npn \text_declare_titlecase_mapping:nn #1#2
+ { \@@_declare_case_mapping:nnn { title } {#1} {#2} }
+\cs_new_protected:Npn \text_declare_uppercase_mapping:nn #1#2
+ { \@@_declare_case_mapping:nnn { upper } {#1} {#2} }
+\cs_new_protected:Npn \@@_declare_case_mapping:nnn #1#2#3
+ {
+ \exp_args:Ne \@@_declare_case_mapping_aux:nnn
+ { \codepoint_str_generate:n {#2} } {#1} {#3}
+ }
+\cs_new_protected:Npn \@@_declare_case_mapping_aux:nnn #1#2#3
+ {
+ \tl_clear_new:c { l_@@_ #2 case _ #1 _tl }
+ \tl_set:cn { l_@@_ #2 case _ #1 _ tl } {#3}
+ }
+\cs_new_protected:Npn \text_declare_lowercase_mapping:nnn #1#2#3
+ { \@@_declare_case_mapping:nnnn { lower } {#1} {#2} {#3} }
+\cs_new_protected:Npn \text_declare_titlecase_mapping:nnn #1#2#3
+ { \@@_declare_case_mapping:nnnn { title } {#1} {#2} {#3} }
+\cs_new_protected:Npn \text_declare_uppercase_mapping:nnn #1#2#3
+ { \@@_declare_case_mapping:nnnn { upper } {#1} {#2} {#3} }
+\cs_new_protected:Npn \@@_declare_case_mapping:nnnn #1#2#3#4
+ {
+ \exp_args:Ne \@@_declare_case_mapping_aux:nnnn
+ { \codepoint_str_generate:n {#3} } {#1} {#2} {#4}
+ }
+\cs_new_protected:Npn \@@_declare_case_mapping_aux:nnnn #1#2#3#4
+ {
+ \tl_clear_new:c { l_@@_ #2 case _ #1 _ #3 _tl }
+ \tl_set:cn { l_@@_ #2 case _ #1 _ #3 _ tl } {#4}
+ \tl_clear_new:c { l_@@_ #2 case_special_ #3 _tl }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\text_case_switch:nnnn}
+% \begin{macro}{\@@_case_switch_marker:}
+% Set up the mechanism for manual case switching.
+% \begin{macrocode}
+\cs_new:Npn \text_case_switch:nnnn #1#2#3#4
+ {
+ \@@_case_switch_marker:
+ #1
+ }
+\cs_new:Npn \@@_case_switch_marker: { }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_change_case_generate:n}
+% A utility.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_generate:n #1
+ { \codepoint_generate:nn {#1} { \char_value_catcode:n {#1} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_upper_de-x-eszett:nnnnn,
+% \@@_change_case_upper_de-alt:nnnnn
+% }
+% A simple alternative version for German.
+% \begin{macrocode}
+\cs_new:cpn { @@_change_case_upper_de-x-eszett:nnnnn } #1#2#3#4#5
+ {
+ \@@_codepoint_compare:nNnTF {#5} = { "00DF }
+ {
+ \@@_change_case_store:e
+ {
+ \codepoint_generate:nn { "1E9E }
+ { \@@_change_case_catcode:nn {#5} { "1E9E } }
+ }
+ \use:c { @@_change_case_next_ #2 :nnn }
+ {#2} {#3} {#4}
+ }
+ { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+\cs_new_eq:cc { @@_change_case_upper_de-alt:nnnnn }
+ { @@_change_case_upper_de-x-eszett:nnnnn }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_upper_el:nnnnn ,
+% \@@_change_case_upper_el-x-iota:nnnnn ,
+% \@@_change_case_upper_el_aux:nnnnn
+% }
+% \begin{macro}[EXP]{\@@_change_case_upper_el:nnnn}
+% \begin{macro}[EXP]{\@@_change_case_upper_el:nnnnw}
+% \begin{macro}[EXP]
+% {\@@_change_case_upper_el:nnnnN, \@@_change_case_upper_el_aux:nnnnN}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_ypogegrammeni:nnnnnnw}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_ypogegrammeni:nnnnnnN}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_ypogegrammeni:nnnnnnn}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_dialytika:nnnn}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_dialytika:n}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_hiatus:nnnnw}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_hiatus:nnnnN}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_hiatus:nnnnn}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_upper_el_ypogegrammeni:n ,
+% \@@_change_case_upper_el-x-iota_ypogegrammeni:n
+% }
+% \begin{macro}[EXP]{\@@_change_case_upper_el_stress:nn}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_gobble:nnnw}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_gobble:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_gobble:nnnn}
+% \begin{macro}[EXP,noTF]
+% {
+% \@@_change_case_if_greek:n ,
+% \@@_change_case_if_greek_spacing_diacritic:n ,
+% \@@_change_case_if_greek_accent:n ,
+% \@@_change_case_if_greek_breathing:n ,
+% \@@_change_case_if_greek_stress:n ,
+% \@@_change_case_if_takes_dialytika:n ,
+% \@@_change_case_if_takes_ypogegrammeni:n
+% }
+% For Greek uppercasing, we need to know if characters \emph{in the Greek
+% range} have accents. That means doing a \textsc{nfd} conversion first, then
+% starting a search. As described by the Unicode \textsc{cldr}, Greek accents
+% need to be found \emph{after} any U+0308 (diaeresis) and are done in two
+% groups to allow for the canonical ordering. The implementation here follows
+% the data and examples from \textsc{icu}
+% (\url{https://icu.unicode.org/design/case/greek-upper}),
+% although necessarily the implementation is somewhat different. The
+% \emph{ypogegrammeni} is filtered out here as it is not actually in the
+% Greek range, so gets lost if we leave until later. The one Greek codepoint
+% we skip is the numeral sign and question mark: the first has an awkward NFD
+% for \pdfTeX{} so is best left unchanged, and the latter has issues concerning
+% how \texttt{LGR} outputs the input and output (differently!).
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_upper_el:nnnnn #1#2#3#4#5
+ {
+ \bool_lazy_and:nnTF
+ { \@@_change_case_if_greek_p:n {#5} }
+ {
+ ! \bool_lazy_or_p:nn
+ { \@@_codepoint_compare_p:nNn {#5} = { "0374 } }
+ { \@@_codepoint_compare_p:nNn {#5} = { "037E } }
+ }
+ {
+ \@@_change_case_if_greek_spacing_diacritic:nTF {#5}
+ {
+ \@@_change_case_store:n {#5}
+ \@@_change_case_loop:nnnw
+ }
+ {
+ \exp_args:Ne \@@_change_case_upper_el:nnnn
+ {
+ \codepoint_to_nfd:n
+ { \@@_codepoint_from_chars:Nw #5 }
+ }
+ }
+ {#2} {#3} {#4}
+ }
+ {
+ \@@_codepoint_compare:nNnTF {#5} = { "0345 }
+ {
+ \@@_change_case_store:e
+ {
+ \codepoint_generate:nn { "0399 }
+ { \char_value_catcode:n { "0399 } }
+ }
+ \@@_change_case_loop:nnnw {#2} {#3} {#4}
+ }
+ { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+ }
+\cs_new_eq:cN { @@_change_case_upper_el-x-iota:nnnnn }
+ \@@_change_case_upper_el:nnnnn
+\cs_new:Npn \@@_change_case_upper_el:nnnn #1#2#3#4
+ {
+ \@@_codepoint_process:nN
+ { \@@_change_case_upper_el:nnnnw {#2} {#3} {#4} } #1
+ }
+% \end{macrocode}
+% At this stage we have the first NFD codepoint as |#3|. What we need to know
+% is whether after that we have another character, either from the NFD or
+% directly in the input. If not, we store the changed character at this stage.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_upper_el:nnnnw #1#2#3#4#5 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#5}
+ { \@@_change_case_upper_el:nnnnN {#4} }
+ {
+ \@@_change_case_store:e
+ { \@@_change_case_codepoint:nn { upper } {#4} }
+ \@@_change_case_loop:nnnw
+ }
+ {#1} {#2} {#3} #5 \q_@@_recursion_stop
+ }
+% \end{macrocode}
+% Now, we check the detail of the next codepoint: again we filter out the
+% not-a-char cases, before checking if it's an dialytika, accent or diacritic.
+% (The latter do not have the same hiatus behavior as accents.) There is
+% additional work if the codepoint can take a ypogegrammeni: there, we need
+% to move any ypogegrammeni to after accents (in case the input is not
+% normalised). The ypogegrammeni itself is handled separately.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_upper_el:nnnnN #1#2#3#4#5
+ {
+ \token_if_cs:NTF #5
+ {
+ \@@_change_case_store:e
+ { \@@_change_case_codepoint:nn { upper } {#1} }
+ \@@_change_case_loop:nnnw {#2} {#3} {#4} #5
+ }
+ {
+ \@@_change_case_if_takes_ypogegrammeni:nTF {#1}
+ {
+ \@@_change_case_upper_el_ypogegrammeni:nnnnnnw
+ {#1} {#2} {#3} {#4} { } { } #5
+ }
+ { \@@_change_case_upper_el_aux:nnnnN {#1} {#2} {#3} {#4} #5 }
+ }
+ }
+\cs_new:Npn \@@_change_case_upper_el_ypogegrammeni:nnnnnnw
+ #1#2#3#4#5#6#7 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#7}
+ {
+ \@@_change_case_upper_el_ypogegrammeni:nnnnnnN
+ {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+ { \@@_change_case_upper_el_aux:nnnnN {#1} {#2} {#3} {#4} #5#6 }
+ #7 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_upper_el_ypogegrammeni:nnnnnnN #1#2#3#4#5#6#7
+ {
+ \token_if_cs:NTF #7
+ { \@@_change_case_upper_el_aux:nnnnN {#1} {#2} {#3} {#4} #5#6 }
+ {
+ \@@_codepoint_process:nN
+ {
+ \@@_change_case_upper_el_ypogegrammeni:nnnnnnn
+ {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+ }
+ #7
+ }
+\cs_new:Npn \@@_change_case_upper_el_ypogegrammeni:nnnnnnn #1#2#3#4#5#6#7
+ {
+ \@@_codepoint_compare:nNnTF {#7} = { "0345 }
+ {
+ \@@_change_case_upper_el_ypogegrammeni:nnnnnnw
+ {#1} {#2} {#3} {#4} {#5} {#7}
+ }
+ {
+ \bool_lazy_or:nnTF
+ { \@@_change_case_if_greek_accent_p:n {#7} }
+ { \@@_change_case_if_greek_breathing_p:n {#7} }
+ {
+ \@@_change_case_upper_el_ypogegrammeni:nnnnnnw
+ {#1} {#2} {#3} {#4} {#5#7} {#6}
+ }
+ { \@@_change_case_upper_el_aux:nnnnN {#1} {#2} {#3} {#4} #5#6 #7 }
+ }
+ }
+\cs_new:Npn \@@_change_case_upper_el_aux:nnnnN #1#2#3#4#5
+ {
+ \@@_codepoint_process:nN
+ { \@@_change_case_upper_el_aux:nnnnn {#1} {#2} {#3} {#4} } #5
+ }
+\cs_new:Npn \@@_change_case_upper_el_aux:nnnnn #1#2#3#4#5
+ {
+ \@@_codepoint_compare:nNnTF {#5} = { "0308 }
+ { \@@_change_case_upper_el_dialytika:nnnn {#2} {#3} {#4} {#1} }
+ {
+ \@@_change_case_if_greek_accent:nTF {#5}
+ { \@@_change_case_upper_el_hiatus:nnnnw {#2} {#3} {#4} {#1} }
+ {
+ \@@_change_case_if_greek_breathing:nTF {#5}
+ { \@@_change_case_upper_el:nnnn {#1} {#2} {#3} {#4} }
+ {
+ \@@_codepoint_compare:nNnTF {#5} = { "0345 }
+ {
+ \@@_change_case_store:e
+ { \use:c { @@_change_case_upper_ #4 _ypogegrammeni:n } {#1} }
+ \@@_change_case_loop:nnnw {#2} {#3} {#4}
+ }
+ {
+ \@@_change_case_if_greek_stress:nTF {#5}
+ {
+ \@@_change_case_store:e
+ { \@@_change_case_upper_el_stress:nn {#1} {#5} }
+ \@@_change_case_loop:nnnw {#2} {#3} {#4}
+
+ }
+ {
+ \@@_change_case_store:e
+ { \@@_change_case_codepoint:nn { upper } {#1} }
+ \@@_change_case_loop:nnnw {#2} {#3} {#4} #5
+ }
+ }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% We handle \emph{dialytika} in parts as it's also needed for the hiatus.
+% We know only two letters take it, so we can shortcut here on the second
+% part of the tests.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_upper_el_dialytika:nnnn #1#2#3#4
+ {
+ \@@_change_case_if_takes_dialytika:nTF {#4}
+ { \@@_change_case_upper_el_dialytika:n {#4} }
+ {
+ \@@_change_case_store:e
+ { \@@_change_case_codepoint:nn { upper } {#4} }
+ }
+ \@@_change_case_upper_el_gobble:nnnw {#1} {#2} {#3}
+ }
+\cs_new:Npn \@@_change_case_upper_el_dialytika:n #1
+ {
+ \@@_change_case_store:e
+ {
+ \bool_lazy_or:nnTF
+ { \@@_codepoint_compare_p:nNn {#1} = { "0399 } }
+ { \@@_codepoint_compare_p:nNn {#1} = { "03B9 } }
+ {
+ \codepoint_generate:nn { "03AA }
+ { \@@_change_case_catcode:nn {#1} { "03AA } }
+ }
+ {
+ \codepoint_generate:nn { "03AB }
+ { \@@_change_case_catcode:nn {#1} { "03AB } }
+ }
+ }
+ }
+% \end{macrocode}
+% Adding a hiatus needs some of the same ideas, but if there is not one we
+% skip this code point, hence needing a separate function.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_upper_el_hiatus:nnnnw
+ #1#2#3#4#5 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#5}
+ { \@@_change_case_upper_el_hiatus:nnnnN {#4} }
+ {
+ \@@_change_case_store:e
+ { \@@_change_case_codepoint:nn { upper } {#4} }
+ \@@_change_case_loop:nnnw
+ }
+ {#1} {#2} {#3} #5 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_upper_el_hiatus:nnnnN #1#2#3#4#5
+ {
+ \token_if_cs:NTF #5
+ {
+ \@@_change_case_store:e
+ { \@@_change_case_codepoint:nn { upper } {#1} }
+ \@@_change_case_loop:nnnw {#2} {#3} {#4} #5
+ }
+ {
+ \@@_codepoint_process:nN
+ { \@@_change_case_upper_el_hiatus:nnnnn {#1} {#2} {#3} {#4} } #5
+ }
+ }
+\cs_new:Npn \@@_change_case_upper_el_hiatus:nnnnn #1#2#3#4#5
+ {
+ \@@_change_case_if_takes_dialytika:nTF {#5}
+ {
+ \@@_change_case_store:e
+ { \@@_change_case_codepoint:nn { upper } {#1} }
+ \@@_change_case_upper_el_dialytika:n {#5}
+ \@@_change_case_upper_el_gobble:nnnw {#2} {#3} {#4}
+ }
+ { \@@_change_case_upper_el:nnnn {#1} {#2} {#3} {#4} #5 }
+ }
+% \end{macrocode}
+% Handling the \emph{ypogegrammeni} output depends on the selected approach
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_upper_el_ypogegrammeni:n #1
+ {
+ \exp_args:Ne \@@_change_case_generate:n
+ {
+ \int_case:nn
+ { \@@_codepoint_from_chars:Nw #1 }
+ {
+ { "0391 } { "1FBC }
+ { "03B1 } { "1FBC }
+ { "0397 } { "1FCC }
+ { "03B7 } { "1FCC }
+ { "03A9 } { "1FFC }
+ { "03C9 } { "1FFC }
+ }
+ }
+ }
+\cs_new:cpn { @@_change_case_upper_el-x-iota_ypogegrammeni:n } #1
+ {
+ \@@_change_case_codepoint:nn { upper } {#1}
+ \codepoint_generate:nn { "0399 }
+ { \char_value_catcode:n { "0399 } }
+ }
+% \end{macrocode}
+% We choose to retain stress diacritics, but we also need to recombine
+% them for pdf\TeX{}. That is handled here.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_upper_el_stress:nn #1#2
+ {
+ \exp_args:Ne \@@_change_case_generate:n
+ {
+ \int_case:nn
+ { \@@_codepoint_from_chars:Nw #2 }
+ {
+ { "0304 }
+ {
+ \int_case:nn { \@@_codepoint_from_chars:Nw #1 }
+ {
+ { "0391 } { "1FB9 }
+ { "03B1 } { "1FB9 }
+ { "0399 } { "1FD9 }
+ { "03B9 } { "1FD9 }
+ { "03A5 } { "1FE9 }
+ { "03C5 } { "1FE9 }
+ }
+ }
+ { "0306 }
+ {
+ \int_case:nn { \@@_codepoint_from_chars:Nw #1 }
+ {
+ { "0391 } { "1FB8 }
+ { "03B1 } { "1FB8 }
+ { "0399 } { "1FD8 }
+ { "03B9 } { "1FD8 }
+ { "03A5 } { "1FE8 }
+ { "03C5 } { "1FE8 }
+ }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% For clearing out trailing combining marks after we have dealt with
+% the first one.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_upper_el_gobble:nnnw
+ #1#2#3#4 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#4}
+ { \@@_change_case_upper_el_gobble:nnnN }
+ { \@@_change_case_loop:nnnw }
+ {#1} {#2} {#3} #4 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_upper_el_gobble:nnnN #1#2#3#4
+ {
+ \token_if_cs:NTF #4
+ { \@@_change_case_loop:nnnw {#1} {#2} {#3} }
+ {
+ \@@_codepoint_process:nN
+ { \@@_change_case_upper_el_gobble:nnnn {#1} {#2} {#3} }
+ }
+ #4
+ }
+\cs_new:Npn \@@_change_case_upper_el_gobble:nnnn #1#2#3#4
+ {
+ \bool_lazy_or:nnTF
+ { \@@_change_case_if_greek_accent_p:n {#4} }
+ { \@@_change_case_if_greek_breathing_p:n {#4} }
+ { \@@_change_case_upper_el_gobble:nnnw {#1} {#2} {#3} }
+ { \@@_change_case_loop:nnnw {#1} {#2} {#3} #4 }
+ }
+% \end{macrocode}
+% Luckily the Greek range is limited and clear.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_change_case_if_greek:n #1 { p , TF }
+ {
+ \exp_args:Nf \@@_change_case_if_greek:n
+ { \int_eval:n { \@@_codepoint_from_chars:Nw #1 } }
+ }
+\cs_new:Npn \@@_change_case_if_greek:n #1
+ {
+ \if_int_compare:w #1 < "0370 \exp_stop_f:
+ \prg_return_false:
+ \else:
+ \if_int_compare:w #1 > "03FF \exp_stop_f:
+ \if_int_compare:w #1 < "1F00 \exp_stop_f:
+ \prg_return_false:
+ \else:
+ \if_int_compare:w #1 > "1FFF \exp_stop_f:
+ \if_int_compare:w #1 = "2126 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \else:
+ \prg_return_true:
+ \fi:
+ \fi:
+ \else:
+ \prg_return_true:
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% We follow ICU in adding a few extras to the accent list here.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_change_case_if_greek_accent:n #1 { TF , p }
+ {
+ \exp_args:Nf \@@_change_case_if_greek_accent:n
+ { \int_eval:n { \@@_codepoint_from_chars:Nw #1 } }
+ }
+\cs_new:Npn \@@_change_case_if_greek_accent:n #1
+ {
+ \if_int_compare:w #1 = "0300 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "0301 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "0342 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "0302 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "0303 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "0311 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ }
+\prg_new_conditional:Npnn \@@_change_case_if_greek_spacing_diacritic:n
+ #1 { TF }
+ {
+ \exp_args:Nf \@@_change_case_if_greek_spacing_diacritic:n
+ { \int_eval:n { \@@_codepoint_from_chars:Nw #1 } }
+ }
+\cs_new:Npn \@@_change_case_if_greek_spacing_diacritic:n #1
+ {
+ \if_int_compare:w #1 < "1FBD \exp_stop_f:
+ \if_int_compare:w #1 = "037A \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \else:
+ \if_int_compare:w #1 = "1FBD \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FBF \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FC0 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FC1 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FCD \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FCE \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FCF \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FDD \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FDE \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FDF \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FED \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FEE \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FEF \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FFD \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FFE \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ }
+\prg_new_conditional:Npnn \@@_change_case_if_greek_breathing:n
+ #1 { TF , p }
+ {
+ \exp_args:Nf \@@_change_case_if_greek_breathing:n
+ { \int_eval:n { \@@_codepoint_from_chars:Nw #1 } }
+ }
+\cs_new:Npn \@@_change_case_if_greek_breathing:n #1
+ {
+ \if_int_compare:w #1 = "0313 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "0314 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ }
+\prg_new_conditional:Npnn \@@_change_case_if_greek_stress:n
+ #1 { TF , p }
+ {
+ \exp_args:Nf \@@_change_case_if_greek_stress:n
+ { \int_eval:n { \@@_codepoint_from_chars:Nw #1 } }
+ }
+\cs_new:Npn \@@_change_case_if_greek_stress:n #1
+ {
+ \if_int_compare:w #1 = "0304 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "0306 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ }
+\prg_new_conditional:Npnn \@@_change_case_if_takes_dialytika:n #1 { TF }
+ {
+ \exp_args:Nf \@@_change_case_if_takes_dialytika:n
+ { \int_eval:n { \@@_codepoint_from_chars:Nw #1 } }
+ }
+\cs_new:Npn \@@_change_case_if_takes_dialytika:n #1
+ {
+ \if_int_compare:w #1 = "0399 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "03B9 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "03A5 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "03C5 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ }
+\prg_new_conditional:Npnn \@@_change_case_if_takes_ypogegrammeni:n #1 { TF }
+ {
+ \exp_args:Nf \@@_change_case_if_takes_ypogegrammeni:n
+ { \int_eval:n { \@@_codepoint_from_chars:Nw #1 } }
+ }
+\cs_new:Npn \@@_change_case_if_takes_ypogegrammeni:n #1
+ {
+ \if_int_compare:w #1 = "03B1 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "03B7 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "03C9 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_boundary_upper_el:Nnnnw,
+% \@@_change_case_boundary_upper_el-x-iota:Nnnnw
+% }
+% \begin{macro}[EXP]{\@@_change_case_boundary_upper_el:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_boundary_upper_el:nnnn}
+% \begin{macro}[EXP]{\@@_change_case_boundary_upper_el:nnnnw}
+% There is one things that need special treatment at the start of
+% words in Greek. For an isolated accent \emph{eta},
+% which is handled by seeing if we have exactly one of the affected
+% codepoints followed by a space or brace group.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_boundary_upper_el:Nnnnw
+ #1#2#3#4#5 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#5}
+ { \@@_change_case_boundary_upper_el:nnnN }
+ { \@@_change_case_loop:nnnw }
+ {#2} {#3} {#4} #5 \q_@@_recursion_stop
+ }
+\cs_new_eq:cN { @@_change_case_boundary_upper_el-x-iota:Nnnnw }
+ \@@_change_case_boundary_upper_el:Nnnnw
+\cs_new:Npn \@@_change_case_boundary_upper_el:nnnN #1#2#3#4
+ {
+ \token_if_cs:NTF #4
+ { \@@_change_case_loop:nnnw {#1} {#2} {#3} }
+ {
+ \@@_codepoint_process:nN
+ { \@@_change_case_boundary_upper_el:nnnn {#1} {#2} {#3} }
+ }
+ #4
+ }
+\cs_new:Npn \@@_change_case_boundary_upper_el:nnnn #1#2#3#4
+ {
+ \bool_lazy_any:nTF
+ {
+ { \@@_codepoint_compare_p:nNn {#4} = { "0389 } }
+ { \@@_codepoint_compare_p:nNn {#4} = { "03AE } }
+ { \@@_codepoint_compare_p:nNn {#4} = { "1F22 } }
+ { \@@_codepoint_compare_p:nNn {#4} = { "1F2A } }
+ }
+ { \@@_change_case_boundary_upper_el:nnnnw {#1} {#2} {#3} {#4} }
+ { \@@_change_case_breathing:nnnn {#1} {#2} {#3} {#4} }
+ }
+\cs_new:Npn \@@_change_case_boundary_upper_el:nnnnw
+ #1#2#3#4#5 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#5}
+ { \@@_change_case_loop:nnnw {#1} {#2} {#3} #4 }
+ {
+ \@@_change_case_store:e
+ {
+ \codepoint_generate:nn { "0389 }
+ { \@@_change_case_catcode:nn {#4} { "0389 } }
+ }
+ \@@_change_case_loop:nnnw {#1} {#2} {#3}
+ }
+ #5 \q_@@_recursion_stop
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{macro}[EXP]{\@@_change_case_breathing:nnnn}
+% \begin{macro}[EXP]{\@@_change_case_breathing:nnnnn}
+% \begin{macro}[EXP]{\@@_change_case_breathing:nnnnnw}
+% \begin{macro}[EXP]{\@@_change_case_breathing:nnnnnnw}
+% \begin{macro}[EXP]{\@@_change_case_breathing_aux:nnnnnn}
+% \begin{macro}[EXP]{\@@_change_case_breathing_aux:nnnnw}
+% \begin{macro}[EXP]{\@@_change_case_breathing_aux:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_breathing_dialytika:nnnn}
+% In Greek, breathing diacritics are normally dropped when uppercasing:
+% see the code for the general case. However, for the first character
+% of a word, if there is a breather \emph{and} the next character takes
+% a \emph{dialytika}, it needs to be added. We start by checking if
+% the current codepoint is in the Greek range, then decomposing.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_breathing:nnnn #1#2#3#4
+ {
+ \@@_change_case_if_greek:nTF {#4}
+ {
+ \exp_args:Ne \@@_change_case_breathing:nnnnn
+ {
+ \codepoint_to_nfd:n
+ { \@@_codepoint_from_chars:Nw #4 }
+ }
+ {#1} {#2} {#3} {#4}
+ }
+ { \@@_change_case_loop:nnnw {#1} {#2} {#3} #4 }
+ }
+\cs_new:Npn \@@_change_case_breathing:nnnnn #1#2#3#4#5
+ {
+ \@@_codepoint_process:nN
+ { \@@_change_case_breathing:nnnnnw {#2} {#3} {#4} {#5} }
+ #1 \q_mark
+ }
+% \end{macrocode}
+% Normal form decomposition will always give between one and three
+% codepoints. Luckily, the two breathing marks (\emph{psili} and
+% \emph{dasia}) will be in a predictable position: last. So we can
+% quickly establish first that there was a change on decomposition,
+% and second if the final resulting codepoint is one of the two we
+% care about.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_breathing:nnnnnw #1#2#3#4#5#6 \q_mark
+ {
+ \tl_if_blank:nTF {#6}
+ { \@@_change_case_loop:nnnw {#1} {#2} {#3} #4 }
+ {
+ \@@_codepoint_process:nN
+ { \@@_change_case_breathing:nnnnnnw {#1} {#2} {#3} {#4} {#5} }
+ #6 \q_mark
+ }
+ }
+\cs_new:Npn \@@_change_case_breathing:nnnnnnw #1#2#3#4#5#6#7 \q_mark
+ {
+ \tl_if_blank:nTF {#7}
+ {
+ \@@_change_case_breathing_aux:nnnnnn
+ {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+ {
+ \@@_codepoint_process:nN
+ { \@@_change_case_breathing:nnnnnnw {#1} {#2} {#3} {#4} {#5} }
+ #7 \q_mark
+ }
+ }
+\cs_new:Npn \@@_change_case_breathing_aux:nnnnnn #1#2#3#4#5#6
+ {
+ \bool_lazy_or:nnTF
+ { \@@_codepoint_compare_p:nNn {#6} = { "0313 } }
+ { \@@_codepoint_compare_p:nNn {#6} = { "0314 } }
+ { \@@_change_case_breathing_aux:nnnnw {#1} {#2} {#3} {#5} }
+ { \@@_change_case_loop:nnnw {#1} {#2} {#3} #4 }
+ }
+% \end{macrocode}
+% Now the lookahead can be fired: check the next codepoint and assess
+% whether it takes a \emph{dialytika}. Drop the
+% breathing mark or generate the \emph{dialytika}: the
+% latter is code shared with the general mechanism.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_breathing_aux:nnnnw #1#2#3#4#5
+ \q_@@_recursion_stop
+ {
+ \@@_change_case_store:e
+ { \@@_change_case_codepoint:nn { upper } {#4} }
+ \tl_if_head_is_N_type:nTF {#5}
+ { \@@_change_case_breathing_aux:nnnN }
+ { \@@_change_case_loop:nnnw }
+ {#1} {#2} {#3} #5 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_breathing_aux:nnnN #1#2#3#4
+ {
+ \@@_codepoint_process:nN
+ { \@@_change_case_breathing_dialytika:nnnn {#1} {#2} {#3} } #4
+ }
+\cs_new:Npn \@@_change_case_breathing_dialytika:nnnn #1#2#3#4
+ {
+ \@@_change_case_if_takes_dialytika:nTF {#4}
+ {
+ \@@_change_case_upper_el_dialytika:n {#4}
+ \@@_change_case_loop:nnnw {#1} {#2} {#3}
+ }
+ { \@@_change_case_loop:nnnw {#1} {#2} {#3} #4 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{macro}[EXP]{\@@_change_case_title_el:nnnnn}
+% Titlecasing retains accents, but to prevent the uppercasing code
+% from kicking in, there has to be an explicit function here.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_title_el:nnnnn #1#2#3#4#5
+ { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_upper_hy:nnnnn ,
+% \@@_change_case_title_hy:nnnnn ,
+% \@@_change_case_upper_hy-x-yiwn:nnnnn ,
+% \@@_change_case_title_hy-x-yiwn:nnnnn
+% }
+% See \url{https://www.unicode.org/L2/L2020/20143-armenian-ech-yiwn.pdf}.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_upper_hy:nnnnn #1#2#3#4#5
+ {
+ \@@_codepoint_compare:nNnTF {#5} = { "0587 }
+ {
+ \@@_change_case_store:e
+ {
+ \codepoint_generate:nn { "0535 }
+ { \@@_change_case_catcode:nn {#5} { "0535 } }
+ \codepoint_generate:nn { "054E }
+ { \@@_change_case_catcode:nn {#5} { "054E } }
+ }
+ \use:c { @@_change_case_next_ #2 :nnn }
+ {#2} {#3} {#4}
+ }
+ { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+\cs_new:Npn \@@_change_case_title_hy:nnnnn #1#2#3#4#5
+ {
+ \@@_codepoint_compare:nNnTF {#5} = { "0587 }
+ {
+ \@@_change_case_store:e
+ {
+ \codepoint_generate:nn { "0535 }
+ { \@@_change_case_catcode:nn {#5} { "0535 } }
+ \codepoint_generate:nn { "057E }
+ { \@@_change_case_catcode:nn {#5} { "057E } }
+ }
+ \use:c { @@_change_case_next_ #2 :nnn }
+ {#2} {#3} {#4}
+ }
+ { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+\cs_new:cpn { @@_change_case_upper_hy-x-yiwn:nnnnn } #1#2#3#4#5
+ { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+\cs_new_eq:cc { @@_change_case_title_hy-x-yiwn:nnnnn }
+ { @@_change_case_upper_hy-x-yiwn:nnnnn }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_change_case_lower_la-x-medieval:nnnnn}
+% \begin{macro}[EXP]{\@@_change_case_upper_la-x-medieval:nnnnn}
+% Simply swaps of characters.
+% \begin{macrocode}
+\cs_new:cpn { @@_change_case_lower_la-x-medieval:nnnnn } #1#2#3#4#5
+ {
+ \@@_codepoint_compare:nNnTF {#5} = { "0056 }
+ {
+ \@@_change_case_store:e
+ { \char_generate:nn { "0075 } { \@@_char_catcode:N #5 } }
+ \use:c { @@_change_case_next_ #2 :nnn }
+ {#2} {#3} {#4}
+ }
+ { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+\cs_new:cpn { @@_change_case_upper_la-x-medieval:nnnnn } #1#2#3#4#5
+ {
+ \@@_codepoint_compare:nNnTF {#5} = { "0075 }
+ {
+ \@@_change_case_store:e
+ { \char_generate:nn { "0056 } { \@@_char_catcode:N #5 } }
+ \use:c { @@_change_case_next_ #2 :nnn }
+ {#2} {#3} {#4}
+ }
+ { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_change_cases_lower_lt:nnnnn ,
+% \@@_change_cases_lower_lt_auxi:nnnnn ,
+% \@@_change_cases_lower_lt_auxii:nnnnn
+% }
+% \begin{macro}[rEXP]{\@@_change_case_lower_lt:nnnw}
+% \begin{macro}[rEXP]{\@@_change_case_lower_lt:nnnN}
+% \begin{macro}[rEXP]{\@@_change_case_lower_lt:nnnn}
+% For Lithuanian, the issue to be dealt with is dots over lower case
+% letters: these should be present if there is another accent. The first step
+% is a simple match attempt: look for the three uppercase accented letters
+% which should gain a dot-above char in their lowercase form.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_lower_lt:nnnnn #1#2#3#4#5
+ {
+ \exp_args:Ne \@@_change_case_lower_lt_auxi:nnnnn
+ {
+ \int_case:nn { \@@_codepoint_from_chars:Nw #5 }
+ {
+ { "00CC } { "0300 }
+ { "00CD } { "0301 }
+ { "0128 } { "0303 }
+ }
+ }
+ {#2} {#3} {#4} {#5}
+ }
+% \end{macrocode}
+% If there was a hit, output the result with the dot-above and move on.
+% Otherwise, look for one of the three letters that can take a combining
+% accent: I, J nd I-ogonek.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_lower_lt_auxi:nnnnn #1#2#3#4#5
+ {
+ \tl_if_blank:nTF {#1}
+ {
+ \exp_args:Ne \@@_change_case_lower_lt_auxii:nnnnn
+ {
+ \int_case:nn { \@@_codepoint_from_chars:Nw #5 }
+ {
+ { "0049 } { "0069 }
+ { "004A } { "006A }
+ { "012E } { "012F }
+ }
+ }
+ {#2} {#3} {#4} {#5}
+ }
+ {
+ \@@_change_case_store:e
+ {
+ \codepoint_generate:nn { "0069 }
+ { \@@_change_case_catcode:nn {#5} { "0069 } }
+ \codepoint_generate:nn { "0307 }
+ { \@@_change_case_catcode:nn {#5} { "0307 } }
+ \codepoint_generate:nn {#1}
+ { \@@_change_case_catcode:nn {#5} {#1} }
+ }
+ \@@_change_case_loop:nnnw {#2} {#3} {#4}
+ }
+ }
+% \end{macrocode}
+% Again, branch depending on a hit. If there is one, we output the character
+% then need to look for a combining accent: as usual, we need to be aware of
+% the loop situation.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_lower_lt_auxii:nnnnn #1#2#3#4#5
+ {
+ \tl_if_blank:nTF {#1}
+ { \@@_change_case_codepoint:nnnnn {#2} {#2} {#3} {#4} {#5} }
+ {
+ \@@_change_case_store:e
+ {
+ \codepoint_generate:nn {#1}
+ { \@@_change_case_catcode:nn {#5} {#1} }
+ }
+ \@@_change_case_lower_lt:nnnw {#2} {#3} {#4}
+ }
+ }
+\cs_new:Npn \@@_change_case_lower_lt:nnnw #1#2#3#4 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#4}
+ { \@@_change_case_lower_lt:nnnN }
+ { \@@_change_case_loop:nnnw }
+ {#1} {#2} {#3} #4 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_lower_lt:nnnN #1#2#3#4
+ {
+ \@@_codepoint_process:nN
+ { \@@_change_case_lower_lt:nnnn {#1} {#2} {#3} } #4
+ }
+\cs_new:Npn \@@_change_case_lower_lt:nnnn #1#2#3#4
+ {
+ \bool_lazy_and:nnT
+ {
+ \bool_lazy_or_p:nn
+ { ! \tl_if_single_p:n {#4} }
+ { ! \token_if_cs_p:N #4 }
+ }
+ {
+ \bool_lazy_any_p:n
+ {
+ { \@@_codepoint_compare_p:nNn {#4} = { "0300 } }
+ { \@@_codepoint_compare_p:nNn {#4} = { "0301 } }
+ { \@@_codepoint_compare_p:nNn {#4} = { "0303 } }
+ }
+ }
+ {
+ \@@_change_case_store:e
+ {
+ \codepoint_generate:nn { "0307 }
+ { \@@_change_case_catcode:nn {#4} { "0307 } }
+ }
+ }
+ \@@_change_case_loop:nnnw {#1} {#2} {#3} #4
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{macro}[EXP]
+% {
+% \@@_change_cases_upper_lt:nnnnn ,
+% \@@_change_cases_upper_lt_aux:nnnnn
+% }
+% \begin{macro}[rEXP]{\@@_change_case_upper_lt:nnnw}
+% \begin{macro}[rEXP]{\@@_change_case_upper_lt:nnnN}
+% \begin{macro}[rEXP]{\@@_change_case_upper_lt:nnnn}
+% The uppercasing version: first find i/j/i-ogonek, then look for the
+% combining char: drop it if present.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_upper_lt:nnnnn #1#2#3#4#5
+ {
+ \exp_args:Ne \@@_change_case_upper_lt_aux:nnnnn
+ {
+ \int_case:nn { \@@_codepoint_from_chars:Nw #5 }
+ {
+ { "0069 } { "0049 }
+ { "006A } { "004A }
+ { "012F } { "012E }
+ }
+ }
+ {#2} {#3} {#4} {#5}
+ }
+\cs_new:Npn \@@_change_case_upper_lt_aux:nnnnn #1#2#3#4#5
+ {
+ \tl_if_blank:nTF {#1}
+ { \@@_change_case_codepoint:nnnnn { upper } {#2} {#3} {#4} {#5} }
+ {
+ \@@_change_case_store:e
+ {
+ \codepoint_generate:nn {#1}
+ { \@@_change_case_catcode:nn {#5} {#1} }
+ }
+ \@@_change_case_upper_lt:nnnw {#2} {#3} {#4}
+ }
+ }
+\cs_new:Npn \@@_change_case_upper_lt:nnnw #1#2#3#4 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#4}
+ { \@@_change_case_upper_lt:nnnN }
+ { \use:c { @@_change_case_next_ #1 :nnn } }
+ {#1} {#2} {#3} #4 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_upper_lt:nnnN #1#2#3#4
+ {
+ \@@_codepoint_process:nN
+ { \@@_change_case_upper_lt:nnnn {#1} {#2} {#3} } #4
+ }
+\cs_new:Npn \@@_change_case_upper_lt:nnnn #1#2#3#4
+ {
+ \bool_lazy_and:nnTF
+ {
+ \bool_lazy_or_p:nn
+ { ! \tl_if_single_p:n {#4} }
+ { ! \token_if_cs_p:N #4 }
+ }
+ { \@@_codepoint_compare_p:nNn {#4} = { "0307 } }
+ { \use:c { @@_change_case_next_ #1 :nnn } {#1} {#2} {#3} }
+ { \use:c { @@_change_case_next_ #1 :nnn } {#1} {#2} {#3} #4 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_change_case_title_nl:nnnnn, \@@_change_case_title_nl_aux:nnnnn}
+% \begin{macro}[EXP]{\@@_change_case_title_nl:nnnw}
+% \begin{macro}[EXP]{\@@_change_case_title_nl:nnnN}
+% For Dutch, there is a single look-ahead test for \texttt{ij} when
+% title casing. If the appropriate letters are found, produce \texttt{IJ}
+% and gobble the \texttt{j}/\texttt{J}.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_title_nl:nnnnn #1#2#3#4#5
+ {
+ \tl_if_single:nTF {#5}
+ { \@@_change_case_title_nl_aux:nnnnn }
+ { \@@_change_case_codepoint:nnnnn }
+ {#1} {#2} {#3} {#4} {#5}
+ }
+\cs_new:Npn \@@_change_case_title_nl_aux:nnnnn #1#2#3#4#5
+ {
+ \bool_lazy_or:nnTF
+ { \int_compare_p:nNn {`#5} = { "0049 } }
+ { \int_compare_p:nNn {`#5} = { "0069 } }
+ {
+ \@@_change_case_store:e
+ { \char_generate:nn { "0049 } { \@@_char_catcode:N #5 } }
+ \@@_change_case_title_nl:nnnw {#2} {#3} {#4}
+ }
+ { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+\cs_new:Npn \@@_change_case_title_nl:nnnw #1#2#3#4 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#4}
+ { \@@_change_case_title_nl:nnnN }
+ { \use:c { @@_change_case_next_ #1 :nnn } }
+ {#1} {#2} {#3} #4 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_title_nl:nnnN #1#2#3#4
+ {
+ \bool_lazy_and:nnTF
+ { ! \token_if_cs_p:N #4 }
+ {
+ \bool_lazy_or_p:nn
+ { \int_compare_p:nNn {`#4} = { "004A } }
+ { \int_compare_p:nNn {`#4} = { "006A } }
+ }
+ {
+ \@@_change_case_store:e
+ { \char_generate:nn { "004A } { \@@_char_catcode:N #4 } }
+ \use:c { @@_change_case_next_ #1 :nnn } {#1} {#2} {#3}
+ }
+ { \use:c { @@_change_case_next_ #1 :nnn } {#1} {#2} {#3} #4 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_change_case_lower_tr:nnnnn}
+% \begin{macro}[EXP]{\@@_change_case_lower_tr:nnnNw}
+% \begin{macro}[EXP]{\@@_change_case_lower_tr:NnnnN}
+% \begin{macro}[EXP]{\@@_change_case_lower_tr:Nnnnn}
+% The Turkic languages need special treatment for dotted-i and dotless-i.
+% The lower casing rule can be expressed in terms of searching first for
+% either a dotless-I or a dotted-I. In the latter case the mapping is
+% easy, but in the former there is a second stage search.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_lower_tr:nnnnn #1#2#3#4#5
+ {
+ \@@_codepoint_compare:nNnTF {#5} = { "0049 }
+ { \@@_change_case_lower_tr:nnnNw {#1} {#3} {#4} #5 }
+ {
+ \@@_codepoint_compare:nNnTF {#5} = { "0130 }
+ {
+ \@@_change_case_store:e
+ {
+ \codepoint_generate:nn { "0069 }
+ { \@@_change_case_catcode:nn {#5} { "0069 } }
+ }
+ \@@_change_case_loop:nnnw {#1} {#3} {#4}
+ }
+ { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+ }
+% \end{macrocode}
+% After a dotless-I there may be a dot-above character. If there is then
+% a dotted-i should be produced, otherwise output a dotless-i. When the
+% combination is found both the dotless-I and the dot-above char have to
+% be removed from the input.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_lower_tr:nnnNw #1#2#3#4#5 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#5}
+ { \@@_change_case_lower_tr:NnnnN #4 {#1} {#2} {#3} }
+ {
+ \@@_change_case_store:e
+ {
+ \codepoint_generate:nn { "0131 }
+ { \@@_change_case_catcode:nn {#4} { "0131 } }
+ }
+ \@@_change_case_loop:nnnw {#1} {#2} {#3}
+ }
+ #5 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_lower_tr:NnnnN #1#2#3#4#5
+ {
+ \@@_codepoint_process:nN
+ { \@@_change_case_lower_tr:Nnnnn #1 {#2} {#3} {#4} } #5
+ }
+\cs_new:Npn \@@_change_case_lower_tr:Nnnnn #1#2#3#4#5
+ {
+ \bool_lazy_or:nnTF
+ {
+ \bool_lazy_and_p:nn
+ { \tl_if_single_p:n {#5} }
+ { \token_if_cs_p:N #5 }
+ }
+ { ! \@@_codepoint_compare_p:nNn {#5} = { "0307 } }
+ {
+ \@@_change_case_store:e
+ {
+ \codepoint_generate:nn { "0131 }
+ { \@@_change_case_catcode:nn {#1} { "0131 } }
+ }
+ \@@_change_case_loop:nnnw {#2} {#3} {#4} #5
+ }
+ {
+ \@@_change_case_store:e
+ {
+ \codepoint_generate:nn { "0069 }
+ { \@@_change_case_catcode:nn {#1} { "0069 } }
+ }
+ \@@_change_case_loop:nnnw {#2} {#3} {#4}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \begin{macro}[EXP]{\@@_change_case_upper_tr:nnnnn}
+% Uppercasing is easier: just one exception with no context.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_upper_tr:nnnnn #1#2#3#4#5
+ {
+ \@@_codepoint_compare:nNnTF {#5} = { "0069 }
+ {
+ \@@_change_case_store:e
+ {
+ \codepoint_generate:nn { "0130 }
+ { \@@_change_case_catcode:nn {#5} { "0130 } }
+ }
+ \use:c { @@_change_case_next_ #2 :nnn } {#2} {#3} {#4}
+ }
+ { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_change_case_lower_az:nnnnn, \@@_change_case_upper_az:nnnnn}
+% Straight copies.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_change_case_lower_az:nnnnn
+ \@@_change_case_lower_tr:nnnnn
+\cs_new_eq:NN \@@_change_case_upper_az:nnnnn
+ \@@_change_case_upper_tr:nnnnn
+% \end{macrocode}
+% \end{macro}
+%
+% The (fixed) look-up mappings for letter-like control sequences.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \@@_change_case_setup:NN #1#2
+ {
+ \quark_if_recursion_tail_stop:N #1
+ \tl_const:cn { c_@@_lowercase_ \token_to_str:N #1 _tl }
+ { #2 }
+ \tl_const:cn { c_@@_uppercase_ \token_to_str:N #2 _tl }
+ { #1 }
+ \@@_change_case_setup:NN
+ }
+ \@@_change_case_setup:NN
+ \AA \aa
+ \AE \ae
+ \DH \dh
+ \DJ \dj
+ \IJ \ij
+ \L \l
+ \NG \ng
+ \O \o
+ \OE \oe
+ \SS \ss
+ \TH \th
+ \q_recursion_tail ?
+ \q_recursion_stop
+ \tl_const:cn { c_@@_uppercase_ \token_to_str:N \i _tl } { I }
+ \tl_const:cn { c_@@_uppercase_ \token_to_str:N \j _tl } { J }
+\group_end:
+% \end{macrocode}
+%
+% To deal with possible encoding-specific extensions to \tn{@uclclist},
+% we check at the end of the preamble. This will therefore only apply
+% to \LaTeXe{} package mode.
+% \begin{macrocode}
+\tl_if_exist:NT \@expl@finalise@setup@@@@
+ {
+ \tl_gput_right:Nn \@expl@finalise@setup@@@@
+ {
+ \tl_gput_right:Nn \@kernel@after@begindocument
+ {
+ \group_begin:
+ \cs_set_protected:Npn \@@_change_case_setup:Nn #1#2
+ {
+ \quark_if_recursion_tail_stop:N #1
+ \tl_if_single_token:nT {#2}
+ {
+ \cs_if_exist:cF
+ { c_@@_uppercase_ \token_to_str:N #1 _tl }
+ {
+ \tl_const:cn
+ { c_@@_uppercase_ \token_to_str:N #1 _tl }
+ { #2 }
+ }
+ \cs_if_exist:cF
+ { c_@@_lowercase_ \token_to_str:N #2 _tl }
+ {
+ \tl_const:cn
+ { c_@@_lowercase_ \token_to_str:N #2 _tl }
+ { #1 }
+ }
+ }
+ \@@_change_case_setup:Nn
+ }
+ \exp_after:wN \@@_change_case_setup:Nn \@uclclist
+ \q_recursion_tail ?
+ \q_recursion_stop
+ \group_end:
+ }
+ }
+ }
+% \end{macrocode}
+%
+% A few adjustments to case mapping for combining chars: these are not needed
+% for the Unicode engines
+% \begin{macrocode}
+\bool_lazy_or:nnF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \text_declare_uppercase_mapping:nn { "01F0 } { \v { J } }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3text-map.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3text-map.dtx
new file mode 100644
index 00000000000..213d8281a96
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3text-map.dtx
@@ -0,0 +1,586 @@
+% \iffalse meta-comment
+%
+%% File: l3text-map.dtx
+%
+% Copyright (C) 2022-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3text-map} module\\ Text processing (mapping)^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3text-map} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=text>
+% \end{macrocode}
+%
+% \subsection{Mapping to text}
+%
+% \begin{macro}[EXP]{\text_map_function:nN}
+% \begin{macro}[EXP]{\@@_map_function:nN}
+% \begin{macro}[EXP]{\@@_map_loop:Nnw}
+% \begin{macro}[EXP]{\@@_map_group:Nnn}
+% \begin{macro}[EXP]{\@@_map_space:Nnw}
+% \begin{macro}[EXP]{\@@_map_N_type:NnN}
+% \begin{macro}[EXP]{\@@_map_codepoint:Nnn}
+% \begin{macro}[EXP]{\@@_map_CR:Nnw}
+% \begin{macro}[EXP]{\@@_map_CR:NnN}
+% \begin{macro}[EXP]{\@@_map_class:Nnnn}
+% \begin{macro}[EXP]{\@@_map_class:nNnnn}
+% \begin{macro}[EXP]{\@@_map_class_loop:Nnnnw}
+% \begin{macro}[EXP]{\@@_map_class_end:nw}
+% \begin{macro}[EXP]
+% {
+% \@@_map_Control:Nnn ,
+% \@@_map_Extend:Nnn ,
+% \@@_map_SpacingMark:Nnn ,
+% \@@_map_Prepend:Nnn ,
+% \@@_map_Prepend_aux:Nnn
+% }
+% \begin{macro}[EXP]{\@@_map_Prepend:nNnn}
+% \begin{macro}[EXP]{\@@_map_Prepend_loop:Nnnw}
+% \begin{macro}[EXP]
+% {
+% \@@_map_not_Control:Nnn ,
+% \@@_map_not_Extend:Nnn ,
+% \@@_map_not_SpacingMark:Nnn ,
+% \@@_map_not_Prepend:Nnn ,
+% \@@_map_not_L:Nnn ,
+% \@@_map_not_LV:Nnn ,
+% \@@_map_not_V:Nnn ,
+% \@@_map_not_LVT:Nnn ,
+% \@@_map_not_T:Nnn
+% }
+% \begin{macro}[EXP]
+% {
+% \@@_map_L:Nnn ,
+% \@@_map_LV:Nnn ,
+% \@@_map_V:Nnn ,
+% \@@_map_LVT:Nnn ,
+% \@@_map_T:Nnn
+% }
+% \begin{macro}[EXP]{\@@_map_hangul:Nnnw}
+% \begin{macro}[EXP]{\@@_map_hangul:NnnN}
+% \begin{macro}[EXP]{\@@_map_hangul:Nnnn}
+% \begin{macro}[EXP]{\@@_map_hangul_aux:Nnnnw}
+% \begin{macro}[EXP]{\@@_map_hangul:nNnnnw}
+% \begin{macro}[EXP]{\@@_map_hangul_loop:Nnnnnw}
+% \begin{macro}[EXP]{\@@_map_hangul_next:Nnnn}
+% \begin{macro}[EXP]{\@@_map_hangul_end:nw}
+% \begin{macro}[EXP]
+% {
+% \@@_map_hangul_L:Nnn ,
+% \@@_map_hangul_LV:Nnn ,
+% \@@_map_hangul_V:Nnn ,
+% \@@_map_hangul_LVT:Nnn ,
+% \@@_map_hangul_T:Nnn
+% }
+% \begin{macro}[EXP]
+% {\@@_map_Regional_Indicator:Nnn, \@@_map_Regional_Indicator_aux:Nnn}
+% \begin{macro}[EXP]{\@@_map_lookahead:NnNw}
+% \begin{macro}[EXP]{\@@_map_lookahead:NnNN}
+% \begin{macro}[EXP]{\@@_map_output:Nn}
+% \begin{macro}[EXP]{\text_map_break:}
+% \begin{macro}[EXP]{\text_map_break:n}
+% The standard lead-off for an action loop.
+% \begin{macrocode}
+\cs_new:Npn \text_map_function:nN #1#2
+ { \exp_args:Ne \@@_map_function:nN { \text_expand:n {#1} } #2 }
+\cs_new:Npn \@@_map_function:nN #1#2
+ {
+ \@@_map_loop:Nnw #2 { } #1
+ \q_@@_recursion_tail \q_@@_recursion_stop
+ \prg_break_point:Nn \text_map_break: { }
+ }
+% \end{macrocode}
+% The standard set up for an \enquote{action} loop. Groups are handled by
+% recursion, spaces are treated similarly: both count as grapheme boundaries.
+% For \texttt{N}-type tokens, we filter out control sequences (again
+% a boundary), then move on to further analysis.
+% \begin{macrocode}
+\cs_new:Npn \@@_map_loop:Nnw #1#2#3 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#3}
+ { \@@_map_N_type:NnN }
+ {
+ \tl_if_head_is_group:nTF {#3}
+ { \@@_map_group:Nnn }
+ { \@@_map_space:Nnw }
+ }
+ #1 {#2} #3 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_map_group:Nnn #1#2#3
+ {
+ \@@_map_output:Nn #1 {#2}
+ {
+ \@@_map_loop:Nnw #1 { } #2
+ \q_@@_recursion_tail \q_@@_recursion_stop
+ \prg_break_point:Nn \text_map_break: { }
+ }
+ \@@_map_loop:Nnw #1 { }
+ }
+\use:e
+ { \cs_new:Npn \exp_not:N \@@_map_space:Nnw #1#2 \c_space_tl }
+ {
+ \@@_map_output:Nn #1 {#2}
+ #1 { ~ }
+ \@@_map_loop:Nnw #1 { }
+ }
+\cs_new:Npn \@@_map_N_type:NnN #1#2#3
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #3
+ {
+ \@@_map_output:Nn #1 {#2}
+ \text_map_break:
+ }
+ \token_if_cs:NTF #3
+ {
+ \@@_map_output:Nn #1 {#2}
+ #1 {#3}
+ \@@_map_loop:Nnw #1 { }
+ }
+ {
+ \@@_codepoint_process:nN
+ { \@@_map_codepoint:Nnn #1 {#2} } #3
+ }
+ }
+% \end{macrocode}
+% We pull out a few special cases here. Carriage returns case needs a bit of
+% context handling so has an auxiliary. Codepoint U+200D is the zero-width
+% joiner, which has no context to concern us: just don't break.
+% \begin{macrocode}
+\cs_new:Npn \@@_map_codepoint:Nnn #1#2#3
+ {
+ \@@_codepoint_compare:nNnTF {#3} = { "0D }
+ {
+ \@@_map_output:Nn #1 {#2}
+ \@@_map_CR:Nnw #1 {#3}
+ }
+ {
+ \@@_codepoint_compare:nNnTF {#3} = { "200D }
+ { \@@_map_loop:Nnw #1 {#2#3} }
+ { \@@_map_class:Nnnn #1 {#2} {#3} { Control } }
+ }
+ }
+% \end{macrocode}
+% A carriage return is a boundary unless it is immediately followed by
+% a line feed, in which case that pair is a boundary.
+% \begin{macrocode}
+\cs_new:Npn \@@_map_CR:Nnw #1#2#3 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#3}
+ { \@@_map_CR:NnN #1 {#2} }
+ {
+ #1 {#2}
+ \@@_map_loop:Nnw #1 { }
+ }
+ #3 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_map_CR:NnN #1#2#3
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #3
+ {
+ #1 {#2}
+ \text_map_break:
+ }
+ \bool_lazy_and:nnTF
+ { ! \token_if_cs_p:N #3 }
+ { \int_compare_p:nNn { `#3 } = { "0A } }
+ {
+ \@@_map_output:Nn #1 {#2#3}
+ \@@_map_loop:Nnw #1 { }
+ }
+ { \@@_map_loop:Nnw #1 { } #3 }
+ }
+% \end{macrocode}
+% There are various classes of character, and we deal with them all in
+% the same general way. We need to example the relevant list of codepoints:
+% if we get a hit, then we do whatever the relevant action is. Otherwise
+% we loop, but only if the current codepoint could still match: the
+% loop stops early otherwise and we move forward.
+% \begin{macrocode}
+\cs_new:Npn \@@_map_class:Nnnn #1#2#3#4
+ {
+ \exp_args:Nv \@@_map_class:nNnnn { c_@@_grapheme_ #4 _clist }
+ #1 {#2} {#3} {#4}
+ }
+\cs_new:Npn \@@_map_class:nNnnn #1#2#3#4#5
+ {
+ \@@_map_class_loop:Nnnnw #2 {#3} {#4} {#5}
+ #1 , \q_@@_recursion_tail .. , \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_map_class_loop:Nnnnw #1#2#3#4 #5 .. #6 ,
+ {
+ \@@_if_q_recursion_tail_stop_do:nn {#5}
+ { \use:c { @@_map_not_ #4 :Nnn } #1 {#2} {#3} }
+ \@@_codepoint_compare:nNnTF {#3} < { "#5 }
+ {
+ \@@_map_class_end:nw
+ { \use:c { @@_map_not_ #4 :Nnn } #1 {#2} {#3} }
+ }
+ {
+ \@@_codepoint_compare:nNnTF {#3} > { "#6 }
+ { \@@_map_class_loop:Nnnnw #1 {#2} {#3} {#4} }
+ {
+ \@@_map_class_end:nw
+ { \use:c { @@_map_ #4 :Nnn } #1 {#2} {#3} }
+ }
+ }
+ }
+\cs_new:Npn \@@_map_class_end:nw #1#2 \q_@@_recursion_stop {#1}
+% \end{macrocode}
+% Break before \emph{and} after.
+% \begin{macrocode}
+\cs_new:Npn \@@_map_Control:Nnn #1#2#3
+ {
+ \@@_map_output:Nn #1 {#2}
+ \@@_map_output:Nn #1 {#3}
+ \@@_map_loop:Nnw #1 { }
+ }
+% \end{macrocode}
+% Keep collecting.
+% \begin{macrocode}
+\cs_new:Npn \@@_map_Extend:Nnn #1#2#3
+ { \@@_map_loop:Nnw #1 {#2#3} }
+\cs_new_eq:NN \@@_map_SpacingMark:Nnn \@@_map_Extend:Nnn
+% \end{macrocode}
+% Outputting anything earlier, the combine with what follows. The only
+% exclusions are control characters.
+% \begin{macrocode}
+\cs_new:Npn \@@_map_Prepend:Nnn #1#2#3
+ {
+ \@@_map_output:Nn #1 {#2}
+ \@@_map_lookahead:NnNw #1 {#3} \@@_map_Prepend_aux:Nnn
+ }
+\cs_new:Npn \@@_map_Prepend_aux:Nnn #1#2#3
+ {
+ \bool_lazy_or:nnTF
+ { \@@_codepoint_compare_p:nNn {#3} = { "0A } }
+ { \@@_codepoint_compare_p:nNn {#3} = { "0D } }
+ {
+ #1 {#2}
+ \@@_map_loop:Nnw #1 {#3}
+ }
+ {
+ \exp_args:NV \@@_map_Prepend:nNnn
+ \c_@@_grapheme_Control_clist
+ #1 {#2} {#3}
+ }
+ }
+\cs_new:Npn \@@_map_Prepend:nNnn #1#2#3#4
+ {
+ \@@_map_Prepend_loop:Nnnw #2 {#3} {#4}
+ #1 , \q_@@_recursion_tail .. , \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_map_Prepend_loop:Nnnw #1#2#3 #4 .. #5 ,
+ {
+ \@@_if_q_recursion_tail_stop_do:nn {#4}
+ { \@@_map_loop:Nnw #1 {#2#3} }
+ \@@_codepoint_compare:nNnTF {#3} < { "#4 }
+ {
+ \@@_map_class_end:nw
+ { \@@_map_loop:Nnw #1 {#2#3} }
+ }
+ {
+ \@@_codepoint_compare:nNnTF {#3} > { "#5 }
+ { \@@_map_Prepend_loop:Nnnw #1 {#2} {#3} }
+ {
+ \@@_map_class_end:nw
+ { \@@_map_loop:Nnw #1 {#2} #3 }
+ }
+ }
+ }
+% \end{macrocode}
+% Dealing with end-of-class is done such that we can be flexible.
+% \begin{macrocode}
+\cs_new:Npn \@@_map_not_Control:Nnn #1#2#3
+ { \@@_map_class:Nnnn #1 {#2} {#3} { Extend } }
+\cs_new:Npn \@@_map_not_Extend:Nnn #1#2#3
+ { \@@_map_class:Nnnn #1 {#2} {#3} { SpacingMark } }
+\cs_new:Npn \@@_map_not_SpacingMark:Nnn #1#2#3
+ { \@@_map_class:Nnnn #1 {#2} {#3} { Prepend } }
+\cs_new:Npn \@@_map_not_Prepend:Nnn #1#2#3
+ { \@@_map_class:Nnnn #1 {#2} {#3} { L } }
+\cs_new:Npn \@@_map_not_L:Nnn #1#2#3
+ { \@@_map_class:Nnnn #1 {#2} {#3} { LV } }
+\cs_new:Npn \@@_map_not_LV:Nnn #1#2#3
+ { \@@_map_class:Nnnn #1 {#2} {#3} { V } }
+\cs_new:Npn \@@_map_not_V:Nnn #1#2#3
+ { \@@_map_class:Nnnn #1 {#2} {#3} { LVT } }
+\cs_new:Npn \@@_map_not_LVT:Nnn #1#2#3
+ { \@@_map_class:Nnnn #1 {#2} {#3} { T } }
+\cs_new:Npn \@@_map_not_T:Nnn #1#2#3
+ { \@@_map_class:Nnnn #1 {#2} {#3} { Regional_Indicator } }
+\cs_new:Npn \@@_map_not_Regional_Indicator:Nnn #1#2#3
+ {
+ \@@_map_output:Nn #1 {#2}
+ \@@_map_loop:Nnw #1 {#3}
+ }
+% \end{macrocode}
+% Hangul needs additional treatment. First we have to deal with
+% the start-of-Hangul position: output what we had up to now, then
+% move the specialist handler. The idea here is to pick off the
+% different codepoint types one at a time, tracking what else can be
+% considered at each stage until we hit the end of the viable types.
+% Other than that, we just keep building up the Hangul codepoints
+% using a dedicated version of the loop from above.
+% \begin{macrocode}
+\cs_new:Npn \@@_map_L:Nnn #1#2#3
+ {
+ \@@_map_output:Nn #1 {#2}
+ \@@_map_hangul:Nnnw
+ #1 {#3} { L ; V ; LV ; LVT }
+ }
+\cs_new:Npn \@@_map_LV:Nnn #1#2#3
+ {
+ \@@_map_output:Nn #1 {#2}
+ \@@_map_hangul:Nnnw
+ #1 {#3} { V ; T }
+ }
+\cs_new_eq:NN \@@_map_V:Nnn \@@_map_LV:Nnn
+\cs_new:Npn \@@_map_LVT:Nnn #1#2#3
+ {
+ \@@_map_output:Nn #1 {#2}
+ \@@_map_hangul:Nnnw
+ #1 {#3} { T }
+ }
+\cs_new_eq:NN \@@_map_T:Nnn \@@_map_LVT:Nnn
+\cs_new:Npn \@@_map_hangul:Nnnw #1#2#3#4 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#4}
+ { \@@_map_hangul:NnnN #1 {#2} {#3} }
+ {
+ #1 {#2}
+ \@@_map_loop:Nnw #1 { }
+ }
+ #4 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_map_hangul:NnnN #1#2#3#4
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #4
+ {
+ #1 {#2}
+ \text_map_break:
+ }
+ \token_if_cs:NTF #4
+ {
+ #1 {#2}
+ \@@_map_loop:Nnw #1 { }
+ }
+ {
+ \@@_codepoint_process:nN
+ { \@@_map_hangul:Nnnn #1 {#2} {#3} } #4
+ }
+ }
+\cs_new:Npn \@@_map_hangul:Nnnn #1#2#3#4
+ {
+ \@@_map_hangul_aux:Nnnw #1 {#2} {#4}
+ #3 ; \q_recursion_tail ; \q_recursion_stop
+ }
+\cs_new:Npn \@@_map_hangul_aux:Nnnw #1#2#3#4 ;
+ {
+ \quark_if_recursion_tail_stop_do:nn {#4}
+ { \@@_map_loop:Nnw #1 {#2} #3 }
+ \exp_args:Nv \@@_map_hangul:nNnnnw { c_@@_grapheme_ #4 _clist }
+ #1 {#2} {#3} {#4}
+ }
+\cs_new:Npn \@@_map_hangul:nNnnnw #1#2#3#4#5#6 \q_recursion_stop
+ {
+ \@@_map_hangul_loop:Nnnnnw #2 {#3} {#4} {#5} {#6}
+ #1 , \q_@@_recursion_tail .. , \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_map_hangul_loop:Nnnnnw #1#2#3#4#5 #6 .. #7 ,
+ {
+ \@@_if_q_recursion_tail_stop_do:nn {#6}
+ { \@@_map_hangul_next:Nnnn #1 {#2} {#3} {#5} }
+ \@@_codepoint_compare:nNnTF {#3} < { "#6 }
+ {
+ \@@_map_hangul_end:nw
+ { \@@_map_hangul_next:Nnnn #1 {#2} {#3} {#5} }
+ }
+ {
+ \@@_codepoint_compare:nNnTF {#3} > { "#7 }
+ { \@@_map_hangul_loop:Nnnnnw #1 {#2} {#3} {#4} {#5} }
+ {
+ \@@_map_hangul_end:nw
+ { \use:c { @@_map_hangul_ #4 :Nnn } #1 {#2} {#3} }
+ }
+ }
+ }
+\cs_new:Npn \@@_map_hangul_next:Nnnn #1#2#3#4
+ { \@@_map_hangul_aux:Nnnw #1 {#2} {#3} #4 \q_recursion_stop }
+\cs_new:Npn \@@_map_hangul_end:nw #1#2 \q_@@_recursion_stop {#1}
+\cs_new:Npn \@@_map_hangul_L:Nnn #1#2#3
+ {
+ \@@_map_hangul:Nnnw
+ #1 {#2#3} { L V { LV } { LVT } }
+ }
+\cs_new:Npn \@@_map_hangul_LV:Nnn #1#2#3
+ {
+ \@@_map_hangul:Nnnw
+ #1 {#2#3} { VT }
+ }
+\cs_new_eq:NN \@@_map_hangul_V:Nnn \@@_map_hangul_LV:Nnn
+\cs_new:Npn \@@_map_hangul_LVT:Nnn #1#2#3
+ {
+ \@@_map_hangul:Nnnw
+ #1 {#2#3} { T }
+ }
+\cs_new_eq:NN \@@_map_hangul_T:Nnn \@@_map_hangul_LVT:Nnn
+% \end{macrocode}
+% The Regional Indicator rule means looking ahead and dealing with the
+% case where there are two in a row. So we use a look ahead to pick them
+% off. As there is only one range the values are hard-coded.
+% \begin{macrocode}
+\cs_new:Npn \@@_map_Regional_Indicator:Nnn #1#2#3
+ {
+ \@@_map_output:Nn #1 {#2}
+ \@@_map_lookahead:NnNw #1 {#3} \@@_map_Regional_Indicator_aux:Nnn
+ }
+\cs_new:Npn \@@_map_Regional_Indicator_aux:Nnn #1#2#3
+ {
+ \bool_lazy_or:nnTF
+ { \@@_codepoint_compare_p:nNn {#3} < { "1F1E6 } }
+ { \@@_codepoint_compare_p:nNn {#3} > { "1F1FF } }
+ {
+ \@@_map_loop:Nnw #1 {#2} #3
+ }
+ { \@@_map_loop:Nnw #1 {#2#3} }
+ }
+% \end{macrocode}
+% A generic loop-ahead setup.
+% \begin{macrocode}
+\cs_new:Npn \@@_map_lookahead:NnNw #1#2#3#4 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#4}
+ { \@@_map_lookahead:NnNN #1 {#2} #3 }
+ { \@@_map_loop:Nnw #1 {#2} }
+ #4 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_map_lookahead:NnNN #1#2#3#4
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #4 { #1 {#2} }
+ \token_if_cs:NTF #4
+ {
+ #1 {#2}
+ \@@_map_loop:Nnw #1 { }
+ }
+ { \@@_codepoint_process:nN { #3 #1 {#2} } }
+ #4
+ }
+% \end{macrocode}
+% For the end of the process.
+% \begin{macrocode}
+\cs_new:Npn \@@_map_output:Nn #1#2
+ { \tl_if_blank:nF {#2} { #1 {#2} } }
+\cs_new:Npn \text_map_break:
+ { \prg_map_break:Nn \text_map_break: { } }
+\cs_new:Npn \text_map_break:n
+ { \prg_map_break:Nn \text_map_break: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\text_map_inline:nn}
+% The standard non-expandable inline version.
+% \begin{macrocode}
+\cs_new_protected:Npn \text_map_inline:nn #1#2
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \cs_gset_protected:cpn
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w } ##1 {#2}
+ \exp_args:Nnc \text_map_function:nN {#1}
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w }
+ \prg_break_point:Nn \text_map_break:
+ { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3text-purify.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3text-purify.dtx
new file mode 100644
index 00000000000..a713cd6e403
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3text-purify.dtx
@@ -0,0 +1,797 @@
+% \iffalse meta-comment
+%
+%% File: l3text-purify.dtx
+%
+% Copyright (C) 2020-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3text-purify} module\\ Text processing (purification)^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3text-purify} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=text>
+% \end{macrocode}
+%
+% \subsection{Purifying text}
+%
+% \begin{macro}[EXP]{\@@_if_recursion_tail_stop:N}
+% Functions to query recursion quarks.
+% \begin{macrocode}
+\__kernel_quark_new_test:N \@@_if_recursion_tail_stop:N
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\text_purify:n, \@@_purify:n}
+% \begin{macro}[EXP]{\@@_purify_store:n}
+% \begin{macro}[EXP]{\@@_purify_store:nw}
+% \begin{macro}[EXP]{\@@_purify_end:w}
+% \begin{macro}[EXP]{\@@_purify_loop:w}
+% \begin{macro}[EXP]{\@@_purify_group:n}
+% \begin{macro}[EXP]{\@@_purify_space:w}
+% \begin{macro}[EXP]{\@@_purify_N_type:N, \@@_purify_N_type_aux:N}
+% \begin{macro}[EXP]{\@@_purify_math_search:NNN}
+% \begin{macro}[EXP]{\@@_purify_math_start:NNw}
+% \begin{macro}[EXP]{\@@_purify_math_store:n}
+% \begin{macro}[EXP]{\@@_purify_math_store:nw}
+% \begin{macro}[EXP]{\@@_purify_math_end:w}
+% \begin{macro}[EXP]{\@@_purify_math_loop:NNw}
+% \begin{macro}[EXP]{\@@_purify_math_N_type:NNN}
+% \begin{macro}[EXP]{\@@_purify_math_group:NNn}
+% \begin{macro}[EXP]{\@@_purify_math_space:NNw}
+% \begin{macro}[EXP]{\@@_purify_math_cmd:N}
+% \begin{macro}[EXP]{\@@_purify_math_cmd:NN}
+% \begin{macro}[EXP]{\@@_purify_math_cmd:Nn}
+% \begin{macro}[EXP]{\@@_purify_replace:N}
+% \begin{macro}[EXP]{\@@_purify_replace_auxi:n, \@@_purify_replace_auxii:n}
+% \begin{macro}[EXP]{\@@_purify_expand:N, \@@_purify_protect:N, \@@_purify_encoding:N}
+% \begin{macro}[EXP]{\@@_purify_encoding_escape:NN}
+% As in the other parts of the module, we start off with a standard
+% \enquote{action} loop, with expansion applied up-front.
+% \begin{macrocode}
+\cs_new:Npn \text_purify:n #1
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \exp:w
+ \exp_args:Ne \@@_purify:n
+ { \text_expand:n {#1} }
+ }
+ }
+\cs_new:Npn \@@_purify:n #1
+ {
+ \group_align_safe_begin:
+ \@@_purify_loop:w #1
+ \q_@@_recursion_tail \q_@@_recursion_stop
+ \@@_purify_result:n { }
+ }
+% \end{macrocode}
+% As for expansion, collect up the tokens for future use.
+% \begin{macrocode}
+\cs_new:Npn \@@_purify_store:n #1
+ { \@@_purify_store:nw {#1} }
+\cs_new:Npn \@@_purify_store:nw #1#2 \@@_purify_result:n #3
+ { #2 \@@_purify_result:n { #3 #1 } }
+\cs_new:Npn \@@_purify_end:w #1 \@@_purify_result:n #2
+ {
+ \group_align_safe_end:
+ \exp_end:
+ #2
+ }
+% \end{macrocode}
+% The main loop is a standard \enquote{tl action}. Unlike the expansion
+% or case changing, here any groups have to be run inline. Most of the
+% business end is as before in the \texttt{N}-type token processing.
+% \begin{macrocode}
+\cs_new:Npn \@@_purify_loop:w #1 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \@@_purify_N_type:N }
+ {
+ \tl_if_head_is_group:nTF {#1}
+ { \@@_purify_group:n }
+ { \@@_purify_space:w }
+ }
+ #1 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_purify_group:n #1 { \@@_purify_loop:w #1 }
+\exp_last_unbraced:NNo \cs_new:Npn \@@_purify_space:w \c_space_tl
+ {
+ \@@_purify_store:n { ~ }
+ \@@_purify_loop:w
+ }
+% \end{macrocode}
+% The first part of handling math mode is exactly the same as in the
+% other functions: look for a start-of-math mode token and if found start
+% a new loop tracking the closing token.
+% \begin{macrocode}
+\cs_new:Npn \@@_purify_N_type:N #1
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #1 { \@@_purify_end:w }
+ \@@_purify_N_type_aux:N #1
+ }
+\cs_new:Npn \@@_purify_N_type_aux:N #1
+ {
+ \exp_after:wN \@@_purify_math_search:NNN
+ \exp_after:wN #1 \l_text_math_delims_tl
+ \q_@@_recursion_tail ?
+ \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_purify_math_search:NNN #1#2#3
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #2
+ { \@@_purify_math_cmd:N #1 }
+ \token_if_eq_meaning:NNTF #1 #2
+ {
+ \@@_use_i_delimit_by_q_recursion_stop:nw
+ { \@@_purify_math_start:NNw #2 #3 }
+ }
+ { \@@_purify_math_search:NNN #1 }
+ }
+\cs_new:Npn \@@_purify_math_start:NNw #1#2#3 \q_@@_recursion_stop
+ {
+ \@@_purify_math_loop:NNw #1#2#3 \q_@@_recursion_stop
+ \@@_purify_math_result:n { }
+ }
+\cs_new:Npn \@@_purify_math_store:n #1
+ { \@@_purify_math_store:nw {#1} }
+\cs_new:Npn \@@_purify_math_store:nw #1#2 \@@_purify_math_result:n #3
+ { #2 \@@_purify_math_result:n { #3 #1 } }
+\cs_new:Npn \@@_purify_math_end:w #1 \@@_purify_math_result:n #2
+ {
+ \@@_purify_store:n { $ #2 $ }
+ \@@_purify_loop:w #1
+ }
+\cs_new:Npn \@@_purify_math_stop:Nw #1 \@@_purify_math_result:n #2
+ {
+ \@@_purify_store:n {#1#2}
+ \@@_purify_end:w
+ }
+\cs_new:Npn \@@_purify_math_loop:NNw #1#2#3 \q_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#3}
+ { \@@_purify_math_N_type:NNN }
+ {
+ \tl_if_head_is_group:nTF {#3}
+ { \@@_purify_math_group:NNn }
+ { \@@_purify_math_space:NNw }
+ }
+ #1#2#3 \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_purify_math_N_type:NNN #1#2#3
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #3
+ { \@@_purify_math_stop:Nw #1 }
+ \token_if_eq_meaning:NNTF #3 #2
+ { \@@_purify_math_end:w }
+ {
+ \@@_purify_math_store:n {#3}
+ \@@_purify_math_loop:NNw #1#2
+ }
+ }
+\cs_new:Npn \@@_purify_math_group:NNn #1#2#3
+ {
+ \@@_purify_math_store:n { {#3} }
+ \@@_purify_math_loop:NNw #1#2
+ }
+\exp_after:wN \cs_new:Npn \exp_after:wN \@@_purify_math_space:NNw
+ \exp_after:wN # \exp_after:wN 1
+ \exp_after:wN # \exp_after:wN 2 \c_space_tl
+ {
+ \@@_purify_math_store:n { ~ }
+ \@@_purify_math_loop:NNw #1#2
+ }
+% \end{macrocode}
+% Then handle math mode as an argument: same outcomes, different input
+% syntax.
+% \begin{macrocode}
+\cs_new:Npn \@@_purify_math_cmd:N #1
+ {
+ \exp_after:wN \@@_purify_math_cmd:NN \exp_after:wN #1
+ \l_text_math_arg_tl \q_@@_recursion_tail \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_purify_math_cmd:NN #1#2
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #2
+ { \@@_purify_replace:N #1 }
+ \cs_if_eq:NNTF #2 #1
+ {
+ \@@_use_i_delimit_by_q_recursion_stop:nw
+ { \@@_purify_math_cmd:n }
+ }
+ { \@@_purify_math_cmd:NN #1 }
+ }
+\cs_new:Npn \@@_purify_math_cmd:n #1
+ { \@@_purify_math_end:w \@@_purify_math_result:n {#1} }
+% \end{macrocode}
+% For \texttt{N}-type tokens, we first look for a string-context replacement
+% before anything else: this can therefore cover anything. Assuming we don't
+% find one, check to see if we can expand control sequences: if not, they have
+% to be dropped. We also allow for \LaTeXe{} \tn{protect}: there's an
+% assumption that we don't have |\protect { \oops }| or similar, but that's
+% also in the expansion code and seems like a reasonable balance.
+% \begin{macrocode}
+\cs_new:Npn \@@_purify_replace:N #1
+ {
+ \bool_lazy_and:nnTF
+ { \cs_if_exist_p:c { l_@@_purify_ \token_to_str:N #1 _tl } }
+ {
+ \bool_lazy_or_p:nn
+ { \token_if_cs_p:N #1 }
+ { \token_if_active_p:N #1 }
+ }
+ {
+ \exp_args:Nv \@@_purify_replace_auxi:n
+ { l_@@_purify_ \token_to_str:N #1 _tl }
+ }
+ {
+ \exp_args:Ne \@@_purify_replace_auxii:n
+ { \@@_token_to_explicit:N #1 }
+ }
+ }
+\cs_new:Npn \@@_purify_replace_auxi:n #1 { \@@_purify_loop:w #1 }
+\cs_new:Npn \@@_purify_replace_auxii:n #1
+ {
+ \token_if_cs:NTF #1
+ { \@@_purify_expand:N #1 }
+ {
+ \@@_purify_store:n {#1}
+ \@@_purify_loop:w
+ }
+ }
+\cs_new:Npn \@@_purify_expand:N #1
+ {
+ \str_if_eq:nnTF {#1} { \protect }
+ { \@@_purify_protect:N }
+ { \@@_purify_encoding:N #1 }
+ }
+\cs_new:Npn \@@_purify_protect:N #1
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #1 { \@@_purify_end:w }
+ \@@_purify_loop:w
+ }
+% \end{macrocode}
+% Handle encoding commands, as detailed for expansion.
+% \begin{macrocode}
+\cs_new:Npn \@@_purify_encoding:N #1
+ {
+ \bool_lazy_or:nnTF
+ { \cs_if_eq_p:NN #1 \@current@cmd }
+ { \cs_if_eq_p:NN #1 \@changed@cmd }
+ { \@@_purify_encoding_escape:NN }
+ {
+ \@@_if_expandable:NTF #1
+ { \exp_after:wN \@@_purify_loop:w #1 }
+ { \@@_purify_loop:w }
+ }
+ }
+\cs_new:Npn \@@_purify_encoding_escape:NN #1#2
+ {
+ \@@_purify_store:n {#1}
+ \@@_purify_loop:w
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \text_declare_purify_equivalent:Nn,
+% \text_declare_purify_equivalent:Ne
+% }
+% \begin{macrocode}
+\cs_new_protected:Npn \text_declare_purify_equivalent:Nn #1#2
+ {
+ \tl_clear_new:c { l_@@_purify_ \token_to_str:N #1 _tl }
+ \tl_set:cn { l_@@_purify_ \token_to_str:N #1 _tl } {#2}
+ }
+\cs_generate_variant:Nn \text_declare_purify_equivalent:Nn { Ne }
+% \end{macrocode}
+% \end{macro}
+%
+% Now pre-define a range of standard commands that need dedicated definitions
+% in purified text. First handle font-related stuff: all of this needs to be
+% disabled.
+% \begin{macrocode}
+\tl_map_inline:nn
+ {
+ \fontencoding
+ \fontfamily
+ \fontseries
+ \fontshape
+ }
+ { \text_declare_purify_equivalent:Nn #1 { \use_none:n } }
+\text_declare_purify_equivalent:Nn \fontsize { \use_none:nn }
+\text_declare_purify_equivalent:Nn \selectfont { }
+\text_declare_purify_equivalent:Nn \usefont { \use_none:nnnn }
+\tl_map_inline:nn
+ {
+ \emph
+ \text
+ \textnormal
+ \textrm
+ \textsf
+ \texttt
+ \textbf
+ \textmd
+ \textit
+ \textsl
+ \textup
+ \textsc
+ \textulc
+ }
+ { \text_declare_purify_equivalent:Nn #1 { \use:n } }
+\tl_map_inline:nn
+ {
+ \normalfont
+ \rmfamily
+ \sffamily
+ \ttfamily
+ \bfseries
+ \mdseries
+ \itshape
+ \scshape
+ \slshape
+ \upshape
+ \em
+ \Huge
+ \LARGE
+ \Large
+ \footnotesize
+ \huge
+ \large
+ \normalsize
+ \scriptsize
+ \small
+ \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.
+%
+% \begin{macro}{\@@_end_env:n}
+% \begin{macrocode}
+\text_declare_purify_equivalent:Nn \begin { \use:c }
+\text_declare_purify_equivalent:Nn \end { \@@_end_env:n }
+\cs_new:Npn \@@_end_env:n #1 { \cs:w end #1 \cs_end: }
+% \end{macrocode}
+% \end{macro}
+%
+% Some common symbols and similar ideas.
+% \begin{macrocode}
+\text_declare_purify_equivalent:Nn \\ { }
+\tl_map_inline:nn
+ { \{ \} \# \$ \% \_ }
+ { \text_declare_purify_equivalent:Ne #1 { \cs_to_str:N #1 } }
+% \end{macrocode}
+% Cross-referencing.
+% \begin{macrocode}
+\text_declare_purify_equivalent:Nn \label { \use_none:n }
+% \end{macrocode}
+% Spaces.
+% \begin{macrocode}
+\group_begin:
+\char_set_catcode_active:N \~
+\use:n
+ {
+ \group_end:
+ \text_declare_purify_equivalent:Ne ~ { \c_space_tl }
+ }
+\text_declare_purify_equivalent:Nn \nobreakspace { ~ }
+\text_declare_purify_equivalent:Nn \ { ~ }
+\text_declare_purify_equivalent:Nn \, { ~ }
+% \end{macrocode}
+%
+% \subsection{Accent and letter-like data for purifying text}
+%
+% In contrast to case changing, both $8$-bit and Unicode engines need
+% information for text purification to handle accents and letter-like
+% functions: these all need to be removed. However, the results are
+% of course engine-dependent.
+%
+% For the letter-like commands, life is relatively easy: they are all
+% simply added as standard exceptions. The only oddity is \tn{SS}, which
+% gets converted to two letters. (At some stage an alternative version
+% can presumably be added to \pkg{babel} or similar.)
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_loop:Nn #1#2
+ {
+ \quark_if_recursion_tail_stop:N #1
+ \text_declare_purify_equivalent:Ne #1
+ {
+ \codepoint_generate:nn {"#2}
+ { \char_value_catcode:n {"#2} }
+ }
+ \@@_loop:Nn
+ }
+\@@_loop:Nn
+ \AA { 00C5 }
+ \AE { 00C6 }
+ \DH { 00D0 }
+ \DJ { 0110 }
+ \IJ { 0132 }
+ \L { 0141 }
+ \NG { 014A }
+ \O { 00D8 }
+ \OE { 0152 }
+ \TH { 00DE }
+ \aa { 00E5 }
+ \ae { 00E6 }
+ \dh { 00F0 }
+ \dj { 0111 }
+ \i { 0131 }
+ \j { 0237 }
+ \ij { 0132 }
+ \l { 0142 }
+ \ng { 014B }
+ \o { 00F8 }
+ \oe { 0153 }
+ \ss { 00DF }
+ \th { 00FE }
+ \q_recursion_tail ?
+ \q_recursion_stop
+\text_declare_purify_equivalent:Nn \SS { SS }
+% \end{macrocode}
+%
+% \begin{macro}[rEXP]{\@@_purify_accent:NN}
+% Accent \textsc{licr} handling is a little more complex. Accents may exist
+% as pre-composed codepoints or as independent glyphs. The former are all
+% saved as single token lists, whilst for the latter the combining accent
+% needs to be re-ordered compared to the character it applies to.
+% \begin{macrocode}
+\cs_new:Npn \@@_purify_accent:NN #1#2
+ {
+ \cs_if_exist:cTF
+ { c_@@_purify_ \token_to_str:N #1 _ \token_to_str:N #2 _tl }
+ {
+ \exp_not:v
+ { c_@@_purify_ \token_to_str:N #1 _ \token_to_str:N #2 _tl }
+ }
+ {
+ \exp_not:n {#2}
+ \exp_not:v { c_@@_purify_ \token_to_str:N #1 _tl }
+ }
+ }
+\tl_map_inline:nn { \` \' \^ \~ \= \u \. \" \r \H \v \d \c \k \b \t }
+ { \text_declare_purify_equivalent:Nn #1 { \@@_purify_accent:NN #1 } }
+% \end{macrocode}
+% First set up the combining accents.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \@@_loop:Nn #1#2
+ {
+ \quark_if_recursion_tail_stop:N #1
+ \tl_const:ce { c_@@_purify_ \token_to_str:N #1 _tl }
+ { \codepoint_generate:nn {"#2} { \char_value_catcode:n { "#2 } } }
+ \@@_loop:Nn
+ }
+ \@@_loop:Nn
+ \` { 0300 }
+ \' { 0301 }
+ \^ { 0302 }
+ \~ { 0303 }
+ \= { 0304 }
+ \u { 0306 }
+ \. { 0307 }
+ \" { 0308 }
+ \r { 030A }
+ \H { 030B }
+ \v { 030C }
+ \d { 0323 }
+ \c { 0327 }
+ \k { 0328 }
+ \b { 0331 }
+ \t { 0361 }
+ \q_recursion_tail { }
+ \q_recursion_stop
+% \end{macrocode}
+% Now we handle the pre-composed accents: the list here is taken from
+% \texttt{puenc.def}. All of the precomposed cases take a single letter
+% as their second argument. We do not try to cover the case where an accent
+% is added to a \enquote{real} dotless-i or -j, or a \ae/\AE. Rather, we
+% assume that if the \textsc{utf}-8 character is used, it will have the
+% real accent character too.
+% \begin{macrocode}
+ \cs_set_protected:Npn \@@_loop:NNn #1#2#3
+ {
+ \quark_if_recursion_tail_stop:N #1
+ \tl_const:ce
+ { c_@@_purify_ \token_to_str:N #1 _ \token_to_str:N #2 _tl }
+ { \codepoint_generate:nn {"#3} { \char_value_catcode:n { "#3 } } }
+ \@@_loop:NNn
+ }
+ \@@_loop:NNn
+ \` A { 00C0 }
+ \' A { 00C1 }
+ \^ A { 00C2 }
+ \~ A { 00C3 }
+ \" A { 00C4 }
+ \r A { 00C5 }
+ \c C { 00C7 }
+ \` E { 00C8 }
+ \' E { 00C9 }
+ \^ E { 00CA }
+ \" E { 00CB }
+ \` I { 00CC }
+ \' I { 00CD }
+ \^ I { 00CE }
+ \" I { 00CF }
+ \~ N { 00D1 }
+ \` O { 00D2 }
+ \' O { 00D3 }
+ \^ O { 00D4 }
+ \~ O { 00D5 }
+ \" O { 00D6 }
+ \` U { 00D9 }
+ \' U { 00DA }
+ \^ U { 00DB }
+ \" U { 00DC }
+ \' Y { 00DD }
+ \` a { 00E0 }
+ \' a { 00E1 }
+ \^ a { 00E2 }
+ \~ a { 00E3 }
+ \" a { 00E4 }
+ \r a { 00E5 }
+ \c c { 00E7 }
+ \` e { 00E8 }
+ \' e { 00E9 }
+ \^ e { 00EA }
+ \" e { 00EB }
+ \` i { 00EC }
+ \` \i { 00EC }
+ \' i { 00ED }
+ \' \i { 00ED }
+ \^ i { 00EE }
+ \^ \i { 00EE }
+ \" i { 00EF }
+ \" \i { 00EF }
+ \~ n { 00F1 }
+ \` o { 00F2 }
+ \' o { 00F3 }
+ \^ o { 00F4 }
+ \~ o { 00F5 }
+ \" o { 00F6 }
+ \` u { 00F9 }
+ \' u { 00FA }
+ \^ u { 00FB }
+ \" u { 00FC }
+ \' y { 00FD }
+ \" y { 00FF }
+ \= A { 0100 }
+ \= a { 0101 }
+ \u A { 0102 }
+ \u a { 0103 }
+ \k A { 0104 }
+ \k a { 0105 }
+ \' C { 0106 }
+ \' c { 0107 }
+ \^ C { 0108 }
+ \^ c { 0109 }
+ \. C { 010A }
+ \. c { 010B }
+ \v C { 010C }
+ \v c { 010D }
+ \v D { 010E }
+ \v d { 010F }
+ \= E { 0112 }
+ \= e { 0113 }
+ \u E { 0114 }
+ \u e { 0115 }
+ \. E { 0116 }
+ \. e { 0117 }
+ \k E { 0118 }
+ \k e { 0119 }
+ \v E { 011A }
+ \v e { 011B }
+ \^ G { 011C }
+ \^ g { 011D }
+ \u G { 011E }
+ \u g { 011F }
+ \. G { 0120 }
+ \. g { 0121 }
+ \c G { 0122 }
+ \c g { 0123 }
+ \^ H { 0124 }
+ \^ h { 0125 }
+ \~ I { 0128 }
+ \~ i { 0129 }
+ \~ \i { 0129 }
+ \= I { 012A }
+ \= i { 012B }
+ \= \i { 012B }
+ \u I { 012C }
+ \u i { 012D }
+ \u \i { 012D }
+ \k I { 012E }
+ \k i { 012F }
+ \k \i { 012F }
+ \. I { 0130 }
+ \^ J { 0134 }
+ \^ j { 0135 }
+ \^ \j { 0135 }
+ \c K { 0136 }
+ \c k { 0137 }
+ \' L { 0139 }
+ \' l { 013A }
+ \c L { 013B }
+ \c l { 013C }
+ \v L { 013D }
+ \v l { 013E }
+ \. L { 013F }
+ \. l { 0140 }
+ \' N { 0143 }
+ \' n { 0144 }
+ \c N { 0145 }
+ \c n { 0146 }
+ \v N { 0147 }
+ \v n { 0148 }
+ \= O { 014C }
+ \= o { 014D }
+ \u O { 014E }
+ \u o { 014F }
+ \H O { 0150 }
+ \H o { 0151 }
+ \' R { 0154 }
+ \' r { 0155 }
+ \c R { 0156 }
+ \c r { 0157 }
+ \v R { 0158 }
+ \v r { 0159 }
+ \' S { 015A }
+ \' s { 015B }
+ \^ S { 015C }
+ \^ s { 015D }
+ \c S { 015E }
+ \c s { 015F }
+ \v S { 0160 }
+ \v s { 0161 }
+ \c T { 0162 }
+ \c t { 0163 }
+ \v T { 0164 }
+ \v t { 0165 }
+ \~ U { 0168 }
+ \~ u { 0169 }
+ \= U { 016A }
+ \= u { 016B }
+ \u U { 016C }
+ \u u { 016D }
+ \r U { 016E }
+ \r u { 016F }
+ \H U { 0170 }
+ \H u { 0171 }
+ \k U { 0172 }
+ \k u { 0173 }
+ \^ W { 0174 }
+ \^ w { 0175 }
+ \^ Y { 0176 }
+ \^ y { 0177 }
+ \" Y { 0178 }
+ \' Z { 0179 }
+ \' z { 017A }
+ \. Z { 017B }
+ \. z { 017C }
+ \v Z { 017D }
+ \v z { 017E }
+ \v A { 01CD }
+ \v a { 01CE }
+ \v I { 01CF }
+ \v \i { 01D0 }
+ \v i { 01D0 }
+ \v O { 01D1 }
+ \v o { 01D2 }
+ \v U { 01D3 }
+ \v u { 01D4 }
+ \v G { 01E6 }
+ \v g { 01E7 }
+ \v K { 01E8 }
+ \v k { 01E9 }
+ \k O { 01EA }
+ \k o { 01EB }
+ \v \j { 01F0 }
+ \v j { 01F0 }
+ \' G { 01F4 }
+ \' g { 01F5 }
+ \` N { 01F8 }
+ \` n { 01F9 }
+ \' \AE { 01FC }
+ \' \ae { 01FD }
+ \' \O { 01FE }
+ \' \o { 01FF }
+ \v H { 021E }
+ \v h { 021F }
+ \. A { 0226 }
+ \. a { 0227 }
+ \c E { 0228 }
+ \c e { 0229 }
+ \. O { 022E }
+ \. o { 022F }
+ \= Y { 0232 }
+ \= y { 0233 }
+ \q_recursion_tail ? { }
+ \q_recursion_stop
+\group_end:
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3text.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3text.dtx
new file mode 100644
index 00000000000..56c9a5533c3
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3text.dtx
@@ -0,0 +1,1453 @@
+% \iffalse meta-comment
+%
+%% File: l3text.dtx
+%
+% Copyright (C) 2020-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3text} module\\ Text processing^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% This module deals with manipulation of (formatted) text; such material is
+% comprised of a restricted set of token list content. The functions provided
+% here concern conversion of textual content for example in case changing,
+% generation of bookmarks and extraction to tags. All of the major functions
+% operate by expansion. Begin-group and end-group tokens in the \meta{text}
+% are normalized and become |{| and |}|, respectively.
+%
+% \section{Expanding text}
+%
+% \begin{function}[EXP, added = 2020-01-02, updated = 2023-06-09]
+% {\text_expand:n}
+% \begin{syntax}
+% \cs{text_expand:n} \Arg{text}
+% \end{syntax}
+% Takes user input \meta{text} and expands the content.
+% Protected commands (typically
+% formatting) are left in place, and no processing takes place of
+% math mode material (as delimited by pairs given in
+% \cs{l_text_math_delims_tl} or as the argument to commands listed
+% in \cs{l_text_math_arg_tl}). Commands which are neither engine-
+% nor \LaTeX{} protected are expanded exhaustively.
+% Any commands listed in \cs{l_text_expand_exclude_tl} are excluded from
+% expansion, as are those in \cs{l_text_case_exclude_arg_tl} and
+% \cs{l_text_math_arg_tl}.
+% \end{function}
+%
+% \begin{function}[added = 2020-01-22]
+% {
+% \text_declare_expand_equivalent:Nn ,
+% \text_declare_expand_equivalent:cn
+% }
+% \begin{syntax}
+% \cs{text_declare_expand_equivalent:Nn} \meta{cmd} \Arg{replacement}
+% \end{syntax}
+% Declares that the \meta{replacement} tokens should be used whenever the
+% \meta{cmd} (a single token) is encountered. The \meta{replacement} tokens
+% should be expandable. A token can be \enquote{replaced} by itself if
+% the defined replacement wraps it in \cs{exp_not:n}, for example
+% \begin{verbatim}
+% \text_declare_expand_equivalent:Nn \' { \exp_not:n { \' } }
+% \end{verbatim}
+% \end{function}
+%
+% \section{Case changing}
+%
+% \begin{function}[EXP, added = 2019-11-20, updated = 2023-07-08]
+% {
+% \text_lowercase:n, \text_uppercase:n, \text_titlecase_all:n,
+% \text_titlecase_first:n,
+% \text_lowercase:nn, \text_uppercase:nn, \text_titlecase_all:nn,
+% \text_titlecase_first:nn
+% }
+% \begin{syntax}
+% \cs{text_uppercase:n} \Arg{tokens}
+% \cs{text_uppercase:nn} \Arg{BCP-47} \Arg{tokens}
+% \end{syntax}
+% Takes user input \meta{text} first applies \cs{text_expand:n}, then
+% transforms the case of character tokens as specified by the
+% function name. The category code of letters are not changed by this
+% process when Unicode engines are used; in $8$-bit engines, case changed
+% charters in the ASCII range will have the current prevailing category code,
+% while those outside of it will be represented by active characters.
+% \end{function}
+%
+% Upper- and lowercase have the obvious meanings. Titlecasing may be regarded
+% informally as converting the first character of the \meta{tokens} to
+% uppercase. However, the process is more complex
+% than this as there are some situations where a single lowercase character
+% maps to a special form, for example \texttt{ij} in Dutch which becomes
+% \texttt{IJ}. There are two functions available for titlecasing: one which
+% applies the change to each \enquote{word} and a second which only applies
+% at the start of the input. (Here, \enquote{word} boundaries are spaces:
+% at present, full Unicode word breaking is not attempted.)
+%
+% Importantly, notice that these functions are intended for working with
+% user \emph{text for typesetting}. For case changing programmatic data see
+% the \pkg{l3str} module and discussion there of \cs{str_lowercase:n},
+% \cs{str_uppercase:n} and \cs{str_casefold:n}.
+%
+% Case changing does not take place within math mode material so for example
+% \begin{verbatim}
+% \text_uppercase:n { Some~text~$y = mx + c$~with~{Braces} }
+% \end{verbatim}
+% becomes
+% \begin{verbatim}
+% SOME TEXT $y = mx + c$ WITH {BRACES}
+% \end{verbatim}
+%
+% The first mandatory argument of commands listed in
+% \cs{l_text_case_exclude_arg_tl}
+% is excluded from case changing; the latter are entirely non-textual
+% content (such as labels).
+%
+% The standard mappings
+% here follow those defined by the \href{http://www.unicode.org}^^A
+% {Unicode Consortium} in \texttt{UnicodeData.txt} and
+% \texttt{SpecialCasing.txt}. For \pTeX{}, only the ASCII range is
+% covered as the engine treats input outside of this range as east Asian.
+%
+% Locale-sensitive conversions are enabled using the \meta{BCP-47}
+% argument, and follow Unicode Consortium guidelines. Currently, the
+% locale strings recognized for special handling are as follows.
+% \begin{itemize}
+% \item Armenian (\texttt{hy} and \texttt{hy-x-yiwn})
+% The setting \texttt{hy} maps the codepoint U+0587, the ligature of
+% letters ech and yiwn, to the codepoints for capital ech and vew
+% when uppercasing: this follows the spelling reform which is used
+% in Armenia. The alternative \texttt{hy-x-yiwn} maps U+0587 to
+% capital ech and yiwn on uppercasing (also the output if Armenian
+% is not selected at all).
+% \item Azeri and Turkish (\texttt{az} and \texttt{tr}).
+% The case pairs I/i-dotless and I-dot/i are activated for these
+% languages. The combining dot mark is removed when lowercasing
+% I-dot and introduced when upper casing i-dotless.
+% \item German (\texttt{de-x-eszett}).
+% An alternative mapping for German in which the lowercase
+% \emph{Eszett} maps to a \emph{gro\ss{}es Eszett}.
+% \item Greek (\texttt{el}).
+% Removes accents from Greek letters when uppercasing; titlecasing
+% leaves accents in place. A variant \texttt{el-x-iota} is available
+% which converts the \textit{ypogegrammeni} (subscript muted iota)
+% to capital iota when uppercasing: the standard version retains the
+% subscript versions.
+% \item Lithuanian (\texttt{lt}).
+% The lowercase letters i and j should retain a dot above when the
+% accents grave, acute or tilde are present. This is implemented for
+% lowercasing of the relevant uppercase letters both when input as
+% single Unicode codepoints and when using combining accents. The
+% combining dot is removed when uppercasing in these cases. Note that
+% \emph{only} the accents used in Lithuanian are covered: the behaviour
+% of other accents are not modified.
+% \item Medieval Latin (\texttt{la-x-medieval}).
+% The characters |u| and |V| are interchanged on case changing.
+% \item Dutch (\texttt{nl}).
+% Capitalisation of \texttt{ij} at the beginning of titlecased
+% input produces \texttt{IJ} rather than \texttt{Ij}.
+% \end{itemize}
+%
+% Determining whether non-letter characters at the start of text should count
+% as the uppercase element is controllable. When
+% \cs{l_text_titlecase_check_letter_bool} is \texttt{true}, codepoints which are
+% not letters (Unicode general category \texttt{L}) are not changed, and only
+% the first \emph{letter} is uppercased.
+% When \cs{l_text_titlecase_check_letter_bool} is \texttt{false}, the first
+% codepoint is uppercased, irrespective of the general code of the character.
+%
+% \begin{function}[added = 2022-07-04]
+% {\text_declare_case_equivalent:Nn}
+% \begin{syntax}
+% \cs{text_declare_case_equivalent:Nn} \meta{cmd} \Arg{replacement}
+% \end{syntax}
+% Declares that the \meta{replacement} tokens should be used whenever the
+% \meta{cmd} (a single token) is encountered during case changing.
+% \end{function}
+%
+% \begin{function}[added = 2023-04-11, updated = 2023-04-20]
+% {
+% \text_declare_lowercase_mapping:nn ,
+% \text_declare_lowercase_mapping:nnn ,
+% \text_declare_titlecase_mapping:nn ,
+% \text_declare_titlecase_mapping:nnn ,
+% \text_declare_uppercase_mapping:nn ,
+% \text_declare_uppercase_mapping:nnn
+% }
+% \begin{syntax}
+% \cs{text_declare_lowercase_mapping:nn} \Arg{codeppoint} \Arg{replacement}
+% \cs{text_declare_lowercase_mapping:nnn} \Arg{BCP-47} \Arg{codeppoint} \Arg{replacement}
+% \end{syntax}
+% Declares that the \meta{replacement} tokens should be used when case mapping
+% the \meta{codepoint}, rather than the standard mapping given in the
+% Unicode data files. The \texttt{nnn} version takes a BCP-47 tag, which
+% can be used to specify that the customisation only applies to that
+% locale.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2022-07-04]{\text_case_switch:nnnn}
+% \begin{syntax}
+% \cs{text_case_switch:nnnn} \Arg{normal} \Arg{upper} \Arg{lower} \Arg{title}
+% \end{syntax}
+% Context-sensitive function which will expand to one of the \meta{normal},
+% \meta{upper}, \meta{lower} or \meta{title} tokens depending on the current
+% case changing operation. Outside of case changing, the \meta{normal} tokens
+% are produced. Within case changing, the appropriate mapping tokens are
+% inserted.
+% \end{function}
+%
+% \section{Removing formatting from text}
+%
+% \begin{function}[EXP, added = 2020-03-05, updated = 2020-05-14]{\text_purify:n}
+% \begin{syntax}
+% \cs{text_purify:n} \Arg{text}
+% \end{syntax}
+% Takes user input \meta{text} and expands as described for
+% \cs{text_expand:n}, then removes all functions from the resulting
+% text. Math mode material (as delimited by pairs given in
+% \cs{l_text_math_delims_tl} or as the argument to commands listed in
+% \cs{l_text_math_arg_tl}) is left contained in a pair of |$| delimiters.
+% Non-expandable functions present in the \meta{text} must either have a
+% defined equivalent (see \cs{text_declare_purify_equivalent:Nn}) or will
+% be removed from the result. Implicit tokens are converted to their
+% explicit equivalent.
+% \end{function}
+%
+% \begin{function}[added = 2020-03-05]
+% {
+% \text_declare_purify_equivalent:Nn ,
+% \text_declare_purify_equivalent:Ne
+% }
+% \begin{syntax}
+% \cs{text_declare_purify_equivalent:Nn} \meta{cmd} \Arg{replacement}
+% \end{syntax}
+% Declares that the \meta{replacement} tokens should be used whenever the
+% \meta{cmd} (a single token) is encountered. The \meta{replacement} tokens
+% should be expandable.
+% \end{function}
+%
+% \section{Control variables}
+%
+% \begin{variable}{\l_text_math_arg_tl}
+% Lists commands present in the \meta{text} where the argument of the
+% command should be treated as math mode material. The treatment here is
+% similar to \cs{l_text_math_delims_tl} but for a command rather than
+% paired delimiters.
+% \end{variable}
+%
+% \begin{variable}{\l_text_math_delims_tl}
+% Lists pairs of tokens which delimit (in-line) math mode content; such
+% content \emph{may} be excluded from processing.
+% \end{variable}
+%
+% \begin{variable}{\l_text_case_exclude_arg_tl}
+% Lists commands where the first mandatory argument is excluded from
+% case changing.
+% \end{variable}
+%
+% \begin{variable}{\l_text_expand_exclude_tl}
+% Lists commands which are excluded from expansion. This protection
+% includes everything up to and including their first braced argument.
+% \end{variable}
+%
+% \begin{variable}{\l_text_titlecase_check_letter_bool}
+% Controls how the start of titlecasing is handled: when \texttt{true}, the
+% first \emph{letter} in text is considered. The standard setting is
+% \texttt{true}.
+% \end{variable}
+%
+% \section{Mapping to graphemes}
+%
+% Grapheme splitting is implemented using the algorithm described in Unicode
+% Standard Annex \#29. This includes support for extended grapheme clusters.
+% Text starting with a line feed or carriage return character will drop this
+% due to standard \TeX{} processing. At present extended pictograms are
+% not supported: these may be added in a future release.
+%
+% \begin{function}[rEXP, added = 2022-08-04]{\text_map_function:nN}
+% \begin{syntax}
+% \cs{text_map_function:nN} \meta{text} \Arg{function}
+% \end{syntax}
+% Takes user input \meta{text} and expands as described for
+% \cs{text_expand:n}, then maps over the \emph{graphemes} within the
+% result, passing each grapheme to the \meta{function}.
+% Broadly a grapheme is a \enquote{user perceived character}:
+% the Unicode Consortium describe the decomposition of input to
+% graphemes in depth, and the approach used here implements that
+% algorithm. The \meta{function} should accept one argument as \meta{balanced
+% text}: this may be comprise codepoints or may be a control sequence.
+% With $8$-bit engines, the codepoint(s) themselves may of course be
+% made up of multiple bytes: the mapping will pass the correct codepoints
+% independent of the engine in use.
+% See also \cs{text_map_inline:nn}.
+% \end{function}
+%
+% \begin{function}[added = 2022-08-04]{\text_map_inline:nn}
+% \begin{syntax}
+% \cs{text_map_inline:nn} \meta{text} \Arg{inline function}
+% \end{syntax}
+% Takes user input \meta{text} and expands as described for
+% \cs{text_expand:n}, then maps over the \emph{graphemes} within the
+% result, passing each grapheme to the \meta{inline function}.
+% Broadly a grapheme is a \enquote{user perceived character}:
+% the Unicode Consortium describe the decomposition of input to
+% graphemes in depth, and the approach used here implements that
+% algorithm. The \meta{inline function} should consist of code which
+% receives the grapheme as \meta{balanced
+% text}: this may be comprise codepoints or may be a control sequence.
+% With $8$-bit engines, the codepoint(s) themselves may of course be
+% made up of multiple bytes: the mapping will pass the correct codepoints
+% independent of the engine in use.
+% See also \cs{text_map_function:nN}.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2022-08-04]
+% {\text_map_break:, \text_map_break:n}
+% \begin{syntax}
+% \cs{text_map_break:}
+% \cs{text_map_break:n} \Arg{code}
+% \end{syntax}
+% Used to terminate a \cs[no-index]{text_map_\ldots} function before all
+% entries in the \meta{text} have been processed. This
+% normally takes place within a conditional statement.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3text} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=text>
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_generate_variant:Nn \tl_if_head_eq_meaning_p:nN { o }
+% \end{macrocode}
+%
+% \subsection{Internal auxiliaries}
+%
+% \begin{variable}{\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\q_@@_nil}
+% Internal quarks.
+% \begin{macrocode}
+\quark_new:N \q_@@_nil
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[pTF]{\@@_quark_if_nil:n}
+% Branching quark conditional.
+% \begin{macrocode}
+\__kernel_quark_new_conditional:Nn \@@_quark_if_nil:n { TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\q_@@_recursion_tail,\q_@@_recursion_stop}
+% Internal recursion quarks.
+% \begin{macrocode}
+\quark_new:N \q_@@_recursion_tail
+\quark_new:N \q_@@_recursion_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_use_i_delimit_by_q_recursion_stop:nw}
+% Functions to gobble up to a quark.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_i_delimit_by_q_recursion_stop:nw
+ #1 #2 \q_@@_recursion_stop {#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_if_q_recursion_tail_stop_do:Nn}
+% \begin{macro}[EXP]{\@@_if_q_recursion_tail_stop_do:nn}
+% Functions to query recursion quarks.
+% \begin{macrocode}
+\__kernel_quark_new_test:N \@@_if_q_recursion_tail_stop_do:Nn
+\__kernel_quark_new_test:N \@@_if_q_recursion_tail_stop_do:nn
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\s_@@_recursion_tail,\s_@@_recursion_stop}
+% Internal scan marks quarks.
+% \begin{macrocode}
+\scan_new:N \s_@@_recursion_tail
+\scan_new:N \s_@@_recursion_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_use_i_delimit_by_s_recursion_stop:nw}
+% Functions to gobble up to a scan mark.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_i_delimit_by_s_recursion_stop:nw
+ #1 #2 \s_@@_recursion_stop {#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_if_s_recursion_tail_stop_do:Nn}
+% Functions to query recursion scan marks. Slower than a quark
+% test but needed to avoid issues in the outer expansion loop
+% with unterminated \tn{romannumeral} primitives.
+% \begin{macrocode}
+\cs_new:Npn \@@_if_s_recursion_tail_stop_do:Nn #1
+ {
+ \bool_lazy_and:nnTF
+ { \cs_if_eq_p:NN \s_@@_recursion_tail #1 }
+ { \str_if_eq_p:nn { \s_@@_recursion_tail } {#1} }
+ { \@@_use_i_delimit_by_s_recursion_stop:nw }
+ { \use_none:n }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Utilities}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_token_to_explicit:N ,
+% \@@_token_to_explicit_char:N ,
+% \@@_token_to_explicit_cs:N ,
+% \@@_token_to_explicit_cs_aux:N
+% }
+% \begin{macro}[EXP]{\@@_token_to_explicit:n}
+% \begin{macro}[EXP]
+% {
+% \@@_token_to_explicit_auxi:w ,
+% \@@_token_to_explicit_auxii:w ,
+% \@@_token_to_explicit_auxiii:w
+% }
+% The idea here is to take a token and ensure that if it's an implicit
+% char, we output the explicit version. Otherwise, the token needs to be
+% unchanged. First, we have to split between control sequences and everything
+% else.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_active:n { 0 }
+ \cs_new:Npn \@@_token_to_explicit:N #1
+ {
+ \if_catcode:w \exp_not:N #1
+ \if_catcode:w \scan_stop: \exp_not:N #1
+ \scan_stop:
+ \else:
+ \exp_not:N ^^@
+ \fi:
+ \exp_after:wN \@@_token_to_explicit_cs:N
+ \else:
+ \exp_after:wN \@@_token_to_explicit_char:N
+ \fi:
+ #1
+ }
+\group_end:
+% \end{macrocode}
+% For control sequences, we can check for macros versus other cases using
+% \cs{if_meaning:w}, then explicitly check for \tn{chardef} and
+% \tn{mathchardef}.
+% \begin{macrocode}
+\cs_new:Npn \@@_token_to_explicit_cs:N #1
+ {
+ \exp_after:wN \if_meaning:w \exp_not:N #1 #1
+ \exp_after:wN \use:nn \exp_after:wN
+ \@@_token_to_explicit_cs_aux:N
+ \else:
+ \exp_after:wN \exp_not:n
+ \fi:
+ {#1}
+ }
+\cs_new:Npn \@@_token_to_explicit_cs_aux:N #1
+ {
+ \bool_lazy_or:nnTF
+ { \token_if_chardef_p:N #1 }
+ { \token_if_mathchardef_p:N #1 }
+ {
+ \char_generate:nn {#1}
+ {
+ \if_int_compare:w \char_value_catcode:n {#1} = 10 \exp_stop_f:
+ 10
+ \else:
+ 12
+ \fi:
+ }
+ }
+ {#1}
+ }
+% \end{macrocode}
+% For character tokens, we need to filter out the implicit characters from
+% those that are explicit. That's done here, then if necessary we work out
+% the category code and generate the char. To avoid issues with alignment
+% tabs, that one is done by elimination rather than looking up the code
+% explicitly. The trick with finding the charcode is that the \TeX{}
+% messages are either \texttt{the \meta{something} character \meta{char}}
+% or \texttt{the \meta{type} \meta{char}}.
+% \begin{macrocode}
+\cs_new:Npn \@@_token_to_explicit_char:N #1
+ {
+ \if:w
+ \if_catcode:w ^ \exp_args:No \str_tail:n { \token_to_str:N #1 } ^
+ \token_to_str:N #1 #1
+ \else:
+ AB
+ \fi:
+ \exp_after:wN \exp_not:n
+ \else:
+ \exp_after:wN \@@_token_to_explicit:n
+ \fi:
+ {#1}
+ }
+\cs_new:Npn \@@_token_to_explicit:n #1
+ {
+ \exp_after:wN \@@_token_to_explicit_auxi:w
+ \int_value:w
+ \if_catcode:w \c_group_begin_token #1 1 \else:
+ \if_catcode:w \c_group_end_token #1 2 \else:
+ \if_catcode:w \c_math_toggle_token #1 3 \else:
+ \if_catcode:w ## #1 6 \else:
+ \if_catcode:w ^ #1 7 \else:
+ \if_catcode:w \c_math_subscript_token #1 8 \else:
+ \if_catcode:w \c_space_token #1 10 \else:
+ \if_catcode:w A #1 11 \else:
+ \if_catcode:w + #1 12 \else:
+ 4 \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi:
+ \exp_after:wN ;
+ \token_to_meaning:N #1 \s_@@_stop
+ }
+\cs_new:Npn \@@_token_to_explicit_auxi:w #1 ; #2 \s_@@_stop
+ {
+ \char_generate:nn
+ {
+ \if_int_compare:w #1 < 9 \exp_stop_f:
+ \exp_after:wN \@@_token_to_explicit_auxii:w
+ \else:
+ \exp_after:wN \@@_token_to_explicit_auxiii:w
+ \fi:
+ #2
+ }
+ {#1}
+ }
+\exp_last_unbraced:NNNNo \cs_new:Npn \@@_token_to_explicit_auxii:w
+ #1 { \tl_to_str:n { character ~ } } { ` }
+\cs_new:Npn \@@_token_to_explicit_auxiii:w #1 ~ #2 ~ { ` }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_char_catcode:N}
+% An idea from \pkg{l3char}: we need to get the category code of
+% a specific token, not the general case.
+% \begin{macrocode}
+\cs_new:Npn \@@_char_catcode:N #1
+ {
+ \if_catcode:w \exp_not:N #1 \c_math_toggle_token
+ 3
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_alignment_token
+ 4
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_math_superscript_token
+ 7
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_math_subscript_token
+ 8
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_space_token
+ 10
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_catcode_letter_token
+ 11
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_catcode_other_token
+ 12
+ \else:
+ 13
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, TF]{\@@_if_expandable:N}
+% Test for tokens that make sense to expand here: that is more
+% restrictive than the engine view.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_if_expandable:N #1 { T , F , TF }
+ {
+ \token_if_expandable:NTF #1
+ {
+ \bool_lazy_any:nTF
+ {
+ { \token_if_protected_macro_p:N #1 }
+ { \token_if_protected_long_macro_p:N #1 }
+ { \token_if_eq_meaning_p:NN \q_@@_recursion_tail #1 }
+ }
+ { \prg_return_false: }
+ { \prg_return_true: }
+ }
+ { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Codepoint utilities}
+%
+% For working with codepoints in an engine-neutral way.
+%
+% \begin{macro}[EXP]{\@@_codepoint_process:nN, \@@_codepoint_process_aux:nN}
+% \begin{macro}[EXP]{\@@_codepoint_process:nNN}
+% \begin{macro}[EXP]{\@@_codepoint_process:nNNN}
+% \begin{macro}[EXP]{\@@_codepoint_process:nNNNN}
+% Grab a codepoint and apply some code to it: here |#1| should expect one
+% following \emph{balanced text}.
+% \begin{macrocode}
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_new:Npn \@@_codepoint_process:nN #1#2 { #1 {#2} }
+ }
+ {
+ \cs_new:Npe \@@_codepoint_process:nN #1#2
+ {
+ \exp_not:N \int_compare:nNnTF {`#2} > { "80 }
+ {
+ \sys_if_engine_pdftex:TF
+ { \exp_not:N \@@_codepoint_process_aux:nN }
+ {
+ \exp_not:N \int_compare:nNnTF {`#2} > { "FF }
+ { \exp_not:N \use:n }
+ { \exp_not:N \@@_codepoint_process_aux:nN }
+ }
+ }
+ { \exp_not:N \use:n }
+ {#1} #2
+ }
+ \cs_new:Npn \@@_codepoint_process_aux:nN #1#2
+ {
+ \int_compare:nNnTF { `#2 } < { "E0 }
+ { \@@_codepoint_process:nNN }
+ {
+ \int_compare:nNnTF { `#2 } < { "F0 }
+ { \@@_codepoint_process:nNNN }
+ { \@@_codepoint_process:nNNNN }
+ }
+ {#1} #2
+ }
+ \cs_new:Npn \@@_codepoint_process:nNN #1#2#3
+ { #1 {#2#3} }
+ \cs_new:Npn \@@_codepoint_process:nNNN #1#2#3#4
+ { #1 {#2#3#4} }
+ \cs_new:Npn \@@_codepoint_process:nNNNN #1#2#3#4#5
+ { #1 {#2#3#4#5} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP, pTF]{\@@_codepoint_compare:nNn}
+% \begin{macro}[EXP]
+% {\@@_codepoint_from_chars:Nw, \@@_codepoint_from_chars_aux:Nw}
+% \begin{macro}[EXP]{\@@_codepoint_from_chars:N}
+% \begin{macro}[EXP]{\@@_codepoint_from_chars:NN}
+% \begin{macro}[EXP]{\@@_codepoint_from_chars:NNN}
+% \begin{macro}[EXP]{\@@_codepoint_from_chars:NNNN}
+% Allows comparison for all engines using a first \enquote{character} followed
+% by a codepoint.
+% \begin{macrocode}
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \prg_new_conditional:Npnn
+ \@@_codepoint_compare:nNn #1#2#3 { TF , p }
+ {
+ \int_compare:nNnTF {`#1} #2 {#3}
+ \prg_return_true: \prg_return_false:
+ }
+ \cs_new:Npn \@@_codepoint_from_chars:Nw #1 {`#1}
+ }
+ {
+ \prg_new_conditional:Npnn
+ \@@_codepoint_compare:nNn #1#2#3 { TF , p }
+ {
+ \int_compare:nNnTF { \@@_codepoint_from_chars:Nw #1 }
+ #2 {#3}
+ \prg_return_true: \prg_return_false:
+ }
+ \cs_new:Npe \@@_codepoint_from_chars:Nw #1
+ {
+ \exp_not:N \if_int_compare:w `#1 > "80 \exp_not:N \exp_stop_f:
+ \sys_if_engine_pdftex:TF
+ {
+ \exp_not:N \exp_after:wN
+ \exp_not:N \@@_codepoint_from_chars_aux:Nw
+ }
+ {
+ \exp_not:N \if_int_compare:w `#1 > "FF \exp_not:N \exp_stop_f:
+ \exp_not:N \exp_after:wN \exp_not:N \exp_after:wN
+ \exp_not:N \exp_after:wN
+ \exp_not:N \@@_codepoint_from_chars:N
+ \exp_not:N \else:
+ \exp_not:N \exp_after:wN \exp_not:N \exp_after:wN
+ \exp_not:N \exp_after:wN
+ \exp_not:N \@@_codepoint_from_chars_aux:Nw
+ \exp_not:N \fi:
+ }
+ \exp_not:N \else:
+ \exp_not:N \exp_after:wN \exp_not:N \@@_codepoint_from_chars:N
+ \exp_not:N \fi:
+ #1
+ }
+ \cs_new:Npn \@@_codepoint_from_chars_aux:Nw #1
+ {
+ \if_int_compare:w `#1 < "E0 \exp_stop_f:
+ \exp_after:wN \@@_codepoint_from_chars:NN
+ \else:
+ \if_int_compare:w `#1 < "F0 \exp_stop_f:
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \@@_codepoint_from_chars:NNN
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \@@_codepoint_from_chars:NNNN
+ \fi:
+ \fi:
+ #1
+ }
+ \cs_new:Npn \@@_codepoint_from_chars:N #1 {`#1}
+ \cs_new:Npn \@@_codepoint_from_chars:NN #1#2
+ { (`#1 - "C0) * "40 + `#2 - "80 }
+ \cs_new:Npn \@@_codepoint_from_chars:NNN #1#2#3
+ { (`#1 - "E0) * "1000 + (`#2 - "80) * "40 + `#3 - "80 }
+ \cs_new:Npn \@@_codepoint_from_chars:NNNN #1#2#3#4
+ {
+ (`#1 - "F0) * "40000
+ + (`#2 - "80) * "1000
+ + (`#3 - "80) * "40
+ + `#4 - "80
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Configuration variables}
+%
+% \begin{variable}[deprecated]{\l_text_accents_tl, \l_text_letterlike_tl}
+% Used to be used for excluding these ideas from expansion: now deprecated.
+% \begin{macrocode}
+\tl_new:N \l_text_accents_tl
+\tl_new:N \l_text_letterlike_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_text_case_exclude_arg_tl}
+% Non-text arguments, including covering the case of \tn{protected@edef}
+% applied to \cs{cite}.
+% \begin{macrocode}
+\tl_new:N \l_text_case_exclude_arg_tl
+\tl_set:Ne \l_text_case_exclude_arg_tl
+ {
+ \exp_not:n { \begin \cite \end \label \ref }
+ \exp_not:c { cite ~ }
+ \exp_not:n { \babelshorthand }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_text_math_arg_tl}
+% Math mode as arguments.
+% \begin{macrocode}
+\tl_new:N \l_text_math_arg_tl
+\tl_set:Nn \l_text_math_arg_tl { \ensuremath }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_text_math_delims_tl}
+% Paired math mode delimiters.
+% \begin{macrocode}
+\tl_new:N \l_text_math_delims_tl
+\tl_set:Nn \l_text_math_delims_tl { $ $ \( \) }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_text_expand_exclude_tl}
+% Commands which need not to expand. We start with a somewhat historical
+% list, and tidy up if possible.
+% \begin{macrocode}
+\tl_new:N \l_text_expand_exclude_tl
+\tl_set:Nn \l_text_expand_exclude_tl
+ { \begin \cite \end \label \ref }
+\bool_lazy_and:nnT
+ { \str_if_eq_p:Vn \fmtname { LaTeX2e } }
+ { \tl_if_exist_p:N \@expl@finalise@setup@@@@ }
+ {
+ \tl_gput_right:Nn \@expl@finalise@setup@@@@
+ {
+ \tl_gput_right:Nn \@kernel@after@begindocument
+ {
+ \group_begin:
+ \cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \tl_clear:N \l_text_expand_exclude_tl
+ \tl_map_inline:nn {#1}
+ {
+ \bool_lazy_any:nF
+ {
+ { \token_if_protected_macro_p:N ##1 }
+ { \token_if_protected_long_macro_p:N ##1 }
+ {
+ \str_if_eq_p:ee
+ { \cs_replacement_spec:N ##1 }
+ { \exp_not:n { \protect ##1 } \c_space_tl }
+ }
+ }
+ { \tl_put_right:Nn \l_text_expand_exclude_tl {##1} }
+ }
+ }
+ \exp_args:NV \@@_tmp:w \l_text_expand_exclude_tl
+ \exp_args:NNNV \group_end:
+ \tl_set:Nn \l_text_expand_exclude_tl \l_text_expand_exclude_tl
+ }
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\l_@@_math_mode_tl}
+% Used to control math mode output: internal as there is a dedicated
+% setter.
+% \begin{macrocode}
+\tl_new:N \l_@@_math_mode_tl
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Expansion to formatted text}
+%
+% \begin{variable}{\c_@@_chardef_space_token, \c_@@_mathchardef_space_token}
+% \begin{variable}
+% {\c_@@_chardef_group_begin_token, \c_@@_mathchardef_group_begin_token}
+% \begin{variable}
+% {\c_@@_chardef_group_end_token, \c_@@_mathchardef_group_end_token}
+% Markers for implict char handling.
+% \begin{macrocode}
+\tex_global:D \tex_chardef:D \c_@@_chardef_space_token = `\ %
+\tex_global:D \tex_mathchardef:D \c_@@_mathchardef_space_token = `\ %
+\tex_global:D \tex_chardef:D \c_@@_chardef_group_begin_token = `\{ % `\}
+\tex_global:D \tex_mathchardef:D \c_@@_mathchardef_group_begin_token = `\{ % `\} `\{
+\tex_global:D \tex_chardef:D \c_@@_chardef_group_end_token = `\} % `\{
+\tex_global:D \tex_mathchardef:D \c_@@_mathchardef_group_end_token = `\} %
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\text_expand:n, \@@_expand:n}
+% \begin{macro}[EXP]{\@@_expand_result:n}
+% \begin{macro}[EXP]{\@@_expand_store:n, \@@_expand_store:o}
+% \begin{macro}[EXP]{\@@_expand_store:nw}
+% \begin{macro}[EXP]{\@@_expand_end:w}
+% \begin{macro}[EXP]{\@@_expand_loop:w}
+% \begin{macro}[EXP]{\@@_expand_group:n}
+% \begin{macro}[EXP]{\@@_expand_space:w}
+% \begin{macro}[EXP]{\@@_expand_N_type:N}
+% \begin{macro}[EXP]{\@@_expand_math_search:NNN}
+% \begin{macro}[EXP]{\@@_expand_math_loop:Nw}
+% \begin{macro}[EXP]{\@@_expand_math_N_type:NN}
+% \begin{macro}[EXP]{\@@_expand_math_group:Nn}
+% \begin{macro}[EXP]{\@@_expand_math_space:Nw}
+% \begin{macro}[EXP]
+% {
+% \@@_expand_explicit:N ,
+% \@@_expand_exclude:N
+% }
+% \begin{macro}[EXP]{\@@_expand_exclude_switch:Nnnnn}
+% \begin{macro}[EXP]{\@@_expand_exclude:nN}
+% \begin{macro}[EXP]{\@@_expand_exclude:NN}
+% \begin{macro}[EXP]{\@@_expand_exclude:Nw}
+% \begin{macro}[EXP]{\@@_expand_exclude:Nnn}
+% \begin{macro}[EXP]{\@@_expand_accent:N}
+% \begin{macro}[EXP]{\@@_expand_accent:NN}
+% \begin{macro}[EXP]{\@@_expand_letterlike:N}
+% \begin{macro}[EXP]{\@@_expand_letterlike:NN}
+% \begin{macro}[EXP]{\@@_expand_cs:N}
+% \begin{macro}[EXP]{\@@_expand_protect:w}
+% \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_encoding:N, \@@_expand_encoding_escape:N}
+% \begin{macro}[EXP]{\@@_expand_replace:N}
+% \begin{macro}[EXP]{\@@_expand_replace:n}
+% \begin{macro}[EXP]{\@@_expand_cs_expand:N}
+% \begin{macro}[EXP]{\@@_expand_unexpanded:w}
+% \begin{macro}[EXP]{\@@_expand_unexpanded_test:w}
+% \begin{macro}[EXP]{\@@_expand_unexpanded:N}
+% \begin{macro}[EXP]{\@@_expand_unexpanded:n}
+% After precautions against |&| tokens, start a simple loop: that of
+% course means that \enquote{text} cannot contain the two recursion
+% quarks. The loop here must be \texttt{f}-type expandable; we have
+% arbitrary user commands which might be protected \emph{and} take
+% arguments, and if the expansion code is used in a typesetting
+% context, that will otherwise explode. (The same issue applies more
+% clearly to case changing: see the example there.) The outer
+% loop has to use scan marks as delimiters to protect against
+% unterminated \tn{romannumeral} usage in the input.
+% \begin{macrocode}
+\cs_new:Npn \text_expand:n #1
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \exp:w
+ \@@_expand:n {#1}
+ }
+ }
+\cs_new:Npn \@@_expand:n #1
+ {
+ \group_align_safe_begin:
+ \@@_expand_loop:w #1
+ \s_@@_recursion_tail \s_@@_recursion_stop
+ \@@_expand_result:n { }
+ }
+% \end{macrocode}
+% The approach to making the code \texttt{f}-type expandable is to usee
+% a marker result token and to shuffle the collected tokens
+% \begin{macrocode}
+\cs_new:Npn \@@_expand_store:n #1
+ { \@@_expand_store:nw {#1} }
+\cs_generate_variant:Nn \@@_expand_store:n { o }
+\cs_new:Npn \@@_expand_store:nw #1#2 \@@_expand_result:n #3
+ { #2 \@@_expand_result:n { #3 #1 } }
+\cs_new:Npn \@@_expand_end:w #1 \@@_expand_result:n #2
+ {
+ \group_align_safe_end:
+ \exp_end:
+ #2
+ }
+% \end{macrocode}
+% The main loop is a standard \enquote{tl action}; groups are handled
+% recursively, while spaces are just passed through. Thus all of the
+% action is in handling \texttt{N}-type tokens.
+% \begin{macrocode}
+\cs_new:Npn \@@_expand_loop:w #1 \s_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \@@_expand_N_type:N }
+ {
+ \tl_if_head_is_group:nTF {#1}
+ { \@@_expand_group:n }
+ { \@@_expand_space:w }
+ }
+ #1 \s_@@_recursion_stop
+ }
+\cs_new:Npn \@@_expand_group:n #1
+ {
+ \@@_expand_store:o
+ {
+ \exp_after:wN
+ {
+ \exp:w
+ \@@_expand:n {#1}
+ }
+ }
+ \@@_expand_loop:w
+ }
+\exp_last_unbraced:NNo \cs_new:Npn \@@_expand_space:w \c_space_tl
+ {
+ \@@_expand_store:n { ~ }
+ \@@_expand_loop:w
+ }
+% \end{macrocode}
+% The first step in dealing with \texttt{N}-type tokens is to look for
+% math mode material: that needs to be left alone. The starting function
+% has to be split into two as we need \cs{quark_if_recursion_tail_stop:N}
+% first before we can trigger the search. We then look for matching
+% pairs of delimiters, allowing for the case where math mode starts
+% but does not end. Within math mode, we simply pass all the tokens
+% through unchanged, just checking the \texttt{N}-type ones against the
+% end marker.
+% \begin{macrocode}
+\cs_new:Npn \@@_expand_N_type:N #1
+ {
+ \@@_if_s_recursion_tail_stop_do:Nn #1
+ { \@@_expand_end:w }
+ \exp_after:wN \@@_expand_math_search:NNN
+ \exp_after:wN #1 \l_text_math_delims_tl
+ \q_@@_recursion_tail \q_@@_recursion_tail
+ \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_expand_math_search:NNN #1#2#3
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #2
+ { \@@_expand_explicit:N #1 }
+ \token_if_eq_meaning:NNTF #1 #2
+ {
+ \@@_use_i_delimit_by_q_recursion_stop:nw
+ {
+ \@@_expand_store:n {#1}
+ \@@_expand_math_loop:Nw #3
+ }
+ }
+ { \@@_expand_math_search:NNN #1 }
+ }
+\cs_new:Npn \@@_expand_math_loop:Nw #1#2 \s_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#2}
+ { \@@_expand_math_N_type:NN }
+ {
+ \tl_if_head_is_group:nTF {#2}
+ { \@@_expand_math_group:Nn }
+ { \@@_expand_math_space:Nw }
+ }
+ #1#2 \s_@@_recursion_stop
+ }
+\cs_new:Npn \@@_expand_math_N_type:NN #1#2
+ {
+ \@@_if_s_recursion_tail_stop_do:Nn #2
+ { \@@_expand_end:w }
+ \token_if_eq_meaning:NNF #2 \exp_not:N
+ { \@@_expand_store:n {#2} }
+ \token_if_eq_meaning:NNTF #2 #1
+ { \@@_expand_loop:w }
+ { \@@_expand_math_loop:Nw #1 }
+ }
+\cs_new:Npn \@@_expand_math_group:Nn #1#2
+ {
+ \@@_expand_store:n { {#2} }
+ \@@_expand_math_loop:Nw #1
+ }
+\exp_after:wN \cs_new:Npn \exp_after:wN \@@_expand_math_space:Nw
+ \exp_after:wN # \exp_after:wN 1 \c_space_tl
+ {
+ \@@_expand_store:n { ~ }
+ \@@_expand_math_loop:Nw #1
+ }
+% \end{macrocode}
+% At this stage, either we have a control sequence or a simple character:
+% split and handle. The need to check for non-protected actives arises
+% from handling of legacy input encodings: they need to end up in a
+% representation we can deal with in further processing. The tests for
+% explicit parts of the \LaTeXe{} UTF-8 mechanism cover the case of
+% bookmarks, where definitions change and are no longer protected. The
+% same is true for \pkg{babel} shorthands.
+% \begin{macrocode}
+\cs_new:Npn \@@_expand_explicit:N #1
+ {
+ \token_if_cs:NTF #1
+ { \@@_expand_exclude:N #1 }
+ {
+ \bool_lazy_and:nnTF
+ { \token_if_active_p:N #1 }
+ {
+ ! \bool_lazy_any_p:n
+ {
+ { \token_if_protected_macro_p:N #1 }
+ { \token_if_protected_long_macro_p:N #1 }
+ { \tl_if_head_eq_meaning_p:oN {#1} \UTFviii@two@octets }
+ { \tl_if_head_eq_meaning_p:oN {#1} \UTFviii@three@octets }
+ { \tl_if_head_eq_meaning_p:oN {#1} \UTFviii@four@octets }
+ { \tl_if_head_eq_meaning_p:oN {#1} \active@prefix }
+ }
+ }
+ { \exp_after:wN \@@_expand_loop:w #1 }
+ {
+ \@@_expand_store:n {#1}
+ \@@_expand_loop:w
+ }
+ }
+ }
+% \end{macrocode}
+% Next we exclude math commands: this is mainly as there \emph{might} be an
+% \cs{ensuremath}. The switching command for case needs special handling
+% as it has to work by meaning.
+% \begin{macrocode}
+\cs_new:Npn \@@_expand_exclude:N #1
+ {
+ \cs_if_eq:NNTF #1 \text_case_switch:nnnn
+ { \@@_expand_exclude_switch:Nnnnn #1 }
+ {
+ \exp_args:Ne \@@_expand_exclude:nN
+ {
+ \exp_not:V \l_text_math_arg_tl
+ \exp_not:V \l_text_expand_exclude_tl
+ \exp_not:V \l_text_case_exclude_arg_tl
+ }
+ #1
+ }
+ }
+\cs_new:Npn \@@_expand_exclude_switch:Nnnnn #1#2#3#4#5
+ {
+ \@@_expand_store:n { #1 {#2} {#3} {#4} {#5} }
+ \@@_expand_loop:w
+ }
+\cs_new:Npn \@@_expand_exclude:nN #1#2
+ {
+ \@@_expand_exclude:NN #2 #1
+ \q_@@_recursion_tail \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_expand_exclude:NN #1#2
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #2
+ { \@@_expand_accent:N #1 }
+ \str_if_eq:nnTF {#1} {#2}
+ {
+ \@@_use_i_delimit_by_q_recursion_stop:nw
+ { \@@_expand_exclude:Nw #1 }
+ }
+ { \@@_expand_exclude:NN #1 }
+ }
+\cs_new:Npn \@@_expand_exclude:Nw #1#2#
+ { \@@_expand_exclude:Nnn #1 {#2} }
+\cs_new:Npn \@@_expand_exclude:Nnn #1#2#3
+ {
+ \@@_expand_store:n { #1#2 {#3} }
+ \@@_expand_loop:w
+ }
+% \end{macrocode}
+% Accents.
+% \begin{macrocode}
+\cs_new:Npn \@@_expand_accent:N #1
+ {
+ \exp_after:wN \@@_expand_accent:NN \exp_after:wN
+ #1 \l_text_accents_tl
+ \q_@@_recursion_tail \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_expand_accent:NN #1#2
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #2
+ { \@@_expand_letterlike:N #1 }
+ \cs_if_eq:NNTF #2 #1
+ {
+ \@@_use_i_delimit_by_q_recursion_stop:nw
+ {
+ \@@_expand_store:n {#1}
+ \@@_expand_loop:w
+ }
+ }
+ { \@@_expand_accent:NN #1 }
+ }
+% \end{macrocode}
+% Another list of exceptions: these ones take no arguments so are
+% easier to handle.
+% \begin{macrocode}
+\cs_new:Npn \@@_expand_letterlike:N #1
+ {
+ \exp_after:wN \@@_expand_letterlike:NN \exp_after:wN
+ #1 \l_text_letterlike_tl
+ \q_@@_recursion_tail \q_@@_recursion_stop
+ }
+\cs_new:Npn \@@_expand_letterlike:NN #1#2
+ {
+ \@@_if_q_recursion_tail_stop_do:Nn #2
+ { \@@_expand_cs:N #1 }
+ \cs_if_eq:NNTF #2 #1
+ {
+ \@@_use_i_delimit_by_q_recursion_stop:nw
+ {
+ \@@_expand_store:n {#1}
+ \@@_expand_loop:w
+ }
+ }
+ { \@@_expand_letterlike:NN #1 }
+ }
+% \end{macrocode}
+% \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.
+% That includes the case where it's not even followed by an \texttt{N}-type
+% token. There is also the case of a straight \tn{@protected@testopt} to
+% cover.
+% \begin{macrocode}
+\cs_new:Npe \@@_expand_cs:N #1
+ {
+ \exp_not:N \str_if_eq:nnTF {#1} { \exp_not:N \protect }
+ { \exp_not:N \@@_expand_protect:w }
+ {
+ \bool_lazy_and:nnTF
+ { \cs_if_exist_p:N \fmtname }
+ { \str_if_eq_p:Vn \fmtname { LaTeX2e } }
+ { \exp_not:N \@@_expand_testopt:N #1 }
+ { \exp_not:N \@@_expand_replace:N #1 }
+ }
+ }
+\cs_new:Npn \@@_expand_protect:w #1 \s_@@_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \@@_expand_protect:N }
+ {
+ \@@_expand_store:n { \protect }
+ \@@_expand_loop:w
+ }
+ #1 \s_@@_recursion_stop
+ }
+\cs_new:Npn \@@_expand_protect:N #1
+ {
+ \@@_if_s_recursion_tail_stop_do:Nn #1
+ {
+ \@@_expand_store:n { \protect }
+ \@@_expand_end:w
+ }
+ \exp_args:Ne \@@_expand_protect:nN
+ { \cs_to_str:N #1 } #1
+ }
+\cs_new:Npn \@@_expand_protect:nN #1#2
+ { \@@_expand_protect:Nw #2 #1 \q_@@_nil #1 ~ \q_@@_nil \q_@@_nil \s_@@_stop }
+\cs_new:Npn \@@_expand_protect:Nw #1 #2 ~ \q_@@_nil #3 \q_@@_nil #4 \s_@@_stop
+ {
+ \@@_quark_if_nil:nTF {#4}
+ {
+ \cs_if_exist:cTF {#2}
+ { \exp_args:Ne \@@_expand_store:n { \exp_not:c {#2} } }
+ { \@@_expand_store:n { \protect #1 } }
+ }
+ { \@@_expand_store:n { \protect #1 } }
+ \@@_expand_loop:w
+ }
+\cs_new:Npn \@@_expand_testopt:N #1
+ {
+ \token_if_eq_meaning: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}
+\cs_new:Npn \@@_expand_encoding:N #1
+ {
+ \bool_lazy_or:nnTF
+ { \cs_if_eq_p:NN #1 \@current@cmd }
+ { \cs_if_eq_p:NN #1 \@changed@cmd }
+ { \exp_after:wN \@@_expand_loop:w \@@_expand_encoding_escape:NN }
+ { \@@_expand_replace:N #1 }
+ }
+\cs_new:Npn \@@_expand_encoding_escape:NN #1#2 { \exp_not:n {#1} }
+% \end{macrocode}
+% See if there is a dedicated replacement, and if there is, insert it.
+% \begin{macrocode}
+\cs_new:Npn \@@_expand_replace:N #1
+ {
+ \bool_lazy_and:nnTF
+ { \cs_if_exist_p:c { l_@@_expand_ \token_to_str:N #1 _tl } }
+ {
+ \bool_lazy_or_p:nn
+ { \token_if_cs_p:N #1 }
+ { \token_if_active_p:N #1 }
+ }
+ {
+ \exp_args:Nv \@@_expand_replace:n
+ { l_@@_expand_ \token_to_str:N #1 _tl }
+ }
+ { \@@_expand_cs_expand:N #1 }
+ }
+\cs_new:Npn \@@_expand_replace:n #1 { \@@_expand_loop:w #1 }
+% \end{macrocode}
+% Finally, expand any macros which can be: this then loops back around to
+% deal with what they produce. The only issue is if the token is
+% \cs{exp_not:n}, as that must apply to the following balanced text.
+% \begin{macrocode}
+\cs_new:Npn \@@_expand_cs_expand:N #1
+ {
+ \@@_if_expandable:NTF #1
+ {
+ \token_if_eq_meaning:NNTF #1 \exp_not:n
+ { \@@_expand_unexpanded:w }
+ { \exp_after:wN \@@_expand_loop:w #1 }
+ }
+ {
+ \@@_expand_store:n {#1}
+ \@@_expand_loop:w
+ }
+ }
+% \end{macrocode}
+% Since \cs{exp_not:n} is actually a primitive, it allows a strange syntax
+% and it particular the primitive expands what follows and discards spaces
+% and \cs{scan_stop:} until finding a braced argument (the opening brace
+% can be implicit but we will not support this here). Here, we repeatedly
+% |f|-expand after such an \cs{exp_not:n}, and test what follows. If
+% it is a brace group, then we found the intended argument of
+% \cs{exp_not:n}. If it is a space, then the next |f|-expansion will
+% eliminate it. If it is an |N|-type token then
+% \cs{@@_expand_unexpanded:N} leaves the token to be expanded if it is
+% expandable, and otherwise removes it, assuming that it is
+% \cs{scan_stop:}. This silently hides errors when \cs{exp_not:n} is
+% incorrectly followed by some non-expandable token other than
+% \cs{scan_stop:}, but this should be pretty rare, and there is no good
+% error recovery anyways.
+% \begin{macrocode}
+\cs_new:Npn \@@_expand_unexpanded:w
+ {
+ \exp_after:wN \@@_expand_unexpanded_test:w
+ \exp:w \exp_end_continue_f:w
+ }
+\cs_new:Npn \@@_expand_unexpanded_test:w #1 \s_@@_recursion_stop
+ {
+ \tl_if_head_is_group:nTF {#1}
+ { \@@_expand_unexpanded:n }
+ {
+ \@@_expand_unexpanded:w
+ \tl_if_head_is_N_type:nT {#1} { \@@_expand_unexpanded:N }
+ }
+ #1 \s_@@_recursion_stop
+ }
+\cs_new:Npn \@@_expand_unexpanded:N #1
+ {
+ \exp_after:wN \if_meaning:w \exp_not:N #1 #1
+ \else:
+ \exp_after:wN #1
+ \fi:
+ }
+\cs_new:Npn \@@_expand_unexpanded:n #1
+ {
+ \@@_expand_store:n {#1}
+ \@@_expand_loop:w
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \text_declare_expand_equivalent:Nn ,
+% \text_declare_expand_equivalent:cn
+% }
+% Create equivalents to allow replacement.
+% \begin{macrocode}
+\cs_new_protected:Npn \text_declare_expand_equivalent:Nn #1#2
+ {
+ \tl_clear_new:c { l_@@_expand_ \token_to_str:N #1 _tl }
+ \tl_set:cn { l_@@_expand_ \token_to_str:N #1 _tl } {#2}
+ }
+\cs_generate_variant:Nn \text_declare_expand_equivalent:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% Prevent expansion of various standard values.
+% \begin{macrocode}
+\tl_map_inline:nn
+ { \` \' \^ \~ \= \u \. \" \r \H \v \d \c \k \b \t }
+ { \text_declare_expand_equivalent:Nn #1 { \exp_not:n {#1} } }
+\tl_map_inline:nn
+ {
+ \AA \aa
+ \AE \ae
+ \DH \dh
+ \DJ \dj
+ \IJ \ij
+ \L \l
+ \NG \ng
+ \O \o
+ \OE \oe
+ \SS \ss
+ \TH \th
+ }
+ { \text_declare_expand_equivalent:Nn #1 { \exp_not:n {#1} } }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3tl-analysis.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3tl-analysis.dtx
new file mode 100644
index 00000000000..a4141dcfe4a
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3tl-analysis.dtx
@@ -0,0 +1,1660 @@
+% \iffalse meta-comment
+%
+%% File: l3tl-analysis.dtx
+%
+% Copyright (C) 2011-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+%
+% \title{^^A
+% The \pkg{l3tl-analysis} module\\ Analysing token lists^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% 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{e}/\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[no-index]{ShowTokens} macro from the \pkg{ted} package.
+%
+% \begin{function}[added = 2021-05-11]
+% {
+% \tl_analysis_show:N, \tl_analysis_show:n,
+% \tl_analysis_log:N, \tl_analysis_log:n
+% }
+% \begin{syntax}
+% \cs{tl_analysis_show:n} \Arg{token list}
+% \cs{tl_analysis_log:n} \Arg{token list}
+% \end{syntax}
+% Displays to the terminal (or log) the detailed decomposition of the
+% \meta{token list} into tokens, showing the category code of each
+% character token, the meaning of control sequences and active
+% characters, and the value of registers.
+% \end{function}
+%
+% \begin{function}[added = 2018-04-09, updated = 2022-03-26]
+% {\tl_analysis_map_inline:nn, \tl_analysis_map_inline:Nn}
+% \begin{syntax}
+% \cs{tl_analysis_map_inline:nn} \Arg{token list} \Arg{inline function}
+% \end{syntax}
+% Applies the \meta{inline function} to each individual \meta{token}
+% in the \meta{token list}. The \meta{inline function} receives three
+% 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}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3tl-analysis} implementation}
+%
+% \begin{macrocode}
+%<@@=tl>
+% \end{macrocode}
+%
+% \subsection{Internal functions}
+%
+% \begin{variable}{\s_@@}
+% The format used to store token lists internally uses the scan mark
+% \cs{s_@@} as a delimiter.
+% \end{variable}
+%
+% \subsection{Internal format}
+%
+% The task of the \pkg{l3tl-analysis} module is to convert token lists
+% to an internal format which allows us to extract all the relevant
+% information about individual tokens (category code, character code),
+% as well as reconstruct the token list quickly. This internal format is
+% used in \pkg{l3regex} where we need to support arbitrary tokens, and
+% it is used in conversion functions in \pkg{l3str-convert}, where we wish to
+% support clusters of characters instead of single tokens.
+%
+% We thus need a way to encode any \meta{token} (even begin-group and
+% end-group character tokens) in a way amenable to manipulating tokens
+% individually. The best we can do is to find \meta{tokens} which both
+% \texttt{o}-expand and \texttt{e}/\texttt{x}-expand to the given
+% \meta{token}. Collecting more information about the category code and
+% character code is also useful for regular expressions, since most
+% regexes are catcode-agnostic. The internal format thus takes the form
+% of a succession of items of the form
+% \begin{quote}
+% \meta{tokens} \cs{s_@@} \meta{catcode} \meta{char code} \cs{s_@@}
+% \end{quote}
+% The \meta{tokens} \texttt{o}- \emph{and} \texttt{e}/\texttt{x}-expand to the
+% original token in the token list or to the cluster of tokens
+% corresponding to one Unicode character in the given encoding (for
+% \pkg{l3str-convert}). The \meta{catcode} is given as a single hexadecimal
+% digit, $0$ for control sequences. The \meta{char code} is given as a
+% decimal number, $-1$ for control sequences.
+%
+% Using delimited arguments lets us build the \meta{tokens}
+% progressively when doing an encoding conversion in \pkg{l3str-convert}. On the
+% other hand, the delimiter \cs{s_@@} may not appear unbraced in
+% \meta{tokens}. This is not a problem because we are careful to wrap
+% control sequences in braces (as an argument to \cs{exp_not:n}) when
+% converting from a general token list to the internal format.
+%
+% The current rule for converting a \meta{token} to a balanced set of
+% \meta{tokens} which both \texttt{o}-expands and \texttt{e}/\texttt{x}-expands to
+% it is the following.
+% \begin{itemize}
+% \item A control sequence |\cs| becomes |\exp_not:n { \cs }|
+% \cs{s_@@} $0$ $-1$ \cs{s_@@}.
+% \item A begin-group character |{| becomes \cs{exp_after:wN} |{|
+% \cs{if_false:} |}| \cs{fi:} \cs{s_@@} $1$ \meta{char code}
+% \cs{s_@@}.
+% \item An end-group character |}| becomes \cs{if_false:} |{| \cs{fi:}
+% |}| \cs{s_@@} $2$ \meta{char code} \cs{s_@@}.
+% \item A character with any other category code becomes
+% \cs{exp_not:n} \Arg{character} \cs{s_@@} \meta{hex catcode}
+% \meta{char code} \cs{s_@@}.
+% \end{itemize}
+% In contrast, for \cs{peek_analysis_map_inline:n} we must allow for an
+% input stream containing \tn{outer} macros, so that wrapping all
+% control sequences in \cs{exp_not:n} is unsafe. Instead, we write the
+% more elaborate \cs{__kernel_exp_not:w} \cs{exp_after:wN} |{|
+% \cs{exp_not:N} |\cs| |}|. (On the other hand we make a better effort
+% by avoiding \cs{exp_not:n} for characters other than active and macro
+% parameters.)
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \subsection{Variables and helper functions}
+%
+% \begin{variable}{\s_@@}
+% The scan mark \cs{s_@@} is used as a delimiter in the internal
+% format. This is more practical than using a quark, because we would
+% then need to control expansion much more carefully: compare
+% \cs{int_value:w} |`#1| \cs{s_@@} with \cs{int_value:w} |`#1|
+% \cs{exp_stop_f:} \cs{exp_not:N} \cs{q_mark} to extract a character
+% code followed by the delimiter in an \texttt{e}-expansion.
+% \begin{macrocode}
+\scan_new:N \s_@@
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {\l_@@_analysis_token, \l_@@_analysis_char_token}
+% The tokens in the token list are probed with the \TeX{} primitive
+% \tn{futurelet}. We use \cs{l_@@_analysis_token} in that
+% construction. In some cases, we convert the following token to a
+% string before probing it: then the token variable used is
+% \cs{l_@@_analysis_char_token}.
+% \begin{macrocode}
+\cs_new_eq:NN \l_@@_analysis_token ?
+\cs_new_eq:NN \l_@@_analysis_char_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, so
+% that we decided to make \cs{char_generate:nn} refuse to create such
+% weird spaces as well. We do not include the macro parameter case
+% (catcode~$6$) because it cannot be used as a macro delimiter.
+% \begin{macrocode}
+\group_begin:
+\char_set_active_eq:NN \ \scan_stop:
+\tl_const:Ne \c_@@_peek_catcodes_tl
+ {
+ \char_generate:nn { 32 } { 3 } 3
+ \char_generate:nn { 32 } { 4 } 4
+ \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}
+% The number of normal (\texttt{N}-type argument) tokens since the
+% last special token.
+% \begin{macrocode}
+\int_new:N \l_@@_analysis_normal_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_analysis_index_int}
+% During the first pass, this is the index in the array being built.
+% During the second pass, it is equal to the maximum index in the
+% array from the first pass.
+% \begin{macrocode}
+\int_new:N \l_@@_analysis_index_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_analysis_nesting_int}
+% Nesting depth of explicit begin-group and end-group characters
+% during the first pass. This lets us detect the end of the token list
+% without a reserved end-marker.
+% \begin{macrocode}
+\int_new:N \l_@@_analysis_nesting_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_analysis_type_int}
+% When encountering special characters, we record their \enquote{type}
+% in this integer.
+% \begin{macrocode}
+\int_new:N \l_@@_analysis_type_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_analysis_result_tl}
+% The result of the conversion is stored in this token list, with a
+% succession of items of the form
+% \begin{quote}
+% \meta{tokens} \cs{s_@@} \meta{catcode} \meta{char code} \cs{s_@@}
+% \end{quote}
+% \begin{macrocode}
+\tl_new:N \g_@@_analysis_result_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_analysis_extract_charcode:}
+% \begin{macro}[EXP]{\@@_analysis_extract_charcode_aux:w}
+% Extracting the character code from the meaning of
+% \cs{l_@@_analysis_token}. This has no error checking, and should
+% only be assumed to work for begin-group and end-group character
+% tokens. It produces a number in the form |`|\meta{char}.
+% \begin{macrocode}
+\cs_new:Npn \@@_analysis_extract_charcode:
+ {
+ \exp_after:wN \@@_analysis_extract_charcode_aux:w
+ \token_to_meaning:N \l_@@_analysis_token
+ }
+\cs_new:Npn \@@_analysis_extract_charcode_aux:w #1 ~ #2 ~ { ` }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_analysis_cs_space_count:NN}
+% \begin{macro}[EXP]{\@@_analysis_cs_space_count:w}
+% \begin{macro}[EXP]{\@@_analysis_cs_space_count_end:w}
+% Counts the number of spaces in the string representation of its
+% second argument, as well as the number of characters following the
+% last space in that representation, and feeds the two numbers as
+% semicolon-delimited arguments to the first argument. When this
+% function is used, the escape character is printable and non-space.
+% \begin{macrocode}
+\cs_new:Npn \@@_analysis_cs_space_count:NN #1 #2
+ {
+ \exp_after:wN #1
+ \int_value:w \int_eval:w 0
+ \exp_after:wN \@@_analysis_cs_space_count:w
+ \token_to_str:N #2
+ \fi: \@@_analysis_cs_space_count_end:w ; ~ !
+ }
+\cs_new:Npn \@@_analysis_cs_space_count:w #1 ~
+ {
+ \if_false: #1 #1 \fi:
+ + 1
+ \@@_analysis_cs_space_count:w
+ }
+\cs_new:Npn \@@_analysis_cs_space_count_end:w ; #1 \fi: #2 !
+ { \exp_after:wN ; \int_value:w \str_count_ignore_spaces:n {#1} ; }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Plan of attack}
+%
+% Our goal is to produce a token list of the form roughly
+% \begin{quote}
+% \meta{token 1} \cs{s_@@} \meta{catcode 1} \meta{char code 1} \cs{s_@@} \\
+% \meta{token 2} \cs{s_@@} \meta{catcode 2} \meta{char code 2} \cs{s_@@} \\
+% \ldots{}
+% \meta{token N} \cs{s_@@} \meta{catcode N} \meta{char code N} \cs{s_@@}
+% \end{quote}
+% Most but not all tokens can be grabbed as an undelimited
+% (\texttt{N}-type) argument by \TeX{}. The plan is to have a two pass
+% system. In the first pass, locate special tokens, and store them in
+% various \tn{toks} registers. In the second pass, which is done within
+% an \texttt{e}-expanding assignment, normal tokens are taken in as
+% \texttt{N}-type arguments, and special tokens are retrieved from the
+% \tn{toks} registers, and removed from the input stream by some means.
+% The whole process takes linear time, because we avoid building the
+% result one item at a time.
+%
+% We make the escape character printable (backslash, but this later
+% oscillates between slash and backslash): this allows us to
+% distinguish characters from control sequences.
+%
+% A token has two characteristics: its \tn{meaning}, and what it looks
+% like for \TeX{} when it is in scanning mode (\emph{e.g.}, when
+% capturing parameters for a macro). For our purposes, we distinguish
+% the following meanings:
+% \begin{itemize}
+% \item begin-group token (category code $1$), either space (character
+% code $32$), or non-space;
+% \item end-group token (category code $2$), either space (character
+% code $32$), or non-space;
+% \item space token (category code $10$, character code $32$);
+% \item anything else (then the token is always an \texttt{N}-type
+% argument).
+% \end{itemize}
+% The token itself can \enquote{look like} one of the following
+% \begin{itemize}
+% \item a non-active character, in which case its meaning is
+% automatically that associated to its character code and category
+% code, we call it \enquote{true} character;
+% \item an active character;
+% \item a control sequence.
+% \end{itemize}
+% The only tokens which are not valid \texttt{N}-type arguments are true
+% begin-group characters, true end-group characters, and true spaces.
+% We detect those characters by scanning ahead with \tn{futurelet},
+% then distinguishing true characters from control sequences set equal
+% to them using the \tn{string} representation.
+%
+% The second pass is a simple exercise in expandable loops.
+%
+% \begin{macro}{\@@_analysis:n}
+% Everything is done within a group, and all definitions are
+% local. We use \cs{group_align_safe_begin/end:} to avoid problems in
+% case \cs{@@_analysis:n} is used within an alignment and its argument
+% contains alignment tab tokens.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_analysis:n #1
+ {
+ \group_begin:
+ \group_align_safe_begin:
+ \@@_analysis_a:n {#1}
+ \@@_analysis_b:n {#1}
+ \group_align_safe_end:
+ \group_end:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Disabling active characters}
+%
+% \begin{macro}{\@@_analysis_disable:n}
+% Active characters can cause problems later on in the processing, so
+% we provide a way to disable them, by setting them to
+% \texttt{undefined}. Since Unicode contains too many characters to
+% loop over all of them, we instead do this whenever we encounter a
+% character. For \pTeX{} and \upTeX{} we skip characters beyond
+% $[0,255]$ because \tn{lccode} only allows those values.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_active:N \^^@
+ \cs_new_protected:Npn \@@_analysis_disable:n #1
+ {
+ \tex_lccode:D 0 = #1 \exp_stop_f:
+ \tex_lowercase:D { \tex_let:D ^^@ } \tex_undefined:D
+ }
+ \bool_lazy_or:nnT
+ { \sys_if_engine_ptex_p: }
+ { \sys_if_engine_uptex_p: }
+ {
+ \cs_gset_protected:Npn \@@_analysis_disable:n #1
+ {
+ \if_int_compare:w 256 > #1 \exp_stop_f:
+ \tex_lccode:D 0 = #1 \exp_stop_f:
+ \tex_lowercase:D { \tex_let:D ^^@ } \tex_undefined:D
+ \fi:
+ }
+ }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_analysis_disable_char:N}
+% Similar to \cs{@@_analysis_disable:n}, but it receives a normal
+% character token, tests if that token is active (by turning it into
+% a space: the active space has been undefined at this point), and
+% if so, disables it. Even if the character is active and set equal
+% to a primitive conditional, nothing blows up.
+% Again, in \pTeX{} and \upTeX{} we skip characters beyond $[0,255]$,
+% which cannot be active anyways.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_active:N \^^@
+ \cs_new_protected:Npn \@@_analysis_disable_char:N #1
+ {
+ \tex_lccode:D `#1 = 32 \exp_stop_f:
+ \tex_lowercase:D { \if_meaning:w #1 } \tex_undefined:D
+ \tex_let:D #1 \tex_undefined:D
+ \fi:
+ }
+ \bool_lazy_or:nnT
+ { \sys_if_engine_ptex_p: }
+ { \sys_if_engine_uptex_p: }
+ {
+ \cs_gset_protected:Npn \@@_analysis_disable_char:N #1
+ {
+ \if_int_compare:w 256 > `#1 \exp_stop_f:
+ \tex_lccode:D `#1 = 32 \exp_stop_f:
+ \tex_lowercase:D { \if_meaning:w #1 } \tex_undefined:D
+ \tex_let:D #1 \tex_undefined:D
+ \fi:
+ \fi:
+ }
+ }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{First pass}
+%
+% The goal of this pass is to detect special (non-\texttt{N}-type) tokens,
+% and count how many \texttt{N}-type tokens lie between special tokens.
+% Also, we wish to store some representation of each special token
+% in a \tn{toks} register.
+%
+% We have $11$ types of tokens:
+% \begin{itemize}
+% \item[1.] a true non-space begin-group character;
+% \item[2.] a true space begin-group character;
+% \item[3.] a true non-space end-group character;
+% \item[4.] a true space end-group character;
+% \item[5.] a true space blank space character;
+% \item[6.] an active character;
+% \item[7.] any other true character;
+% \item[8.] a control sequence equal to a begin-group token (category code $1$);
+% \item[9.] a control sequence equal to an end-group token (category code $2$);
+% \item[10.] a control sequence equal to a space token
+% (character code $32$, category code $10$);
+% \item[11.] any other control sequence.
+% \end{itemize}
+% Our first tool is \tn{futurelet}. This cannot distinguish
+% case $8$ from $1$ or $2$, nor case $9$ from $3$ or $4$,
+% nor case $10$ from case $5$. Those cases are later distinguished
+% by applying the \tn{string} primitive to the following token,
+% after possibly changing the escape character to ensure that
+% a control sequence's string representation cannot be mistaken
+% for the true character.
+%
+% In cases $6$, $7$, and $11$, the following token is a valid
+% \texttt{N}-type argument, so we grab it and distinguish the case
+% of a character from a control sequence: in the latter case,
+% \cs{str_tail:n} \Arg{token} is non-empty, because the
+% escape character is printable.
+%
+% \begin{macro}{\@@_analysis_a:n}
+% We read tokens one by one using \tn{futurelet}.
+% While performing the loop, we keep track of the number of
+% true begin-group characters minus the number of
+% true end-group characters in \cs{l_@@_analysis_nesting_int}.
+% This reaches $-1$ when we read the closing brace.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_analysis_a:n #1
+ {
+ \@@_analysis_disable:n { 32 }
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \int_zero:N \l_@@_analysis_normal_int
+ \int_zero:N \l_@@_analysis_index_int
+ \int_zero:N \l_@@_analysis_nesting_int
+ \if_false: { \fi: \@@_analysis_a_loop:w #1 }
+ \int_decr:N \l_@@_analysis_index_int
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_analysis_a_loop:w}
+% Read one character and check its type.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_analysis_a_loop:w
+ { \tex_futurelet:D \l_@@_analysis_token \@@_analysis_a_type:w }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_analysis_a_type:w}
+% At this point, \cs{l_@@_analysis_token} holds the meaning
+% of the following token. We store in \cs{l_@@_analysis_type_int}
+% information about the meaning of the token ahead:
+% \begin{itemize}
+% \item 0 space token;
+% \item 1 begin-group token;
+% \item -1 end-group token;
+% \item 2 other.
+% \end{itemize}
+% The values $0$, $1$, $-1$ correspond to how much a true such
+% character changes the nesting level ($2$ is used only here,
+% and is irrelevant later). Then call the auxiliary for each case.
+% Note that nesting conditionals here is safe because we only skip
+% over \cs{l_@@_analysis_token} if it matches with one of the
+% character tokens (hence is not a primitive conditional).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_analysis_a_type:w
+ {
+ \l_@@_analysis_type_int =
+ \if_meaning:w \l_@@_analysis_token \c_space_token
+ 0
+ \else:
+ \if_catcode:w \exp_not:N \l_@@_analysis_token \c_group_begin_token
+ 1
+ \else:
+ \if_catcode:w \exp_not:N \l_@@_analysis_token \c_group_end_token
+ - 1
+ \else:
+ 2
+ \fi:
+ \fi:
+ \fi:
+ \exp_stop_f:
+ \if_case:w \l_@@_analysis_type_int
+ \exp_after:wN \@@_analysis_a_space:w
+ \or: \exp_after:wN \@@_analysis_a_bgroup:w
+ \or: \exp_after:wN \@@_analysis_a_safe:N
+ \else: \exp_after:wN \@@_analysis_a_egroup:w
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_analysis_a_space:w}
+% \begin{macro}{\@@_analysis_a_space_test:w}
+% In this branch, the following token's meaning is a blank space.
+% Apply \tn{string} to that token: a true blank space gives a space, a
+% control sequence gives a result starting with the escape character,
+% an active character gives something else than a space since we
+% disabled the space. We grab as \cs{l_@@_analysis_char_token} the first
+% character of the string representation then test it in
+% \cs{@@_analysis_a_space_test:w}.
+% Also, since \cs{@@_analysis_a_store:} expects the special token to be
+% stored in the relevant \tn{toks} register, we do that. The extra
+% \cs{exp_not:n} is unnecessary of course, but it makes the treatment
+% of all tokens more homogeneous.
+% If we discover that the next token was actually a control sequence
+% or an active character
+% instead of a true space, then we step the counter of normal tokens.
+% We now have in front of us the whole string representation of
+% the control sequence, including potential spaces; those will appear
+% to be true spaces later in this pass. Hence, all other branches of
+% the code in this first pass need to consider the string representation,
+% so that the second pass does not need to test the meaning of tokens,
+% only strings.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_analysis_a_space:w
+ {
+ \tex_afterassignment:D \@@_analysis_a_space_test:w
+ \exp_after:wN \cs_set_eq:NN
+ \exp_after:wN \l_@@_analysis_char_token
+ \token_to_str:N
+ }
+\cs_new_protected:Npn \@@_analysis_a_space_test:w
+ {
+ \if_meaning:w \l_@@_analysis_char_token \c_space_token
+ \tex_toks:D \l_@@_analysis_index_int { \exp_not:n { ~ } }
+ \@@_analysis_a_store:
+ \else:
+ \int_incr:N \l_@@_analysis_normal_int
+ \fi:
+ \@@_analysis_a_loop:w
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_analysis_a_bgroup:w, \@@_analysis_a_egroup:w}
+% \begin{macro}
+% {\@@_analysis_a_group:nw, \@@_analysis_a_group_aux:w, \@@_analysis_a_group_auxii:w, \@@_analysis_a_group_test:w}
+% The token is most likely a true character token with catcode $1$ or
+% $2$, but it might be a control sequence, or an active character.
+% Optimizing for the first case, we store in a toks register some code
+% that expands to that token. Since we will turn what follows into
+% a string, we make sure the escape character is different from the
+% current character code (by switching between solidus and backslash).
+% To detect the special case of an active character let to the catcode
+% $1$ or~$2$ character with the same character code, we disable the
+% active character with that character code and re-test: if the
+% following token has become undefined we can in fact safely grab it.
+% We are finally ready to turn what follows to a string and test it.
+% This is one place where we need \cs{l_@@_analysis_char_token} to be a
+% separate control sequence from \cs{l_@@_analysis_token}, to compare them.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_group_begin:N \^^@ % {
+ \cs_new_protected:Npn \@@_analysis_a_bgroup:w
+ { \@@_analysis_a_group:nw { \exp_after:wN ^^@ \if_false: } \fi: } }
+ \char_set_catcode_group_end:N \^^@
+ \cs_new_protected:Npn \@@_analysis_a_egroup:w
+ { \@@_analysis_a_group:nw { \if_false: { \fi: ^^@ } } % }
+\group_end:
+\cs_new_protected:Npn \@@_analysis_a_group:nw #1
+ {
+ \tex_lccode:D 0 = \@@_analysis_extract_charcode: \scan_stop:
+ \tex_lowercase:D { \tex_toks:D \l_@@_analysis_index_int {#1} }
+ \if_int_compare:w \tex_lccode:D 0 = \tex_escapechar:D
+ \int_set:Nn \tex_escapechar:D { 139 - \tex_escapechar:D }
+ \fi:
+ \@@_analysis_disable:n { \tex_lccode:D 0 }
+ \tex_futurelet:D \l_@@_analysis_token \@@_analysis_a_group_aux:w
+ }
+\cs_new_protected:Npn \@@_analysis_a_group_aux:w
+ {
+ \if_meaning:w \l_@@_analysis_token \tex_undefined:D
+ \exp_after:wN \@@_analysis_a_safe:N
+ \else:
+ \exp_after:wN \@@_analysis_a_group_auxii:w
+ \fi:
+ }
+\cs_new_protected:Npn \@@_analysis_a_group_auxii:w
+ {
+ \tex_afterassignment:D \@@_analysis_a_group_test:w
+ \exp_after:wN \cs_set_eq:NN
+ \exp_after:wN \l_@@_analysis_char_token
+ \token_to_str:N
+ }
+\cs_new_protected:Npn \@@_analysis_a_group_test:w
+ {
+ \if_charcode:w \l_@@_analysis_token \l_@@_analysis_char_token
+ \@@_analysis_a_store:
+ \else:
+ \int_incr:N \l_@@_analysis_normal_int
+ \fi:
+ \@@_analysis_a_loop:w
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_analysis_a_store:}
+% This function is called each time we meet a special token;
+% at this point, the \tn{toks} register \cs{l_@@_analysis_index_int}
+% holds a token list which expands to the given special token.
+% Also, the value of \cs{l_@@_analysis_type_int} indicates which case
+% we are in:
+% \begin{itemize}
+% \item -1 end-group character;
+% \item 0 space character;
+% \item 1 begin-group character.
+% \end{itemize}
+% We need to distinguish further the case of a space character
+% (code $32$) from other character codes, because those
+% behave differently in the second pass. Namely, after testing
+% the \tn{lccode} of $0$ (which holds the present character code)
+% we change the cases above to
+% \begin{itemize}
+% \item -2 space end-group character;
+% \item -1 non-space end-group character;
+% \item 0 space blank space character;
+% \item 1 non-space begin-group character;
+% \item 2 space begin-group character.
+% \end{itemize}
+% This has the property that non-space characters correspond to odd
+% values of \cs{l_@@_analysis_type_int}. The number of normal tokens until
+% here and the type of special token are packed into a \tn{skip}
+% register. Finally, we check whether we reached the last closing
+% brace, in which case we stop by disabling the looping function
+% (locally).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_analysis_a_store:
+ {
+ \tex_advance:D \l_@@_analysis_nesting_int \l_@@_analysis_type_int
+ \if_int_compare:w \tex_lccode:D 0 = `\ \exp_stop_f:
+ \tex_advance:D \l_@@_analysis_type_int \l_@@_analysis_type_int
+ \fi:
+ \tex_skip:D \l_@@_analysis_index_int
+ = \l_@@_analysis_normal_int sp
+ plus \l_@@_analysis_type_int sp \scan_stop:
+ \int_incr:N \l_@@_analysis_index_int
+ \int_zero:N \l_@@_analysis_normal_int
+ \if_int_compare:w \l_@@_analysis_nesting_int = - \c_one_int
+ \cs_set_eq:NN \@@_analysis_a_loop:w \scan_stop:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_analysis_a_safe:N}
+% \begin{macro}{\@@_analysis_a_cs:ww}
+% This should be the simplest case: since the upcoming token is safe,
+% we can simply grab it in a second pass. If the token is a single
+% character (including space), the \cs{if_charcode:w} test yields
+% true; we disable a potentially active character (that could
+% otherwise masquerade as the true character in the next pass) and we
+% count one \enquote{normal} token. On the other
+% hand, if the token is a control sequence, we should replace it by
+% its string representation for compatibility with other code
+% branches. Instead of slowly looping through the characters with
+% the main code, we use the knowledge of how the second pass works:
+% if the control sequence name contains no space, count that token
+% as a number of normal tokens equal to its string length. If the
+% control sequence contains spaces, they should be registered as
+% special characters by increasing \cs{l_@@_analysis_index_int}
+% (no need to carefully count character between each space), and
+% all characters after the last space should be counted in the
+% following sequence of \enquote{normal} tokens.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_analysis_a_safe:N #1
+ {
+ \if_charcode:w
+ \scan_stop:
+ \exp_after:wN \use_none:n \token_to_str:N #1 \prg_do_nothing:
+ \scan_stop:
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ {
+ \@@_analysis_disable_char:N #1
+ \int_incr:N \l_@@_analysis_normal_int
+ }
+ { \@@_analysis_cs_space_count:NN \@@_analysis_a_cs:ww #1 }
+ \@@_analysis_a_loop:w
+ }
+\cs_new_protected:Npn \@@_analysis_a_cs:ww #1; #2;
+ {
+ \if_int_compare:w #1 > \c_zero_int
+ \tex_skip:D \l_@@_analysis_index_int
+ = \int_eval:n { \l_@@_analysis_normal_int + 1 } sp \exp_stop_f:
+ \tex_advance:D \l_@@_analysis_index_int #1 \exp_stop_f:
+ \else:
+ \tex_advance:D
+ \fi:
+ \l_@@_analysis_normal_int #2 \exp_stop_f:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Second pass}
+%
+% The second pass is an exercise in expandable loops.
+% All the necessary information is stored in \tn{skip}
+% and \tn{toks} registers.
+%
+% \begin{macro}{\@@_analysis_b:n}
+% \begin{macro}[EXP]{\@@_analysis_b_loop:w}
+% Start the loop with the index $0$. No need for an end-marker:
+% the loop stops by itself when the last index is read.
+% We repeatedly oscillate between reading long stretches
+% of normal tokens, and reading special tokens.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_analysis_b:n #1
+ {
+ \__kernel_tl_gset:Nx \g_@@_analysis_result_tl
+ {
+ \@@_analysis_b_loop:w 0; #1
+ \prg_break_point:
+ }
+ }
+\cs_new:Npn \@@_analysis_b_loop:w #1;
+ {
+ \exp_after:wN \@@_analysis_b_normals:ww
+ \int_value:w \tex_skip:D #1 ; #1 ;
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_analysis_b_normals:ww}
+% \begin{macro}[EXP]{\@@_analysis_b_normal:wwN}
+% The first argument is the number of normal tokens which remain
+% to be read, and the second argument is the index in the array
+% produced in the first step.
+% A character's string representation is always one character long,
+% while a control sequence is always longer (we have set the escape
+% character to a printable value). In both cases, we leave
+% \cs{exp_not:n} \Arg{token} \cs{s_@@} in the input stream
+% (after \texttt{e}-expansion). Here, \cs{exp_not:n} is used
+% rather than \cs{exp_not:N} because |#3| could be
+% a macro parameter character or could be \cs{s_@@}
+% (which must be hidden behind braces in the result).
+% \begin{macrocode}
+\cs_new:Npn \@@_analysis_b_normals:ww #1;
+ {
+ \if_int_compare:w #1 = \c_zero_int
+ \@@_analysis_b_special:w
+ \fi:
+ \@@_analysis_b_normal:wwN #1;
+ }
+\cs_new:Npn \@@_analysis_b_normal:wwN #1; #2; #3
+ {
+ \exp_not:n { \exp_not:n { #3 } } \s_@@
+ \if_charcode:w
+ \scan_stop:
+ \exp_after:wN \use_none:n \token_to_str:N #3 \prg_do_nothing:
+ \scan_stop:
+ \exp_after:wN \@@_analysis_b_char:Nn
+ \exp_after:wN \@@_analysis_b_char_aux:nww
+ \else:
+ \exp_after:wN \@@_analysis_b_cs:Nww
+ \fi:
+ #3 #1; #2;
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_analysis_b_char:Nn, \@@_analysis_b_char_aux:nww}
+% This function is called here with arguments
+% \cs{@@_analysis_b_char_aux:nww} and a normal character, while in the
+% peek analysis code it is called with \cs{use_none:n} and possibly a
+% space character, which is why the function has signature |Nn|.
+% If the normal token we grab is a character, leave
+% \meta{catcode} \meta{charcode} followed by \cs{s_@@}
+% in the input stream, and call \cs{@@_analysis_b_normals:ww}
+% with its first argument decremented.
+% \begin{macrocode}
+\cs_new:Npe \@@_analysis_b_char:Nn #1#2
+ {
+ \exp_not:N \if_meaning:w #2 \exp_not:N \tex_undefined:D
+ \token_to_str:N D \exp_not:N \else:
+ \exp_not:N \if_catcode:w #2 \c_catcode_other_token
+ \token_to_str:N C \exp_not:N \else:
+ \exp_not:N \if_catcode:w #2 \c_catcode_letter_token
+ \token_to_str:N B \exp_not:N \else:
+ \exp_not:N \if_catcode:w #2 \c_math_toggle_token 3
+ \exp_not:N \else:
+ \exp_not:N \if_catcode:w #2 \c_alignment_token 4
+ \exp_not:N \else:
+ \exp_not:N \if_catcode:w #2 \c_math_superscript_token 7
+ \exp_not:N \else:
+ \exp_not:N \if_catcode:w #2 \c_math_subscript_token 8
+ \exp_not:N \else:
+ \exp_not:N \if_catcode:w #2 \c_space_token
+ \token_to_str:N A \exp_not:N \else:
+ 6
+ \exp_not:n { \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: }
+ #1 {#2}
+ }
+\cs_new:Npn \@@_analysis_b_char_aux:nww #1
+ {
+ \int_value:w `#1 \s_@@
+ \exp_after:wN \@@_analysis_b_normals:ww
+ \int_value:w \int_eval:w - 1 +
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_analysis_b_cs:Nww}
+% \begin{macro}[EXP]{\@@_analysis_b_cs_test:ww}
+% If the token we grab is a control sequence, leave
+% |0 -1| (as category code and character code) in the input stream,
+% followed by \cs{s_@@},
+% and call \cs{@@_analysis_b_normals:ww} with updated arguments.
+% \begin{macrocode}
+\cs_new:Npn \@@_analysis_b_cs:Nww #1
+ {
+ 0 -1 \s_@@
+ \@@_analysis_cs_space_count:NN \@@_analysis_b_cs_test:ww #1
+ }
+\cs_new:Npn \@@_analysis_b_cs_test:ww #1 ; #2 ; #3 ; #4 ;
+ {
+ \exp_after:wN \@@_analysis_b_normals:ww
+ \int_value:w \int_eval:w
+ \if_int_compare:w #1 = \c_zero_int
+ #3
+ \else:
+ \tex_skip:D \int_eval:n { #4 + #1 } \exp_stop_f:
+ \fi:
+ - #2
+ \exp_after:wN ;
+ \int_value:w \int_eval:n { #4 + #1 } ;
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_analysis_b_special:w}
+% \begin{macro}[EXP]{\@@_analysis_b_special_char:wN}
+% \begin{macro}[EXP]{\@@_analysis_b_special_space:w}
+% Here, |#1| is the current index in the array built in the first pass.
+% Check now whether we reached the end (we shouldn't keep the trailing
+% end-group character that marked the end of the token list in the
+% first pass).
+% Unpack the \tn{toks} register: when \texttt{e}/\texttt{x}-expanding again,
+% we will get the special token.
+% Then leave the category code in the input stream, followed by
+% the character code, and call \cs{@@_analysis_b_loop:w} with the next index.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_other:N A
+ \cs_new:Npn \@@_analysis_b_special:w
+ \fi: \@@_analysis_b_normal:wwN 0 ; #1 ;
+ {
+ \fi:
+ \if_int_compare:w #1 = \l_@@_analysis_index_int
+ \exp_after:wN \prg_break:
+ \fi:
+ \tex_the:D \tex_toks:D #1 \s_@@
+ \if_case:w \tex_gluestretch:D \tex_skip:D #1 \exp_stop_f:
+ \token_to_str:N A
+ \or: 1
+ \or: 1
+ \else: 2
+ \fi:
+ \if_int_odd:w \tex_gluestretch:D \tex_skip:D #1 \exp_stop_f:
+ \exp_after:wN \@@_analysis_b_special_char:wN \int_value:w
+ \else:
+ \exp_after:wN \@@_analysis_b_special_space:w \int_value:w
+ \fi:
+ \int_eval:n { 1 + #1 } \exp_after:wN ;
+ \token_to_str:N
+ }
+\group_end:
+\cs_new:Npn \@@_analysis_b_special_char:wN #1 ; #2
+ {
+ \int_value:w `#2 \s_@@
+ \@@_analysis_b_loop:w #1 ;
+ }
+\cs_new:Npn \@@_analysis_b_special_space:w #1 ; ~
+ {
+ 32 \s_@@
+ \@@_analysis_b_loop:w #1 ;
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Mapping through the analysis}
+%
+% \begin{macro}{\tl_analysis_map_inline:Nn, \tl_analysis_map_inline:nn}
+% \begin{macro}{\@@_analysis_map:Nn}
+% \begin{macro}{\@@_analysis_map:NwNw}
+% First obtain the analysis of the token list into
+% \cs{g_@@_analysis_result_tl}. To allow nested mappings, increase the
+% nesting depth \cs{g__kernel_prg_map_int} (shared between all
+% modules), then define the payload macro, which runs the user code
+% and has a name specific to that nesting depth. The looping macro
+% grabs the \meta{tokens}, \meta{catcode} and \meta{char code}; it
+% checks for the end of the loop with \cs{use_none:n} |##2|, normally
+% empty, but which becomes \cs{tl_map_break:} at the end; it then
+% calls the payload macro with the arguments in the correct order
+% (this is the reason why we cannot directly use the same macro for
+% looping and payload), and loops by calling itself. When the loop
+% ends, remember to decrease the nesting depth.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_analysis_map_inline:Nn #1
+ { \exp_args:No \tl_analysis_map_inline:nn #1 }
+\cs_new_protected:Npn \tl_analysis_map_inline:nn #1
+ {
+ \@@_analysis:n {#1}
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:Nc \@@_analysis_map:Nn
+ { @@_analysis_map_inline_ \int_use:N \g__kernel_prg_map_int :wNw }
+ }
+\cs_new_protected:Npn \@@_analysis_map:Nn #1#2
+ {
+ \cs_gset_protected:Npn #1 ##1##2##3 {#2}
+ \exp_after:wN \@@_analysis_map:NwNw \exp_after:wN #1
+ \g_@@_analysis_result_tl
+ \s_@@ { ? \tl_map_break: } \s_@@
+ \prg_break_point:Nn \tl_map_break:
+ { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+\cs_new_protected:Npn \@@_analysis_map:NwNw #1 #2 \s_@@ #3 #4 \s_@@
+ {
+ \use_none:n #3
+ #1 {#2} {#4} {#3}
+ \@@_analysis_map:NwNw #1
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Showing the results}
+%
+% \begin{macro}{\tl_analysis_show:N, \tl_analysis_log:N, \@@_analysis_show:NNN}
+% Add to \cs{@@_analysis:n} a third pass to display tokens to the terminal.
+% If the token list variable is not defined, throw the same error
+% as \cs{tl_show:N} by simply calling that function.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_analysis_show:N
+ { \@@_analysis_show:NNN \msg_show:nneeee \tl_show:N }
+\cs_new_protected:Npn \tl_analysis_log:N
+ { \@@_analysis_show:NNN \msg_log:nneeee \tl_log:N }
+\cs_new_protected:Npn \@@_analysis_show:NNN #1#2#3
+ {
+ \tl_if_exist:NTF #3
+ {
+ \exp_args:No \@@_analysis:n {#3}
+ #1 { tl } { show-analysis }
+ { \token_to_str:N #3 } { \@@_analysis_show: } { } { }
+ }
+ { #2 #3 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\tl_analysis_show:n, \tl_analysis_log:n, \@@_analysis_show:Nn}
+% No existence test needed here.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_analysis_show:n
+ { \@@_analysis_show:Nn \msg_show:nneeee }
+\cs_new_protected:Npn \tl_analysis_log:n
+ { \@@_analysis_show:Nn \msg_log:nneeee }
+\cs_new_protected:Npn \@@_analysis_show:Nn #1#2
+ {
+ \@@_analysis:n {#2}
+ #1 { tl } { show-analysis } { } { \@@_analysis_show: } { } { }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_analysis_show:, \@@_analysis_show_loop:wNw}
+% Here, |#1| \texttt{o}- and \texttt{e}/\texttt{x}-expands to the token;
+% |#2| is the category code (one uppercase hexadecimal digit),
+% $0$ for control sequences;
+% |#3| is the character code, which we ignore.
+% In the cases of control sequences and active characters,
+% the meaning may overflow one line, and we want to truncate
+% it. Those cases are thus separated out.
+% \begin{macrocode}
+\cs_new:Npn \@@_analysis_show:
+ {
+ \exp_after:wN \@@_analysis_show_loop:wNw \g_@@_analysis_result_tl
+ \s_@@ { ? \prg_break: } \s_@@
+ \prg_break_point:
+ }
+\cs_new:Npn \@@_analysis_show_loop:wNw #1 \s_@@ #2 #3 \s_@@
+ {
+ \use_none:n #2
+ \iow_newline: > \use:nn { ~ } { ~ }
+ \if_int_compare:w "#2 = \c_zero_int
+ \exp_after:wN \@@_analysis_show_cs:n
+ \else:
+ \if_int_compare:w "#2 = 13 \exp_stop_f:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_analysis_show_active:n
+ \else:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_analysis_show_normal:n
+ \fi:
+ \fi:
+ {#1}
+ \@@_analysis_show_loop:wNw
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_analysis_show_normal:n}
+% Non-active characters are a simple matter of printing
+% the character, and its meaning. Our test suite checks that
+% begin-group and end-group characters do not mess up
+% \TeX{}'s alignment status.
+% \begin{macrocode}
+\cs_new:Npn \@@_analysis_show_normal:n #1
+ {
+ \exp_after:wN \token_to_str:N #1 ~
+ ( \exp_after:wN \token_to_meaning:N #1 )
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_analysis_show_value:N}
+% This expands to the value of |#1| if it has any.
+% \begin{macrocode}
+\cs_new:Npn \@@_analysis_show_value:N #1
+ {
+ \token_if_expandable:NF #1
+ {
+ \token_if_chardef:NTF #1 \prg_break: { }
+ \token_if_mathchardef:NTF #1 \prg_break: { }
+ \token_if_dim_register:NTF #1 \prg_break: { }
+ \token_if_int_register:NTF #1 \prg_break: { }
+ \token_if_skip_register:NTF #1 \prg_break: { }
+ \token_if_toks_register:NTF #1 \prg_break: { }
+ \use_none:nnn
+ \prg_break_point:
+ \use:n { \exp_after:wN = \tex_the:D #1 }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_analysis_show_cs:n}
+% \begin{macro}[rEXP]{\@@_analysis_show_active:n}
+% \begin{macro}[rEXP]{\@@_analysis_show_long:nn}
+% \begin{macro}[rEXP]{\@@_analysis_show_long_aux:nnnn}
+% Control sequences and active characters are printed in the same way,
+% making sure not to go beyond the \cs{l_iow_line_count_int}. In case
+% of an overflow, we replace the last characters by
+% \cs{c_@@_analysis_show_etc_str}.
+% \begin{macrocode}
+\cs_new:Npn \@@_analysis_show_cs:n #1
+ { \exp_args:No \@@_analysis_show_long:nn {#1} { control~sequence= } }
+\cs_new:Npn \@@_analysis_show_active:n #1
+ { \exp_args:No \@@_analysis_show_long:nn {#1} { active~character= } }
+\cs_new:Npn \@@_analysis_show_long:nn #1
+ {
+ \@@_analysis_show_long_aux:oofn
+ { \token_to_str:N #1 }
+ { \token_to_meaning:N #1 }
+ { \@@_analysis_show_value:N #1 }
+ }
+\cs_new:Npn \@@_analysis_show_long_aux:nnnn #1#2#3#4
+ {
+ \int_compare:nNnTF
+ { \str_count:n { #1 ~ ( #4 #2 #3 ) } }
+ > { \l_iow_line_count_int - 3 }
+ {
+ \str_range:nnn { #1 ~ ( #4 #2 #3 ) } { 1 }
+ {
+ \l_iow_line_count_int - 3
+ - \str_count:N \c_@@_analysis_show_etc_str
+ }
+ \c_@@_analysis_show_etc_str
+ }
+ { #1 ~ ( #4 #2 #3 ) }
+ }
+\cs_generate_variant:Nn \@@_analysis_show_long_aux:nnnn { oof }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \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_exp:N, \@@_peek_analysis_exp_aux:N,
+% \@@_peek_analysis_nonexp:N, \@@_peek_analysis_cs:N,
+% \@@_peek_analysis_char:N, \@@_peek_analysis_char:w,
+% \@@_peek_analysis_special:, \@@_peek_analysis_retest:,
+% \@@_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:NNNN
+% }
+% Save the user's code in a control sequence that is suitable for
+% nested maps. We may wish to pass to this function an \tn{outer}
+% control sequence or active character; for this we will undefine
+% any expandable token (testing if it is \tn{outer} is much slower)
+% within a group, closed immediately after the function reads its
+% arguments to avoid affecting the user's code or even our peek code
+% (there is no risk of undefining \cs{group_end:} itself since that is
+% not expandable). 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
+ {
+ \group_align_safe_begin:
+ \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:
+ {
+ \int_gdecr:N \g__kernel_prg_map_int
+ \group_align_safe_end:
+ }
+ }
+ \@@_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 (distinguishing expandable from
+% non-expandable tokens). The test for nonexpandable tokens in
+% \cs{@@_peek_analysis_test:} must be done after the tests for
+% begin-group, end-group, and space tokens, in case \cs{l_peek_token}
+% is either \tn{outer} or is a primitive \TeX{} conditional, as such
+% tokens cannot be skipped over correctly by conditional code.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_peek_analysis_loop:NNn #1#2#3
+ {
+ \group_begin:
+ \tl_set:Ne \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_case:w
+ \if_catcode:w \exp_not:N \l_peek_token { \c_max_int \fi:
+ \if_catcode:w \exp_not:N \l_peek_token } \c_max_int \fi:
+ \if_meaning:w \l_peek_token \c_space_token \c_max_int \fi:
+ \exp_after:wN \if_meaning:w \exp_not:N \l_peek_token \l_peek_token
+ \c_one_int
+ \fi:
+ \c_zero_int
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_peek_analysis_exp:N
+ \exp_after:wN \exp_not:N
+ \or:
+ \exp_after:wN \@@_peek_analysis_nonexp:N
+ \else:
+ \exp_after:wN \@@_peek_analysis_special:
+ \fi:
+ }
+% \end{macrocode}
+% Expandable tokens (which are automatically |N|-type) can be
+% \tn{outer} macros, hence the need for \cs{exp_after:wN} and
+% \cs{exp_not:N} in the code above, which allows the next function to
+% safely grab the token as an argument. We run some code that is
+% expanded using the primitive \cs{cs_set_nopar:Npe} rather than
+% \cs{tl_set:Ne} to avoid grabbing it as an argument as |#1| may be
+% \tn{outer}. To allow~|#1| as an argument of the user's function
+% (stored in \cs{l_@@_peek_code_tl}), we set it equal to
+% \cs{scan_stop:}, but we do it at the last minute because |#1|
+% may be some pretty important function such as \cs{exp_after:wN}.
+% Then we put the user's function and the elaborate first argument
+% \cs{__kernel_exp_not:w} \cs{exp_after:wN} |{| \cs{exp_not:N} |#1| |}|:
+% indeed we cannot use \cs{exp_not:n} |{#1}| as this breaks for an
+% \tn{outer} macro and we cannot use \cs{exp_not:N} |#1|, as
+% \texttt{o}-expanding this yields a \enquote{notexpanded} token equal
+% to (a weird) \tn{relax}, which would have the wrong value for
+% primitive \TeX{} conditionals such as \cs{if_meaning:w}.
+%
+% Then we must add |{-1}0| if the token is a
+% control sequence and \Arg{charcode}|D| otherwise. Distinguishing
+% the two cases is easy: since we have made the escape character
+% printable, \cs{token_to_str:N} gives at least two characters for a
+% control sequence versus a single one for an active character
+% (possibly being a space). Importantly, once we apply
+% \cs{token_to_str:N} we no longer need to worry about \tn{outer}
+% tokens.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_peek_analysis_exp:N #1
+ {
+ \cs_set_nopar:Npe \l_@@_peek_code_tl
+ {
+ \tex_let:D \exp_not:N #1 \scan_stop:
+ \exp_not:o \l_@@_peek_code_tl
+ {
+ \exp_not:n { \__kernel_exp_not:w \exp_after:wN }
+ { \exp_not:N \exp_not:N \exp_not:N #1 }
+ }
+ \exp_after:wN \@@_peek_analysis_exp_aux:Nw
+ \token_to_str:N #1 \prg_do_nothing: \s_@@
+ }
+ \l_@@_peek_code_tl
+ }
+\cs_new:Npe \@@_peek_analysis_exp_aux:Nw #1#2 \s_@@
+ {
+ \exp_not:N \if:w \scan_stop: #2 \scan_stop:
+ { \exp_not:N \int_value:w `#1 } \token_to_str:N D
+ \exp_not:N \else:
+ { -1 } 0
+ \exp_not:N \fi:
+ }
+% \end{macrocode}
+% For normal non-expandable tokens we must distinguish characters
+% (including active ones and macro parameter characters) from control
+% sequences (whose string representation is more than one character
+% because we made the escape character printable). For a control
+% sequence call the user code with suitable arguments, wrapping |#1|
+% within \cs{exp_not:n} just in case it happens to be equal to a macro
+% parameter character. We do not skip \cs{exp_not:n} when
+% unnecessary, because this auxiliary is also called in
+% \cs{@@_peek_analysis_retest:} where we have changed some control
+% sequences or active characters to \cs{scan_stop:} temporarily.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_peek_analysis_nonexp:N #1
+ {
+ \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
+ \else:
+ \exp_after:wN \@@_peek_analysis_cs:N
+ \fi:
+ #1
+ }
+\cs_new_protected:Npn \@@_peek_analysis_cs:N #1
+ { \l_@@_peek_code_tl { \exp_not:n {#1} } { -1 } 0 }
+% \end{macrocode}
+% For normal characters we must determine their catcode. The main
+% difficulty is that the character may be an active character
+% masquerading as (i.e., set equal to) itself with a different
+% catcode. Two approaches based on \tn{lowercase} can detect this.
+% One could make an active character with the same catcode as~|#1| and
+% change its definition before testing the catcode of~|#1|, but in
+% some Unicode engine this fills up the hash table uselessly.
+% Instead, we lowercase~|#1| itself, changing its character code
+% to~$32$, namely space (because \LuaTeX{} cannot turn catcode~$10$
+% characters to anything else than character code~$32$), then we apply
+% \cs{@@_analysis_b_char:Nn}, which detects active characters by
+% comparing them to \cs{tex_undefined:D}, and we must have undefined
+% the active space (locally) for this test to work.
+% To define \cs{@@_peek_analysis_char:N} itself we use an
+% |e|-expanding assignment to get the active space in the right place
+% after making it (just for this definition) unexpandable.
+% Finally \cs{@@_peek_analysis_char:w} receives the \meta{charcode},
+% \meta{user function}, \meta{catcode}, and \meta{token}, and places
+% the arguments in the correct order. It keeps \cs{exp_not:n} for
+% macro parameter characters and active characters (the latter could
+% be macro parameter characters, and it seems more uniform to always
+% put \cs{exp_not:n}), and otherwise eliminates it by expanding once
+% with \cs{exp_args:NNNo}.
+% \begin{macrocode}
+\group_begin:
+\char_set_active_eq:NN \ \scan_stop:
+\cs_new_protected:Npe \@@_peek_analysis_char:N #1
+ {
+ \cs_set_eq:NN
+ \char_generate:nn { 32 } { 13 }
+ \exp_not:N \tex_undefined:D
+ \tex_lccode:D `#1 = 32 \exp_stop_f:
+ \tex_lowercase:D
+ {
+ \tl_put_right:Ne \exp_not:N \l_@@_peek_code_tl
+ { \exp_not:n { \@@_analysis_b_char:Nn \use_none:n } {#1} }
+ }
+ \exp_not:n
+ {
+ \exp_after:wN \@@_peek_analysis_char:w
+ \int_value:w
+ }
+ `#1
+ \exp_not:n { \exp_after:wN \s_@@ \l_@@_peek_code_tl }
+ #1
+ }
+\group_end:
+\cs_new_protected:Npn \@@_peek_analysis_char:w #1 \s_@@ #2#3#4
+ {
+ \if_charcode:w 6 #3
+ \else:
+ \if_charcode:w D #3
+ \else:
+ \exp_args:NNNo
+ \fi:
+ \fi:
+ #2 { \exp_not:n {#4} } {#1} #3
+ }
+% \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_nonexp:N
+ \else:
+ \exp_after:wN \@@_peek_analysis_str:
+ \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). The idea is to apply \cs{token_to_str:N} to
+% the \meta{token} then grab characters (of category code~$12$ except
+% for spaces that have category code~$10$) to reconstruct it. In
+% earlier versions of the code we would peek at the \meta{next token}
+% that lies after \meta{token} in the input stream, which would help
+% us be more accurate in reconstructing the \meta{token} case in edge
+% cases (mentioned below), but this had the side-effect of tokenizing
+% the input stream (turning characters into tokens) farther ahead than
+% needed.
+%
+% We hit the \meta{token} with \cs{token_to_str:N} and start grabbing
+% characters. More
+% precisely, by looking at the first character in the string
+% representation of the \meta{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 active
+% character we find anything else (we made sure to exclude the case of
+% an active character whose string representation coincides with the
+% other two cases).
+% \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:Ne \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{e}/\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:Ne \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 the constructed csname has the expected
+% meaning. This fails if someone defines a token like
+% \cs[no-index]{bgroup@my} whose string representation starts the same
+% as another token with the same meaning being an implicit character
+% token of category code $1$, $2$, or $10$.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_peek_analysis_escape:
+ {
+ \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:
+ {
+ \exp_after:wN \if_meaning:w
+ \cs:w
+ \if_cs_exist:w \l_@@_internal_a_tl \cs_end:
+ \l_@@_internal_a_tl
+ \else:
+ c_one % anything short
+ \fi:
+ \cs_end:
+ \l_peek_token
+ \@@_peek_analysis_collect_end:NNNN
+ \fi:
+ \tex_futurelet:D \l_@@_analysis_token
+ \@@_peek_analysis_collect:w
+ }
+% \end{macrocode}
+% As in all other cases, end by calling the user code with suitable
+% arguments (here |#1| is \cs{fi:}).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_peek_analysis_collect_end:NNNN #1#2#3#4
+ {
+ #1
+ \tl_put_right:Ne \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}
+% When a control sequence (or active character)
+% and its meaning are too long to fit in one line
+% of the terminal, the end is replaced by this token list.
+% \begin{macrocode}
+\tl_const:Ne \c_@@_analysis_show_etc_str % (
+ { \token_to_str:N \ETC.) }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macrocode}
+\msg_new:nnn { tl } { show-analysis }
+ {
+ The~token~list~ \tl_if_empty:nF {#1} { #1 ~ }
+ \tl_if_empty:nTF {#2}
+ { is~empty }
+ { contains~the~tokens: #2 }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3tl-build.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3tl-build.dtx
new file mode 100644
index 00000000000..859dd5025c8
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3tl-build.dtx
@@ -0,0 +1,356 @@
+% \iffalse meta-comment
+%
+%% File: l3tl-build.dtx
+%
+% Copyright (C) 2018-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3tl-build} module\\ Piecewise \texttt{tl} constructions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{Constructing \meta{tl~var} by accumulation}
+%
+% When creating a \meta{tl~var} by accumulation of many tokens, the performance
+% available using a combination of \cs{tl_set:Nn} and \cs{tl_put_right:Nn} or
+% similar begins to become an issue. To address this, a set of functions are
+% available to \enquote{build} a \meta{tl~var}. The performance of this approach
+% is much more efficient than the standard \cs{tl_put_right:Nn}, but the
+% constructed token list cannot be accessed during construction other than
+% by methods provided in this section.
+%
+% Whilst the exact performance difference is dependent on the size of each
+% added block of tokens and the total number of blocks, in general, the
+% \cs[no-index]{tl_build_(g)put...} functions will out-perform the basic
+% \cs[no-index]{tl_(g)put...} equivalent if more than 100 non-empty addition
+% operations occur. See
+% \url{https://github.com/latex3/latex3/issues/1393#issuecomment-1880164756}
+% for a more detailed analysis.
+%
+% \begin{function}[added = 2018-04-01]{\tl_build_begin:N, \tl_build_gbegin:N}
+% \begin{syntax}
+% \cs{tl_build_begin:N} \meta{tl~var}
+% \end{syntax}
+% Clears the \meta{tl~var} and sets it up to support other
+% \cs[no-index]{tl_build_\ldots{}} functions. Until \cs{tl_build_end:N}
+% \meta{tl~var} or \cs{tl_build_gend:N} \meta{tl~var} is called,
+% applying any function from \pkg{l3tl} other
+% than \cs[no-index]{tl_build_\ldots{}} will lead to incorrect
+% results. The |begin| and |gbegin| functions must be used for local
+% and global \meta{tl~var} respectively.
+% \end{function}
+%
+% \begin{function}[added = 2018-04-01]
+% {
+% \tl_build_put_left:Nn, \tl_build_put_left:Ne,
+% \tl_build_gput_left:Nn, \tl_build_gput_left:Ne,
+% \tl_build_put_right:Nn, \tl_build_put_right:Ne,
+% \tl_build_gput_right:Nn, \tl_build_gput_right:Ne
+% }
+% \begin{syntax}
+% \cs{tl_build_put_left:Nn} \meta{tl~var} \Arg{tokens}
+% \cs{tl_build_put_right:Nn} \meta{tl~var} \Arg{tokens}
+% \end{syntax}
+% Adds \meta{tokens} to the left or right side of the current contents
+% of \meta{tl~var}. The \meta{tl~var} must have been set up with
+% \cs{tl_build_begin:N} or \cs{tl_build_gbegin:N}. The |put| and
+% |gput| functions must be used for local and global \meta{tl~var}
+% respectively. The |right| functions are about twice faster than the
+% |left| functions.
+% \end{function}
+%
+% \begin{function}[added = 2018-04-01]{\tl_build_end:N, \tl_build_gend:N}
+% \begin{syntax}
+% \cs{tl_build_end:N} \meta{tl~var}
+% \end{syntax}
+% Gets the contents of \meta{tl~var} and stores that into the
+% \meta{tl~var} using \cs{tl_set:Nn} or \cs{tl_gset:Nn}.
+% The \meta{tl~var} must have
+% been set up with \cs{tl_build_begin:N} or \cs{tl_build_gbegin:N}.
+% The |end| and |gend| functions must be used for local and global
+% \meta{tl~var} respectively. These functions completely remove the
+% setup code that enabled \meta{tl~var} to be used for other
+% \cs[no-index]{tl_build_\ldots{}} functions. After the action of
+% |end|/|gend|, the \meta{tl~var} may be manipulated using standard
+% \texttt{tl} functions.
+% \end{function}
+%
+% \begin{function}[added = 2023-12-14]{\tl_build_get_intermediate:NN}
+% \begin{syntax}
+% \cs{tl_build_get_intermediate:NN} \meta{tl~var_1} \meta{tl~var_2}
+% \end{syntax}
+% Stores the contents of the \meta{tl~var_1} in the \meta{tl~var_2}.
+% The \meta{tl~var_1} must have been set up with \cs{tl_build_begin:N}
+% or \cs{tl_build_gbegin:N}. The \meta{tl~var_2} is a
+% \enquote{normal} token list variable, assigned locally using
+% \cs{tl_set:Nn}.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3tl-build} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=tl>
+% \end{macrocode}
+%
+% Between \cs{tl_build_begin:N} \meta{tl~var} and \cs{tl_build_end:N}
+% \meta{tl~var}, the \meta{tl~var} has the structure
+% \begin{quote}
+% \cs{exp_end:} \ldots{} \cs{exp_end:} \cs{@@_build_last:NNn}
+% \meta{assignment} \meta{next~tl} \Arg{left} \meta{right}
+% \end{quote}
+% where \meta{right} is not braced. The \enquote{data} it represents is
+% \meta{left} followed by the \enquote{data} of \meta{next~tl} followed
+% by \meta{right}. The \meta{next~tl} is a token list variable whose
+% name is that of \meta{tl~var} followed by~|'|. There are between $0$
+% and $4$ \cs{exp_end:} to keep track of when \meta{left} and
+% \meta{right} should be put into the \meta{next~tl}. The
+% \meta{assignment} is \cs{cs_set_nopar:Npe} if the variable is local,
+% and \cs{cs_gset_nopar:Npe} if it is global.
+%
+% \begin{macro}{\tl_build_begin:N, \tl_build_gbegin:N}
+% \begin{macro}{\@@_build_begin:NN, \@@_build_begin:NNN}
+% First construct the \meta{next~tl}: using a prime here conflicts
+% with the usual \pkg{expl3} convention but we need a name that can be
+% derived from |#1| without any external data such as a counter.
+% Empty that \meta{next~tl} and setup the structure. The local and
+% global versions only differ by a single function
+% \cs[no-index]{cs_(g)set_nopar:Npe} used for all assignments: this is
+% important because only that function is stored in the \meta{tl~var}
+% and \meta{next~tl} for subsequent assignments. In principle
+% \cs{@@_build_begin:NNN} could use \cs[no-index]{tl_(g)clear_new:N}
+% to empty |#1| and make sure it is defined, but logging the
+% definition does not seem useful so we just do |#3| |#1| |{}| to
+% clear it locally or globally as appropriate.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_build_begin:N #1
+ { \@@_build_begin:NN \cs_set_nopar:Npe #1 }
+\cs_new_protected:Npn \tl_build_gbegin:N #1
+ { \@@_build_begin:NN \cs_gset_nopar:Npe #1 }
+\cs_new_protected:Npn \@@_build_begin:NN #1#2
+ { \exp_args:Nc \@@_build_begin:NNN { \cs_to_str:N #2 ' } #2 #1 }
+\cs_new_protected:Npn \@@_build_begin:NNN #1#2#3
+ {
+ #3 #1 { }
+ #3 #2
+ {
+ \exp_not:n { \exp_end: \exp_end: \exp_end: \exp_end: }
+ \exp_not:n { \@@_build_last:NNn #3 #1 { } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \tl_build_put_right:Nn, \tl_build_put_right:Ne, \tl_build_put_right:Nx,
+% \tl_build_gput_right:Nn, \tl_build_gput_right:Ne, \tl_build_gput_right:Nx
+% }
+% \begin{macro}{\@@_build_last:NNn}
+% \begin{macro}{\@@_build_put:nn}
+% \begin{macro}{\@@_build_put:nw}
+% Similar to \cs{tl_put_right:Nn}, but apply \cs{exp:w} to |#1|. Most
+% of the time this just removes one \cs{exp_end:}. When there are
+% none left, \cs{@@_build_last:NNn} is expanded instead. It resets
+% the definition of the \meta{tl~var} by ending the \cs{exp_not:n} and
+% the definition early. Then it makes sure the \meta{next~tl} (its
+% argument |#1|) is set-up and starts a new definition. Then
+% \cs{@@_build_put:nn} and \cs{@@_build_put:nw} place the \meta{left}
+% part of the original \meta{tl~var} as appropriate for the definition
+% of the \meta{next~tl} (the \meta{right} part is left in the right
+% place without ever becoming a macro argument). We use
+% \cs{exp_after:wN} rather than some \cs{exp_args:No} to avoid reading
+% arguments that are likely very long token lists. We use
+% \cs[no-index]{cs_(g)set_nopar:Npe} rather than
+% \cs[no-index]{tl_(g)set:Ne} partly for the same reason and partly
+% because the assignments are interrupted by brace tricks, which
+% implies that the assignment does not simply set the token list to an
+% |e|-expansion of the second argument.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_build_put_right:Nn #1#2
+ {
+ \cs_set_nopar:Npe #1
+ { \__kernel_exp_not:w \exp_after:wN { \exp:w #1 #2 } }
+ }
+\cs_generate_variant:Nn \tl_build_put_right:Nn { Ne , Nx }
+\cs_new_protected:Npn \tl_build_gput_right:Nn #1#2
+ {
+ \cs_gset_nopar:Npe #1
+ { \__kernel_exp_not:w \exp_after:wN { \exp:w #1 #2 } }
+ }
+\cs_generate_variant:Nn \tl_build_gput_right:Nn { Ne , Nx }
+\cs_new_protected:Npn \@@_build_last:NNn #1#2
+ {
+ \if_false: { { \fi:
+ \exp_end: \exp_end: \exp_end: \exp_end: \exp_end:
+ \@@_build_last:NNn #1 #2 { }
+ }
+ }
+ \if_meaning:w \c_empty_tl #2
+ \@@_build_begin:NN #1 #2
+ \fi:
+ #1 #2
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \exp:w \if_false: } } \fi:
+ \exp_after:wN \@@_build_put:nn \exp_after:wN {#2}
+ }
+\cs_new_protected:Npn \@@_build_put:nn #1#2 { \@@_build_put:nw {#2} #1 }
+\cs_new_protected:Npn \@@_build_put:nw #1#2 \@@_build_last:NNn #3#4#5
+ { #2 \@@_build_last:NNn #3 #4 { #1 #5 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \tl_build_put_left:Nn, \tl_build_put_left:Ne, \tl_build_put_left:Nx,
+% \tl_build_gput_left:Nn, \tl_build_gput_left:Ne, \tl_build_gput_left:Nx
+% }
+% \begin{macro}{\@@_build_put_left:NNn}
+% See \cs{tl_build_put_right:Nn} for all the machinery. We could
+% easily provide \cs[no-index]{tl_build_put_left_right:Nnn}, by just
+% adding the \meta{right} material after the \Arg{left} in the
+% |e|-expanding assignment.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_build_put_left:Nn #1
+ { \@@_build_put_left:NNn \cs_set_nopar:Npe #1 }
+\cs_generate_variant:Nn \tl_build_put_left:Nn { Ne , Nx }
+\cs_new_protected:Npn \tl_build_gput_left:Nn #1
+ { \@@_build_put_left:NNn \cs_gset_nopar:Npe #1 }
+\cs_generate_variant:Nn \tl_build_gput_left:Nn { Ne , Nx }
+\cs_new_protected:Npn \@@_build_put_left:NNn #1#2#3
+ {
+ #1 #2
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \exp:w \exp_after:wN \@@_build_put:nn
+ \exp_after:wN {#2} {#3}
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_build_end:N, \tl_build_gend:N}
+% \begin{macro}{\@@_build_end_loop:NN}
+% Get the data then clear the \meta{next~tl} recursively until finding
+% an empty one. It is perhaps wasteful to repeatedly use
+% \cs{cs_to_str:N}. The local/global scope is checked by
+% \cs{tl_set:Ne} or \cs{tl_gset:Ne}.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_build_end:N #1
+ {
+ \@@_build_get:NNN \__kernel_tl_set:Nx #1 #1
+ \exp_args:Nc \@@_build_end_loop:NN { \cs_to_str:N #1 ' } \tl_clear:N
+ }
+\cs_new_protected:Npn \tl_build_gend:N #1
+ {
+ \@@_build_get:NNN \__kernel_tl_gset:Nx #1 #1
+ \exp_args:Nc \@@_build_end_loop:NN { \cs_to_str:N #1 ' } \tl_gclear:N
+ }
+\cs_new_protected:Npn \@@_build_end_loop:NN #1#2
+ {
+ \if_meaning:w \c_empty_tl #1
+ \exp_after:wN \use_none:nnnnnn
+ \fi:
+ #2 #1
+ \exp_args:Nc \@@_build_end_loop:NN { \cs_to_str:N #1 ' } #2
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_build_get_intermediate:NN}
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_build_get_intermediate:NN
+ { \@@_build_get:NNN \__kernel_tl_set:Nx }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_build_get:NNN}
+% \begin{macro}{\@@_build_get:w, \@@_build_get_end:w}
+% The idea is to expand the \meta{tl~var} then the \meta{next~tl} and
+% so on, all within an |e|-expanding assignment, and wrap as
+% appropriate in \cs{exp_not:n}. The various \meta{left} parts are
+% left in the assignment as we go, which enables us to expand the
+% \meta{next~tl} at the right place. The various \meta{right} parts
+% are eventually picked up in one last \cs{exp_not:n}, with a brace
+% trick to wrap all the \meta{right} parts together.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_build_get:NNN #1#2#3
+ { #1 #3 { \if_false: { \fi: \exp_after:wN \@@_build_get:w #2 } } }
+\cs_new:Npn \@@_build_get:w #1 \@@_build_last:NNn #2#3#4
+ {
+ \exp_not:n {#4}
+ \if_meaning:w \c_empty_tl #3
+ \exp_after:wN \@@_build_get_end:w
+ \fi:
+ \exp_after:wN \@@_build_get:w #3
+ }
+\cs_new:Npn \@@_build_get_end:w #1#2#3
+ { \__kernel_exp_not:w \exp_after:wN { \if_false: } \fi: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3tl.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3tl.dtx
new file mode 100644
index 00000000000..30e16636e54
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3tl.dtx
@@ -0,0 +1,3843 @@
+% \iffalse meta-comment
+%
+%% File: l3tl.dtx
+%
+% Copyright (C) 1990-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3tl} module\\ Token lists^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \TeX{} works with tokens, and \LaTeX3 therefore provides a number of
+% functions to deal with lists of tokens. Token lists may be present
+% directly in the argument to a function:
+% \begin{verbatim}
+% \foo:n { a collection of \tokens }
+% \end{verbatim}
+% or may be stored in a so-called \enquote{token list variable}, which
+% have the suffix \texttt{tl}: a token list variable can also be used as
+% the argument to a function, for example
+% \begin{verbatim}
+% \foo:N \l_some_tl
+% \end{verbatim}
+% In both cases, functions are available to test and manipulate the lists
+% of tokens, and these have the module prefix \texttt{tl}.
+% In many cases, functions which can be applied to token list variables
+% are paired with similar functions for application to explicit lists
+% of tokens: the two \enquote{views} of a token list are therefore collected
+% together here.
+%
+% A token list (explicit, or stored in a variable) can be seen either
+% as a list of \enquote{items},
+% or a list of \enquote{tokens}. An item is whatever \cs{use:n} would
+% grab as its argument: a single non-space token or a brace group,
+% with optional leading explicit space characters (each item is thus
+% itself a token list). A token is either a normal \texttt{N} argument,
+% or \verb*| |, |{|, or |}| (assuming normal \TeX{} category codes).
+% Thus for example
+% \begin{verbatim}
+% { Hello } ~ world
+% \end{verbatim}
+% contains six items (\texttt{Hello}, \texttt{w}, \texttt{o}, \texttt{r},
+% \texttt{l} and \texttt{d}), but thirteen tokens (|{|, \texttt{H}, \texttt{e},
+% \texttt{l}, \texttt{l}, \texttt{o}, |}|, \verb*| |, \texttt{w}, \texttt{o},
+% \texttt{r}, \texttt{l} and \texttt{d}).
+% Functions which act on items are often faster than their analogue acting
+% directly on tokens.
+%
+% \section{Creating and initialising token list variables}
+%
+% \begin{function}{\tl_new:N, \tl_new:c}
+% \begin{syntax}
+% \cs{tl_new:N} \meta{tl~var}
+% \end{syntax}
+% Creates a new \meta{tl~var} or raises an error if the
+% name is already taken. The declaration is global. The
+% \meta{tl~var} is initially empty.
+% \end{function}
+%
+% \begin{function}
+% {
+% \tl_const:Nn, \tl_const:Ne,
+% \tl_const:cn, \tl_const:ce
+% }
+% \begin{syntax}
+% \cs{tl_const:Nn} \meta{tl~var} \Arg{tokens}
+% \end{syntax}
+% Creates a new constant \meta{tl~var} or raises an error
+% if the name is already taken. The value of the
+% \meta{tl~var} is set globally to the \meta{tokens}.
+% \end{function}
+%
+% \begin{function}{\tl_clear:N, \tl_clear:c, \tl_gclear:N, \tl_gclear:c}
+% \begin{syntax}
+% \cs{tl_clear:N} \meta{tl~var}
+% \end{syntax}
+% Clears all entries from the \meta{tl~var}.
+% \end{function}
+%
+% \begin{function}
+% {\tl_clear_new:N, \tl_clear_new:c, \tl_gclear_new:N, \tl_gclear_new:c}
+% \begin{syntax}
+% \cs{tl_clear_new:N} \meta{tl~var}
+% \end{syntax}
+% Ensures that the \meta{tl~var} exists globally by applying
+% \cs{tl_new:N} if necessary, then applies \cs[index=tl_clear:N]{tl_(g)clear:N} to leave
+% the \meta{tl~var} empty.
+% \end{function}
+%
+% \begin{function}
+% {
+% \tl_set_eq:NN, \tl_set_eq:cN, \tl_set_eq:Nc, \tl_set_eq:cc,
+% \tl_gset_eq:NN, \tl_gset_eq:cN, \tl_gset_eq:Nc, \tl_gset_eq:cc
+% }
+% \begin{syntax}
+% \cs{tl_set_eq:NN} \meta{tl~var_1} \meta{tl~var_2}
+% \end{syntax}
+% Sets the content of \meta{tl~var_1} equal to that of
+% \meta{tl~var_2}.
+% \end{function}
+%
+% \begin{function}[added = 2012-05-18]
+% {
+% \tl_concat:NNN, \tl_concat:ccc,
+% \tl_gconcat:NNN, \tl_gconcat:ccc
+% }
+% \begin{syntax}
+% \cs{tl_concat:NNN} \meta{tl~var_1} \meta{tl~var_2} \meta{tl~var_3}
+% \end{syntax}
+% Concatenates the content of \meta{tl~var_2} and \meta{tl~var_3}
+% together and saves the result in \meta{tl~var_1}. The \meta{tl~var_2}
+% is placed at the left side of the new token list.
+% \end{function}
+%
+% \begin{function}[EXP, pTF, added=2012-03-03]{\tl_if_exist:N, \tl_if_exist:c}
+% \begin{syntax}
+% \cs{tl_if_exist_p:N} \meta{tl~var}
+% \cs{tl_if_exist:NTF} \meta{tl~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{tl~var} is currently defined. This does not
+% check that the \meta{tl~var} really is a token list variable.
+% \end{function}
+%
+% \section{Adding data to token list variables}
+%
+% \begin{function}
+% {
+% \tl_set:Nn, \tl_set:NV, \tl_set:Nv, \tl_set:No, \tl_set:Ne, \tl_set:Nf,
+% \tl_set:cn, \tl_set:cV, \tl_set:cv, \tl_set:co, \tl_set:ce, \tl_set:cf,
+% \tl_gset:Nn, \tl_gset:NV, \tl_gset:Nv,
+% \tl_gset:No, \tl_gset:Ne, \tl_gset:Nf,
+% \tl_gset:cn, \tl_gset:cV, \tl_gset:cv,
+% \tl_gset:co, \tl_gset:ce, \tl_gset:cf
+% }
+% \begin{syntax}
+% \cs{tl_set:Nn} \meta{tl~var} \Arg{tokens}
+% \end{syntax}
+% Sets \meta{tl~var} to contain \meta{tokens},
+% removing any previous content from the variable.
+% \end{function}
+%
+% \begin{function}
+% {
+% \tl_put_left:Nn, \tl_put_left:NV, \tl_put_left:Nv, \tl_put_left:Ne,
+% \tl_put_left:No,
+% \tl_put_left:cn, \tl_put_left:cV, \tl_put_left:cv, \tl_put_left:ce,
+% \tl_put_left:co,
+% \tl_gput_left:Nn, \tl_gput_left:NV, \tl_gput_left:Nv, \tl_gput_left:Ne,
+% \tl_gput_left:No,
+% \tl_gput_left:cn, \tl_gput_left:cV, \tl_gput_left:cv, \tl_gput_left:ce,
+% \tl_gput_left:co
+% }
+% \begin{syntax}
+% \cs{tl_put_left:Nn} \meta{tl~var} \Arg{tokens}
+% \end{syntax}
+% Appends \meta{tokens} to the left side of the current content of
+% \meta{tl~var}.
+% \end{function}
+%
+% \begin{function}
+% {
+% \tl_put_right:Nn, \tl_put_right:NV, \tl_put_right:Nv, \tl_put_right:Ne,
+% \tl_put_right:No,
+% \tl_put_right:cn, \tl_put_right:cV, \tl_put_right:cv, \tl_put_right:ce,
+% \tl_put_right:co,
+% \tl_gput_right:Nn, \tl_gput_right:NV, \tl_gput_right:Nv, \tl_gput_right:Ne,
+% \tl_gput_right:No,
+% \tl_gput_right:cn, \tl_gput_right:cV, \tl_gput_right:cv, \tl_gput_right:ce,
+% \tl_gput_right:co
+% }
+% \begin{syntax}
+% \cs{tl_put_right:Nn} \meta{tl~var} \Arg{tokens}
+% \end{syntax}
+% Appends \meta{tokens} to the right side of the current content of
+% \meta{tl~var}.
+% \end{function}
+%
+% \section{Token list conditionals}
+%
+% \begin{function}[EXP,pTF, updated = 2019-09-04]
+% {\tl_if_blank:n, \tl_if_blank:e, \tl_if_blank:V, \tl_if_blank:o}
+% \begin{syntax}
+% \cs{tl_if_blank_p:n} \Arg{token list}
+% \cs{tl_if_blank:nTF} \Arg{token list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token list} consists only of blank spaces
+% (\emph{i.e.}~contains no item). The test is \texttt{true} if
+% \meta{token list} is zero or more explicit space characters
+% (explicit tokens with character code~$32$ and category code~$10$),
+% and is \texttt{false} otherwise.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\tl_if_empty:N, \tl_if_empty:c}
+% \begin{syntax}
+% \cs{tl_if_empty_p:N} \meta{tl~var}
+% \cs{tl_if_empty:NTF} \meta{tl~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{tl~var} is entirely empty
+% (\emph{i.e.}~contains no tokens at all).
+% \end{function}
+%
+% \begin{function}[added = 2012-05-24, updated = 2012-06-05, EXP,pTF]
+% {\tl_if_empty:n, \tl_if_empty:V, \tl_if_empty:o, \tl_if_empty:e}
+% \begin{syntax}
+% \cs{tl_if_empty_p:n} \Arg{token list}
+% \cs{tl_if_empty:nTF} \Arg{token list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token list} is entirely empty
+% (\emph{i.e.}~contains no tokens at all).
+% \end{function}
+%
+% \begin{function}[EXP,pTF]
+% {\tl_if_eq:NN, \tl_if_eq:Nc, \tl_if_eq:cN, \tl_if_eq:cc}
+% \begin{syntax}
+% \cs{tl_if_eq_p:NN} \meta{tl~var_1} \meta{tl~var_2}
+% \cs{tl_if_eq:NNTF} \meta{tl~var_1} \meta{tl~var_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Compares the content of \meta{tl~var_1} and \meta{tl~var_2} and
+% is logically \texttt{true} if the two contain the same list of
+% tokens (\emph{i.e.}~identical in both the list of characters they
+% contain and the category codes of those characters). Thus for example
+% \begin{verbatim}
+% \tl_set:Nn \l_tmpa_tl { abc }
+% \tl_set:Ne \l_tmpb_tl { \tl_to_str:n { abc } }
+% \tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl { true } { false }
+% \end{verbatim}
+% yields \texttt{false}.
+% See also \cs{str_if_eq:nnTF} for a comparison that ignores category codes.
+% \end{function}
+%
+% \begin{function}[TF, added = 2020-07-14]{\tl_if_eq:Nn, \tl_if_eq:cn}
+% \begin{syntax}
+% \cs{tl_if_eq:NnTF} \meta{tl~var_1} \Arg{token list_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{tl~var_1} and the \meta{token
+% list_2} contain the same list of tokens, both in respect of
+% character codes and category codes. This conditional is not
+% expandable: see \cs{tl_if_eq:NNTF} for an expandable version when
+% both token lists are stored in variables, or \cs{str_if_eq:nnTF} if
+% category codes are not important.
+% \end{function}
+%
+% \begin{function}[TF]
+% {
+% \tl_if_eq:nn, \tl_if_eq:nV, \tl_if_eq:ne, \tl_if_eq:Vn, \tl_if_eq:en,
+% \tl_if_eq:ee
+% }
+% \begin{syntax}
+% \cs{tl_if_eq:nnTF} \Arg{token list_1} \Arg{token list_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{token list_1} and \meta{token list_2} contain the
+% same list of tokens, both in respect of character codes and category
+% codes. This conditional is not expandable: see \cs{tl_if_eq:NNTF}
+% for an expandable version when token lists are stored in variables,
+% or \cs{str_if_eq:nnTF} if category codes are not important.
+% \end{function}
+%
+% \begin{function}[TF]
+% {
+% \tl_if_in:Nn, \tl_if_in:NV, \tl_if_in:No,
+% \tl_if_in:cn, \tl_if_in:cV, \tl_if_in:co
+% }
+% \begin{syntax}
+% \cs{tl_if_in:NnTF} \meta{tl~var} \Arg{token list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token list} is found in the content of the
+% \meta{tl~var}. The \meta{token list} cannot contain
+% the tokens |{|, |}| or |#|
+% (more precisely, explicit character tokens with category code $1$
+% (begin-group) or $2$ (end-group), and tokens with category code $6$).
+% \end{function}
+%
+% \begin{function}[TF]
+% {
+% \tl_if_in:nn, \tl_if_in:Vn, \tl_if_in:VV, \tl_if_in:on, \tl_if_in:oo,
+% \tl_if_in:nV, \tl_if_in:no
+% }
+% \begin{syntax}
+% \cs{tl_if_in:nnTF} \Arg{token list_1} \Arg{token list_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{token list_2} is found inside \meta{token list_1}.
+% The \meta{token list_2} cannot contain the tokens |{|, |}| or |#|
+% (more precisely, explicit character tokens with category code $1$
+% (begin-group) or $2$ (end-group), and tokens with category code $6$).
+% The search does \emph{not} enter brace (category code $1$/$2$) groups.
+% \end{function}
+%
+% \begin{function}[added = 2017-11-14, EXP,pTF]{\tl_if_novalue:n}
+% \begin{syntax}
+% \cs{tl_if_novalue_p:n} \Arg{token list}
+% \cs{tl_if_novalue:nTF} \Arg{token list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token list} and the special \cs{c_novalue_tl} marker
+% contain the same list of tokens, both in respect of character codes and
+% category codes. This means that
+% \cs{exp_args:No} \cs{tl_if_novalue:nTF} \{ \cs{c_novalue_tl} \} is
+% logically \texttt{true} but \cs{tl_if_novalue:nTF} \{ \cs{c_novalue_tl} \}
+% is logically \texttt{false}.
+% This function is intended to allow construction
+% of flexible document interface structures in which missing optional
+% arguments are detected.
+% \end{function}
+%
+% \begin{function}[updated = 2011-08-13, EXP,pTF]
+% {\tl_if_single:N, \tl_if_single:c}
+% \begin{syntax}
+% \cs{tl_if_single_p:N} \meta{tl~var}
+% \cs{tl_if_single:NTF} \meta{tl~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the content of the \meta{tl~var} consists of a single \meta{item},
+% \emph{i.e.}~is a single normal token (neither an explicit space
+% character nor a begin-group character) or a single brace group,
+% surrounded by optional spaces on both sides. In other words, such a
+% token list has token count $1$ according to \cs{tl_count:N}.
+% \end{function}
+%
+% \begin{function}[updated = 2011-08-13, EXP,pTF]{\tl_if_single:n}
+% \begin{syntax}
+% \cs{tl_if_single_p:n} \Arg{token list}
+% \cs{tl_if_single:nTF} \Arg{token list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token list} has exactly one \meta{item}, \emph{i.e.}~is
+% a single normal token (neither an explicit space character nor a
+% begin-group character) or a single brace group, surrounded by
+% optional spaces on both sides. In other words, such a token list has
+% token count $1$ according to \cs{tl_count:n}.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\tl_if_single_token:n}
+% \begin{syntax}
+% \cs{tl_if_single_token_p:n} \Arg{token list}
+% \cs{tl_if_single_token:nTF} \Arg{token list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the token list consists of exactly one token, \emph{i.e.}~is
+% either a single space character or a single normal token.
+% Token groups (|{|\ldots|}|) are not single tokens.
+% \end{function}
+%
+% \subsection{Testing the first token}
+%
+% \begin{function}[updated = 2012-07-09, EXP, pTF]
+% {
+% \tl_if_head_eq_catcode:nN, \tl_if_head_eq_catcode:VN,
+% \tl_if_head_eq_catcode:eN, \tl_if_head_eq_catcode:oN
+% }
+% \begin{syntax}
+% \cs{tl_if_head_eq_catcode_p:nN} \Arg{token list} \meta{test token}
+% \cs{tl_if_head_eq_catcode:nNTF} \Arg{token list} \meta{test token}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the first \meta{token} in the \meta{token list} has the
+% same category code as the \meta{test token}. In the case where the
+% \meta{token list} is empty, the test is always \texttt{false}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-07-09, EXP, pTF]
+% {
+% \tl_if_head_eq_charcode:nN, \tl_if_head_eq_charcode:VN,
+% \tl_if_head_eq_charcode:eN, \tl_if_head_eq_charcode:fN
+% }
+% \begin{syntax}
+% \cs{tl_if_head_eq_charcode_p:nN} \Arg{token list} \meta{test token}
+% \cs{tl_if_head_eq_charcode:nNTF} \Arg{token list} \meta{test token}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the first \meta{token} in the \meta{token list} has the
+% same character code as the \meta{test token}. In the case where the
+% \meta{token list} is empty, the test is always \texttt{false}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-07-09, EXP, pTF]
+% {
+% \tl_if_head_eq_meaning:nN, \tl_if_head_eq_meaning:VN,
+% \tl_if_head_eq_meaning:eN
+% }
+% \begin{syntax}
+% \cs{tl_if_head_eq_meaning_p:nN} \Arg{token list} \meta{test token}
+% \cs{tl_if_head_eq_meaning:nNTF} \Arg{token list} \meta{test token}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the first \meta{token} in the \meta{token list} has the
+% same meaning as the \meta{test token}. In the case where
+% \meta{token list} is empty, the test is always \texttt{false}.
+% \end{function}
+%
+% \begin{function}[added = 2012-07-08, EXP, pTF]{\tl_if_head_is_group:n}
+% \begin{syntax}
+% \cs{tl_if_head_is_group_p:n} \Arg{token list}
+% \cs{tl_if_head_is_group:nTF} \Arg{token list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the first \meta{token} in the \meta{token list}
+% is an explicit begin-group character (with category code~$1$
+% and any character code), in other words, if the \meta{token list}
+% starts with a brace group. In particular, the test is \texttt{false}
+% if the \meta{token list} starts with an implicit token such as
+% \cs{c_group_begin_token}, or if it is empty.
+% This function is useful to implement actions on token lists on
+% a token by token basis.
+% \end{function}
+%
+% \begin{function}[added = 2012-07-08, EXP, pTF]{\tl_if_head_is_N_type:n}
+% \begin{syntax}
+% \cs{tl_if_head_is_N_type_p:n} \Arg{token list}
+% \cs{tl_if_head_is_N_type:nTF} \Arg{token list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the first \meta{token} in the \meta{token list}
+% is a normal \texttt{N}-type argument. In other words,
+% it is neither an explicit space character
+% (explicit token with character code~$32$ and category code~$10$)
+% nor an explicit begin-group character
+% (with category code~1 and any character code). An empty
+% argument yields \texttt{false}, as it does not have a normal
+% first token.
+% This function is useful to implement actions on token lists on
+% a token by token basis.
+% \end{function}
+%
+% \begin{function}[updated = 2012-07-08, EXP, pTF]{\tl_if_head_is_space:n}
+% \begin{syntax}
+% \cs{tl_if_head_is_space_p:n} \Arg{token list}
+% \cs{tl_if_head_is_space:nTF} \Arg{token list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the first \meta{token} in the \meta{token list}
+% is an explicit space character
+% (explicit token with character code~$32$ and category code~$10$).
+% In particular, the test is \texttt{false} if the \meta{token list}
+% starts with an implicit token such as \cs{c_space_token}, or if it
+% is empty.
+% This function is useful to implement actions on token lists on
+% a token by token basis.
+% \end{function}
+%
+% \section{Working with token lists as a whole}
+%
+% \subsection{Using token lists}
+%
+% \begin{function}[EXP]
+% {\tl_to_str:n, \tl_to_str:o, \tl_to_str:V, \tl_to_str:v, \tl_to_str:e}
+% \begin{syntax}
+% \cs{tl_to_str:n} \Arg{token list}
+% \end{syntax}
+% Converts the \meta{token list} to a \meta{string}, leaving the
+% resulting character tokens in the input stream. A \meta{string}
+% is a series of tokens with category code $12$ (other) with the exception
+% of spaces, which retain category code $10$ (space).
+% The base function requires only a single expansion.
+% Its argument \emph{must} be braced.
+% \begin{texnote}
+% This is the \eTeX{} primitive \tn{detokenize}.
+% Converting a \meta{token list} to a \meta{string} yields a
+% concatenation of the string representations of every token in the
+% \meta{token list}.
+% The string representation of a control sequence is
+% \begin{itemize}
+% \item an escape character, whose character code is given by the
+% internal parameter \tn{escapechar}, absent if the
+% \tn{escapechar} is negative or greater than the largest
+% character code;
+% \item the control sequence name, as defined by \cs{cs_to_str:N};
+% \item a space, unless the control sequence name is a single
+% character whose category at the time of expansion of
+% \cs{tl_to_str:n} is not \enquote{letter}.
+% \end{itemize}
+% The string representation of an explicit character token is that
+% character, doubled in the case of (explicit) macro parameter
+% characters (normally |#|).
+% In particular, the string representation of a token list may
+% depend on the category codes in effect when it is evaluated, and
+% the value of the \tn{escapechar}: for instance |\tl_to_str:n {\a}|
+% normally produces the three character \enquote{backslash},
+% \enquote{lower-case a}, \enquote{space}, but it may also produce a
+% single \enquote{lower-case a} if the escape character is negative
+% and \texttt{a} is currently not a letter.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\tl_to_str:N, \tl_to_str:c}
+% \begin{syntax}
+% \cs{tl_to_str:N} \meta{tl~var}
+% \end{syntax}
+% Converts the content of the \meta{tl~var} into a series of characters
+% with category code $12$ (other) with the exception of spaces, which
+% retain category code $10$ (space). This \meta{string} is then left
+% in the input stream. For low-level details, see the notes given for
+% \cs{tl_to_str:n}.
+% \end{function}
+%
+% \begin{function}[EXP]{\tl_use:N, \tl_use:c}
+% \begin{syntax}
+% \cs{tl_use:N} \meta{tl~var}
+% \end{syntax}
+% Recovers the content of a \meta{tl~var} and places it
+% directly in the input stream. An error is raised if the variable
+% does not exist or if it is invalid. Note that it is possible to use
+% a \meta{tl~var} directly without an accessor function.
+% \end{function}
+%
+% \subsection{Counting and reversing token lists}
+%
+% \begin{function}[added = 2012-05-13, EXP]
+% {\tl_count:n, \tl_count:V, \tl_count:v, \tl_count:e, \tl_count:o}
+% \begin{syntax}
+% \cs{tl_count:n} \Arg{token list}
+% \end{syntax}
+% Counts the number of \meta{items} in the \meta{token list} and leaves this
+% information in the input stream. Unbraced tokens count as one
+% element as do each token group (|{|\ldots|}|). This process
+% ignores any unprotected spaces within the \meta{token list}. See also
+% \cs{tl_count:N}. This function requires three expansions,
+% giving an \meta{integer denotation}.
+% \end{function}
+%
+% \begin{function}[added = 2012-05-13, EXP]{\tl_count:N, \tl_count:c}
+% \begin{syntax}
+% \cs{tl_count:N} \meta{tl~var}
+% \end{syntax}
+% Counts the number of \meta{items} in the \meta{tl~var}
+% and leaves this information in the input stream. Unbraced tokens
+% count as one element as do each token group (|{|\ldots|}|). This
+% process ignores any unprotected spaces within the \meta{tl~var}.
+% See also \cs{tl_count:n}. This function requires three expansions,
+% giving an \meta{integer denotation}.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2019-02-25]{\tl_count_tokens:n}
+% \begin{syntax}
+% \cs{tl_count_tokens:n} \Arg{token list}
+% \end{syntax}
+% Counts the number of \TeX{} tokens in the \meta{token list} and leaves
+% this information in the input stream. Every token, including spaces and
+% braces, contributes one to the total; thus for instance, the token count of
+% |a~{bc}| is $6$.
+% \end{function}
+%
+% \begin{function}[updated = 2012-01-08, EXP]
+% {\tl_reverse:n, \tl_reverse:V, \tl_reverse:o, \tl_reverse:f, \tl_reverse:e}
+% \begin{syntax}
+% \cs{tl_reverse:n} \Arg{token list}
+% \end{syntax}
+% Reverses the order of the \meta{items} in the \meta{token list},
+% so that \meta{item_1}\meta{item_2}\meta{item_3} \ldots \meta{item_n}
+% becomes \meta{item_n}\ldots \meta{item_3}\meta{item_2}\meta{item_1}.
+% This process preserves unprotected space within the
+% \meta{token list}. Tokens are not reversed within braced token
+% groups, which keep their outer set of braces.
+% In situations where performance is important,
+% consider \cs{tl_reverse_items:n}.
+% See also \cs{tl_reverse:N}.
+% \begin{texnote}
+% The result is returned within \tn{unexpanded}, which means that the token
+% list does not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[updated = 2012-01-08]
+% {\tl_reverse:N, \tl_reverse:c, \tl_greverse:N, \tl_greverse:c}
+% \begin{syntax}
+% \cs{tl_reverse:N} \meta{tl~var}
+% \end{syntax}
+% Sets the \meta{tl~var} to contain the result of reversing
+% the order of its \meta{items}, so
+% that \meta{item_1}\meta{item_2}\meta{item_3} \ldots \meta{item_n}
+% becomes \meta{item_n}\ldots \meta{item_3}\meta{item_2}\meta{item_1}.
+% This process preserves unprotected spaces within the
+% \meta{tl~var}. Braced token groups are copied without
+% reversing the order of tokens, but keep the outer set of braces.
+% This is equivalent to a combination of an assignment and
+% \cs{tl_reverse:V}. See also \cs{tl_reverse_items:n} for improved
+% performance.
+% \end{function}
+%
+% \begin{function}[added = 2012-01-08, EXP]{\tl_reverse_items:n}
+% \begin{syntax}
+% \cs{tl_reverse_items:n} \Arg{token list}
+% \end{syntax}
+% Reverses the order of the \meta{items} in the \meta{token list},
+% so that \meta{item_1}\meta{item_2}\meta{item_3} \ldots \meta{item_n}
+% becomes \Arg{item_n} \ldots{} \Arg{item_3}\Arg{item_2}\Arg{item_1}.
+% This process removes any unprotected space within the
+% \meta{token list}. Braced token groups are copied without
+% reversing the order of tokens, and keep the outer set of braces.
+% Items which are initially not braced are copied with braces in
+% the result. In cases where preserving spaces is important,
+% consider the slower function \cs{tl_reverse:n}.
+% \begin{texnote}
+% The result is returned within \tn{unexpanded}, which means that the token
+% list does not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2011-07-09, updated = 2012-06-25, EXP]
+% {
+% \tl_trim_spaces:n, \tl_trim_spaces:V, \tl_trim_spaces:v,
+% \tl_trim_spaces:e,
+% \tl_trim_spaces:o
+% }
+% \begin{syntax}
+% \cs{tl_trim_spaces:n} \Arg{token list}
+% \end{syntax}
+% Removes any leading and trailing explicit space characters
+% (explicit tokens with character code~$32$ and category code~$10$)
+% from the \meta{token list} and leaves the result in the input
+% stream.
+% \begin{texnote}
+% The result is returned within \tn{unexpanded}, which means that the token
+% list does not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2018-04-12, EXP]
+% {\tl_trim_spaces_apply:nN, \tl_trim_spaces_apply:oN}
+% \begin{syntax}
+% \cs{tl_trim_spaces_apply:nN} \Arg{token list} \meta{function}
+% \end{syntax}
+% Removes any leading and trailing explicit space characters (explicit
+% tokens with character code~$32$ and category code~$10$) from the
+% \meta{token list} and passes the result to the \meta{function} as an
+% \texttt{n}-type argument.
+% \end{function}
+%
+% \begin{function}[added = 2011-07-09]
+% {
+% \tl_trim_spaces:N, \tl_trim_spaces:c,
+% \tl_gtrim_spaces:N, \tl_gtrim_spaces:c
+% }
+% \begin{syntax}
+% \cs{tl_trim_spaces:N} \meta{tl~var}
+% \end{syntax}
+% Sets the \meta{tl~var} to contain the result of removing any leading
+% and trailing explicit space characters (explicit tokens with
+% character code~$32$ and category code~$10$) from its contents.
+% \end{function}
+%
+% \subsection{Viewing token lists}
+%
+% \begin{function}[updated = 2021-04-29]{\tl_show:N, \tl_show:c}
+% \begin{syntax}
+% \cs{tl_show:N} \meta{tl~var}
+% \end{syntax}
+% Displays the content of the \meta{tl~var} on the terminal.
+% \begin{texnote}
+% This is similar to the \TeX{} primitive \tn{show}, wrapped to a
+% fixed number of characters per line.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[updated = 2015-08-07]{\tl_show:n, \tl_show:e}
+% \begin{syntax}
+% \cs{tl_show:n} \Arg{token list}
+% \end{syntax}
+% Displays the \meta{token list} on the terminal.
+% \begin{texnote}
+% This is similar to the \eTeX{} primitive \tn{showtokens}, wrapped
+% to a fixed number of characters per line.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2021-04-29]{\tl_log:N, \tl_log:c}
+% \begin{syntax}
+% \cs{tl_log:N} \meta{tl~var}
+% \end{syntax}
+% Writes the content of the \meta{tl~var} in the log file. See also
+% \cs{tl_show:N} which displays the result in the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2015-08-07]
+% {\tl_log:n, \tl_log:e, \tl_log:x}
+% \begin{syntax}
+% \cs{tl_log:n} \Arg{token list}
+% \end{syntax}
+% Writes the \meta{token list} in the log file. See also
+% \cs{tl_show:n} which displays the result in the terminal.
+% \end{function}
+%
+% \section{Manipulating items in token lists}
+%
+% \subsection{Mapping over token lists}
+%
+% All mappings are done at the current group level, \emph{i.e.}~any
+% local assignments made by the \meta{function} or \meta{code} discussed
+% below remain in effect after the loop.
+%
+% \begin{function}[updated = 2012-06-29, rEXP]
+% {\tl_map_function:NN, \tl_map_function:cN}
+% \begin{syntax}
+% \cs{tl_map_function:NN} \meta{tl~var} \meta{function}
+% \end{syntax}
+% Applies \meta{function} to every \meta{item} in the \meta{tl~var}.
+% The \meta{function} receives one argument for each iteration.
+% This may be a number of tokens if the \meta{item} was stored within
+% braces. Hence the \meta{function} should anticipate receiving
+% \texttt{n}-type arguments. See also \cs{tl_map_function:nN}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-06-29, rEXP]{\tl_map_function:nN}
+% \begin{syntax}
+% \cs{tl_map_function:nN} \Arg{token list} \meta{function}
+% \end{syntax}
+% Applies \meta{function} to every \meta{item} in the \meta{token list},
+% The \meta{function} receives one argument for each iteration.
+% This may be a number of tokens if the \meta{item} was stored within
+% braces. Hence the \meta{function} should anticipate receiving
+% \texttt{n}-type arguments. See also \cs{tl_map_function:NN}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-06-29]
+% {\tl_map_inline:Nn, \tl_map_inline:cn}
+% \begin{syntax}
+% \cs{tl_map_inline:Nn} \meta{tl~var} \Arg{inline function}
+% \end{syntax}
+% Applies the \meta{inline function} to every \meta{item} stored within the
+% \meta{tl~var}. The \meta{inline function} should consist of code which
+% receives the \meta{item} as |#1|. See also \cs{tl_map_function:NN}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-06-29]{\tl_map_inline:nn}
+% \begin{syntax}
+% \cs{tl_map_inline:nn} \Arg{token list} \Arg{inline function}
+% \end{syntax}
+% Applies the \meta{inline function} to every \meta{item} stored within the
+% \meta{token list}. The \meta{inline function} should consist of code which
+% receives the \meta{item} as |#1|. See also \cs{tl_map_function:nN}.
+% \end{function}
+%
+% \begin{function}[rEXP, added = 2019-09-02]
+% {\tl_map_tokens:Nn, \tl_map_tokens:cn, \tl_map_tokens:nn}
+% \begin{syntax}
+% \cs{tl_map_tokens:Nn} \meta{tl~var} \Arg{code}
+% \cs{tl_map_tokens:nn} \Arg{token list} \Arg{code}
+% \end{syntax}
+% Analogue of \cs{tl_map_function:NN} which maps several tokens
+% instead of a single function. The \meta{code} receives each \meta{item} in
+% the \meta{tl~var} or in the \meta{token list} as a trailing brace group. For
+% instance,
+% \begin{verbatim}
+% \tl_map_tokens:Nn \l_my_tl { \prg_replicate:nn { 2 } }
+% \end{verbatim}
+% expands to twice each \meta{item} in the \meta{tl~var}: for each \meta{item} in
+% \cs[no-index]{l_my_tl} the function \cs{prg_replicate:nn} receives |2| and
+% \meta{item} as its two arguments. The function
+% \cs{tl_map_inline:Nn} is typically faster but is not expandable.
+% \end{function}
+%
+% \begin{function}[updated = 2012-06-29]
+% {\tl_map_variable:NNn, \tl_map_variable:cNn}
+% \begin{syntax}
+% \cs{tl_map_variable:NNn} \meta{tl~var} \meta{variable} \Arg{code}
+% \end{syntax}
+% Stores each \meta{item} of the \meta{tl~var} in turn in the (token
+% list) \meta{variable} and applies the \meta{code}. The \meta{code}
+% will usually make use of the \meta{variable}, but this is not
+% enforced. The assignments to the \meta{variable} are local. Its
+% value after the loop is the last \meta{item} in the \meta{tl~var},
+% or its original value if the \meta{tl~var} is blank. See also
+% \cs{tl_map_inline:Nn}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-06-29]{\tl_map_variable:nNn}
+% \begin{syntax}
+% \cs{tl_map_variable:nNn} \Arg{token list} \meta{variable} \Arg{code}
+% \end{syntax}
+% Stores each \meta{item} of the \meta{token list} in turn in the
+% (token list) \meta{variable} and applies the \meta{code}. The
+% \meta{code} will usually make use of the \meta{variable}, but this
+% is not enforced. The assignments to the \meta{variable} are local.
+% Its value after the loop is the last \meta{item} in the
+% \meta{tl~var}, or its original value if the \meta{tl~var} is blank.
+% See also \cs{tl_map_inline:nn}.
+% \end{function}
+%
+% \begin{function}[updated = 2012-06-29, rEXP]{\tl_map_break:}
+% \begin{syntax}
+% \cs{tl_map_break:}
+% \end{syntax}
+% Used to terminate a \cs[no-index]{tl_map_\ldots} function before all
+% entries in the \meta{token list} have been processed. This
+% normally takes place within a conditional statement, for example
+% \begin{verbatim}
+% \tl_map_inline:Nn \l_my_tl
+% {
+% \str_if_eq:nnT { #1 } { bingo } { \tl_map_break: }
+% % Do something useful
+% }
+% \end{verbatim}
+% See also \cs{tl_map_break:n}.
+% Use outside of a \cs[no-index]{tl_map_\ldots} scenario leads to low
+% level \TeX{} errors.
+% \begin{texnote}
+% When the mapping is broken, additional tokens may be inserted
+% before further items are taken
+% from the input stream. This depends on the design of the mapping
+% function.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[updated = 2012-06-29, rEXP]{\tl_map_break:n}
+% \begin{syntax}
+% \cs{tl_map_break:n} \Arg{code}
+% \end{syntax}
+% Used to terminate a \cs[no-index]{tl_map_\ldots} function before all
+% entries in the \meta{token list} have been processed, inserting
+% the \meta{code} after the mapping has ended. This
+% normally takes place within a conditional statement, for example
+% \begin{verbatim}
+% \tl_map_inline:Nn \l_my_tl
+% {
+% \str_if_eq:nnT { #1 } { bingo }
+% { \tl_map_break:n { <code> } }
+% % Do something useful
+% }
+% \end{verbatim}
+% Use outside of a \cs[no-index]{tl_map_\ldots} scenario leads to low
+% level \TeX{} errors.
+% \begin{texnote}
+% When the mapping is broken, additional tokens may be inserted
+% before the \meta{code} is
+% inserted into the input stream.
+% This depends on the design of the mapping function.
+% \end{texnote}
+% \end{function}
+%
+% \subsection{Head and tail of token lists}
+%
+% Functions which deal with either only the very first item (balanced
+% text or single normal token) in a token list, or the remaining tokens.
+%
+% \begin{function}[updated = 2012-09-09, EXP]
+% {\tl_head:N, \tl_head:n, \tl_head:V, \tl_head:v, \tl_head:f}
+% \begin{syntax}
+% \cs{tl_head:n} \Arg{token list}
+% \end{syntax}
+% Leaves in the input stream the first \meta{item} in the
+% \meta{token list}, discarding the rest of the \meta{token list}.
+% All leading explicit space characters
+% (explicit tokens with character code~$32$ and category code~$10$)
+% are discarded; for example
+% \begin{verbatim}
+% \tl_head:n { abc }
+% \end{verbatim}
+% and
+% \begin{verbatim}
+% \tl_head:n { ~ abc }
+% \end{verbatim}
+% both leave |a| in the input stream. If the \enquote{head} is a
+% brace group, rather than a single token, the braces are removed, and
+% so
+% \begin{verbatim}
+% \tl_head:n { ~ { ~ ab } c }
+% \end{verbatim}
+% yields \verb*| ab|.
+% A blank \meta{token list} (see \cs{tl_if_blank:nTF}) results in
+% \cs{tl_head:n} leaving nothing in the input stream.
+% \begin{texnote}
+% The result is returned within \cs{exp_not:n}, which means that the token
+% list does not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\tl_head:w}
+% \begin{syntax}
+% \cs{tl_head:w} \meta{token list} | { } | \cs{q_stop}
+% \end{syntax}
+% Leaves in the input stream the first \meta{item} in the
+% \meta{token list}, discarding the rest of the \meta{token list}.
+% All leading explicit space characters
+% (explicit tokens with character code~$32$ and category code~$10$)
+% are discarded.
+% A blank \meta{token list} (which consists only of space characters)
+% results in a low-level \TeX{} error, which may be avoided by the
+% inclusion of an empty group in the input (as shown), without the need
+% for an explicit test. Alternatively, \cs{tl_if_blank:nF} may be used to
+% avoid using the function with a \enquote{blank} argument.
+% This function requires only a single expansion, and thus is suitable for
+% use within an \texttt{o}-type expansion. In general, \cs{tl_head:n} should
+% be preferred if the number of expansions is not critical.
+% \end{function}
+%
+% \begin{function}[updated = 2012-09-01, EXP]
+% {\tl_tail:N, \tl_tail:n, \tl_tail:V, \tl_tail:v, \tl_tail:f}
+% \begin{syntax}
+% \cs{tl_tail:n} \Arg{token list}
+% \end{syntax}
+% Discards all leading explicit space characters
+% (explicit tokens with character code~$32$ and category code~$10$)
+% and the first \meta{item} in the \meta{token list}, and leaves the
+% remaining tokens in the input stream. Thus for example
+% \begin{verbatim}
+% \tl_tail:n { a ~ {bc} d }
+% \end{verbatim}
+% and
+% \begin{verbatim}
+% \tl_tail:n { ~ a ~ {bc} d }
+% \end{verbatim}
+% both leave \verb*| {bc}d| in the input stream. A blank
+% \meta{token list} (see \cs{tl_if_blank:nTF}) results
+% in \cs{tl_tail:n} leaving nothing in the input stream.
+% \begin{texnote}
+% The result is returned within \cs{exp_not:n}, which means that the
+% token list does not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% If you wish to handle token lists where the first token may be a space, and
+% this needs to be treated as the head/tail, this can be accomplished using
+% \cs{tl_if_head_is_space:nTF}, for example
+% \begin{verbatim}
+% \exp_last_unbraced:NNo
+% \cs_new:Npn \__mypkg_gobble_space:w \c_space_tl { }
+% \cs_new:Npn \mypkg_tl_head_keep_space:n #1
+% {
+% \tl_if_head_is_space:nTF {#1}
+% { ~ }
+% { \tl_head:n {#1} }
+% }
+% \cs_new:Npn \mypkg_tl_tail_keep_space:n #1
+% {
+% \tl_if_head_is_space:nTF {#1}
+% { \exp_not:o { \__mypkg_gobble_space:w #1 } }
+% { \tl_tail:n {#1} }
+% }
+% \end{verbatim}
+%
+% \subsection{Items and ranges in token lists}
+%
+% \begin{function}[added = 2014-07-17, EXP]
+% {\tl_item:nn, \tl_item:Nn, \tl_item:cn}
+% \begin{syntax}
+% \cs{tl_item:nn} \Arg{token list} \Arg{integer expression}
+% \end{syntax}
+% Indexing items in the \meta{token list} from~$1$ on the left, this
+% function evaluates the \meta{integer expression} and leaves the
+% appropriate item from the \meta{token list} in the input stream.
+% If the \meta{integer expression} is negative, indexing occurs from
+% the right of the token list, starting at $-1$ for the right-most item.
+% If the index is out of bounds, then the function expands to nothing.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{item}
+% does not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2016-12-06]
+% {\tl_rand_item:N, \tl_rand_item:c, \tl_rand_item:n}
+% \begin{syntax}
+% \cs{tl_rand_item:N} \meta{tl~var}
+% \cs{tl_rand_item:n} \Arg{token list}
+% \end{syntax}
+% Selects a pseudo-random item of the \meta{token list}. If the
+% \meta{token list} is blank, the result is empty.
+% This is not available in older versions of \XeTeX{}.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{item}
+% does not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2017-02-17, updated = 2017-07-15]
+% {\tl_range:Nnn, \tl_range:nnn}
+% \begin{syntax}
+% \cs{tl_range:Nnn} \meta{tl~var} \Arg{start index} \Arg{end index}
+% \cs{tl_range:nnn} \Arg{token list} \Arg{start index} \Arg{end index}
+% \end{syntax}
+% Leaves in the input stream the items from the \meta{start index} to the
+% \meta{end index} inclusive. Spaces and braces are preserved between
+% the items returned (but never at either end of the list).
+% Here \meta{start index} and \meta{end index} should be \meta{integer expressions}.
+% For describing in detail the functions' behavior, let $m$ and $n$ be the start
+% and end index respectively. If either is $0$, the result is empty. A positive
+% index means `start counting from the left end', and a negative index means
+% `from the right end'. Let $l$ be the count of the token list.
+%
+% The \emph{actual start point} is determined as $M=m$ if~$m>0$ and as $M=l+m+1$
+% if~$m<0$. Similarly the \emph{actual end point} is $N=n$ if~$n>0$ and $N=l+n+1$
+% if~$n<0$. If $M>N$, the result is empty. Otherwise it consists of all items from
+% position $M$ to position $N$ inclusive; for the purpose of this rule, we can
+% imagine that the token list extends at infinity on either side, with void items
+% at positions $s$ for $s\le0$ or $s>l$.
+%
+% Spaces in between items in the actual range are preserved. Spaces at either end
+% of the token list will be removed anyway (think to the token list being passed to
+% |\tl_trim_spaces:n| to begin with.
+%
+% Thus, with $l=7$ as in the examples below, all of the following are equivalent
+% and result in the whole token list
+% \begin{verbatim}
+% \tl_range:nnn { abcd~{e{}}fg } { 1 } { 7 }
+% \tl_range:nnn { abcd~{e{}}fg } { 1 } { 12 }
+% \tl_range:nnn { abcd~{e{}}fg } { -7 } { 7 }
+% \tl_range:nnn { abcd~{e{}}fg } { -12 } { 7 }
+% \end{verbatim}
+% Here are some more interesting examples. The calls
+% \begin{verbatim}
+% \iow_term:e { \tl_range:nnn { abcd{e{}}fg } { 2 } { 5 } }
+% \iow_term:e { \tl_range:nnn { abcd{e{}}fg } { 2 } { -3 } }
+% \iow_term:e { \tl_range:nnn { abcd{e{}}fg } { -6 } { 5 } }
+% \iow_term:e { \tl_range:nnn { abcd{e{}}fg } { -6 } { -3 } }
+% \end{verbatim}
+% are all equivalent and will print |bcd{e{}}| on the terminal; similarly
+% \begin{verbatim}
+% \iow_term:e { \tl_range:nnn { abcd~{e{}}fg } { 2 } { 5 } }
+% \iow_term:e { \tl_range:nnn { abcd~{e{}}fg } { 2 } { -3 } }
+% \iow_term:e { \tl_range:nnn { abcd~{e{}}fg } { -6 } { 5 } }
+% \iow_term:e { \tl_range:nnn { abcd~{e{}}fg } { -6 } { -3 } }
+% \end{verbatim}
+% are all equivalent and will print |bcd {e{}}| on the
+% terminal (note the space in the middle). To the contrary,
+% \begin{verbatim}
+% \tl_range:nnn { abcd~{e{}}f } { 2 } { 4 }
+% \end{verbatim}
+% will discard the space after `d'.
+%
+% If we want to get the items from, say, the third to the last in a token
+% list |<tl>|, the call
+% is |\tl_range:nnn { <tl> } { 3 } { -1 }|. Similarly, for discarding
+% the last item, we can do |\tl_range:nnn { <tl> } { 1 } { -2 }|.
+%
+%^^A The behavior of \cs{tl_range:Nnn} is exactly the same, acting on the
+%^^A contents of the tl variable.
+%
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{item}
+% does not expand further when appearing in an \texttt{e}-type
+% or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \subsection{Sorting token lists}
+%
+% \begin{function}[added = 2017-02-06]
+% {\tl_sort:Nn, \tl_sort:cn, \tl_gsort:Nn, \tl_gsort:cn}
+% \begin{syntax}
+% \cs{tl_sort:Nn} \meta{tl var} \Arg{comparison code}
+% \end{syntax}
+% Sorts the items in the \meta{tl var} according to the
+% \meta{comparison code}, and assigns the result to
+% \meta{tl var}. The details of sorting comparison are
+% described in Section~\ref{sec:l3sort:mech}.
+% \end{function}
+%
+% \begin{function}[added = 2017-02-06, EXP]{\tl_sort:nN}
+% \begin{syntax}
+% \cs{tl_sort:nN} \Arg{token list} \meta{conditional}
+% \end{syntax}
+% Sorts the items in the \meta{token list}, using the
+% \meta{conditional} to compare items, and leaves the result in the
+% input stream. The \meta{conditional} should have signature |:nnTF|,
+% and return \texttt{true} if the two items being compared should be
+% left in the same order, and \texttt{false} if the items should be
+% swapped. The details of sorting comparison are
+% described in Section~\ref{sec:l3sort:mech}.
+% \begin{texnote}
+% The result is returned within \cs{exp_not:n}, which means that the
+% token list does not expand further when appearing in an
+% \texttt{e}-type or \texttt{x}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \section{Manipulating tokens in token lists}
+%
+% \subsection{Replacing tokens}
+%
+% Within token lists, replacement takes place at the top level: there is
+% no recursion into brace groups (more precisely, within a group defined by
+% a categroy code $1$/$2$ pair).
+%
+% \begin{function}[updated = 2011-08-11]
+% {
+% \tl_replace_once:Nnn, \tl_replace_once:NVn, \tl_replace_once:NnV,
+% \tl_replace_once:Nen, \tl_replace_once:Nne, \tl_replace_once:Nee,
+% \tl_replace_once:cnn, \tl_replace_once:cVn, \tl_replace_once:cnV,
+% \tl_replace_once:cen, \tl_replace_once:cne, \tl_replace_once:cee,
+% \tl_greplace_once:Nnn, \tl_greplace_once:NVn, \tl_greplace_once:NnV,
+% \tl_greplace_once:Nen, \tl_greplace_once:Nne, \tl_greplace_once:Nee,
+% \tl_greplace_once:cnn, \tl_greplace_once:cVn, \tl_greplace_once:cnV,
+% \tl_greplace_once:cen, \tl_greplace_once:cne, \tl_greplace_once:cee
+% }
+% \begin{syntax}
+% \cs{tl_replace_once:Nnn} \meta{tl~var} \Arg{old tokens} \Arg{new tokens}
+% \end{syntax}
+% Replaces the first (leftmost) occurrence of \meta{old tokens} in the
+% \meta{tl~var} with \meta{new tokens}. \meta{Old tokens}
+% cannot contain |{|, |}| or |#|
+% (more precisely, explicit character tokens with category code $1$
+% (begin-group) or $2$ (end-group), and tokens with category code $6$).
+% \end{function}
+%
+% \begin{function}[updated = 2011-08-11]
+% {
+% \tl_replace_all:Nnn, \tl_replace_all:NVn, \tl_replace_all:NnV,
+% \tl_replace_all:Nen, \tl_replace_all:Nne, \tl_replace_all:Nee,
+% \tl_replace_all:cnn, \tl_replace_all:cVn, \tl_replace_all:cnV,
+% \tl_replace_all:cen, \tl_replace_all:cne, \tl_replace_all:cee,
+% \tl_greplace_all:Nnn, \tl_greplace_all:NVn, \tl_greplace_all:NnV,
+% \tl_greplace_all:Nen, \tl_greplace_all:Nne, \tl_greplace_all:Nee,
+% \tl_greplace_all:cnn, \tl_greplace_all:cVn, \tl_greplace_all:cnV,
+% \tl_greplace_all:cen, \tl_greplace_all:cne, \tl_greplace_all:cee
+% }
+% \begin{syntax}
+% \cs{tl_replace_all:Nnn} \meta{tl~var} \Arg{old tokens} \Arg{new tokens}
+% \end{syntax}
+% Replaces all occurrences of \meta{old tokens} in the
+% \meta{tl~var} with \meta{new tokens}. \meta{Old tokens}
+% cannot contain |{|, |}| or |#|
+% (more precisely, explicit character tokens with category code $1$
+% (begin-group) or $2$ (end-group), and tokens with category code $6$).
+% As this function
+% operates from left to right, the pattern \meta{old tokens}
+% may remain after the replacement (see \cs{tl_remove_all:Nn}
+% for an example).
+% \end{function}
+%
+% \begin{function}[updated = 2011-08-11]
+% {
+% \tl_remove_once:Nn, \tl_remove_once:NV, \tl_remove_once:Ne,
+% \tl_remove_once:cn, \tl_remove_once:cV, \tl_remove_once:ce,
+% \tl_gremove_once:Nn, \tl_gremove_once:NV, \tl_gremove_once:Ne,
+% \tl_gremove_once:cn, \tl_gremove_once:cV, \tl_gremove_once:ce
+% }
+% \begin{syntax}
+% \cs{tl_remove_once:Nn} \meta{tl~var} \Arg{tokens}
+% \end{syntax}
+% Removes the first (leftmost) occurrence of \meta{tokens} from the
+% \meta{tl~var}. The \meta{tokens} cannot contain |{|, |}| or |#|
+% (more precisely, explicit character tokens with category code $1$
+% (begin-group) or $2$ (end-group), and tokens with category code $6$).
+% \end{function}
+%
+% \begin{function}[updated = 2011-08-11]
+% {
+% \tl_remove_all:Nn, \tl_remove_all:NV, \tl_remove_all:Ne,
+% \tl_remove_all:cn, \tl_remove_all:cV, \tl_remove_all:ce,
+% \tl_gremove_all:Nn, \tl_gremove_all:NV, \tl_gremove_all:Ne,
+% \tl_gremove_all:cn, \tl_gremove_all:cV, \tl_gremove_all:ce,
+% }
+% \begin{syntax}
+% \cs{tl_remove_all:Nn} \meta{tl~var} \Arg{tokens}
+% \end{syntax}
+% Removes all occurrences of \meta{tokens} from the
+% \meta{tl~var}. The \meta{tokens} cannot contain |{|, |}| or |#|
+% (more precisely, explicit character tokens with category code $1$
+% (begin-group) or $2$ (end-group), and tokens with category code $6$).
+% As this function
+% operates from left to right, the pattern \meta{tokens}
+% may remain after the removal, for instance,
+% \begin{quote}
+% \cs{tl_set:Nn} \cs{l_tmpa_tl} |{abbccd}|
+% \cs{tl_remove_all:Nn} \cs{l_tmpa_tl} |{bc}|
+% \end{quote}
+% results in \cs{l_tmpa_tl} containing \texttt{abcd}.
+% \end{function}
+%
+% \subsection{Reassigning category codes}
+%
+% These functions allow the rescanning of tokens: re-apply \TeX{}'s
+% tokenization process to apply category codes different from those
+% in force when the tokens were absorbed. Whilst this functionality is
+% supported, it is often preferable to find alternative approaches
+% to achieving outcomes rather than rescanning tokens (for example
+% construction of token lists token-by-token with intervening category
+% code changes or using \cs{char_generate:nn}).
+%
+% \begin{function}[updated = 2015-08-11]
+% {
+% \tl_set_rescan:Nnn, \tl_set_rescan:NnV, \tl_set_rescan:Nne,
+% \tl_set_rescan:Nno,
+% \tl_set_rescan:cnn, \tl_set_rescan:cnV, \tl_set_rescan:cne,
+% \tl_set_rescan:cno,
+% \tl_gset_rescan:Nnn, \tl_gset_rescan:NnV, \tl_gset_rescan:Nne,
+% \tl_gset_rescan:Nno,
+% \tl_gset_rescan:cnn, \tl_gset_rescan:cnV, \tl_gset_rescan:cne,
+% \tl_gset_rescan:cno,
+% }
+% \begin{syntax}
+% \cs{tl_set_rescan:Nnn} \meta{tl~var} \Arg{setup} \Arg{tokens}
+% \end{syntax}
+% Sets \meta{tl~var} to contain \meta{tokens}, applying the category
+% code r\'{e}gime specified in the \meta{setup} before carrying out
+% the assignment. (Category codes applied to tokens not explicitly covered
+% by the \meta{setup} are those in force at the point of use of
+% \cs{tl_set_rescan:Nnn}.)
+% This allows the \meta{tl~var} to contain material
+% with category codes other than those that apply when \meta{tokens}
+% are absorbed. The \meta{setup} is run within a group and may
+% contain any valid input, although only changes in category codes,
+% such as uses of \cs{cctab_select:N},
+% are relevant. See also \cs{tl_rescan:nn}.
+% \begin{texnote}
+% The \meta{tokens} are first turned into a string (using
+% \cs{tl_to_str:n}). If the string contains one or more characters
+% with character code \tn{newlinechar} (set equal to
+% \tn{endlinechar} unless that is equal to $32$, before the user
+% \meta{setup}), then it is split into lines at these characters,
+% then read as if reading multiple lines from a file, ignoring
+% spaces (catcode $10$) at the beginning and spaces and tabs
+% (character code $32$ or $9$) at the end of every line.
+% Otherwise, spaces (and tabs) are retained at both ends of the
+% single-line string, as if it appeared in the middle of a line
+% read from a file.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[updated = 2015-08-11]{\tl_rescan:nn, \tl_rescan:nV}
+% \begin{syntax}
+% \cs{tl_rescan:nn} \Arg{setup} \Arg{tokens}
+% \end{syntax}
+% Rescans \meta{tokens} applying the category code r\'{e}gime
+% specified in the \meta{setup}, and leaves the resulting tokens in
+% the input stream. (Category codes applied to tokens not explicitly covered
+% by the \meta{setup} are those in force at the point of use of
+% \cs{tl_rescan:nn}.)
+% The \meta{setup} is run within a group and may
+% contain any valid input, although only changes in category codes,
+% such as uses of \cs{cctab_select:N},
+% are relevant. See also \cs{tl_set_rescan:Nnn}, which is more
+% robust than using \cs{tl_set:Nn} in the \meta{tokens} argument of
+% \cs{tl_rescan:nn}.
+% \begin{texnote}
+% The \meta{tokens} are first turned into a string (using
+% \cs{tl_to_str:n}). If the string contains one or more characters
+% with character code \tn{newlinechar} (set equal to
+% \tn{endlinechar} unless that is equal to $32$, before the user
+% \meta{setup}), then it is split into lines at these characters,
+% then read as if reading multiple lines from a file, ignoring
+% spaces (catcode $10$) at the beginning and spaces and tabs
+% (character code $32$ or $9$) at the end of every line.
+% Otherwise, spaces (and tabs) are retained at both ends of the
+% single-line string, as if it appeared in the middle of a line
+% read from a file.
+%
+% Contrarily to the \tn{scantokens} \eTeX{} primitive, \cs{tl_rescan:nn}
+% tokenizes the whole string in the same category code regime rather
+% than one token at a time, so that directives such as \tn{verb}
+% that rely on changing category codes will not function properly.
+% \end{texnote}
+% \end{function}
+%
+% \section{Constant token lists}
+%
+% \begin{variable}{\c_empty_tl}
+% Constant that is always empty.
+% \end{variable}
+%
+% \begin{variable}[added = 2017-11-14]{\c_novalue_tl}
+% A marker for the absence of an argument. This constant |tl| can safely
+% be typeset (\emph{cf.}~\cs{q_nil}), with the result being |-NoValue-|.
+% It is important to note that \cs{c_novalue_tl} is constructed such that it
+% will \emph{not} match the simple text input |-NoValue-|, \emph{i.e.}
+% that
+% \begin{verbatim}
+% \tl_if_eq:NnTF \c_novalue_tl { -NoValue- }
+% \end{verbatim}
+% is logically \texttt{false}. The \cs{c_novalue_tl} marker is intended for
+% use in creating document-level interfaces, where it serves as an indicator
+% that an (optional) argument was omitted. In particular, it is distinct
+% from a simple empty |tl|.
+% \end{variable}
+%
+% \begin{variable}{\c_space_tl}
+% An explicit space character contained in a token list (compare this with
+% \cs{c_space_token}). For use where an explicit space is required.
+% \end{variable}
+%
+% \section{Scratch token lists}
+%
+% \begin{variable}{\l_tmpa_tl, \l_tmpb_tl}
+% Scratch token lists for local assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \begin{variable}{\g_tmpa_tl, \g_tmpb_tl}
+% Scratch token lists for global assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3tl} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=tl>
+% \end{macrocode}
+%
+% A token list variable is a \TeX{} macro that holds tokens. By using the
+% \eTeX{} primitive \tn{unexpanded} inside a \TeX{} \tn{edef} it is
+% possible to store any tokens, including |#|, in this way.
+%
+% \subsection{Functions}
+%
+% \begin{macro}{\__kernel_tl_set:Nx,\__kernel_tl_gset:Nx}
+% These two are supplied to get better performance for macros which would
+% otherwise use \cs{tl_set:Ne} or \cs{tl_gset:Ne} internally.
+% \begin{macrocode}
+\cs_new_eq:NN \__kernel_tl_set:Nx \cs_set_nopar:Npe
+\cs_new_eq:NN \__kernel_tl_gset:Nx \cs_gset_nopar:Npe
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\tl_new:N, \tl_new:c}
+% Creating new token list variables is a case of checking for an
+% existing definition and doing the definition.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_eq:NN #1 \c_empty_tl
+ }
+\cs_generate_variant:Nn \tl_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \tl_const:Nn, \tl_const:Ne, \tl_const:Nx,
+% \tl_const:cn, \tl_const:ce, \tl_const:cx
+% }
+% Constants are also easy to generate. They use \cs{cs_gset_nopar:Npe} instead
+% of \cs{__kernel_tl_gset:Nx} so that the correct scope checking for |c|,
+% instead of for |g|, is applied when
+% \cs{debug_on:n} |{ check-declarations }| is used.
+% Constant assignment functions are patched specially in \pkg{l3debug} to
+% apply such checks.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_const:Nn #1#2
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_nopar:Npe #1 { \__kernel_exp_not:w {#2} }
+ }
+\cs_generate_variant:Nn \tl_const:Nn { Ne , c , ce }
+\cs_generate_variant:Nn \tl_const:Nn { Nx , cx }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\tl_clear:N, \tl_clear:c}
+% \begin{macro}{\tl_gclear:N, \tl_gclear:c}
+% Clearing a token list variable means setting it to an empty value.
+% Error checking is sorted out by the parent function.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_clear:N #1
+ { \tex_let:D #1 = ~ \c_empty_tl }
+\cs_new_protected:Npn \tl_gclear:N #1
+ { \tex_global:D \tex_let:D #1 ~ \c_empty_tl }
+\cs_generate_variant:Nn \tl_clear:N { c }
+\cs_generate_variant:Nn \tl_gclear:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_clear_new:N, \tl_clear_new:c}
+% \begin{macro}{\tl_gclear_new:N, \tl_gclear_new:c}
+% Clearing a token list variable means setting it to an empty value.
+% Error checking is sorted out by the parent function.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_clear_new:N #1
+ { \tl_if_exist:NTF #1 { \tl_clear:N #1 } { \tl_new:N #1 } }
+\cs_new_protected:Npn \tl_gclear_new:N #1
+ { \tl_if_exist:NTF #1 { \tl_gclear:N #1 } { \tl_new:N #1 } }
+\cs_generate_variant:Nn \tl_clear_new:N { c }
+\cs_generate_variant:Nn \tl_gclear_new:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_set_eq:NN, \tl_set_eq:Nc, \tl_set_eq:cN, \tl_set_eq:cc}
+% \begin{macro}{\tl_gset_eq:NN, \tl_gset_eq:Nc, \tl_gset_eq:cN, \tl_gset_eq:cc}
+% For setting token list variables equal to each other. To allow for
+% patching, the arguments have to be explicit. In addition this
+% ensures that a braced second argument will not cause problems.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_set_eq:NN #1#2
+ { \tex_let:D #1 = ~ #2 }
+\cs_new_protected:Npn \tl_gset_eq:NN #1#2
+ { \tex_global:D \tex_let:D #1 = ~ #2 }
+\cs_generate_variant:Nn \tl_set_eq:NN { cN, Nc, cc }
+\cs_generate_variant:Nn \tl_gset_eq:NN { cN, Nc, cc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_concat:NNN, \tl_concat:ccc}
+% \begin{macro}{\tl_gconcat:NNN, \tl_gconcat:ccc}
+% Concatenating token lists is easy. When checking is turned on, all
+% three arguments must be checked: a token list |#2| or |#3| equal to
+% \cs{scan_stop:} would lead to problems later on.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_concat:NNN #1#2#3
+ {
+ \__kernel_tl_set:Nx #1
+ {
+ \__kernel_exp_not:w \exp_after:wN {#2}
+ \__kernel_exp_not:w \exp_after:wN {#3}
+ }
+ }
+\cs_new_protected:Npn \tl_gconcat:NNN #1#2#3
+ {
+ \__kernel_tl_gset:Nx #1
+ {
+ \__kernel_exp_not:w \exp_after:wN {#2}
+ \__kernel_exp_not:w \exp_after:wN {#3}
+ }
+ }
+\cs_generate_variant:Nn \tl_concat:NNN { ccc }
+\cs_generate_variant:Nn \tl_gconcat:NNN { ccc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\tl_if_exist:N, \tl_if_exist:c}
+% Copies of the \texttt{cs} functions defined in \pkg{l3basics}.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \tl_if_exist:N \cs_if_exist:N { TF , T , F , p }
+\prg_new_eq_conditional:NNn \tl_if_exist:c \cs_if_exist:c { TF , T , F , p }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Constant token lists}
+%
+% \begin{variable}{\c_empty_tl}
+% Never full. We need to define that constant before using \cs{tl_new:N}.
+% \begin{macrocode}
+\tl_const:Nn \c_empty_tl { }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_novalue_tl}
+% A special marker: as we don't have |\char_generate:nn| yet, has to be
+% created the old-fashioned way.
+% \begin{macrocode}
+\group_begin:
+\tex_catcode:D `- = 11 ~
+\tl_const:Ne \c_novalue_tl { - NoValue \token_to_str:N - }
+\group_end:
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_space_tl}
+% A space as a token list (as opposed to as a character).
+% \begin{macrocode}
+\tl_const:Nn \c_space_tl { ~ }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Adding to token list variables}
+%
+% \begin{macro}
+% {
+% \tl_set:Nn, \tl_set:NV, \tl_set:Nv, \tl_set:No, \tl_set:Ne, \tl_set:Nf, \tl_set:Nx,
+% \tl_set:cn, \tl_set:cV, \tl_set:cv, \tl_set:co, \tl_set:ce, \tl_set:cf, \tl_set:cx
+% }
+% \begin{macro}
+% {
+% \tl_gset:Nn, \tl_gset:NV, \tl_gset:Nv,
+% \tl_gset:No, \tl_gset:Ne, \tl_gset:Nf, \tl_gset:Nx,
+% \tl_gset:cn, \tl_gset:cV, \tl_gset:cv,
+% \tl_gset:co, \tl_gset:ce, \tl_gset:cf, \tl_gset:cx
+% }
+% By using \cs{exp_not:n} token list variables can contain |#| tokens,
+% which makes the token list registers provided by \TeX{}
+% more or less redundant. The \cs{tl_set:No} version is done
+% by hand as it is used quite a lot.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_set:Nn #1#2
+ { \__kernel_tl_set:Nx #1 { \__kernel_exp_not:w {#2} } }
+\cs_new_protected:Npn \tl_set:No #1#2
+ { \__kernel_tl_set:Nx #1 { \__kernel_exp_not:w \exp_after:wN {#2} } }
+\cs_new_protected:Npn \tl_gset:Nn #1#2
+ { \__kernel_tl_gset:Nx #1 { \__kernel_exp_not:w {#2} } }
+\cs_new_protected:Npn \tl_gset:No #1#2
+ { \__kernel_tl_gset:Nx #1 { \__kernel_exp_not:w \exp_after:wN {#2} } }
+\cs_generate_variant:Nn \tl_set:Nn { NV , Nv , Ne , Nf }
+\cs_generate_variant:Nn \tl_set:Nn { c, cV , cv , ce , cf }
+\cs_generate_variant:Nn \tl_set:No { c }
+\cs_generate_variant:Nn \tl_set:Nn { Nx , cx }
+\cs_generate_variant:Nn \tl_gset:Nn { NV , Nv , Ne , Nf }
+\cs_generate_variant:Nn \tl_gset:Nn { c, cV , cv , ce , cf }
+\cs_generate_variant:Nn \tl_gset:No { c }
+\cs_generate_variant:Nn \tl_gset:Nn { Nx , cx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \tl_put_left:Nn, \tl_put_left:NV, \tl_put_left:Nv, \tl_put_left:Ne,
+% \tl_put_left:No, \tl_put_left:Nx,
+% \tl_put_left:cn, \tl_put_left:cV, \tl_put_left:cv, \tl_put_left:ce,
+% \tl_put_left:co, \tl_put_left:cx
+% }
+% \begin{macro}
+% {
+% \tl_gput_left:Nn, \tl_gput_left:NV, \tl_gput_left:Nv, \tl_gput_left:Ne,
+% \tl_gput_left:No, \tl_gput_left:Nx,
+% \tl_gput_left:cn, \tl_gput_left:cV, \tl_gput_left:cv, \tl_gput_left:ce,
+% \tl_gput_left:co, \tl_gput_left:cx
+% }
+% Adding to the left is done directly to gain a little performance.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_put_left:Nn #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \__kernel_exp_not:w {#2} \__kernel_exp_not:w \exp_after:wN {#1} }
+ }
+\cs_new_protected:Npn \tl_put_left:NV #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \exp_not:V #2 \__kernel_exp_not:w \exp_after:wN {#1} }
+ }
+\cs_new_protected:Npn \tl_put_left:Nv #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \exp_not:v {#2} \__kernel_exp_not:w \exp_after:wN {#1} }
+ }
+\cs_new_protected:Npn \tl_put_left:Ne #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ {
+ \__kernel_exp_not:w \tex_expanded:D { {#2} }
+ \__kernel_exp_not:w \exp_after:wN {#1}
+ }
+ }
+\cs_new_protected:Npn \tl_put_left:No #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ {
+ \__kernel_exp_not:w \exp_after:wN {#2}
+ \__kernel_exp_not:w \exp_after:wN {#1}
+ }
+ }
+\cs_new_protected:Npn \tl_gput_left:Nn #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \__kernel_exp_not:w {#2} \__kernel_exp_not:w \exp_after:wN {#1} }
+ }
+\cs_new_protected:Npn \tl_gput_left:NV #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \exp_not:V #2 \__kernel_exp_not:w \exp_after:wN {#1} }
+ }
+\cs_new_protected:Npn \tl_gput_left:Nv #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \exp_not:v {#2} \__kernel_exp_not:w \exp_after:wN {#1} }
+ }
+\cs_new_protected:Npn \tl_gput_left:Ne #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ {
+ \__kernel_exp_not:w \tex_expanded:D { {#2} }
+ \__kernel_exp_not:w \exp_after:wN {#1}
+ }
+ }
+\cs_new_protected:Npn \tl_gput_left:No #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ {
+ \__kernel_exp_not:w \exp_after:wN {#2}
+ \__kernel_exp_not:w \exp_after:wN {#1}
+ }
+ }
+\cs_generate_variant:Nn \tl_put_left:Nn { c }
+\cs_generate_variant:Nn \tl_put_left:NV { c }
+\cs_generate_variant:Nn \tl_put_left:Nv { c }
+\cs_generate_variant:Nn \tl_put_left:Ne { c }
+\cs_generate_variant:Nn \tl_put_left:No { c }
+\cs_generate_variant:Nn \tl_put_left:Nn { Nx, cx }
+\cs_generate_variant:Nn \tl_gput_left:Nn { c }
+\cs_generate_variant:Nn \tl_gput_left:NV { c }
+\cs_generate_variant:Nn \tl_gput_left:Nv { c }
+\cs_generate_variant:Nn \tl_gput_left:Ne { c }
+\cs_generate_variant:Nn \tl_gput_left:No { c }
+\cs_generate_variant:Nn \tl_gput_left:Nn { Nx , cx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \tl_put_right:Nn, \tl_put_right:NV, \tl_put_right:Nv, \tl_put_right:Ne,
+% \tl_put_right:No, \tl_put_right:Nx,
+% \tl_put_right:cn, \tl_put_right:cV, \tl_put_right:cv, \tl_put_right:ce,
+% \tl_put_right:co, \tl_put_right:cx
+% }
+% \begin{macro}
+% {
+% \tl_gput_right:Nn, \tl_gput_right:NV, \tl_gput_right:Nv, \tl_gput_right:Ne,
+% \tl_gput_right:No, \tl_gput_right:Nx,
+% \tl_gput_right:cn, \tl_gput_right:cV, \tl_gput_right:cv, \tl_gput_right:ce,
+% \tl_gput_right:co, \tl_gput_right:cx
+% }
+% The same on the right.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_put_right:Nn #1#2
+ { \__kernel_tl_set:Nx #1 { \__kernel_exp_not:w \exp_after:wN { #1 #2 } } }
+\cs_new_protected:Npn \tl_put_right:NV #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \__kernel_exp_not:w \exp_after:wN {#1} \exp_not:V #2 }
+ }
+\cs_new_protected:Npn \tl_put_right:Nv #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \__kernel_exp_not:w \exp_after:wN {#1} \exp_not:v {#2} }
+ }
+\cs_new_protected:Npn \tl_put_right:Ne #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ {
+ \__kernel_exp_not:w \exp_after:wN {#1}
+ \__kernel_exp_not:w \tex_expanded:D { {#2} }
+ }
+ }
+\cs_new_protected:Npn \tl_put_right:No #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ {
+ \__kernel_exp_not:w \exp_after:wN {#1}
+ \__kernel_exp_not:w \exp_after:wN {#2}
+ }
+ }
+\cs_new_protected:Npn \tl_gput_right:Nn #1#2
+ { \__kernel_tl_gset:Nx #1 { \__kernel_exp_not:w \exp_after:wN { #1 #2 } } }
+\cs_new_protected:Npn \tl_gput_right:NV #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \__kernel_exp_not:w \exp_after:wN {#1} \exp_not:V #2 }
+ }
+\cs_new_protected:Npn \tl_gput_right:Nv #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \__kernel_exp_not:w \exp_after:wN {#1} \exp_not:v {#2} }
+ }
+\cs_new_protected:Npn \tl_gput_right:Ne #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ {
+ \__kernel_exp_not:w \exp_after:wN {#1}
+ \__kernel_exp_not:w \tex_expanded:D { {#2} }
+ }
+ }
+\cs_new_protected:Npn \tl_gput_right:No #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ {
+ \__kernel_exp_not:w \exp_after:wN {#1}
+ \__kernel_exp_not:w \exp_after:wN {#2}
+ }
+ }
+\cs_generate_variant:Nn \tl_put_right:Nn { c }
+\cs_generate_variant:Nn \tl_put_right:NV { c }
+\cs_generate_variant:Nn \tl_put_right:Nv { c }
+\cs_generate_variant:Nn \tl_put_right:Ne { c }
+\cs_generate_variant:Nn \tl_put_right:No { c }
+\cs_generate_variant:Nn \tl_put_right:Nn { Nx , cx }
+\cs_generate_variant:Nn \tl_gput_right:Nn { c }
+\cs_generate_variant:Nn \tl_gput_right:NV { c }
+\cs_generate_variant:Nn \tl_gput_right:Nv { c }
+\cs_generate_variant:Nn \tl_gput_right:Ne { c }
+\cs_generate_variant:Nn \tl_gput_right:No { c }
+\cs_generate_variant:Nn \tl_gput_right:Nn { Nx, cx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Internal quarks and quark-query functions}
+%
+% \begin{variable}{\q_@@_nil,\q_@@_mark,\q_@@_stop}
+% Internal quarks.
+% \begin{macrocode}
+\quark_new:N \q_@@_nil
+\quark_new:N \q_@@_mark
+\quark_new:N \q_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\q_@@_recursion_tail,\q_@@_recursion_stop}
+% Internal recursion quarks.
+% \begin{macrocode}
+\quark_new:N \q_@@_recursion_tail
+\quark_new:N \q_@@_recursion_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_if_recursion_tail_break:nN}
+% \begin{macro}[pTF]{\@@_if_recursion_tail_stop:n}
+% Functions to query recursion quarks.
+% \begin{macrocode}
+\__kernel_quark_new_test:N \@@_if_recursion_tail_break:nN
+\__kernel_quark_new_conditional:Nn \@@_quark_if_nil:n { TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Reassigning token list category codes}
+%
+% \begin{variable}{\c_@@_rescan_marker_tl}
+% The rescanning code needs a special token list containing the same
+% character (chosen here to be a colon) with two different category
+% codes: it cannot appear in the tokens being rescanned since all
+% colons have the same category code.
+% \begin{macrocode}
+\tl_const:Ne \c_@@_rescan_marker_tl { : \token_to_str:N : }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}
+% {
+% \tl_set_rescan:Nnn, \tl_set_rescan:NnV, \tl_set_rescan:Nne,
+% \tl_set_rescan:Nno, \tl_set_rescan:Nnx,
+% \tl_set_rescan:cnn, \tl_set_rescan:cnV, \tl_set_rescan:cne,
+% \tl_set_rescan:cno, \tl_set_rescan:cnx,
+% }
+% \begin{macro}
+% {
+% \tl_gset_rescan:Nnn, \tl_gset_rescan:NnV, \tl_gset_rescan:Nne,
+% \tl_gset_rescan:Nno, \tl_gset_rescan:Nnx,
+% \tl_gset_rescan:cnn, \tl_gset_rescan:cnV, \tl_gset_rescan:cne,
+% \tl_gset_rescan:cno, \tl_gset_rescan:cnx
+% }
+% \begin{macro}{\tl_rescan:nn, \tl_rescan:nV}
+% \begin{macro}{\@@_rescan_aux:}
+% \begin{macro}{\@@_set_rescan:NNnn, \@@_set_rescan_multi:nNN}
+% \begin{macro}[EXP]{\@@_rescan:NNw}
+% In a group, after some initial setup explained below and the user
+% setup~|#3| (followed by \cs{scan_stop:} to be safe), there is a call
+% to \cs{@@_set_rescan:nNN}. This shared auxiliary defined later
+% distinguishes single-line and multi-line ``files''. In the simplest
+% case of multi-line files, it calls (with the same arguments)
+% \cs{@@_set_rescan_multi:nNN}, whose code is included here to help
+% understand the approach. This function rescans its argument |#1|,
+% closes the group, and performs the assignment.
+%
+% One difficulty when rescanning is that \tn{scantokens} treats the
+% argument as a file, and without the correct settings a \TeX{} error
+% occurs:
+% \begin{verbatim}
+% ! File ended while scanning definition of ...
+% \end{verbatim}
+% A related minor issue is a warning due to opening a group before the
+% \tn{scantokens} and closing it inside that temporary file; we avoid
+% that by setting \tn{tracingnesting}. The standard solution to the
+% ``File ended'' error is to grab the rescanned tokens as a delimited
+% argument of an auxiliary, here \cs{@@_rescan:NNw}, that performs the
+% assignment, then let \TeX{} ``execute'' the end of file marker. As
+% usual in delimited arguments we use \cs{prg_do_nothing:} to avoid
+% stripping an outer set braces: this is removed by using
+% \texttt{o}-expanding assignments. The delimiter cannot appear
+% within the rescanned token list because it contains twice the same
+% character, with different catcodes.
+%
+% For \cs{tl_rescan:nn} we cannot simply call \cs{@@_set_rescan:NNnn}
+% \cs{prg_do_nothing:} \cs{use:n} because that would leave the
+% end-of-file marker \emph{after} the result of rescanning. If that
+% rescanned result is code that looks further in the input stream for
+% arguments, it would break.
+%
+% For multi-line files the only subtlety is that \tn{newlinechar}
+% should be equal to \tn{endlinechar} because \tn{newlinechar}
+% characters become new lines and then become \tn{endlinechar}
+% characters when writing to an abstract file and reading back. This
+% equality is ensured by setting \tn{newlinechar} equal to
+% \tn{endlinechar}. Prior to this, \tn{endlinechar} is set to $-1$ if
+% it was $32$ (in particular true after \cs{ExplSyntaxOn}) to avoid
+% unreasonable line-breaks at every space for instance in error
+% messages triggered by the user setup. Another side effect of
+% reading back from the file is that spaces (catcode $10$) are ignored
+% at the beginning of lines, and spaces and tabs (character code $32$
+% and $9$) are ignored at the end of lines.
+%
+% The two \cs{if_false:} \ldots{} \cs{fi:} are there to prevent
+% alignment tabs to cause a change of tabular cell while rescanning.
+% We put the \enquote{opening} one after \cs{group_begin:} so that if
+% one accidentally \texttt{f}-expands \cs{tl_set_rescan:Nnn} braces
+% remain balanced. This is essential in \texttt{e}-type arguments
+% when \tn{expanded} is not available.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_rescan:nn #1#2
+ {
+ \tl_set_rescan:Nnn \l_@@_internal_a_tl {#1} {#2}
+ \exp_after:wN \@@_rescan_aux:
+ \l_@@_internal_a_tl
+ }
+\cs_generate_variant:Nn \tl_rescan:nn { nV }
+\exp_args:NNo \cs_new_protected:Npn \@@_rescan_aux:
+ { \tl_clear:N \l_@@_internal_a_tl }
+\cs_new_protected:Npn \tl_set_rescan:Nnn
+ { \@@_set_rescan:NNnn \tl_set:No }
+\cs_new_protected:Npn \tl_gset_rescan:Nnn
+ { \@@_set_rescan:NNnn \tl_gset:No }
+\cs_new_protected:Npn \@@_set_rescan:NNnn #1#2#3#4
+ {
+ \group_begin:
+ \if_false: { \fi:
+ \int_set_eq:NN \tex_tracingnesting:D \c_zero_int
+ \int_compare:nNnT \tex_endlinechar:D = { 32 }
+ { \int_set:Nn \tex_endlinechar:D { -1 } }
+ \int_set_eq:NN \tex_newlinechar:D \tex_endlinechar:D
+ #3 \scan_stop:
+ \exp_args:No \@@_set_rescan:nNN { \tl_to_str:n {#4} } #1 #2
+ \if_false: } \fi:
+ }
+\cs_new_protected:Npn \@@_set_rescan_multi:nNN #1#2#3
+ {
+ \tex_everyeof:D \exp_after:wN { \c_@@_rescan_marker_tl }
+ \exp_after:wN \@@_rescan:NNw
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN \prg_do_nothing:
+ \tex_scantokens:D {#1}
+ }
+\exp_args:Nno \use:nn
+ { \cs_new:Npn \@@_rescan:NNw #1#2#3 } \c_@@_rescan_marker_tl
+ {
+ \group_end:
+ #1 #2 {#3}
+ }
+\cs_generate_variant:Nn \tl_set_rescan:Nnn { NnV , Nne , c , cnV , cne }
+\cs_generate_variant:Nn \tl_set_rescan:Nnn { Nno , Nnx , cno , cnx }
+\cs_generate_variant:Nn \tl_gset_rescan:Nnn { NnV , Nne , c , cnV , cne }
+\cs_generate_variant:Nn \tl_gset_rescan:Nnn { Nno , Nnx , cno , cnx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_set_rescan:nNN}
+% \begin{macro}{\@@_set_rescan_single:nnNN, \@@_set_rescan_single_aux:nnnNN}
+% \begin{macro}[rEXP]{\@@_set_rescan_single_aux:w}
+% The function \cs{@@_set_rescan:nNN} calls \cs{@@_set_rescan_multi:nNN} or
+% \cs{@@_set_rescan_single:nnNN} |{ ' }| depending on whether its argument
+% is a single-line fragment of code/data or is made of multiple lines
+% by testing for the presence of a \tn{newlinechar} character. If
+% \tn{newlinechar} is out of range, the argument is assumed to be a
+% single line.
+%
+% For a single line, no \tn{endlinechar} should be added, so it is
+% set to $-1$, and spaces should not be removed.
+% Trailing spaces and tabs are a difficult matter, as \TeX{} removes
+% these at a very low level. The only way to preserve them is to
+% rescan not the argument but the argument followed by a character
+% with a reasonable category code. Here, $11$ (letter) and $12$ (other)
+% are accepted, as these are convenient, suitable for
+% delimiting an argument, and it is very unlikely that none of the
+% ASCII characters are in one of these categories. To avoid
+% selecting one particular character to put at the end, whose
+% category code may have been modified, there is a loop through
+% characters from |'| (ASCII $39$) to |~| (ASCII $127$). The choice
+% of starting point was made because this is the start of a very long
+% range of characters whose standard category is letter or other,
+% thus minimizing the number of steps needed by the loop (most often
+% just a single one). If no valid character is found (very rare),
+% fall-back on \cs{@@_set_rescan_multi:nNN}.
+%
+% Otherwise, once a valid character is found (let us use |'| in this
+% explanation) run some code very similar to \cs{@@_set_rescan_multi:nNN}
+% but with |'| added at both ends of the input. Of course, we need to
+% define the auxiliary \cs{@@_set_rescan_single:NNww} on the fly to remove
+% the additional~|'| that is just before |::| (by which we mean
+% \cs{c_@@_rescan_marker_tl}). Note that the argument must be
+% delimited by |'| with the current catcode; this is done thanks to
+% \cs{char_generate:nn}. Yet another issue is that the rescanned
+% token list may contain a comment character, in which case the |'| we
+% expected is not there. We fix this as follows: rather than just
+% |::| we set \tn{everyeof} to |::|\Arg{code1} |'::|\Arg{code2}
+% \cs{s_@@_stop}. The auxiliary \cs{@@_set_rescan_single:NNww} runs the
+% \texttt{o}-expanding assignment, expanding either \meta{code1} or
+% \meta{code2} before its the main argument~|#3|. In the typical case
+% without comment character, \meta{code1} is expanded, removing the
+% leading~|'|. In the rarer case with comment character, \meta{code2}
+% is expanded, calling \cs{@@_set_rescan_single_aux:w}, which removes the
+% trailing |::|\Arg{code1} and the leading~|'|.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_set_rescan:nNN #1
+ {
+ \int_compare:nNnTF \tex_newlinechar:D < 0
+ { \use_ii:nn }
+ {
+ \exp_args:Nnf \tl_if_in:nnTF {#1}
+ { \char_generate:nn { \tex_newlinechar:D } { 12 } }
+ }
+ { \@@_set_rescan_multi:nNN }
+ {
+ \int_set:Nn \tex_endlinechar:D { -1 }
+ \@@_set_rescan_single:nnNN { `' }
+ }
+ {#1}
+ }
+\cs_new_protected:Npn \@@_set_rescan_single:nnNN #1
+ {
+ \int_compare:nNnTF
+ { \char_value_catcode:n {#1} / 2 } = 6
+ {
+ \exp_args:Nof \@@_set_rescan_single_aux:nnnNN
+ \c_@@_rescan_marker_tl
+ { \char_generate:nn {#1} { \char_value_catcode:n {#1} } }
+ }
+ {
+ \int_compare:nNnTF {#1} < { `\~ }
+ {
+ \exp_args:Nf \@@_set_rescan_single:nnNN
+ { \int_eval:n { #1 + 1 } }
+ }
+ { \@@_set_rescan_multi:nNN }
+ }
+ }
+\cs_new_protected:Npn \@@_set_rescan_single_aux:nnnNN #1#2#3#4#5
+ {
+ \tex_everyeof:D
+ {
+ #1 \use_none:n
+ #2 #1 { \exp:w \@@_set_rescan_single_aux:w }
+ \s_@@_stop
+ }
+ \cs_set:Npn \@@_rescan:NNw ##1##2##3 #2 #1 ##4 ##5 \s_@@_stop
+ {
+ \group_end:
+ ##1 ##2 { ##4 ##3 }
+ }
+ \exp_after:wN \@@_rescan:NNw
+ \exp_after:wN #4
+ \exp_after:wN #5
+ \tex_scantokens:D { #2 #3 #2 }
+ }
+\exp_args:Nno \use:nn
+ { \cs_new:Npn \@@_set_rescan_single_aux:w #1 }
+ \c_@@_rescan_marker_tl #2
+ { \use_i:nn \exp_end: #1 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Modifying token list variables}
+%
+% \begin{macro}
+% {
+% \tl_replace_once:Nnn, \tl_replace_once:NVn, \tl_replace_once:NnV,
+% \tl_replace_once:Nen, \tl_replace_once:Nne, \tl_replace_once:Nee,
+% \tl_replace_once:Nxn, \tl_replace_once:Nnx, \tl_replace_once:Nxx,
+% \tl_replace_once:cnn, \tl_replace_once:cVn, \tl_replace_once:cnV,
+% \tl_replace_once:cen, \tl_replace_once:cne, \tl_replace_once:cee,
+% \tl_replace_once:cxn, \tl_replace_once:cnx, \tl_replace_once:cxx
+% }
+% \begin{macro}
+% {
+% \tl_greplace_once:Nnn, \tl_greplace_once:NVn, \tl_greplace_once:NnV,
+% \tl_greplace_once:Nen, \tl_greplace_once:Nne, \tl_greplace_once:Nee,
+% \tl_greplace_once:Nxn, \tl_greplace_once:Nnx, \tl_greplace_once:Nxx,
+% \tl_greplace_once:cnn, \tl_greplace_once:cVn, \tl_greplace_once:cnV,
+% \tl_greplace_once:cen, \tl_greplace_once:cne, \tl_greplace_once:cee,
+% \tl_greplace_once:cxn, \tl_greplace_once:cnx, \tl_greplace_once:cxx
+% }
+% \begin{macro}
+% {
+% \tl_replace_all:Nnn, \tl_replace_all:NVn, \tl_replace_all:NnV,
+% \tl_replace_all:Nen, \tl_replace_all:Nne, \tl_replace_all:Nee,
+% \tl_replace_all:Nxn, \tl_replace_all:Nnx, \tl_replace_all:Nxx,
+% \tl_replace_all:cnn, \tl_replace_all:cVn, \tl_replace_all:cnV,
+% \tl_replace_all:cen, \tl_replace_all:cne, \tl_replace_all:cee,
+% \tl_replace_all:cxn, \tl_replace_all:cnx, \tl_replace_all:cxx
+% }
+% \begin{macro}
+% {
+% \tl_greplace_all:Nnn, \tl_greplace_all:NVn, \tl_greplace_all:NnV,
+% \tl_greplace_all:Nen, \tl_greplace_all:Nne, \tl_greplace_all:Nee,
+% \tl_greplace_all:Nxn, \tl_greplace_all:Nnx, \tl_greplace_all:Nxx,
+% \tl_greplace_all:cnn, \tl_greplace_all:cVn, \tl_greplace_all:cnV,
+% \tl_greplace_all:cen, \tl_greplace_all:cne, \tl_greplace_all:cee,
+% \tl_greplace_all:cxn, \tl_greplace_all:cnx, \tl_greplace_all:cxx
+% }
+% All of the \texttt{replace} functions call \cs{@@_replace:NnNNNnn}
+% with appropriate arguments. The first two arguments are explained
+% later. The next controls whether the replacement function calls
+% itself (\cs{@@_replace_next:w}) or stops (\cs{@@_replace_wrap:w})
+% after the first replacement. Next comes an \texttt{e}-type
+% assignment function \cs{tl_set:Ne} or \cs{tl_gset:Ne} for local or
+% global replacements. Finally, the three arguments \meta{tl~var}
+% \Arg{pattern} \Arg{replacement} provided by the user. When
+% describing the auxiliary functions below, we denote the contents of
+% the \meta{tl~var} by \meta{token list}.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_replace_once:Nnn
+ { \@@_replace:NnNNNnn \q_@@_mark ? \@@_replace_wrap:w \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \tl_greplace_once:Nnn
+ { \@@_replace:NnNNNnn \q_@@_mark ? \@@_replace_wrap:w \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \tl_replace_all:Nnn
+ { \@@_replace:NnNNNnn \q_@@_mark ? \@@_replace_next:w \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \tl_greplace_all:Nnn
+ { \@@_replace:NnNNNnn \q_@@_mark ? \@@_replace_next:w \__kernel_tl_gset:Nx }
+\cs_generate_variant:Nn \tl_replace_once:Nnn
+ { NnV , Nne , NV , Ne , Nee , c , cnV , cne , cV , ce , cee }
+\cs_generate_variant:Nn \tl_replace_once:Nnn
+ { Nx , Nnx , Nxx , cxn , cnx , cxx }
+\cs_generate_variant:Nn \tl_greplace_once:Nnn
+ { NnV , Nne , NV , Ne , Nee , c , cnV , cne , cV , ce , cee }
+\cs_generate_variant:Nn \tl_greplace_once:Nnn
+ { Nx , Nnx , Nxx , cxn , cnx , cxx }
+\cs_generate_variant:Nn \tl_replace_all:Nnn
+ { NnV , Nne , NV , Ne , Nee , c , cnV , cne , cV , ce , cee }
+\cs_generate_variant:Nn \tl_replace_all:Nnn
+ { Nx , Nnx , Nxx , cxn , cnx , cxx }
+\cs_generate_variant:Nn \tl_greplace_all:Nnn
+ { NnV , Nne , NV , Ne , Nee , c , cnV , cne , cV , ce , cee }
+\cs_generate_variant:Nn \tl_greplace_all:Nnn
+ { Nx , Nnx , Nxx , cxn , cnx , cxx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_replace:NnNNNnn,
+% \@@_replace_auxi:NnnNNNnn,
+% \@@_replace_auxii:nNNNnn,
+% \@@_replace_next:w,
+% \@@_replace_next_aux:w,
+% \@@_replace_wrap:w,
+% }
+% To implement the actual replacement auxiliary
+% \cs{@@_replace_auxii:nNNNnn} we need a \meta{delimiter} with
+% the following properties:
+% \begin{itemize}
+% \item all occurrences of the \meta{pattern}~|#6| in
+% \enquote{\meta{token list} \meta{delimiter}} belong to the
+% \meta{token list} and have no overlap with the \meta{delimiter},
+% \item the first occurrence of the \meta{delimiter} in
+% \enquote{\meta{token list} \meta{delimiter}} is the trailing
+% \meta{delimiter}.
+% \end{itemize}
+% We first find the building blocks for the \meta{delimiter}, namely
+% two tokens \meta{A} and~\meta{B} such that \meta{A} does not appear
+% in~|#6| and |#6| is not~\meta{B} (this condition is trivial if |#6|
+% has more than one token). Then we consider the delimiters
+% \enquote{\meta{A}} and \enquote{\meta{A} \meta{A}$^n$ \meta{B}
+% \meta{A}$^n$ \meta{B}}, for $n\geq 1$, where $\meta{A}^n$ denotes
+% $n$~copies of \meta{A}, and we choose as our \meta{delimiter} the
+% first one which is not in the \meta{token list}.
+%
+% Every delimiter in the set obeys the first condition: |#6|~does not
+% contain~\meta{A} hence cannot be overlapping with the \meta{token
+% list} and the \meta{delimiter}, and it cannot be within the
+% \meta{delimiter} since it would have to be in one of the two
+% \meta{B} hence be equal to this single token (or empty, but this is
+% an error case filtered separately). Given the particular form of
+% these delimiters, for which no prefix is also a suffix, the second
+% condition is actually a consequence of the weaker condition that the
+% \meta{delimiter} we choose does not appear in the \meta{token list}.
+% Additionally, the set of delimiters is such that a \meta{token list}
+% of $n$~tokens can contain at most $O(n^{1/2})$ of them, hence we
+% find a \meta{delimiter} with at most $O(n^{1/2})$ tokens in a time
+% at most $O(n^{3/2})$. Bear in mind that these upper bounds are
+% reached only in very contrived scenarios: we include the case
+% \enquote{\meta{A}} in the list of delimiters to try, so that the
+% \meta{delimiter} is simply \cs{q_@@_mark} in the most common
+% situation where neither the \meta{token list} nor the \meta{pattern}
+% contains \cs{q_@@_mark}.
+%
+% Let us now ahead, optimizing for this most common case. First, two
+% special cases: an empty \meta{pattern}~|#6| is an error, and if
+% |#1|~is absent from both the \meta{token list}~|#5| and the
+% \meta{pattern}~|#6| then we can use it as the \meta{delimiter}
+% through \cs{@@_replace_auxii:nNNNnn} |{#1}|. Otherwise, we end up
+% calling \cs{@@_replace:NnNNNnn} repeatedly with the first two
+% arguments \cs{q_@@_mark} |{?}|, |\?| |{??}|, |\??| |{???}|, and so on,
+% until |#6|~does not contain the control sequence~|#1|, which we take
+% as our~\meta{A}. The argument~|#2| only serves to collect~|?|
+% characters for~|#1|. Note that the order of the tests means that
+% the first two are done every time, which is wasteful (for instance,
+% we repeatedly test for the emptyness of~|#6|). However, this is
+% rare enough not to matter. Finally, choose~\meta{B} to be
+% \cs{q_@@_nil} or~\cs{q_@@_stop} such that it is not equal to~|#6|.
+%
+% The \cs{@@_replace_auxi:NnnNNNnn} auxiliary receives \Arg{A} and
+% |{|\meta{A}$^n$\meta{B}|}| as its arguments, initially with $n=1$.
+% If \enquote{\meta{A} \meta{A}$^n$\meta{B} \meta{A}$^n$\meta{B}} is
+% in the \meta{token list} then increase~$n$ and try again. Once it
+% is not anymore in the \meta{token list} we take it as our
+% \meta{delimiter} and pass this to the \texttt{auxii} auxiliary.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replace:NnNNNnn #1#2#3#4#5#6#7
+ {
+ \tl_if_empty:nTF {#6}
+ {
+ \msg_error:nne { kernel } { empty-search-pattern }
+ { \tl_to_str:n {#7} }
+ }
+ {
+ \tl_if_in:onTF { #5 #6 } {#1}
+ {
+ \tl_if_in:nnTF {#6} {#1}
+ { \exp_args:Nc \@@_replace:NnNNNnn {#2} {#2?} }
+ {
+ \@@_quark_if_nil:nTF {#6}
+ { \@@_replace_auxi:NnnNNNnn #5 {#1} { #1 \q_@@_stop } }
+ { \@@_replace_auxi:NnnNNNnn #5 {#1} { #1 \q_@@_nil } }
+ }
+ }
+ { \@@_replace_auxii:nNNNnn {#1} }
+ #3#4#5 {#6} {#7}
+ }
+ }
+\cs_new_protected:Npn \@@_replace_auxi:NnnNNNnn #1#2#3
+ {
+ \tl_if_in:NnTF #1 { #2 #3 #3 }
+ { \@@_replace_auxi:NnnNNNnn #1 { #2 #3 } {#2} }
+ { \@@_replace_auxii:nNNNnn { #2 #3 #3 } }
+ }
+% \end{macrocode}
+% The auxiliary \cs{@@_replace_auxii:nNNNnn} receives the following
+% arguments:
+% \begin{quote}
+% \Arg{delimiter} \meta{function} \meta{assignment} \\
+% \meta{tl~var} \Arg{pattern} \Arg{replacement}
+% \end{quote}
+% All of its work is done between
+% \cs{group_align_safe_begin:} and \cs{group_align_safe_end:} to avoid
+% issues in alignments. It does the actual replacement within
+% |#3|~|#4|~|{...}|, an \texttt{e}-expanding \meta{assignment}~|#3| to
+% the \meta{tl~var}~|#4|. The auxiliary \cs{@@_replace_next:w} is
+% called, followed by the \meta{token list}, some tokens including the
+% \meta{delimiter}~|#1|, followed by the \meta{pattern}~|#5|.
+% This auxiliary finds an argument delimited by~|#5| (the presence of
+% a trailing~|#5| avoids runaway arguments) and calls
+% \cs{@@_replace_wrap:w} to test whether this |#5| is found within the
+% \meta{token list} or is the trailing one.
+%
+% If on the one hand it is found within the \meta{token list}, then
+% |##1| cannot contain the \meta{delimiter}~|#1| that we worked so
+% hard to obtain, thus \cs{@@_replace_wrap:w} gets~|##1| as its own
+% argument~|##1|, and protects it against
+% the \texttt{e}-expanding assignment. It also finds \cs{exp_not:n}
+% as~|##2| and does nothing to it, thus letting through \cs{exp_not:n}
+% \Arg{replacement} into the assignment. Note that
+% \cs{@@_replace_next:w} and \cs{@@_replace_wrap:w} are always called
+% followed by two empty brace groups. These are safe because no
+% delimiter can match them. They prevent losing braces when grabbing
+% delimited arguments, but require the use of \cs{exp_not:o} and
+% \cs{use_none:nn}, rather than simply \cs{exp_not:n}.
+% Afterwards, \cs{@@_replace_next:w} is called
+% to repeat the replacement, or \cs{@@_replace_wrap:w} if we only want
+% a single replacement. In this second case, |##1| is the
+% \meta{remaining tokens} in the \meta{token list} and |##2| is some
+% \meta{ending code} which ends the assignment and removes the
+% trailing tokens |#5| using some \cs{if_false:} |{| \cs{fi:} |}|
+% trickery because~|#5| may contain any delimiter.
+%
+% If on the other hand the argument~|##1| of \cs{@@_replace_next:w} is
+% delimited by the trailing \meta{pattern}~|#5|, then |##1| is
+% \enquote{\{ \} \{ \} \meta{token list} \meta{delimiter}
+% \Arg{ending code}}, hence \cs{@@_replace_wrap:w} finds
+% \enquote{\{ \} \{ \} \meta{token list}} as |##1| and the
+% \meta{ending code} as~|##2|. It leaves the \meta{token list} into
+% the assignment and unbraces the \meta{ending code} which removes
+% what remains (essentially the \meta{delimiter} and
+% \meta{replacement}).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replace_auxii:nNNNnn #1#2#3#4#5#6
+ {
+ \group_align_safe_begin:
+ \cs_set:Npn \@@_replace_wrap:w ##1 #1 ##2
+ { \__kernel_exp_not:w \exp_after:wN { \use_none:nn ##1 } ##2 }
+ \cs_set:Npe \@@_replace_next:w ##1 #5
+ {
+ \exp_not:N \@@_replace_wrap:w ##1
+ \exp_not:n { #1 }
+ \exp_not:n { \exp_not:n {#6} }
+ \exp_not:n { #2 { } { } }
+ }
+ #3 #4
+ {
+ \exp_after:wN \@@_replace_next_aux:w
+ #4
+ #1
+ {
+ \if_false: { \fi: }
+ \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi:
+ }
+ #5
+ }
+ \group_align_safe_end:
+ }
+\cs_new:Npn \@@_replace_next_aux:w { \@@_replace_next:w { } { } }
+\cs_new_eq:NN \@@_replace_wrap:w ?
+\cs_new_eq:NN \@@_replace_next:w ?
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \tl_remove_once:Nn, \tl_remove_once:NV, \tl_remove_once:Ne,
+% \tl_remove_once:cn, \tl_remove_once:cV, \tl_remove_once:ce
+% }
+% \begin{macro}
+% {
+% \tl_gremove_once:Nn, \tl_gremove_once:NV,
+% \tl_gremove_once:cn, \tl_gremove_once:cV,
+% }
+% Removal is just a special case of replacement.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_remove_once:Nn #1#2
+ { \tl_replace_once:Nnn #1 {#2} { } }
+\cs_new_protected:Npn \tl_gremove_once:Nn #1#2
+ { \tl_greplace_once:Nnn #1 {#2} { } }
+\cs_generate_variant:Nn \tl_remove_once:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \tl_gremove_once:Nn { NV , Ne , c , cV , ce }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \tl_remove_all:Nn, \tl_remove_all:NV, \tl_remove_all:Ne,
+% \tl_remove_all:Nx,
+% \tl_remove_all:cn, \tl_remove_all:cV, \tl_remove_all:ce,
+% \tl_remove_all:cx,
+% }
+% \begin{macro}
+% {
+% \tl_gremove_all:Nn, \tl_gremove_all:NV, \tl_gremove_all:Ne,
+% \tl_gremove_all:Nx,
+% \tl_gremove_all:cn, \tl_gremove_all:cV, \tl_gremove_all:ce,
+% \tl_gremove_all:cx,
+% }
+% Removal is just a special case of replacement.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_remove_all:Nn #1#2
+ { \tl_replace_all:Nnn #1 {#2} { } }
+\cs_new_protected:Npn \tl_gremove_all:Nn #1#2
+ { \tl_greplace_all:Nnn #1 {#2} { } }
+\cs_generate_variant:Nn \tl_remove_all:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \tl_remove_all:Nn { Nx , cx }
+\cs_generate_variant:Nn \tl_gremove_all:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \tl_gremove_all:Nn { Nx , cx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Token list conditionals}
+%
+% \begin{macro}[pTF]{\tl_if_empty:N, \tl_if_empty:c}
+% These functions check whether the token list in the argument is
+% empty and execute the proper code from their argument(s).
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_empty:N #1 { p , T , F , TF }
+ {
+ \if_meaning:w #1 \c_empty_tl
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_empty:N
+ { c } { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\tl_if_empty:n, \tl_if_empty:V, \tl_if_empty:e}
+% The \cs{if:w} triggers the expansion of \cs{tl_to_str:n} which converts the
+% argument to a string: this is empty if and only if the argument is. Then
+% |\if:w \scan_stop: ... \scan_stop:| is \texttt{true} if and only if the
+% string |...| is empty.
+% It could be tempting to use |\if:w \scan_stop: #1 \scan_stop:| directly.
+% But this fails on a token list expanding to anything starting with
+% \cs{scan_stop:} leaving everything that follows in the input stream.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_empty:n #1 { p , TF , T , F }
+ {
+ \if:w \scan_stop: \tl_to_str:n {#1} \scan_stop:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_empty:n
+ { V , e } { p , TF , T , F }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF,documented-as=\tl_if_empty:nTF]{\tl_if_empty:o}
+% \begin{macro}[EXP]{\@@_if_empty_if:o}
+% The auxiliary function \cs{@@_if_empty_if:o} is for use
+% in various token list conditionals which reduce to testing
+% if a given token list is empty after applying a simple function
+% to it.
+% The test for emptiness is based on \cs{tl_if_empty:nTF}, but
+% the expansion is hard-coded for efficiency, as this auxiliary
+% function is used in several places.
+% We don't put \cs{prg_return_true:} and so on in the definition of
+% the auxiliary, because that would prevent an optimization applied to
+% conditionals that end with this code.
+% Also the |\@@_if_empty_if:o| is expanded once in |\tl_if_empty:oTF| for
+% efficiency as well (and to reduce code doubling).
+% \begin{macrocode}
+\cs_new:Npn \@@_if_empty_if:o #1
+ {
+ \if:w \scan_stop: \__kernel_tl_to_str:w \exp_after:wN {#1} \scan_stop:
+ }
+\exp_args:Nno \use:n
+ { \prg_new_conditional:Npnn \tl_if_empty:o #1 { p , TF , T , F } }
+ {
+ \@@_if_empty_if:o {#1}
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\tl_if_blank:n, \tl_if_blank:V, \tl_if_blank:o}
+% \begin{macro}{\@@_if_blank_p:NNw}
+% \TeX{} skips spaces when reading a non-delimited arguments. Thus,
+% a \meta{token list} is blank if and only if \cs{use_none:n}
+% \meta{token list} |?| is empty after one expansion. The auxiliary
+% \cs{@@_if_empty_if:o} is a fast emptyness test, converting its
+% argument to a string (after one expansion) and using the test
+% \cs{if:w} \cs{scan_stop:} |...| \cs{scan_stop:}.
+% \begin{macrocode}
+\exp_args:Nno \use:n
+ { \prg_new_conditional:Npnn \tl_if_blank:n #1 { p , T , F , TF } }
+ {
+ \@@_if_empty_if:o { \use_none:n #1 ? }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_blank:n
+ { e , V , o } { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\tl_if_eq:NN, \tl_if_eq:Nc, \tl_if_eq:cN, \tl_if_eq:cc}
+% Returns \cs{c_true_bool} if and only if the two token list variables are
+% equal.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \tl_if_eq:NN \cs_if_eq:NN { p , T , F , TF }
+\prg_generate_conditional_variant:Nnn \tl_if_eq:NN
+ { Nc , c , cc } { p , TF , T , F }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_internal_a_tl, \l_@@_internal_b_tl}
+% Temporary storage.
+% \begin{macrocode}
+\tl_new:N \l_@@_internal_a_tl
+\tl_new:N \l_@@_internal_b_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[TF]{\tl_if_eq:Nn}
+% A simple store and compare routine.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \tl_if_eq:Nn #1#2 { T , F , TF }
+ {
+ \group_begin:
+ \tl_set:Nn \l_@@_internal_b_tl {#2}
+ \exp_after:wN
+ \group_end:
+ \if_meaning:w #1 \l_@@_internal_b_tl
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_eq:Nn { c } { TF , T , F }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[TF]
+% {
+% \tl_if_eq:nn, \tl_if_eq:nV, \tl_if_eq:ne, \tl_if_eq:Vn, \tl_if_eq:en,
+% \tl_if_eq:ee,
+% \tl_if_eq:xn, \tl_if_eq:nx, \tl_if_eq:xx,
+% }
+% A simple store and compare routine.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \tl_if_eq:nn #1#2 { T , F , TF }
+ {
+ \group_begin:
+ \tl_set:Nn \l_@@_internal_a_tl {#1}
+ \tl_set:Nn \l_@@_internal_b_tl {#2}
+ \exp_after:wN
+ \group_end:
+ \if_meaning:w \l_@@_internal_a_tl \l_@@_internal_b_tl
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_eq:nn
+ { nV , ne , nx , V, e , ee , x , xx }
+ { TF , T , F }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[TF]
+% {
+% \tl_if_in:Nn, \tl_if_in:NV, \tl_if_in:No,
+% \tl_if_in:cn, \tl_if_in:cV, \tl_if_in:co
+% }
+% See \cs{tl_if_in:nnTF} for further comments. Here we simply
+% expand the token list variable and pass it to \cs{tl_if_in:nnTF}.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_if_in:NnT { \exp_args:No \tl_if_in:nnT }
+\cs_new_protected:Npn \tl_if_in:NnF { \exp_args:No \tl_if_in:nnF }
+\cs_new_protected:Npn \tl_if_in:NnTF { \exp_args:No \tl_if_in:nnTF }
+\prg_generate_conditional_variant:Nnn \tl_if_in:Nn
+ { NV , No , c , cV , co } { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[TF]
+% {
+% \tl_if_in:nn, \tl_if_in:Vn, \tl_if_in:VV, \tl_if_in:on, \tl_if_in:oo,
+% \tl_if_in:nV, \tl_if_in:no
+% }
+% Once more, the test relies on the emptiness test for robustness.
+% The function \cs{@@_tmp:w} removes tokens until the first occurrence
+% of |#2|. If this does not appear in |#1|, then the final |#2| is removed,
+% leaving an empty token list. Otherwise some tokens remain, and the
+% test is \texttt{false}. See \cs{tl_if_empty:nTF} for details on
+% the emptiness test.
+%
+% Treating correctly cases like
+% |\tl_if_in:nnTF {a state}{states}|, where |#1#2| contains |#2| before
+% the end, requires special care.
+% To cater for this case, we insert |{}{}| between the two token
+% lists. This marker may not appear in |#2| because of \TeX{} limitations
+% on what can delimit a parameter, hence we are safe. Using two brace
+% groups makes the test work also for empty arguments.
+% The \cs{if_false:} constructions are a faster way to do
+% \cs{group_align_safe_begin:} and \cs{group_align_safe_end:}.
+% The \cs{scan_stop:} ensures that \texttt{f}-expanding
+% \cs{tl_if_in:nnTF} does not lead to unbalanced braces.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \tl_if_in:nn #1#2 { T , F , TF }
+ {
+ \scan_stop:
+ \if_false: { \fi:
+ \cs_set:Npn \@@_tmp:w ##1 #2 { }
+ \tl_if_empty:oTF { \@@_tmp:w #1 {} {} #2 }
+ { \prg_return_false: } { \prg_return_true: }
+ \if_false: } \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_in:nn
+ { V , VV , o , oo , nV , no } { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF, EXP]{\tl_if_novalue:n}
+% \begin{macro}[EXP]{\@@_if_novalue:w}
+% Tests whether |##1| matches |-NoValue-| exactly (with suitable
+% catcodes): this is similar to \cs{quark_if_nil:nTF}. The first
+% argument of \cs{@@_if_novalue:w} is empty if and only if |##1|
+% starts with |-NoValue-|, while the second argument is empty if |##1|
+% is exactly |-NoValue-| or if it has a question mark just following
+% |-NoValue-|. In this second case, however, the material after the
+% first |?!| remains and makes the emptyness test return
+% \texttt{false}.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \prg_new_conditional:Npnn \tl_if_novalue:n ##1
+ { p , T , F , TF }
+ {
+ \@@_if_empty_if:o { \@@_if_novalue:w {} ##1 {} ? ! #1 ? ? ! }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ \cs_new:Npn \@@_if_novalue:w ##1 #1 ##2 ? ##3 ? ! { ##1 ##2 }
+ }
+\exp_args:No \@@_tmp:w { \c_novalue_tl }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP,pTF]{\tl_if_single:N, \tl_if_single:c}
+% Expand the token list and feed it to \cs{tl_if_single:nTF}.
+% \begin{macrocode}
+\cs_new:Npn \tl_if_single_p:N { \exp_args:No \tl_if_single_p:n }
+\cs_new:Npn \tl_if_single:NT { \exp_args:No \tl_if_single:nT }
+\cs_new:Npn \tl_if_single:NF { \exp_args:No \tl_if_single:nF }
+\cs_new:Npn \tl_if_single:NTF { \exp_args:No \tl_if_single:nTF }
+\prg_generate_conditional_variant:Nnn \tl_if_single:N {c} { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP,pTF]{\tl_if_single:n}
+% \begin{macro}[EXP]{\@@_if_single:nnw}
+% This test is similar to \cs{tl_if_empty:nTF}. Expanding
+% \cs{use_none:nn} |#1| |??| once yields an empty result if |#1| is
+% blank, a single~|?| if |#1| has a single item, and otherwise yields
+% some tokens ending with |??|. Then, \cs{__kernel_tl_to_str:w} makes sure
+% there are no odd category codes. An earlier version would compare
+% the result to a single~|?| using string comparison, but the Lua call
+% is slow in \LuaTeX{}. Instead, \cs{@@_if_single:nnw} picks the
+% second token in front of it. If |#1| is empty, this token is
+% the trailing~|?| and the |\if:w| test yields \texttt{false}. If
+% |#1| has a single item, the token is~|\scan_stop:| and the |\if:w| test
+% yields \texttt{true}. Otherwise, it is one of the characters
+% resulting from \cs{tl_to_str:n}, and the |\if:w| test yields
+% \texttt{false}. Note that \cs{if:w} and
+% \cs{__kernel_tl_to_str:w} are primitives that take care of
+% expansion.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_single:n #1 { p , T , F , TF }
+ {
+ \if:w \scan_stop: \exp_after:wN \@@_if_single:nnw
+ \__kernel_tl_to_str:w
+ \exp_after:wN { \use_none:nn #1 ?? } \scan_stop: ? \s_@@_stop
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \@@_if_single:nnw #1#2#3 \s_@@_stop {#2}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP,pTF]{\tl_if_single_token:n}
+% There are four cases: empty token list, token list starting with a
+% normal token, with a brace group, or with a space token. If the
+% token list starts with a normal token, remove it and check for
+% emptiness. For the next case, an empty token list is not a single
+% token. Finally, we have a non-empty token list starting with a
+% space or a brace group. Applying \texttt{f}-expansion yields an
+% empty result if and only if the token list is a single space.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_single_token:n #1 { p , T , F , TF }
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \@@_if_empty_if:o { \use_none:n #1 } }
+ {
+ \tl_if_empty:nTF {#1}
+ { \if_false: }
+ { \@@_if_empty_if:o { \exp:w \exp_end_continue_f:w #1 } }
+ }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Mapping over token lists}
+%
+% \begin{macro}
+% {
+% \tl_map_function:nN, \tl_map_function:NN, \tl_map_function:cN,
+% \@@_map_function:Nnnnnnnnn, \@@_map_function_end:w,
+% \@@_use_none_delimit_by_s_stop:w
+% }
+% Expandable loop macro for token lists. We use the internal scan
+% mark \cs{s_@@_stop} (defined later), which is not allowed to show up
+% in the token list |#1| since it is internal to \pkg{l3tl}. This
+% allows us a very fast test of whether some \meta{item} is the
+% end-marker \cs{s_@@_stop}, namely call
+% \cs{@@_use_none_delimit_by_s_stop:w} \meta{item} \meta{function}
+% \cs{s_@@_stop}, which calls \meta{function} if the \meta{item} is
+% the end-marker. To speed up the loop even more, only test one out
+% of eight items, and once we hit one of the eight end-markers,
+% go more slowly through the last few items of the list using
+% \cs{@@_map_function_end:w}.
+% \begin{macrocode}
+\cs_new:Npn \tl_map_function:nN #1#2
+ {
+ \@@_map_function:Nnnnnnnnn #2 #1
+ \s_@@_stop \s_@@_stop \s_@@_stop \s_@@_stop
+ \s_@@_stop \s_@@_stop \s_@@_stop \s_@@_stop
+ \prg_break_point:Nn \tl_map_break: { }
+ }
+\cs_new:Npn \tl_map_function:NN
+ { \exp_args:No \tl_map_function:nN }
+\cs_generate_variant:Nn \tl_map_function:NN { c }
+\cs_new:Npn \@@_map_function:Nnnnnnnnn #1#2#3#4#5#6#7#8#9
+ {
+ \@@_use_none_delimit_by_s_stop:w
+ #9 \@@_map_function_end:w \s_@@_stop
+ #1 {#2} #1 {#3} #1 {#4} #1 {#5} #1 {#6} #1 {#7} #1 {#8} #1 {#9}
+ \@@_map_function:Nnnnnnnnn #1
+ }
+\cs_new:Npn \@@_map_function_end:w \s_@@_stop #1#2
+ {
+ \@@_use_none_delimit_by_s_stop:w #2 \tl_map_break: \s_@@_stop
+ #1 {#2}
+ \@@_map_function_end:w \s_@@_stop
+ }
+\cs_new:Npn \@@_use_none_delimit_by_s_stop:w #1 \s_@@_stop { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\tl_map_inline:nn}
+% \begin{macro}{\tl_map_inline:Nn, \tl_map_inline:cn}
+% The inline functions are straight forward by now. We use a little
+% trick with the counter \cs{g__kernel_prg_map_int} to make
+% them nestable. We can also make use of \cs{@@_map_function:Nnnnnnnnn}
+% from before.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_map_inline:nn #1#2
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \cs_gset_protected:cpn
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w } ##1 {#2}
+ \exp_args:Nc \@@_map_function:Nnnnnnnnn
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w }
+ #1
+ \s_@@_stop \s_@@_stop \s_@@_stop \s_@@_stop
+ \s_@@_stop \s_@@_stop \s_@@_stop \s_@@_stop
+ \prg_break_point:Nn \tl_map_break:
+ { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+\cs_new_protected:Npn \tl_map_inline:Nn
+ { \exp_args:No \tl_map_inline:nn }
+\cs_generate_variant:Nn \tl_map_inline:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \tl_map_tokens:nn, \tl_map_tokens:Nn, \tl_map_tokens:cn,
+% \@@_map_tokens:nnnnnnnnn, \@@_map_tokens_end:w
+% }
+% Much like the function mapping.
+% \begin{macrocode}
+\cs_new:Npn \tl_map_tokens:nn #1#2
+ {
+ \@@_map_tokens:nnnnnnnnn {#2} #1
+ \s_@@_stop \s_@@_stop \s_@@_stop \s_@@_stop
+ \s_@@_stop \s_@@_stop \s_@@_stop \s_@@_stop
+ \prg_break_point:Nn \tl_map_break: { }
+ }
+\cs_new:Npn \tl_map_tokens:Nn
+ { \exp_args:No \tl_map_tokens:nn }
+\cs_generate_variant:Nn \tl_map_tokens:Nn { c }
+\cs_new:Npn \@@_map_tokens:nnnnnnnnn #1#2#3#4#5#6#7#8#9
+ {
+ \@@_use_none_delimit_by_s_stop:w
+ #9 \@@_map_tokens_end:w \s_@@_stop
+ \use:n {#1} {#2} \use:n {#1} {#3} \use:n {#1} {#4} \use:n {#1} {#5}
+ \use:n {#1} {#6} \use:n {#1} {#7} \use:n {#1} {#8} \use:n {#1} {#9}
+ \@@_map_tokens:nnnnnnnnn {#1}
+ }
+\cs_new:Npn \@@_map_tokens_end:w \s_@@_stop \use:n #1#2
+ {
+ \@@_use_none_delimit_by_s_stop:w #2 \tl_map_break: \s_@@_stop
+ #1 {#2}
+ \@@_map_tokens_end:w \s_@@_stop
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\tl_map_variable:nNn}
+% \begin{macro}{\tl_map_variable:NNn, \tl_map_variable:cNn}
+% \begin{macro}{\@@_map_variable:Nnn}
+% \cs{tl_map_variable:nNn} \Arg{token list} \meta{tl~var}
+% \Arg{action} assigns \meta{tl~var} to each element and executes
+% \meta{action}. The assignment to \meta{tl~var} is done after the
+% quark test so that this variable does not get set to a quark.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_map_variable:nNn #1#2#3
+ { \tl_map_tokens:nn {#1} { \@@_map_variable:Nnn #2 {#3} } }
+\cs_new_protected:Npn \@@_map_variable:Nnn #1#2#3
+ { \tl_set:Nn #1 {#3} #2 }
+\cs_new_protected:Npn \tl_map_variable:NNn
+ { \exp_args:No \tl_map_variable:nNn }
+\cs_generate_variant:Nn \tl_map_variable:NNn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_map_break:}
+% \begin{macro}{\tl_map_break:n}
+% The break statements use the general \cs{prg_map_break:Nn}.
+% \begin{macrocode}
+\cs_new:Npn \tl_map_break:
+ { \prg_map_break:Nn \tl_map_break: { } }
+\cs_new:Npn \tl_map_break:n
+ { \prg_map_break:Nn \tl_map_break: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Using token lists}
+%
+% \begin{macro}{\tl_to_str:n, \tl_to_str:o, \tl_to_str:V, \tl_to_str:v, \tl_to_str:e}
+% Another name for a primitive: defined in \pkg{l3basics}.
+% \begin{macrocode}
+\cs_generate_variant:Nn \tl_to_str:n { o , V , v , e }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\tl_to_str:N, \tl_to_str:c}
+% These functions return the replacement text of a token list as a
+% string.
+% \begin{macrocode}
+\cs_new:Npn \tl_to_str:N #1 { \__kernel_tl_to_str:w \exp_after:wN {#1} }
+\cs_generate_variant:Nn \tl_to_str:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\tl_use:N, \tl_use:c}
+% Token lists which are simply not defined give a clear \TeX{}
+% error here. No such luck for ones equal to \cs{scan_stop:} so
+% instead a test is made and if there is an issue an error is forced.
+% \begin{macrocode}
+\cs_new:Npn \tl_use:N #1
+ {
+ \tl_if_exist:NTF #1 {#1}
+ {
+ \msg_expandable_error:nnn
+ { kernel } { bad-variable } {#1}
+ }
+ }
+\cs_generate_variant:Nn \tl_use:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Working with the contents of token lists}
+%
+% \begin{macro}{\tl_count:n, \tl_count:V, \tl_count:v, \tl_count:e, \tl_count:o}
+% \begin{macro}{\tl_count:N, \tl_count:c}
+% \begin{macro}{\@@_count:n}
+% Count number of elements within a token list or token list
+% variable. Brace groups within the list are read as a single
+% element. Spaces are ignored.
+% \cs{@@_count:n} grabs the element and replaces it by |+1|.
+% The |0| ensures that it works on an empty list.
+% \begin{macrocode}
+\cs_new:Npn \tl_count:n #1
+ {
+ \int_eval:n
+ { 0 \tl_map_function:nN {#1} \@@_count:n }
+ }
+\cs_new:Npn \tl_count:N #1
+ {
+ \int_eval:n
+ { 0 \tl_map_function:NN #1 \@@_count:n }
+ }
+\cs_new:Npn \@@_count:n #1 { + 1 }
+\cs_generate_variant:Nn \tl_count:n { V , v , e , o }
+\cs_generate_variant:Nn \tl_count:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+%
+% \begin{macro}[EXP]{\tl_count_tokens:n}
+% \begin{macro}[EXP]{\@@_act_count_normal:nN,
+% \@@_act_count_group:nn, \@@_act_count_space:n}
+% The token count is computed through an \cs{int_eval:n} construction.
+% Each \texttt{1+} is output to the \emph{left}, into the integer
+% expression, and the sum is ended by the \cs{exp_end:} inserted by
+% \cs{@@_act_end:wn} (which is technically implemented as \cs{c_zero_int}).
+% Somewhat a hack!
+% \begin{macrocode}
+\cs_new:Npn \tl_count_tokens:n #1
+ {
+ \int_eval:n
+ {
+ \@@_act:NNNn
+ \@@_act_count_normal:N
+ \@@_act_count_group:n
+ \@@_act_count_space:
+ {#1}
+ }
+ }
+\cs_new:Npn \@@_act_count_normal:N #1 { 1 + }
+\cs_new:Npn \@@_act_count_space: { 1 + }
+\cs_new:Npn \@@_act_count_group:n #1 { 2 + \tl_count_tokens:n {#1} + }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_reverse_items:n}
+% \begin{macro}{\@@_reverse_items:nwNwn}
+% \begin{macro}{\@@_reverse_items:wn}
+% Reversal of a token list is done by taking one item at a time
+% and putting it after \cs{s_@@_stop}.
+% \begin{macrocode}
+\cs_new:Npn \tl_reverse_items:n #1
+ {
+ \@@_reverse_items:nwNwn #1 ?
+ \s_@@_mark \@@_reverse_items:nwNwn
+ \s_@@_mark \@@_reverse_items:wn
+ \s_@@_stop { }
+ }
+\cs_new:Npn \@@_reverse_items:nwNwn #1 #2 \s_@@_mark #3 #4 \s_@@_stop #5
+ {
+ #3 #2
+ \s_@@_mark \@@_reverse_items:nwNwn
+ \s_@@_mark \@@_reverse_items:wn
+ \s_@@_stop { {#1} #5 }
+ }
+\cs_new:Npn \@@_reverse_items:wn #1 \s_@@_stop #2
+ { \__kernel_exp_not:w \exp_after:wN { \use_none:nn #2 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \tl_trim_spaces:n, \tl_trim_spaces:V, \tl_trim_spaces:v,
+% \tl_trim_spaces:e,
+% \tl_trim_spaces:o,
+% }
+% \begin{macro}{\tl_trim_spaces_apply:nN, \tl_trim_spaces_apply:oN}
+% \begin{macro}
+% {
+% \tl_trim_spaces:N, \tl_trim_spaces:c,
+% \tl_gtrim_spaces:N, \tl_gtrim_spaces:c
+% }
+% Trimming spaces from around the input is deferred to an internal
+% function whose first argument is the token list to trim, augmented
+% by an initial \cs{@@_trim_mark:}, and whose second argument is a
+% \meta{continuation}, which receives as a braced argument
+% \cs{@@_trim_mark:} \meta{trimmed token list}. The control sequence
+% \cs{@@_trim_mark:} expands to nothing in a single expansion. In the case
+% at hand, we take \cs{__kernel_exp_not:w} \cs{exp_after:wN} as our
+% continuation, so that space trimming behaves correctly within an
+% \texttt{e}-type or \texttt{x}-type expansion.
+% \begin{macrocode}
+\cs_new:Npn \tl_trim_spaces:n #1
+ {
+ \@@_trim_spaces:nn
+ { \@@_trim_mark: #1 }
+ { \__kernel_exp_not:w \exp_after:wN }
+ }
+\cs_generate_variant:Nn \tl_trim_spaces:n { V , v , e , o }
+\cs_new:Npn \tl_trim_spaces_apply:nN #1#2
+ { \@@_trim_spaces:nn { \@@_trim_mark: #1 } { \exp_args:No #2 } }
+\cs_generate_variant:Nn \tl_trim_spaces_apply:nN { o }
+\cs_new_protected:Npn \tl_trim_spaces:N #1
+ { \__kernel_tl_set:Nx #1 { \exp_args:No \tl_trim_spaces:n {#1} } }
+\cs_new_protected:Npn \tl_gtrim_spaces:N #1
+ { \__kernel_tl_gset:Nx #1 { \exp_args:No \tl_trim_spaces:n {#1} } }
+\cs_generate_variant:Nn \tl_trim_spaces:N { c }
+\cs_generate_variant:Nn \tl_gtrim_spaces:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_trim_spaces:nn}
+% \begin{macro}
+% {
+% \@@_trim_spaces_auxi:w, \@@_trim_spaces_auxii:w,
+% \@@_trim_spaces_auxiii:w, \@@_trim_spaces_auxiv:w
+% }
+% \begin{macro}{\@@_trim_mark:}
+% Trimming spaces from around the input is done using delimited
+% arguments and quarks, and to get spaces at odd places in the
+% definitions, we nest those in \cs{@@_tmp:w}, which then receives
+% a single space as its argument: |#1| is \verb*+ +.
+% Removing leading spaces is done with \cs{@@_trim_spaces_auxi:w},
+% which loops until \cs{@@_trim_mark:}\verb*+ + matches the end of the token
+% list: then |##1| is the token list and |##3| is
+% \cs{@@_trim_spaces_auxii:w}. This hands the relevant tokens to the
+% loop \cs{@@_trim_spaces_auxiii:w}, responsible for trimming
+% trailing spaces. The end is reached when \verb*+ + \cs{s_@@_nil}
+% matches the one present in the definition of \cs{tl_trim_spaces:n}.
+% Then \cs{@@_trim_spaces_auxiv:w} puts the token list into a group,
+% with a lingering \cs{@@_trim_mark:} at the start (which will expand to
+% nothing in one step of expansion), and feeds this to the
+% \meta{continuation}.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_new:Npn \@@_trim_spaces:nn ##1
+ {
+ \@@_trim_spaces_auxi:w
+ ##1
+ \s_@@_nil
+ \@@_trim_mark: #1 { }
+ \@@_trim_mark: \@@_trim_spaces_auxii:w
+ \@@_trim_spaces_auxiii:w
+ #1 \s_@@_nil
+ \@@_trim_spaces_auxiv:w
+ \s_@@_stop
+ }
+ \cs_new:Npn
+ \@@_trim_spaces_auxi:w ##1 \@@_trim_mark: #1 ##2 \@@_trim_mark: ##3
+ {
+ ##3
+ \@@_trim_spaces_auxi:w
+ \@@_trim_mark:
+ ##2
+ \@@_trim_mark: #1 {##1}
+ }
+ \cs_new:Npn \@@_trim_spaces_auxii:w
+ \@@_trim_spaces_auxi:w \@@_trim_mark: \@@_trim_mark: ##1
+ {
+ \@@_trim_spaces_auxiii:w
+ ##1
+ }
+ \cs_new:Npn \@@_trim_spaces_auxiii:w ##1 #1 \s_@@_nil ##2
+ {
+ ##2
+ ##1 \s_@@_nil
+ \@@_trim_spaces_auxiii:w
+ }
+ \cs_new:Npn \@@_trim_spaces_auxiv:w ##1 \s_@@_nil ##2 \s_@@_stop ##3
+ { ##3 { ##1 } }
+ \cs_new:Npn \@@_trim_mark: {}
+ }
+\@@_tmp:w { ~ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\tl_sort:Nn, \tl_sort:cn, \tl_gsort:Nn, \tl_gsort:cn, \tl_sort:nN}
+% Implemented in \pkg{l3sort}.
+% \end{macro}
+%
+% \subsection{The first token from a token list}
+%
+% \begin{macro}{\tl_head:N, \tl_head:n, \tl_head:V, \tl_head:v, \tl_head:f}
+% \begin{macro}{\@@_head_auxi:nw, \@@_head_auxii:n}
+% \begin{macro}{\tl_head:w,\@@_tl_head:w}
+% \begin{macro}{\tl_tail:N, \tl_tail:n, \tl_tail:V, \tl_tail:v, \tl_tail:f}
+% Finding the head of a token list expandably always strips braces, which
+% is fine as this is consistent with for example mapping over a list. The
+% empty brace groups in \cs{tl_head:n} ensure that a blank argument gives an
+% empty result. The result is returned within the \tn{unexpanded} primitive.
+% The approach here is to use \cs{if_false:} to allow us to use |}| as
+% the closing delimiter: this is the only safe choice, as any other token
+% would not be able to parse it's own code. More detail in
+% \url{http://tex.stackexchange.com/a/70168}.
+% \begin{macrocode}
+\cs_new:Npn \tl_head:n #1
+ {
+ \__kernel_exp_not:w \tex_expanded:D
+ { { \if_false: { \fi: \@@_head_aux:n #1 { } } } }
+ }
+\cs_new:Npn \@@_head_aux:n #1
+ {
+ \__kernel_exp_not:w {#1}
+ \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi:
+ }
+\cs_generate_variant:Nn \tl_head:n { V , v , f }
+\cs_new:Npn \tl_head:w #1#2 \q_stop {#1}
+\cs_new:Npn \@@_tl_head:w #1#2 \s_@@_stop {#1}
+\cs_new:Npn \tl_head:N { \exp_args:No \tl_head:n }
+% \end{macrocode}
+% To correctly leave the tail of a token list, it's important \emph{not} to
+% absorb any of the tail part as an argument. For example, the simple
+% definition
+% \begin{verbatim}
+% \cs_new:Npn \tl_tail:n #1 { \tl_tail:w #1 \q_stop }
+% \cs_new:Npn \tl_tail:w #1#2 \q_stop
+% \end{verbatim}
+% would give the wrong result for |\tl_tail:n { a { bc } }| (the braces would
+% be stripped). Thus the only safe way to proceed is to first check that
+% there is an item to grab (\emph{i.e.}~that the argument is not blank) and
+% assuming there is to dispose of the first item. As with \cs{tl_head:n},
+% the result is protected from further expansion by \tn{unexpanded}.
+% While we could optimise the test here, this would leave some tokens
+% \enquote{banned} in the input, which we do not have with this definition.
+% \begin{macrocode}
+\exp_args:Nno \use:n { \cs_new:Npn \tl_tail:n #1 }
+ {
+ \exp_after:wN \__kernel_exp_not:w
+ \tl_if_blank:nTF {#1}
+ { { } }
+ { \exp_after:wN { \use_none:n #1 } }
+ }
+\cs_generate_variant:Nn \tl_tail:n { V , v , f }
+\cs_new:Npn \tl_tail:N { \exp_args:No \tl_tail:n }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]
+% {
+% \tl_if_head_eq_meaning:nN, \tl_if_head_eq_meaning:VN,
+% \tl_if_head_eq_meaning:eN
+% }
+% \begin{macro}[pTF]
+% {
+% \tl_if_head_eq_charcode:nN, \tl_if_head_eq_charcode:VN,
+% \tl_if_head_eq_charcode:eN, \tl_if_head_eq_charcode:fN
+% }
+% \begin{macro}[pTF]
+% {
+% \tl_if_head_eq_catcode:nN, \tl_if_head_eq_catcode:VN,
+% \tl_if_head_eq_catcode:eN, \tl_if_head_eq_catcode:oN
+% }
+% \begin{macro}[EXP]{\@@_head_exp_not:w}
+% \begin{macro}[EXP]{\@@_if_head_eq_empty_arg:w}
+% Accessing the first token of a token list is tricky in three cases:
+% when it has category code $1$ (begin-group token), when it is an
+% explicit space, with category code $10$ and character code $32$, or
+% when the token list is empty (obviously).
+%
+% Forgetting temporarily about this issue we would use the following
+% test in \cs{tl_if_head_eq_charcode:nN}. Here, \cs{tl_head:w} yields
+% the first token of the token list, then passed to \cs{exp_not:N}.
+% \begin{verbatim}
+% \if_charcode:w
+% \exp_after:wN \exp_not:N \tl_head:w #1 \q_nil \q_stop
+% \exp_not:N #2
+% \end{verbatim}
+% The two first special cases are detected by testing if the token
+% list starts with an \texttt{N}-type token (the extra |?| sends empty
+% token lists to the \texttt{true} branch of this test). In those
+% cases, the first token is a character, and since we only care about
+% its character code, we can use \cs{str_head:n} to access it (this
+% works even if it is a space character). An empty argument
+% results in \cs{tl_head:w} leaving two token: |^| and
+% \cs{@@_if_head_eq_empty_arg:w} which will result in the \cs{if_charcode:w}
+% test being false and remove \cs{exp_not:N} and |#2|.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_head_eq_charcode:nN #1#2 { p , T , F , TF }
+ {
+ \if_charcode:w
+ \tl_if_head_is_N_type:nTF { #1 ? }
+ { \@@_head_exp_not:w #1 { ^ \@@_if_head_eq_empty_arg:w } \s_@@_stop }
+ { \str_head:n {#1} }
+ \exp_not:N #2
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_head_eq_charcode:nN
+ { V , e , f } { p , TF , T , F }
+% \end{macrocode}
+% For \cs{tl_if_head_eq_catcode:nN}, again we detect special cases
+% with a \cs{tl_if_head_is_N_type:n}. Then we need to test if the
+% first token is a begin-group token or an explicit space token, and
+% produce the relevant token, either \cs{c_group_begin_token} or
+% \cs{c_space_token}. Again, for an empty argument, a hack is used,
+% removing the token given by the user and leaving two tokens in the input
+% stream which will make the \cs{if_catcode:w} test return \texttt{false}.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_head_eq_catcode:nN #1 #2 { p , T , F , TF }
+ {
+ \if_catcode:w
+ \tl_if_head_is_N_type:nTF { #1 ? }
+ { \@@_head_exp_not:w #1 { ^ \@@_if_head_eq_empty_arg:w } \s_@@_stop }
+ {
+ \tl_if_head_is_group:nTF {#1}
+ \c_group_begin_token
+ \c_space_token
+ }
+ \exp_not:N #2
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_head_eq_catcode:nN
+ { V , e , o } { p , TF , T , F }
+% \end{macrocode}
+% For \cs{tl_if_head_eq_meaning:nN}, again, detect special cases. In
+% the normal case, use \cs{tl_head:w}, with no \cs{exp_not:N} this
+% time, since \cs{if_meaning:w} causes no expansion. With an empty
+% argument, the test is \texttt{true}, and \cs{use_none:nnn} removes
+% |#2| and \cs{prg_return_true:} and \cs{else:} (it is safe this way here as
+% in this case \cs{prg_new_conditional:Npnn} didn't optimize these two away).
+% In the special cases, we know that the first token is a character,
+% hence \cs{if_charcode:w} and \cs{if_catcode:w} together are enough.
+% We combine them in some order, hopefully faster than the reverse.
+% Tests are not nested because the arguments may contain unmatched
+% primitive conditionals.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_head_eq_meaning:nN #1#2 { p , T , F , TF }
+ {
+ \tl_if_head_is_N_type:nTF { #1 ? }
+ \@@_if_head_eq_meaning_normal:nN
+ \@@_if_head_eq_meaning_special:nN
+ {#1} #2
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_head_eq_meaning:nN
+ { V , e } { p , TF , T , F }
+\cs_new:Npn \@@_if_head_eq_meaning_normal:nN #1 #2
+ {
+ \exp_after:wN \if_meaning:w
+ \@@_tl_head:w #1 { ?? \use_none:nnn } \s_@@_stop #2
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \@@_if_head_eq_meaning_special:nN #1 #2
+ {
+ \if_charcode:w \str_head:n {#1} \exp_not:N #2
+ \exp_after:wN \use_ii:nn
+ \else:
+ \prg_return_false:
+ \fi:
+ \use_none:n
+ {
+ \if_catcode:w \exp_not:N #2
+ \tl_if_head_is_group:nTF {#1}
+ { \c_group_begin_token }
+ { \c_space_token }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ }
+% \end{macrocode}
+%
+% Both \cs{tl_if_head_eq_charcode:nN} and \cs{tl_if_head_eq_catcode:nN} will
+% need to get the first token of their argument and apply \cs{exp_not:N} to
+% it. \cs{@@_head_exp_not:w} does exactly that.
+% \begin{macrocode}
+\cs_new:Npn \@@_head_exp_not:w #1 #2 \s_@@_stop
+ { \exp_not:N #1 }
+% \end{macrocode}
+%
+% If the argument of \cs{tl_if_head_eq_charcode:nN} and
+% \cs{tl_if_head_eq_catcode:nN} was empty \cs{@@_if_head_eq_empty_arg:w} will
+% be left in the input stream. This macro has to remove \cs{exp_not:N} and
+% the following token from the input stream to make sure no unbalanced
+% if-construct is created and leave tokens there which make the two tests
+% return \texttt{false}.
+% \begin{macrocode}
+\cs_new:Npn \@@_if_head_eq_empty_arg:w \exp_not:N #1
+ { ? }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\tl_if_head_is_N_type:n}
+% \begin{macro}[EXP]
+% {
+% \@@_if_head_is_N_type_auxi:w ,
+% \@@_if_head_is_N_type_auxii:n ,
+% }
+% A token list can be empty, can start with an explicit space
+% character (catcode 10 and charcode 32), can start with a begin-group
+% token (catcode 1), or start with an \texttt{N}-type argument. In
+% the first two cases, and when |#1~| starts with |{}~|,
+% \cs{@@_if_head_is_N_type_auxi:w} receives an empty argument hence
+% produces |f| and removes everything before the first
+% \cs{scan_stop:}. In the third case (except when |#1~| starts with
+% |{}~|), the second auxiliary removes the first copy of~|#1| that was
+% used for the space test, then expands \cs{token_to_str:N} which hits
+% the leading begin-group token, leaving a single closing brace to be
+% compared with \cs{scan_stop:}. In the last case,
+% \cs{token_to_str:N} does not change the brace balance so that only
+% \cs{scan_stop:} \cs{scan_stop:} remain, making the character code
+% test true. One cannot optimize by
+% moving one of the \cs{scan_stop:} to the beginning: if |#1| contains
+% primitive conditionals, all of its occurrences must be dealt with before
+% the \cs{if:w} tries to skip the \texttt{true} branch of the
+% conditional.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_head_is_N_type:n #1 { p , T , F , TF }
+ {
+ \if:w
+ \if_false: { \fi: \@@_if_head_is_N_type_auxi:w #1 ~ }
+ { \exp_after:wN { \token_to_str:N #1 } }
+ \scan_stop: \scan_stop:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_head_is_N_type_auxi:w #1 ~ }
+ {
+ \tl_if_empty:nTF {#1}
+ { f \exp_after:wN \use_none:nn }
+ { \exp_after:wN \@@_if_head_is_N_type_auxii:n }
+ \exp_after:wN { \if_false: } \fi:
+ }
+\cs_new:Npn \@@_if_head_is_N_type_auxii:n #1
+ { \exp_after:wN \use_none:n \exp_after:wN }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP,pTF]{\tl_if_head_is_group:n}
+% \begin{macro}[EXP]{\@@_if_head_is_group_fi_false:w}
+% Pass the first token of |#1| through \cs{token_to_str:N}, then check
+% for the brace balance. The extra \texttt{?} caters for an empty
+% argument. This could be made faster, but we need all brace tricks
+% to happen in one step of expansion, keeping the token list brace
+% balanced at all times.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_head_is_group:n #1 { p , T , F , TF }
+ {
+ \if:w
+ \exp_after:wN \use_none:n
+ \exp_after:wN { \exp_after:wN { \token_to_str:N #1 ? } }
+ \scan_stop: \scan_stop:
+ \@@_if_head_is_group_fi_false:w
+ \fi:
+ \if_true:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \@@_if_head_is_group_fi_false:w \fi: \if_true: { \fi: \if_false: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP,pTF]{\tl_if_head_is_space:n}
+% \begin{macro}[EXP]{\@@_if_head_is_space:w}
+% The auxiliary's argument is all that is before the first explicit
+% space in |\prg_do_nothing:#1?~|. If that is a single~|\prg_do_nothing:| the
+% test yields \texttt{true}. Otherwise, that is more than one token, and the
+% test yields \texttt{false}. The work is done within braces (with an
+% |\if_false: { \fi: ... }| construction) both to hide potential
+% alignment tab characters from \TeX{} in a table, and to allow for
+% removing what remains of the token list after its first space. The use of
+% \cs{if:w} ensures that the result of a single step of expansion directly
+% yields a balanced token list (no trailing closing brace).
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_head_is_space:n #1 { p , T , F , TF }
+ {
+ \if:w
+ \if_false: { \fi: \@@_if_head_is_space:w \prg_do_nothing: #1 ? ~ }
+ \scan_stop: \scan_stop:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\exp_args:Nno \use:n { \cs_new:Npn \@@_if_head_is_space:w #1 ~ }
+ {
+ \@@_if_empty_if:o {#1} \else: f \fi:
+ \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Token by token changes}
+%
+% \begin{variable}{\s_@@_act_stop}
+% The \cs[no-index]{@@_act_\ldots{}} functions may be applied to any token list.
+% Hence, we use a private quark, to allow any token, even quarks,
+% in the token list.
+% Only \cs{s_@@_act_stop} may not appear in the token lists manipulated by
+% \cs{@@_act:NNNn} functions.
+% \begin{macrocode}
+\scan_new:N \s_@@_act_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_act:NNNn}
+% \begin{macro}[EXP]{\@@_act_output:n, \@@_act_reverse_output:n}
+% \begin{macro}[EXP]{\@@_act_loop:w}
+% \begin{macro}[EXP]{\@@_act_normal:NwNNN}
+% \begin{macro}[EXP]{\@@_act_group:nwNNN}
+% \begin{macro}[EXP]{\@@_act_space:wwNNN}
+% \begin{macro}[EXP]{\@@_act_end:wn}
+% \begin{macro}[EXP]
+% {
+% \@@_act_if_head_is_space:nTF,
+% \@@_act_if_head_is_space:w,
+% \@@_act_if_head_is_space_true:w
+% }
+% \begin{macro}[EXP]{\@@_use_none_delimit_by_q_act_stop:w}
+% To help control the expansion, \cs{@@_act:NNNn} should always
+% be preceeded by \cs{exp:w} and ends by producing \cs{exp_end:}
+% once the result has been obtained. This way no internal token of it can be
+% accidentally end up in the input stream.
+% Because \cs{s_@@_act_stop} can't appear without braces around it in the
+% argument~|#1| of \cs{@@_act_loop:w}, we can use this marker to set up a fast
+% test for leading spaces.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_new:Npn \@@_act_if_head_is_space:nTF ##1
+ {
+ \@@_act_if_head_is_space:w
+ \s_@@_act_stop ##1 \s_@@_act_stop \@@_act_if_head_is_space_true:w
+ \s_@@_act_stop #1 \s_@@_act_stop \use_ii:nn
+ }
+ \cs_new:Npn \@@_act_if_head_is_space:w
+ ##1 \s_@@_act_stop #1 ##2 \s_@@_act_stop
+ {}
+ \cs_new:Npn \@@_act_if_head_is_space_true:w
+ \s_@@_act_stop #1 \s_@@_act_stop \use_ii:nn ##1 ##2
+ {##1}
+ }
+\@@_tmp:w { ~ }
+% \end{macrocode}
+% (We expand the definition \cs{@@_act_if_head_is_space:nTF} when
+% setting up \cs{@@_act_loop:w}, so we can then undefine the auxiliary.)
+% In the loop, we check how the token list begins and act
+% accordingly. In the \enquote{group} case, we may have
+% reached \cs{s_@@_act_stop}, the end of the list. Then
+% leave \cs{exp_end:} and the result in the input stream,
+% to terminate the expansion of \cs{exp:w}.
+% Otherwise, apply the relevant function to the
+% \enquote{arguments}, |#3|
+% and to the head of the token list. Then repeat the loop.
+% The scheme is the same if the token list starts with an |N|-type
+% or with a space, making sure that
+% \cs{@@_act_space:wwNNN} gobbles the space.
+% \begin{macrocode}
+\exp_args:Nne \use:n { \cs_new:Npn \@@_act_loop:w #1 \s_@@_act_stop }
+ {
+ \exp_not:o { \@@_act_if_head_is_space:nTF {#1} }
+ \exp_not:N \@@_act_space:wwNNN
+ {
+ \exp_not:o { \tl_if_head_is_group:nTF {#1} }
+ \exp_not:N \@@_act_group:nwNNN
+ \exp_not:N \@@_act_normal:NwNNN
+ }
+ \exp_not:n {#1} \s_@@_act_stop
+ }
+\cs_undefine:N \@@_act_if_head_is_space:nTF
+\cs_new:Npn \@@_act_normal:NwNNN #1 #2 \s_@@_act_stop #3
+ {
+ #3 #1
+ \@@_act_loop:w #2 \s_@@_act_stop
+ #3
+ }
+\cs_new:Npn \@@_use_none_delimit_by_s_act_stop:w #1 \s_@@_act_stop { }
+\cs_new:Npn \@@_act_end:wn #1 \@@_act_result:n #2
+ { \group_align_safe_end: \exp_end: #2 }
+\cs_new:Npn \@@_act_group:nwNNN #1 #2 \s_@@_act_stop #3#4#5
+ {
+ \@@_use_none_delimit_by_s_act_stop:w #1 \@@_act_end:wn \s_@@_act_stop
+ #5 {#1}
+ \@@_act_loop:w #2 \s_@@_act_stop
+ #3 #4 #5
+ }
+\exp_last_unbraced:NNo
+ \cs_new:Npn \@@_act_space:wwNNN \c_space_tl #1 \s_@@_act_stop #2#3
+ {
+ #3
+ \@@_act_loop:w #1 \s_@@_act_stop
+ #2 #3
+ }
+% \end{macrocode}
+% \cs{@@_act:NNNn} loops over tokens, groups, and spaces in |#4|.
+% |{\s_@@_act_stop}| serves as the end of token list marker, the |?| after it
+% avoids losing outer braces. The result is stored as an argument for the
+% dummy function \cs{@@_act_result:n}.
+% \begin{macrocode}
+\cs_new:Npn \@@_act:NNNn #1#2#3#4
+ {
+ \group_align_safe_begin:
+ \@@_act_loop:w #4 { \s_@@_act_stop } ? \s_@@_act_stop
+ #1 #3 #2
+ \@@_act_result:n { }
+ }
+% \end{macrocode}
+% Typically, the output is done to the right of what was already output,
+% using \cs{@@_act_output:n}, but for the \cs{@@_act_reverse} functions,
+% it should be done to the left.
+% \begin{macrocode}
+\cs_new:Npn \@@_act_output:n #1 #2 \@@_act_result:n #3
+ { #2 \@@_act_result:n { #3 #1 } }
+\cs_new:Npn \@@_act_reverse_output:n #1 #2 \@@_act_result:n #3
+ { #2 \@@_act_result:n { #1 #3 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\tl_reverse:n, \tl_reverse:o, \tl_reverse:V, \tl_reverse:f, \tl_reverse:e}
+% \begin{macro}[EXP]{\@@_reverse_normal:nN}
+% \begin{macro}[EXP]{\@@_reverse_group_preserve:nn}
+% \begin{macro}[EXP]{\@@_reverse_space:n}
+% The goal here is to reverse without losing spaces nor braces.
+% This is done using the general internal function \cs{@@_act:NNNn}.
+% Spaces and \enquote{normal} tokens are output on the left of the current
+% output. Grouped tokens are output to the left but without any reversal
+% within the group.
+% \begin{macrocode}
+\cs_new:Npn \tl_reverse:n #1
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \exp:w
+ \@@_act:NNNn
+ \@@_reverse_normal:N
+ \@@_reverse_group_preserve:n
+ \@@_reverse_space:
+ {#1}
+ }
+ }
+\cs_generate_variant:Nn \tl_reverse:n { o , V , f , e }
+\cs_new:Npn \@@_reverse_normal:N
+ { \@@_act_reverse_output:n }
+\cs_new:Npn \@@_reverse_group_preserve:n #1
+ { \@@_act_reverse_output:n { {#1} } }
+\cs_new:Npn \@@_reverse_space:
+ { \@@_act_reverse_output:n { ~ } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_reverse:N, \tl_reverse:c, \tl_greverse:N, \tl_greverse:c}
+% This reverses the list, leaving \cs{exp_stop_f:} in front,
+% which stops the \texttt{f}-expansion.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_reverse:N #1
+ { \__kernel_tl_set:Nx #1 { \exp_args:No \tl_reverse:n { #1 } } }
+\cs_new_protected:Npn \tl_greverse:N #1
+ { \__kernel_tl_gset:Nx #1 { \exp_args:No \tl_reverse:n { #1 } } }
+\cs_generate_variant:Nn \tl_reverse:N { c }
+\cs_generate_variant:Nn \tl_greverse:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Using a single item}
+%
+% \begin{macro}{\tl_item:nn, \tl_item:Nn, \tl_item:cn}
+% \begin{macro}{\@@_item_aux:nn, \@@_item:nn}
+% The idea here is to find the offset of the item from the left, then use
+% a loop to grab the correct item. If the resulting offset is too large,
+% then \cs{@@_if_recursion_tail_break:nN} terminates the loop, and returns
+% nothing at all.
+% \begin{macrocode}
+\cs_new:Npn \tl_item:nn #1#2
+ {
+ \exp_args:Nf \@@_item:nn
+ { \exp_args:Nf \@@_item_aux:nn { \int_eval:n {#2} } {#1} }
+ #1
+ \q_@@_recursion_tail
+ \prg_break_point:
+ }
+\cs_new:Npn \@@_item_aux:nn #1#2
+ {
+ \int_compare:nNnTF {#1} < 0
+ { \int_eval:n { \tl_count:n {#2} + 1 + #1 } }
+ {#1}
+ }
+\cs_new:Npn \@@_item:nn #1#2
+ {
+ \@@_if_recursion_tail_break:nN {#2} \prg_break:
+ \int_compare:nNnTF {#1} = 1
+ { \prg_break:n { \exp_not:n {#2} } }
+ { \exp_args:Nf \@@_item:nn { \int_eval:n { #1 - 1 } } }
+ }
+\cs_new:Npn \tl_item:Nn { \exp_args:No \tl_item:nn }
+\cs_generate_variant:Nn \tl_item:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_rand_item:n, \tl_rand_item:N, \tl_rand_item:c}
+% Importantly \cs{tl_item:nn} only evaluates its argument once.
+% \begin{macrocode}
+\cs_new:Npn \tl_rand_item:n #1
+ {
+ \tl_if_blank:nF {#1}
+ { \tl_item:nn {#1} { \int_rand:nn { 1 } { \tl_count:n {#1} } } }
+ }
+\cs_new:Npn \tl_rand_item:N { \exp_args:No \tl_rand_item:n }
+\cs_generate_variant:Nn \tl_rand_item:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\tl_range:Nnn, \tl_range:cnn, \tl_range:nnn}
+% \begin{macro}
+% {
+% \@@_range:Nnnn, \@@_range:nnnNn, \@@_range:nnNn, \@@_range_skip:w,
+% \@@_range:w, \@@_range_skip_spaces:n, \@@_range_collect:nn,
+% \@@_range_collect:ff, \@@_range_collect_space:nw,
+% \@@_range_collect_N:nN, \@@_range_collect_group:nN,
+% }
+% To avoid checking for the end of the token list at every step, start
+% by counting the number $l$ of items and \enquote{normalizing} the
+% bounds, namely clamping them to the interval $[0,l]$ and dealing
+% with negative indices. More precisely, \cs{@@_range_items:nnNn}
+% receives the number of items to skip at the beginning of the token
+% list, the index of the last item to keep, a function which is either
+% \cs{@@_range:w} or the token list itself. If nothing should be kept,
+% leave |{}|: this stops the \texttt{f}-expansion of \cs{tl_head:f} and that
+% function produces an empty result. Otherwise, repeatedly call
+% \cs{@@_range_skip:w} to delete |#1| items from the input stream (the
+% extra brace group avoids an off-by-one shift). For the braced
+% version \cs{@@_range_braced:w} sets up
+% \cs{@@_range_collect_braced:w} which stores items one by one in an
+% argument after the semicolon. Depending on the first token of the tail,
+% either just move it (if it is a space) or also decrement the number of
+% items left to find. Eventually, the result is a brace group followed by
+% the rest of the token list, and \cs{tl_head:f} cleans up and gives the
+% result in \cs{exp_not:n}.
+% \begin{macrocode}
+\cs_new:Npn \tl_range:Nnn { \exp_args:No \tl_range:nnn }
+\cs_generate_variant:Nn \tl_range:Nnn { c }
+\cs_new:Npn \tl_range:nnn { \@@_range:Nnnn \@@_range:w }
+\cs_new:Npn \@@_range:Nnnn #1#2#3#4
+ {
+ \tl_head:f
+ {
+ \exp_args:Nf \@@_range:nnnNn
+ { \tl_count:n {#2} } {#3} {#4} #1 {#2}
+ }
+ }
+\cs_new:Npn \@@_range:nnnNn #1#2#3
+ {
+ \exp_args:Nff \@@_range:nnNn
+ {
+ \exp_args:Nf \@@_range_normalize:nn
+ { \int_eval:n { #2 - 1 } } {#1}
+ }
+ {
+ \exp_args:Nf \@@_range_normalize:nn
+ { \int_eval:n {#3} } {#1}
+ }
+ }
+\cs_new:Npn \@@_range:nnNn #1#2#3#4
+ {
+ \if_int_compare:w #2 > #1 \exp_stop_f: \else:
+ \exp_after:wN { \exp_after:wN }
+ \fi:
+ \exp_after:wN #3
+ \int_value:w \int_eval:n { #2 - #1 } \exp_after:wN ;
+ \exp_after:wN { \exp:w \@@_range_skip:w #1 ; { } #4 }
+ }
+\cs_new:Npn \@@_range_skip:w #1 ; #2
+ {
+ \if_int_compare:w #1 > \c_zero_int
+ \exp_after:wN \@@_range_skip:w
+ \int_value:w \int_eval:n { #1 - 1 } \exp_after:wN ;
+ \else:
+ \exp_after:wN \exp_end:
+ \fi:
+ }
+\cs_new:Npn \@@_range:w #1 ; #2
+ {
+ \exp_args:Nf \@@_range_collect:nn
+ { \@@_range_skip_spaces:n {#2} } {#1}
+ }
+\cs_new:Npn \@@_range_skip_spaces:n #1
+ {
+ \tl_if_head_is_space:nTF {#1}
+ { \exp_args:Nf \@@_range_skip_spaces:n {#1} }
+ { { } #1 }
+ }
+\cs_new:Npn \@@_range_collect:nn #1#2
+ {
+ \int_compare:nNnTF {#2} = 0
+ {#1}
+ {
+ \exp_args:No \tl_if_head_is_space:nTF { \use_none:n #1 }
+ {
+ \exp_args:Nf \@@_range_collect:nn
+ { \@@_range_collect_space:nw #1 }
+ {#2}
+ }
+ {
+ \@@_range_collect:ff
+ {
+ \exp_args:No \tl_if_head_is_N_type:nTF { \use_none:n #1 }
+ { \@@_range_collect_N:nN }
+ { \@@_range_collect_group:nn }
+ #1
+ }
+ { \int_eval:n { #2 - 1 } }
+ }
+ }
+ }
+\cs_new:Npn \@@_range_collect_space:nw #1 ~ { { #1 ~ } }
+\cs_new:Npn \@@_range_collect_N:nN #1#2 { { #1 #2 } }
+\cs_new:Npn \@@_range_collect_group:nn #1#2 { { #1 {#2} } }
+\cs_generate_variant:Nn \@@_range_collect:nn { ff }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_range_normalize:nn}
+% This function converts an \meta{index} argument into an explicit
+% position in the token list (a result of $0$ denoting \enquote{out of
+% bounds}). Expects two explicit integer arguments: the \meta{index}
+% |#1| and the string count~|#2|. If |#1| is negative, replace it by
+% $|#1| + |#2| + 1$, then limit to the range $[0, |#2|]$.
+% \begin{macrocode}
+\cs_new:Npn \@@_range_normalize:nn #1#2
+ {
+ \int_eval:n
+ {
+ \if_int_compare:w #1 < \c_zero_int
+ \if_int_compare:w #1 < -#2 \exp_stop_f:
+ 0
+ \else:
+ #1 + #2 + 1
+ \fi:
+ \else:
+ \if_int_compare:w #1 < #2 \exp_stop_f:
+ #1
+ \else:
+ #2
+ \fi:
+ \fi:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Viewing token lists}
+%
+% \begin{macro}{\tl_show:N, \tl_show:c, \tl_log:N, \tl_log:c, \@@_show:NN}
+% Showing token list variables is done after checking that the
+% variable is defined (see \cs{__kernel_register_show:N}).
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_show:N { \@@_show:NN \tl_show:n }
+\cs_generate_variant:Nn \tl_show:N { c }
+\cs_new_protected:Npn \tl_log:N { \@@_show:NN \tl_log:n }
+\cs_generate_variant:Nn \tl_log:N { c }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ \exp_args:Nf \tl_if_empty:nTF
+ { \cs_prefix_spec:N #2 \cs_parameter_spec:N #2 }
+ {
+ \exp_args:Ne #1
+ { \token_to_str:N #2 = \__kernel_exp_not:w \exp_after:wN {#2} }
+ }
+ {
+ \msg_error:nneee { kernel } { bad-type }
+ { \token_to_str:N #2 } { \token_to_meaning:N #2 } { tl }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\tl_show:n, \tl_show:e, \tl_show:x, \@@_show:n}
+% \begin{macro}[EXP]{\@@_show:w}
+% Many |show| functions are based on \cs{tl_show:n}.
+% The argument of \cs{tl_show:n} is line-wrapped using
+% \cs{iow_wrap:nnnN} but with a leading |>~| and trailing period, both
+% removed before passing the wrapped text to the \tn{showtokens}
+% primitive. This primitive shows the result with a leading |>~| and
+% trailing period.
+%
+% The token list \cs{l_@@_internal_a_tl} containing the result
+% of all these manipulations is displayed to the terminal using
+% \cs{tex_showtokens:D} and an odd \cs{exp_after:wN} which expand the
+% closing brace to improve the output slightly. The calls to
+% \cs{__kernel_iow_with:Nnn} ensure that the \tn{newlinechar} is set to~$10$
+% so that the \cs{iow_newline:} inserted by the line-wrapping code
+% are correctly recognized by \TeX{}, and that \tn{errorcontextlines}
+% is $-1$ to avoid printing irrelevant context.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_show:n #1
+ { \iow_wrap:nnnN { >~ \tl_to_str:n {#1} . } { } { } \@@_show:n }
+\cs_generate_variant:Nn \tl_show:n { e , x }
+\cs_new_protected:Npn \@@_show:n #1
+ {
+ \tl_set:Nf \l_@@_internal_a_tl { \@@_show:w #1 \s_@@_stop }
+ \__kernel_iow_with:Nnn \tex_newlinechar:D { 10 }
+ {
+ \__kernel_iow_with:Nnn \tex_errorcontextlines:D { -1 }
+ {
+ \tex_showtokens:D \exp_after:wN \exp_after:wN \exp_after:wN
+ { \exp_after:wN \l_@@_internal_a_tl }
+ }
+ }
+ }
+\cs_new:Npn \@@_show:w #1 > #2 . \s_@@_stop {#2}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_log:n, \tl_log:e, \tl_log:x}
+% Logging is much easier, simply line-wrap. The |>~| and trailing
+% period is there to match the output of \cs{tl_show:n}.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_log:n #1
+ { \iow_wrap:nnnN { > ~ \tl_to_str:n {#1} . } { } { } \iow_log:n }
+\cs_generate_variant:Nn \tl_log:n { e , x }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__kernel_chk_tl_type:NnnT}
+% Helper for checking that |#1| has the correct internal structure to
+% be of a certain type. Make sure that it is defined and that it is a
+% token list, namely a macro with no \tn{long} nor \tn{protected}
+% prefix. Then compare |#1| to an attempt at reconstructing a valid
+% structure of the given type using |#2| (see implementation of
+% \cs{seq_show:N} for instance). If that is successful run the
+% requested code~|#4|.
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_chk_tl_type:NnnT #1#2#3#4
+ {
+ \__kernel_chk_defined:NT #1
+ {
+ \exp_args:Nf \tl_if_empty:nTF
+ { \cs_prefix_spec:N #1 \cs_parameter_spec:N #1 }
+ {
+ \tl_set:Ne \l_@@_internal_a_tl {#3}
+ \tl_if_eq:NNTF #1 \l_@@_internal_a_tl
+ {#4}
+ {
+ \msg_error:nneeee { kernel } { bad-type }
+ { \token_to_str:N #1 } { \tl_to_str:N #1 }
+ {#2} { \tl_to_str:N \l_@@_internal_a_tl }
+ }
+ }
+ {
+ \msg_error:nneee { kernel } { bad-type }
+ { \token_to_str:N #1 } { \token_to_meaning:N #1 } {#2}
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Internal scan marks}
+%
+% \begin{variable}{\s_@@_nil,\s_@@_mark,\s_@@_stop}
+% Internal scan marks. These are defined here at the end because the
+% code for \cs{scan_new:N} depends on some \pkg{l3tl} functions.
+% \begin{macrocode}
+\scan_new:N \s_@@_nil
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Scratch token lists}
+%
+% \begin{variable}{\g_tmpa_tl, \g_tmpb_tl}
+% Global temporary token list variables.
+% They are supposed to be set and used immediately,
+% with no delay between the definition and the use because you
+% can't count on other macros not to redefine them from under you.
+% \begin{macrocode}
+\tl_new:N \g_tmpa_tl
+\tl_new:N \g_tmpb_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_tmpa_tl, \l_tmpb_tl}
+% These are local temporary token list variables. Be sure not to assume
+% that the value you put into them will survive for
+% long---see discussion above.
+% \begin{macrocode}
+\tl_new:N \l_tmpa_tl
+\tl_new:N \l_tmpb_tl
+% \end{macrocode}
+% \end{variable}
+%
+% We finally clean up a temporary control sequence that we have used at
+% various points to set up some definitions.
+% \begin{macrocode}
+\cs_undefine:N \@@_tmp:w
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3token.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3token.dtx
new file mode 100644
index 00000000000..a38860b548c
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3token.dtx
@@ -0,0 +1,2936 @@
+% \iffalse meta-comment
+%
+%% File: l3token.dtx
+%
+% Copyright (C) 2005-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3token} module\\ Token manipulation^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% This module deals with tokens. Now this is perhaps not the most
+% precise description so let's try with a better description: When
+% programming in \TeX{}, it is often desirable to know just what a
+% certain token is: is it a control sequence or something
+% else. Similarly one often needs to know if a control sequence is
+% expandable or not, a macro or a primitive, how many arguments it
+% takes etc. Another thing of great importance (especially when it
+% comes to document commands) is looking ahead in the token stream to
+% see if a certain character is present and maybe even remove it or
+% disregard other tokens while scanning. This module provides
+% functions for both and as such has two primary function
+% categories: |\token_| for anything that deals with tokens and
+% |\peek_| for looking ahead in the token stream.
+%
+% Most functions we describe here can be used on control sequences,
+% as those are tokens as well.
+%
+% It is important to distinguish two aspects of a token: its
+% \enquote{shape} (for lack of a better word), which affects the
+% matching of delimited arguments and the comparison of token lists
+% containing this token, and its \enquote{meaning}, which affects
+% whether the token expands or what operation it performs. One can have
+% tokens of different shapes with the same meaning, but not the
+% converse.
+%
+% For instance, \cs{if:w}, \cs{if_charcode:w}, and \cs[no-index]{tex_if:D} are
+% three names for the same internal operation of \TeX{}, namely the
+% primitive testing the next two characters for equality of their
+% character code. They have the same meaning hence behave identically
+% in many situations. However, \TeX{} distinguishes them when searching
+% for a delimited argument. Namely, the example function
+% |\show_until_if:w| defined below takes everything until \cs{if:w}
+% as an argument, despite the presence of other copies of \cs{if:w}
+% under different names.
+% \begin{verbatim}
+% \cs_new:Npn \show_until_if:w #1 \if:w { \tl_show:n {#1} }
+% \show_until_if:w \tex_if:D \if_charcode:w \if:w
+% \end{verbatim}
+% A list of all possible shapes and a list of all possible meanings are
+% given in section~\ref{sec:l3token:all-tokens}.
+%
+% \section{Creating character tokens}
+%
+% \begin{function}[updated = 2015-11-12]
+% {
+% \char_set_active_eq:NN, \char_set_active_eq:Nc,
+% \char_gset_active_eq:NN, \char_gset_active_eq:Nc
+% }
+% \begin{syntax}
+% \cs{char_set_active_eq:NN} \meta{char} \meta{function}
+% \end{syntax}
+% Sets the behaviour of the \meta{char} in situations where it is
+% active (category code $13$) to be equivalent to that of the
+% definition of the \meta{function} at the time \cs{char_set_active_eq:NN}
+% is used. The category code of the \meta{char} is
+% \emph{unchanged} by this process. The \meta{function} may itself
+% be an active character.
+% \end{function}
+%
+% \begin{function}[added = 2015-11-12]
+% {
+% \char_set_active_eq:nN, \char_set_active_eq:nc,
+% \char_gset_active_eq:nN, \char_gset_active_eq:nc
+% }
+% \begin{syntax}
+% \cs{char_set_active_eq:nN} \Arg{integer expression} \meta{function}
+% \end{syntax}
+% Sets the behaviour of the \meta{char} which has character
+% code as given by the \meta{integer expression} in situations
+% where it is active (category code $13$) to be equivalent to that of the
+% \meta{function} at the time \cs{char_set_active_eq:nN}
+% is used. The category code of the \meta{char} is
+% \emph{unchanged} by this process. The \meta{function} may itself
+% be an active character.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2015-09-09, updated = 2019-01-16]
+% {\char_generate:nn}
+% \begin{syntax}
+% \cs{char_generate:nn} \Arg{charcode} \Arg{catcode}
+% \end{syntax}
+% Generates a character token of the given \meta{charcode} and \meta{catcode}
+% (both of which may be integer expressions). The \meta{catcode} may be
+% one of
+% \begin{itemize}
+% \item $1$ (begin group)
+% \item $2$ (end group)
+% \item $3$ (math toggle)
+% \item $4$ (alignment)
+% \item $6$ (parameter)
+% \item $7$ (math superscript)
+% \item $8$ (math subscript)
+% \item $10$ (space)
+% \item $11$ (letter)
+% \item $12$ (other)
+% \item $13$ (active)
+% \end{itemize}
+% and other values raise an error. The \meta{charcode} may be any one valid
+% for the engine in use, except that for \meta{catcode} $10$, \meta{charcode}
+% $0$ is not allowed.
+% Active characters cannot be generated in older versions of \XeTeX{}.
+% Another way to build token lists with unusual category codes is
+% \cs{regex_replace:nnN} |{.*}| \Arg{replacement} \meta{tl~var}.
+% \begin{texnote}
+% Exactly two expansions are needed to produce the character.
+% \end{texnote}
+% \end{function}
+%
+% \begin{variable}[added = 2017-08-07]{\c_catcode_active_space_tl}
+% Token list containing one character with category code $13$,
+% (\enquote{active}), and character code $32$ (space).
+% \end{variable}
+%
+% \begin{variable}[added = 2011-09-05]{\c_catcode_other_space_tl}
+% Token list containing one character with category code $12$,
+% (\enquote{other}), and character code $32$ (space).
+% \end{variable}
+%
+% \section{Manipulating and interrogating character tokens}
+%
+% \begin{function}[updated = 2015-11-11]
+% {
+% \char_set_catcode_escape:N ,
+% \char_set_catcode_group_begin:N ,
+% \char_set_catcode_group_end:N ,
+% \char_set_catcode_math_toggle:N ,
+% \char_set_catcode_alignment:N ,
+% \char_set_catcode_end_line:N ,
+% \char_set_catcode_parameter:N ,
+% \char_set_catcode_math_superscript:N ,
+% \char_set_catcode_math_subscript:N ,
+% \char_set_catcode_ignore:N ,
+% \char_set_catcode_space:N ,
+% \char_set_catcode_letter:N ,
+% \char_set_catcode_other:N ,
+% \char_set_catcode_active:N ,
+% \char_set_catcode_comment:N ,
+% \char_set_catcode_invalid:N
+% }
+% \begin{syntax}
+% \cs{char_set_catcode_letter:N} \meta{character}
+% \end{syntax}
+% Sets the category code of the \meta{character} to that indicated in
+% the function name. Depending on the current category code of the
+% \meta{token} the escape token may also be needed:
+% \begin{verbatim}
+% \char_set_catcode_other:N \%
+% \end{verbatim}
+% The assignment is local.
+% \end{function}
+%
+% \begin{function}[updated = 2015-11-11]
+% {
+% \char_set_catcode_escape:n ,
+% \char_set_catcode_group_begin:n ,
+% \char_set_catcode_group_end:n ,
+% \char_set_catcode_math_toggle:n ,
+% \char_set_catcode_alignment:n ,
+% \char_set_catcode_end_line:n ,
+% \char_set_catcode_parameter:n ,
+% \char_set_catcode_math_superscript:n ,
+% \char_set_catcode_math_subscript:n ,
+% \char_set_catcode_ignore:n ,
+% \char_set_catcode_space:n ,
+% \char_set_catcode_letter:n ,
+% \char_set_catcode_other:n ,
+% \char_set_catcode_active:n ,
+% \char_set_catcode_comment:n ,
+% \char_set_catcode_invalid:n
+% }
+% \begin{syntax}
+% \cs{char_set_catcode_letter:n} \Arg{integer expression}
+% \end{syntax}
+% Sets the category code of the \meta{character} which has character
+% code as given by the \meta{integer expression}. This version can be
+% used to set up characters which cannot otherwise be given
+% (\emph{cf.}~the \texttt{N}-type variants). The assignment is local.
+% \end{function}
+%
+% \begin{function}[updated = 2015-11-11]{\char_set_catcode:nn}
+% \begin{syntax}
+% \cs{char_set_catcode:nn} \Arg{int expr_1} \Arg{int expr_2}
+% \end{syntax}
+% These functions set the category code of the \meta{character} which
+% has character code as given by the \meta{integer expression}.
+% The first \meta{integer expression}
+% is the character code and the second is the category code to apply.
+% The setting applies within the current \TeX{} group. In general, the
+% symbolic functions \cs[no-index]{char_set_catcode_\meta{type}} should be preferred,
+% but there are cases where these lower-level functions may be useful.
+% \end{function}
+%
+% \begin{function}[EXP]{\char_value_catcode:n}
+% \begin{syntax}
+% \cs{char_value_catcode:n} \Arg{integer expression}
+% \end{syntax}
+% Expands to the current category code of the \meta{character} with
+% character code given by the
+% \meta{integer expression}.
+% \end{function}
+%
+% \begin{function}{\char_show_value_catcode:n}
+% \begin{syntax}
+% \cs{char_show_value_catcode:n} \Arg{integer expression}
+% \end{syntax}
+% Displays the current category code of the \meta{character} with
+% character code given by the \meta{integer expression} on the
+% terminal.
+% \end{function}
+%
+% \begin{function}[updated = 2015-08-06]{\char_set_lccode:nn}
+% \begin{syntax}
+% \cs{char_set_lccode:nn} \Arg{int expr_1} \Arg{int expr_2}
+% \end{syntax}
+% Sets up the behaviour of the \meta{character} when
+% found inside \cs{text_lowercase:n}, such that \meta{character_1}
+% will be converted into \meta{character_2}. The two \meta{characters}
+% may be specified using an \meta{integer expression} for the character code
+% concerned. This may include the \TeX{} |`|\meta{character}
+% method for converting a single character into its character
+% code:
+% \begin{verbatim}
+% \char_set_lccode:nn { `\A } { `\a } % Standard behaviour
+% \char_set_lccode:nn { `\A } { `\A + 32 }
+% \char_set_lccode:nn { 50 } { 60 }
+% \end{verbatim}
+% The setting applies within the current \TeX{} group.
+% \end{function}
+%
+% \begin{function}[EXP]{\char_value_lccode:n}
+% \begin{syntax}
+% \cs{char_value_lccode:n} \Arg{integer expression}
+% \end{syntax}
+% Expands to the current lower case code of the \meta{character} with
+% character code given by the
+% \meta{integer expression}.
+% \end{function}
+%
+% \begin{function}{\char_show_value_lccode:n}
+% \begin{syntax}
+% \cs{char_show_value_lccode:n} \Arg{integer expression}
+% \end{syntax}
+% Displays the current lower case code of the \meta{character} with
+% character code given by the \meta{integer expression} on the
+% terminal.
+% \end{function}
+%
+% \begin{function}[updated = 2015-08-06]{\char_set_uccode:nn}
+% \begin{syntax}
+% \cs{char_set_uccode:nn} \Arg{int expr_1} \Arg{int expr_2}
+% \end{syntax}
+% Sets up the behaviour of the \meta{character} when
+% found inside \cs{text_uppercase:n}, such that \meta{character_1}
+% will be converted into \meta{character_2}. The two \meta{characters}
+% may be specified using an \meta{integer expression} for the character code
+% concerned. This may include the \TeX{} |`|\meta{character}
+% method for converting a single character into its character
+% code:
+% \begin{verbatim}
+% \char_set_uccode:nn { `\a } { `\A } % Standard behaviour
+% \char_set_uccode:nn { `\A } { `\A - 32 }
+% \char_set_uccode:nn { 60 } { 50 }
+% \end{verbatim}
+% The setting applies within the current \TeX{} group.
+% \end{function}
+%
+% \begin{function}[EXP]{\char_value_uccode:n}
+% \begin{syntax}
+% \cs{char_value_uccode:n} \Arg{integer expression}
+% \end{syntax}
+% Expands to the current upper case code of the \meta{character} with
+% character code given by the
+% \meta{integer expression}.
+% \end{function}
+%
+% \begin{function}{\char_show_value_uccode:n}
+% \begin{syntax}
+% \cs{char_show_value_uccode:n} \Arg{integer expression}
+% \end{syntax}
+% Displays the current upper case code of the \meta{character} with
+% character code given by the \meta{integer expression} on the
+% terminal.
+% \end{function}
+%
+% \begin{function}[updated = 2015-08-06]{\char_set_mathcode:nn}
+% \begin{syntax}
+% \cs{char_set_mathcode:nn} \Arg{int expr_1} \Arg{int expr_2}
+% \end{syntax}
+% This function sets up the math code of \meta{character}.
+% The \meta{character} is specified as
+% an \meta{integer expression} which will be used as the character
+% code of the relevant character. The setting applies within the
+% current \TeX{} group.
+% \end{function}
+%
+% \begin{function}[EXP]{\char_value_mathcode:n}
+% \begin{syntax}
+% \cs{char_value_mathcode:n} \Arg{integer expression}
+% \end{syntax}
+% Expands to the current math code of the \meta{character} with
+% character code given by the
+% \meta{integer expression}.
+% \end{function}
+%
+% \begin{function}{\char_show_value_mathcode:n}
+% \begin{syntax}
+% \cs{char_show_value_mathcode:n} \Arg{integer expression}
+% \end{syntax}
+% Displays the current math code of the \meta{character} with
+% character code given by the \meta{integer expression} on the
+% terminal.
+% \end{function}
+%
+% \begin{function}[updated = 2015-08-06]{\char_set_sfcode:nn}
+% \begin{syntax}
+% \cs{char_set_sfcode:nn} \Arg{int expr_1} \Arg{int expr_2}
+% \end{syntax}
+% This function sets up the space factor for the \meta{character}.
+% The \meta{character} is specified as
+% an \meta{integer expression} which will be used as the character
+% code of the relevant character. The setting applies within the
+% current \TeX{} group.
+% \end{function}
+%
+% \begin{function}[EXP]{\char_value_sfcode:n}
+% \begin{syntax}
+% \cs{char_value_sfcode:n} \Arg{integer expression}
+% \end{syntax}
+% Expands to the current space factor for the \meta{character} with
+% character code given by the
+% \meta{integer expression}.
+% \end{function}
+%
+% \begin{function}{\char_show_value_sfcode:n}
+% \begin{syntax}
+% \cs{char_show_value_sfcode:n} \Arg{integer expression}
+% \end{syntax}
+% Displays the current space factor for the \meta{character} with
+% character code given by the \meta{integer expression} on the
+% terminal.
+% \end{function}
+%
+% \begin{variable}[added = 2012-01-23, updated = 2015-11-11]{\l_char_active_seq}
+% Used to track which tokens may require special handling at the document
+% level as they are (or have been at some point)
+% of category \meta{active} (catcode~$13$). Each entry in
+% the sequence consists of a single escaped token, for example |\~|.
+% Active tokens should be added to the sequence when they are defined for
+% general document use.
+% \end{variable}
+%
+% \begin{variable}[added = 2012-01-23, updated = 2015-11-11]{\l_char_special_seq}
+% Used to track which tokens will require special handling when working with
+% verbatim-like material at the document level as they are not of categories
+% \meta{letter} (catcode~$11$) or \meta{other} (catcode~$12$). Each entry in
+% the sequence consists of a single escaped token, for example |\\| for the
+% backslash or |\{| for an opening brace. ^^A \}
+% Escaped tokens should be added to the sequence when they are defined for
+% general document use.
+% \end{variable}
+%
+% \section{Generic tokens}
+%
+% \begin{variable}[module = token]
+% {
+% \c_group_begin_token,
+% \c_group_end_token,
+% \c_math_toggle_token,
+% \c_alignment_token,
+% \c_parameter_token,
+% \c_math_superscript_token,
+% \c_math_subscript_token,
+% \c_space_token
+% }
+% These are implicit tokens which have the category code described
+% by their name. They are used internally for test purposes but
+% are also available to the programmer for other uses.
+% \begin{texnote}
+% The tokens \cs{c_group_begin_token}, \cs{c_group_end_token}, and
+% \cs{c_space_token} are \pkg{expl3} counterparts of \LaTeXe{}'s
+% \tn{bgroup}, \tn{egroup}, and \cs{@sptoken}.
+% \end{texnote}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \c_catcode_letter_token,
+% \c_catcode_other_token
+% }
+% These are implicit tokens which have the category code described
+% by their name. They are used internally for test purposes and should
+% not be used other than for category code tests.
+% \end{variable}
+%
+% \begin{variable}{\c_catcode_active_tl}
+% A token list containing an active token. This is used internally
+% for test purposes and should not be used other than in
+% appropriately-constructed category code tests.
+% \end{variable}
+%
+% \section{Converting tokens}
+%
+% \begin{function}[EXP]{\token_to_meaning:N, \token_to_meaning:c}
+% \begin{syntax}
+% \cs{token_to_meaning:N} \meta{token}
+% \end{syntax}
+% Inserts the current meaning of the \meta{token} into the input
+% stream as a series of characters of category code $12$ (other).
+% This is the primitive \TeX{} description of the \meta{token},
+% thus for example both functions defined by \cs{cs_set_nopar:Npn}
+% and token list variables defined using \cs{tl_new:N} are described
+% as |macro|s.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{meaning}.
+% The \meta{token} can thus be an explicit space token or an
+% explicit begin-group or end-group character token (|{|~or~|}| when
+% normal \TeX{} category codes apply) even though these are not
+% valid \texttt{N}-type arguments.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\token_to_str:N, \token_to_str:c}
+% \begin{syntax}
+% \cs{token_to_str:N} \meta{token}
+% \end{syntax}
+% Converts the given \meta{token} into a series of characters with
+% category code $12$ (other). If the \meta{token} is a control
+% sequence, this will start with the current escape character with
+% category code $12$ (the escape character is part of the
+% \meta{token}). This function requires only a single expansion.
+% \begin{texnote}
+% \cs{token_to_str:N} is the \TeX{} primitive \tn{string}.
+% The \meta{token} can thus be an explicit space tokens or an
+% explicit begin-group or end-group character token (|{|~or~|}| when
+% normal \TeX{} category codes apply) even though these are not
+% valid \texttt{N}-type arguments.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP,added = 2023-10-15]{\token_to_catcode:N}
+% \begin{syntax}
+% \cs{token_to_catcode:N} \meta{token}
+% \end{syntax}
+% Converts the given \meta{token} into a number describing its category code.
+% If \meta{token} is a control sequence this expands to $16$. This can't
+% detect the categories $0$ (escape character), $5$ (end of line), $9$
+% (ignored character), $14$ (comment character), or $15$ (invalid character).
+% Control sequences or active characters let to a token of one of the
+% detectable category codes will yield that category.
+% \end{function}
+%
+% \section{Token conditionals}
+%
+% \begin{function}[EXP,pTF]{\token_if_group_begin:N}
+% \begin{syntax}
+% \cs{token_if_group_begin_p:N} \meta{token} \\
+% \cs{token_if_group_begin:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{token} has the category code of a begin group token
+% (|{| when normal \TeX{} category codes are in ^^A }
+% force).
+% Note that an explicit begin group token cannot be tested in this way,
+% as it is not a valid \texttt{N}-type argument.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\token_if_group_end:N}
+% \begin{syntax}
+% \cs{token_if_group_end_p:N} \meta{token} \\
+% \cs{token_if_group_end:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{token} has the category code of an end group token
+% (^^A {
+% |}| when normal \TeX{} category codes are in force).
+% Note that an explicit end group token cannot be tested in this way,
+% as it is not a valid \texttt{N}-type argument.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\token_if_math_toggle:N}
+% \begin{syntax}
+% \cs{token_if_math_toggle_p:N} \meta{token} \\
+% \cs{token_if_math_toggle:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{token} has the category code of a math shift token
+% (|$| when normal \TeX{} category codes are in force).
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\token_if_alignment:N}
+% \begin{syntax}
+% \cs{token_if_alignment_p:N} \meta{token} \\
+% \cs{token_if_alignment:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{token} has the category code of an alignment token
+% (|&| when normal \TeX{} category codes are in force).
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\token_if_parameter:N}
+% \begin{syntax}
+% \cs{token_if_parameter_p:N} \meta{token} \\
+% \cs{token_if_parameter:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{token} has the category code of a macro parameter token
+% (|#| when normal \TeX{} category codes are in force).
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\token_if_math_superscript:N}
+% \begin{syntax}
+% \cs{token_if_math_superscript_p:N} \meta{token} \\
+% \cs{token_if_math_superscript:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{token} has the category code of a superscript token
+% (|^| when normal \TeX{} category codes are in force).
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\token_if_math_subscript:N}
+% \begin{syntax}
+% \cs{token_if_math_subscript_p:N} \meta{token} \\
+% \cs{token_if_math_subscript:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{token} has the category code of a subscript token
+% (|_| when normal \TeX{} category codes are in force).
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\token_if_space:N}
+% \begin{syntax}
+% \cs{token_if_space_p:N} \meta{token} \\
+% \cs{token_if_space:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{token} has the category code of a space token.
+% Note that an explicit space token with character code $32$ cannot
+% be tested in this way, as it is not a valid \texttt{N}-type argument.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\token_if_letter:N}
+% \begin{syntax}
+% \cs{token_if_letter_p:N} \meta{token} \\
+% \cs{token_if_letter:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{token} has the category code of a letter token.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\token_if_other:N}
+% \begin{syntax}
+% \cs{token_if_other_p:N} \meta{token} \\
+% \cs{token_if_other:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{token} has the category code of an \enquote{other}
+% token.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\token_if_active:N}
+% \begin{syntax}
+% \cs{token_if_active_p:N} \meta{token} \\
+% \cs{token_if_active:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if \meta{token} has the category code of an active character.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\token_if_eq_catcode:NN}
+% \begin{syntax}
+% \cs{token_if_eq_catcode_p:NN} \meta{token_1} \meta{token_2} \\
+% \cs{token_if_eq_catcode:NNTF} \meta{token_1} \meta{token_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the two \meta{tokens} have the same category code.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\token_if_eq_charcode:NN}
+% \begin{syntax}
+% \cs{token_if_eq_charcode_p:NN} \meta{token_1} \meta{token_2} \\
+% \cs{token_if_eq_charcode:NNTF} \meta{token_1} \meta{token_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the two \meta{tokens} have the same character code.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\token_if_eq_meaning:NN}
+% \begin{syntax}
+% \cs{token_if_eq_meaning_p:NN} \meta{token_1} \meta{token_2} \\
+% \cs{token_if_eq_meaning:NNTF} \meta{token_1} \meta{token_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the two \meta{tokens} have the same meaning when expanded.
+% \end{function}
+%
+% \begin{function}[updated = 2011-05-23, EXP,pTF]{\token_if_macro:N}
+% \begin{syntax}
+% \cs{token_if_macro_p:N} \meta{token} \\
+% \cs{token_if_macro:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is a \TeX{} macro.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\token_if_cs:N}
+% \begin{syntax}
+% \cs{token_if_cs_p:N} \meta{token} \\
+% \cs{token_if_cs:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is a control sequence.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\token_if_expandable:N}
+% \begin{syntax}
+% \cs{token_if_expandable_p:N} \meta{token} \\
+% \cs{token_if_expandable:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is expandable. This test returns \meta{false}
+% for an undefined token.
+% \end{function}
+%
+% \begin{function}[EXP,pTF, updated=2012-01-20]{\token_if_long_macro:N}
+% \begin{syntax}
+% \cs{token_if_long_macro_p:N} \meta{token} \\
+% \cs{token_if_long_macro:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is a long macro.
+% \end{function}
+%
+% \begin{function}[EXP,pTF, updated=2012-01-20]{\token_if_protected_macro:N}
+% \begin{syntax}
+% \cs{token_if_protected_macro_p:N} \meta{token} \\
+% \cs{token_if_protected_macro:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is a protected macro: for a macro which
+% is both protected and long this returns \texttt{false}.
+% \end{function}
+%
+% \begin{function}[EXP,pTF, updated=2012-01-20]{\token_if_protected_long_macro:N}
+% \begin{syntax}
+% \cs{token_if_protected_long_macro_p:N} \meta{token} \\
+% \cs{token_if_protected_long_macro:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is a protected long macro.
+% \end{function}
+%
+% \begin{function}[EXP,pTF, updated=2012-01-20]{\token_if_chardef:N}
+% \begin{syntax}
+% \cs{token_if_chardef_p:N} \meta{token} \\
+% \cs{token_if_chardef:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is defined to be a chardef.
+% \begin{texnote}
+% Booleans, boxes and small integer constants are implemented as
+% \tn{chardef}s.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP,pTF, updated=2012-01-20]{\token_if_mathchardef:N}
+% \begin{syntax}
+% \cs{token_if_mathchardef_p:N} \meta{token} \\
+% \cs{token_if_mathchardef:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is defined to be a mathchardef.
+% \end{function}
+%
+% \begin{function}[EXP,pTF, added=2020-10-27]{\token_if_font_selection:N}
+% \begin{syntax}
+% \cs{token_if_font_selection_p:N} \meta{token} \\
+% \cs{token_if_font_selection:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is defined to be a font selection command.
+% \end{function}
+%
+% \begin{function}[EXP,pTF, updated=2012-01-20]{\token_if_dim_register:N}
+% \begin{syntax}
+% \cs{token_if_dim_register_p:N} \meta{token} \\
+% \cs{token_if_dim_register:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is defined to be a dimension register.
+% \end{function}
+%
+% \begin{function}[EXP,pTF, updated=2012-01-20]{\token_if_int_register:N}
+% \begin{syntax}
+% \cs{token_if_int_register_p:N} \meta{token} \\
+% \cs{token_if_int_register:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is defined to be a integer register.
+% \begin{texnote}
+% Constant integers may be implemented as integer registers,
+% \tn{chardef}s, or \tn{mathchardef}s depending on their value.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP,pTF, added=2012-02-15]{\token_if_muskip_register:N}
+% \begin{syntax}
+% \cs{token_if_muskip_register_p:N} \meta{token} \\
+% \cs{token_if_muskip_register:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is defined to be a muskip register.
+% \end{function}
+%
+% \begin{function}[EXP,pTF, updated=2012-01-20]{\token_if_skip_register:N}
+% \begin{syntax}
+% \cs{token_if_skip_register_p:N} \meta{token} \\
+% \cs{token_if_skip_register:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is defined to be a skip register.
+% \end{function}
+%
+% \begin{function}[EXP,pTF, updated=2012-01-20]{\token_if_toks_register:N}
+% \begin{syntax}
+% \cs{token_if_toks_register_p:N} \meta{token} \\
+% \cs{token_if_toks_register:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is defined to be a toks register
+% (not used by \LaTeX3).
+% \end{function}
+%
+% \begin{function}[updated = 2020-09-11, EXP,pTF]{\token_if_primitive:N}
+% \begin{syntax}
+% \cs{token_if_primitive_p:N} \meta{token} \\
+% \cs{token_if_primitive:NTF} \meta{token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{token} is an engine primitive. In \LuaTeX{}
+% 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. Peeking
+% ahead does \emph{not} skip spaces: rather, \cs{peek_remove_spaces:n}.
+% should be used. In addition, using \cs{peek_analysis_map_inline:n},
+% one can map through the following tokens in the input stream and
+% repeatedly perform some tests.
+%
+% \begin{function}{\peek_after:Nw}
+% \begin{syntax}
+% \cs{peek_after:Nw} \meta{function} \meta{token}
+% \end{syntax}
+% Locally sets the test variable \cs{l_peek_token} equal to \meta{token}
+% (as an implicit token, \emph{not} as a token list), and then
+% expands the \meta{function}. The \meta{token} remains in
+% the input stream as the next item after the \meta{function}.
+% The \meta{token} here may be \verb*| |, |{| or |}| (assuming
+% normal \TeX{} category codes), \emph{i.e.}~it is not necessarily the
+% next argument which would be grabbed by a normal function.
+% \end{function}
+%
+% \begin{function}{\peek_gafter:Nw}
+% \begin{syntax}
+% \cs{peek_gafter:Nw} \meta{function} \meta{token}
+% \end{syntax}
+% Globally sets the test variable \cs{g_peek_token} equal to \meta{token}
+% (as an implicit token, \emph{not} as a token list), and then
+% expands the \meta{function}. The \meta{token} remains in
+% the input stream as the next item after the \meta{function}.
+% The \meta{token} here may be \verb*| |, |{| or |}| (assuming
+% normal \TeX{} category codes), \emph{i.e.}~it is not necessarily the
+% next argument which would be grabbed by a normal function.
+% \end{function}
+%
+% \begin{variable}{\l_peek_token}
+% Token set by \cs{peek_after:Nw} and available for testing
+% as described above.
+% \end{variable}
+%
+% \begin{variable}{\g_peek_token}
+% Token set by \cs{peek_gafter:Nw} and available for testing
+% as described above.
+% \end{variable}
+%
+% \begin{function}[updated = 2012-12-20, TF]{\peek_catcode:N}
+% \begin{syntax}
+% \cs{peek_catcode:NTF} \meta{test token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the next \meta{token} in the input stream has the same
+% category code as the \meta{test token} (as defined by the test
+% \cs{token_if_eq_catcode:NNTF}). Spaces are respected by the test
+% and the \meta{token} is left in the input stream after
+% the \meta{true code} or \meta{false code} (as appropriate to the
+% result of the test).
+% \end{function}
+%
+% \begin{function}[updated = 2012-12-20, TF]{\peek_catcode_remove:N}
+% \begin{syntax}
+% \cs{peek_catcode_remove:NTF} \meta{test token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the next \meta{token} in the input stream has the same
+% category code as the \meta{test token} (as defined by the test
+% \cs{token_if_eq_catcode:NNTF}). Spaces are respected by the test
+% and the \meta{token} is removed from the input stream if the
+% test is true. The function then places either the
+% \meta{true code} or \meta{false code} in the input stream (as
+% appropriate to the result of the test).
+% \end{function}
+%
+% \begin{function}[updated = 2012-12-20, TF]{\peek_charcode:N}
+% \begin{syntax}
+% \cs{peek_charcode:NTF} \meta{test token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the next \meta{token} in the input stream has the same
+% character code as the \meta{test token} (as defined by the test
+% \cs{token_if_eq_charcode:NNTF}). Spaces are respected by the test
+% and the \meta{token} is left in the input stream after
+% the \meta{true code} or \meta{false code} (as appropriate to the
+% result of the test).
+% \end{function}
+%
+% \begin{function}[updated = 2012-12-20, TF]{\peek_charcode_remove:N}
+% \begin{syntax}
+% \cs{peek_charcode_remove:NTF} \meta{test token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the next \meta{token} in the input stream has the same
+% character code as the \meta{test token} (as defined by the test
+% \cs{token_if_eq_charcode:NNTF}). Spaces are respected by the test
+% and the \meta{token} is removed from the input stream if the
+% test is true. The function then places either the
+% \meta{true code} or \meta{false code} in the input stream (as
+% appropriate to the result of the test).
+% \end{function}
+%
+% \begin{function}[updated = 2011-07-02, TF]{\peek_meaning:N}
+% \begin{syntax}
+% \cs{peek_meaning:NTF} \meta{test token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the next \meta{token} in the input stream has the same
+% meaning as the \meta{test token} (as defined by the test
+% \cs{token_if_eq_meaning:NNTF}). Spaces are respected by the test
+% and the \meta{token} is left in the input stream after
+% the \meta{true code} or \meta{false code} (as appropriate to the
+% result of the test).
+% \end{function}
+%
+% \begin{function}[updated = 2011-07-02, TF]{\peek_meaning_remove:N}
+% \begin{syntax}
+% \cs{peek_meaning_remove:NTF} \meta{test token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the next \meta{token} in the input stream has the same
+% meaning as the \meta{test token} (as defined by the test
+% \cs{token_if_eq_meaning:NNTF}). Spaces are respected by the test
+% and the \meta{token} is removed from the input stream if the
+% test is true. The function then places either the
+% \meta{true code} or \meta{false code} in the input stream (as
+% appropriate to the result of the test).
+% \end{function}
+%
+% \begin{function}[added = 2018-10-01]{\peek_remove_spaces:n}
+% \begin{syntax}
+% \cs{peek_remove_spaces:n} \Arg{code}
+% \end{syntax}
+% Peeks ahead and detect if the following token is a space (category code
+% $10$ and character code $32$). If so, removes the token and checks the
+% next token. Once a non-space token is found, the \meta{code} will be
+% inserted into the input stream. Typically this will contain a \texttt{peek}
+% operation, but this is not required.
+% \end{function}
+%
+% \begin{function}[added = 2022-01-10]{\peek_remove_filler:n}
+% \begin{syntax}
+% \cs{peek_remove_filler:n} \Arg{code}
+% \end{syntax}
+% Peeks ahead and detect if the following token is a space (category code
+% $10$) or has meaning equal to \cs{scan_stop:}. If so,
+% removes the token and checks the next token. If neither of these cases
+% apply, expands the next token using \texttt{f}-type expansion, then checks
+% the resulting leading token in the same way. If after expansion the next
+% token is neither of the two test cases, the \meta{code} will be inserted
+% into the input stream. Typically this will contain a \texttt{peek}
+% operation, but this is not required.
+% \begin{texnote}
+% This is essentially a macro-based implementation of how \TeX{} handles
+% the search for a left brace after for example \tn{everypar}, except that
+% any non-expandable token cleanly ends the \meta{filler} (i.e.~it does not
+% lead to a \TeX{} error).
+%
+% In contrast to \TeX{}'s filler removal, a construct
+% \verb|\exp_not:N \foo| will be treated in the same way as \verb|\foo|.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[TF, updated = 2012-12-20]{\peek_N_type:}
+% \begin{syntax}
+% \cs{peek_N_type:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the next \meta{token} in the input stream can be safely
+% grabbed as an \texttt{N}-type argument. The test is \meta{false}
+% if the next \meta{token} is either an explicit or implicit
+% begin-group or end-group token (with any character code), or
+% an explicit or implicit space character (with character code $32$
+% and category code $10$), or an outer token (never used in \LaTeX3)
+% and \meta{true} in all other cases.
+% Note that a \meta{true} result ensures that the next \meta{token} is
+% a valid \texttt{N}-type argument. However, if the next \meta{token}
+% is for instance \cs{c_space_token}, the test takes the
+% \meta{false} branch, even though the next \meta{token} is in fact
+% a valid \texttt{N}-type argument. The \meta{token} is left
+% in the input stream after the \meta{true code} or \meta{false code}
+% (as appropriate to the result of the test).
+% \end{function}
+%
+% \begin{function}[added = 2020-12-03, updated = 2024-02-07]
+% {\peek_analysis_map_inline:n}
+% \begin{syntax}
+% \cs{peek_analysis_map_inline:n} \Arg{inline function}
+% \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{e}/\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.
+%
+% Peek functions cannot be used within this mapping function (nor
+% other mapping functions) since the input stream contains trailing
+% material necessary for the functioning of the loop.
+% \begin{texnote}
+% In case the input stream has not yet been tokenized (converted
+% from characters to tokens), characters are tokenized one by one as
+% needed by \cs{peek_analysis_map_inline:n} using the current
+% category code regime.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2020-12-03]
+% {\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.
+%
+% The \cs{peek_regex:nTF} function only inspects as few tokens as
+% necessary to determine whether the regular expression matches.
+% For instance \cs{peek_regex:nTF} \verb"{ abc | [a-z] }" |{ } { }|
+% |abc| will only inspect the first token~|a| even though the first
+% branch |abc| of the alternative is preferred in functions such as
+% \cs{peek_regex_remove_once:nTF}. This may have an effect on
+% tokenization if the input stream has not yet been tokenized and
+% category codes are changed.
+% \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}
+%
+% Let us end by reviewing every case that a given token can fall into.
+% This section is quite technical and some details are only meant for
+% completeness. We distinguish the meaning of the token, which controls
+% the expansion of the token and its effect on \TeX{}'s state, and its
+% shape, which is used when comparing token lists such as for delimited
+% arguments. Two tokens of the same shape must have the same meaning,
+% but the converse does not hold.
+%
+% A token has one of the following shapes.
+% \begin{itemize}
+% \item A control sequence, characterized by the sequence of
+% characters that constitute its name: for instance, \cs{use:n} is a
+% five-letter control sequence.
+% \item An active character token, characterized by its character code
+% (between $0$ and $1114111$ for \LuaTeX{} and \XeTeX{} and less for
+% other engines) and category code~$13$.
+% \item A character token, characterized by its character code and
+% category code (one of $1$, $2$, $3$, $4$, $6$, $7$, $8$, $10$,
+% $11$ or~$12$ whose meaning is described below).
+% \end{itemize}
+% There are also a few internal tokens. The following list may be
+% incomplete in some engines.
+% \begin{itemize}
+% \item Expanding \tn{the}\tn{font} results in a token that looks
+% identical to the command that was used to select the current font
+% (such as \tn{tenrm}) but it differs from it in shape.
+% \item A \enquote{frozen} |\relax|, which differs from the primitive in
+% shape (but has the same meaning), is inserted when the closing \tn{fi} of a
+% conditional is encountered before the conditional is evaluated.
+% \item Expanding \tn{noexpand} \meta{token} (when the \meta{token} is
+% expandable) results in an internal token, displayed (temporarily)
+% as \cs[module = {}]{notexpanded: \meta{token}}, whose shape coincides with the
+% \meta{token} and whose meaning differs from \tn{relax}.
+% \item An |\outer endtemplate:| can be encountered when peeking ahead
+% at the next token; this expands to another internal token,
+% |end of alignment template|.
+% \item Tricky programming might access a frozen |\endwrite|.
+% \item Some frozen tokens can only be accessed in interactive
+% sessions: |\cr|, |\right|, |\endgroup|, |\fi|, |\inaccessible|.
+% \item In \LuaTeX{}, there is also the strange case of \enquote{bytes}
+% |^^^^^^1100|$xy$ where $x,y$ are any two lowercase hexadecimal
+% digits, so that the hexadecimal number ranges from
+% |"|$11\,0000=1\,114\,112$ to~|"|$110\,0\mathrm{ff}=1\,114\,367$. These are
+% used to output individual bytes to files, rather than UTF-8. For
+% the purposes of token comparisons they behave like non-expandable
+% primitive control sequences (\emph{not characters}) whose
+% \tn{meaning} is \verb*|the character | followed by the given byte.
+% If this byte is in the range |80|--|ff| this gives an ``invalid
+% utf-8 sequence'' error: applying \cs{token_to_str:N} or
+% \cs{token_to_meaning:N} to these tokens is unsafe. Unfortunately,
+% they don't seem to be detectable safely by any means except perhaps
+% Lua code.
+% \end{itemize}
+%
+% The meaning of a (non-active) character token is fixed by its category
+% code (and character code) and cannot be changed. We call these
+% tokens \emph{explicit} character tokens. Category codes that a
+% character token can have are listed below by giving a sample output of
+% the \TeX{} primitive \tn{meaning}, together with their \LaTeX3 names
+% and most common example:
+% \begin{itemize}
+% \item[1] begin-group character (|group_begin|, often |{|),
+% \item[2] end-group character (|group_end|, often |}|),
+% \item[3] math shift character (|math_toggle|, often |$|), %^^A $
+% \item[4] alignment tab character (|alignment|, often |&|),
+% \item[6] macro parameter character (|parameter|, often |#|),
+% \item[7] superscript character (|math_superscript|, often |^|),
+% \item[8] subscript character (|math_subscript|, often |_|),
+% \item[10] blank space (|space|, often character code~$32$),
+% \item[11] the letter (|letter|, such as |A|),
+% \item[12] the character (|other|, such as |0|).
+% \end{itemize}
+% Category code~$13$ (|active|) is discussed below. Input characters
+% can also have several other category codes which do not lead to
+% character tokens for later processing: $0$~(|escape|),
+% $5$~(|end_line|), $9$~(|ignore|), $14$~(|comment|), and
+% $15$~(|invalid|).
+%
+% The meaning of a control sequence or active character can be identical
+% to that of any character token listed above (with any character code),
+% and we call such tokens \emph{implicit} character tokens. The
+% meaning is otherwise in the following list:
+% \begin{itemize}
+% \item a macro, used in \LaTeX3 for most functions and some variables
+% (|tl|, |fp|, |seq|, \ldots{}),
+% \item a primitive such as \tn{def} or \tn{topmark}, used in \LaTeX3
+% for some functions,
+% \item a register such as \tn{count}|123|, used in \LaTeX3{} for the
+% implementation of some variables (|int|, |dim|, \ldots{}),
+% \item a constant integer such as \tn{char}|"56| or \tn{mathchar}|"121|,
+% \item a font selection command,
+% \item undefined.
+% \end{itemize}
+% Macros can be \tn{protected} or not, \tn{long} or not (the opposite of
+% what \LaTeX3 calls |nopar|), and \tn{outer} or not (unused in
+% \LaTeX3). Their \tn{meaning} takes the form
+% \begin{quote}
+% \meta{prefix} |macro:|\meta{argument}|->|\meta{replacement}
+% \end{quote}
+% where \meta{prefix} is among \tn{protected}\tn{long}\tn{outer},
+% \meta{argument} describes parameters that the macro expects, such as
+% |#1#2#3|, and \meta{replacement} describes how the parameters are
+% manipulated, such as~|\int_eval:n{#2+#1*#3}|.
+%
+% Now is perhaps a good time to mention some subtleties relating to
+% tokens with category code $10$ (space). Any input character with this
+% category code (normally, space and tab characters) becomes a normal
+% space, with character code~$32$ and category code~$10$.
+%
+% When a macro takes an undelimited argument, explicit space characters
+% (with character code $32$ and category code $10$) are ignored. If the
+% following token is an explicit character token with category code $1$
+% (begin-group) and an arbitrary character code, then \TeX{} scans ahead
+% to obtain an equal number of explicit character tokens with category
+% code $1$ (begin-group) and $2$ (end-group), and the resulting list of
+% tokens (with outer braces removed) becomes the argument. Otherwise, a
+% single token is taken as the argument for the macro: we call such
+% single tokens \enquote{N-type}, as they are suitable to be used as an
+% argument for a function with the signature~\texttt{:N}.
+%
+% When a macro takes a delimited argument \TeX{} scans ahead until
+% finding the delimiter (outside any pairs of begin-group/end-group
+% explicit characters), and the resulting list of tokens (with outer
+% braces removed) becomes the argument. Note that explicit space
+% characters at the start of the argument are \emph{not} ignored in this
+% case (and they prevent brace-stripping).
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3token} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*tex>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=char>
+% \end{macrocode}
+%
+% \subsection{Internal auxiliaries}
+%
+% \begin{variable}{\s_@@_stop}
+% Internal scan mark.
+% \begin{macrocode}
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\q_@@_no_value}
+% Internal recursion quarks.
+% \begin{macrocode}
+\quark_new:N \q_@@_no_value
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[pTF]{\@@_quark_if_no_value:N}
+% Functions to query recursion quarks.
+% \begin{macrocode}
+\__kernel_quark_new_conditional:Nn \@@_quark_if_no_value:N { TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Manipulating and interrogating character tokens}
+%
+% \begin{macro}{\char_set_catcode:nn}
+% \begin{macro}{\char_value_catcode:n}
+% \begin{macro}{\char_show_value_catcode:n}
+% Simple wrappers around the primitives.
+% \begin{macrocode}
+\cs_new_protected:Npn \char_set_catcode:nn #1#2
+ { \tex_catcode:D \int_eval:n {#1} = \int_eval:n {#2} \exp_stop_f: }
+\cs_new:Npn \char_value_catcode:n #1
+ { \tex_the:D \tex_catcode:D \int_eval:n {#1} \exp_stop_f: }
+\cs_new_protected:Npn \char_show_value_catcode:n #1
+ { \exp_args:Nf \tl_show:n { \char_value_catcode:n {#1} } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \char_set_catcode_escape:N ,
+% \char_set_catcode_group_begin:N ,
+% \char_set_catcode_group_end:N ,
+% \char_set_catcode_math_toggle:N ,
+% \char_set_catcode_alignment:N ,
+% \char_set_catcode_end_line:N ,
+% \char_set_catcode_parameter:N ,
+% \char_set_catcode_math_superscript:N ,
+% \char_set_catcode_math_subscript:N ,
+% \char_set_catcode_ignore:N ,
+% \char_set_catcode_space:N ,
+% \char_set_catcode_letter:N ,
+% \char_set_catcode_other:N ,
+% \char_set_catcode_active:N ,
+% \char_set_catcode_comment:N ,
+% \char_set_catcode_invalid:N
+% }
+% \begin{macrocode}
+\cs_new_protected:Npn \char_set_catcode_escape:N #1
+ { \char_set_catcode:nn { `#1 } { 0 } }
+\cs_new_protected:Npn \char_set_catcode_group_begin:N #1
+ { \char_set_catcode:nn { `#1 } { 1 } }
+\cs_new_protected:Npn \char_set_catcode_group_end:N #1
+ { \char_set_catcode:nn { `#1 } { 2 } }
+\cs_new_protected:Npn \char_set_catcode_math_toggle:N #1
+ { \char_set_catcode:nn { `#1 } { 3 } }
+\cs_new_protected:Npn \char_set_catcode_alignment:N #1
+ { \char_set_catcode:nn { `#1 } { 4 } }
+\cs_new_protected:Npn \char_set_catcode_end_line:N #1
+ { \char_set_catcode:nn { `#1 } { 5 } }
+\cs_new_protected:Npn \char_set_catcode_parameter:N #1
+ { \char_set_catcode:nn { `#1 } { 6 } }
+\cs_new_protected:Npn \char_set_catcode_math_superscript:N #1
+ { \char_set_catcode:nn { `#1 } { 7 } }
+\cs_new_protected:Npn \char_set_catcode_math_subscript:N #1
+ { \char_set_catcode:nn { `#1 } { 8 } }
+\cs_new_protected:Npn \char_set_catcode_ignore:N #1
+ { \char_set_catcode:nn { `#1 } { 9 } }
+\cs_new_protected:Npn \char_set_catcode_space:N #1
+ { \char_set_catcode:nn { `#1 } { 10 } }
+\cs_new_protected:Npn \char_set_catcode_letter:N #1
+ { \char_set_catcode:nn { `#1 } { 11 } }
+\cs_new_protected:Npn \char_set_catcode_other:N #1
+ { \char_set_catcode:nn { `#1 } { 12 } }
+\cs_new_protected:Npn \char_set_catcode_active:N #1
+ { \char_set_catcode:nn { `#1 } { 13 } }
+\cs_new_protected:Npn \char_set_catcode_comment:N #1
+ { \char_set_catcode:nn { `#1 } { 14 } }
+\cs_new_protected:Npn \char_set_catcode_invalid:N #1
+ { \char_set_catcode:nn { `#1 } { 15 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \char_set_catcode_escape:n ,
+% \char_set_catcode_group_begin:n ,
+% \char_set_catcode_group_end:n ,
+% \char_set_catcode_math_toggle:n ,
+% \char_set_catcode_alignment:n ,
+% \char_set_catcode_end_line:n ,
+% \char_set_catcode_parameter:n ,
+% \char_set_catcode_math_superscript:n ,
+% \char_set_catcode_math_subscript:n ,
+% \char_set_catcode_ignore:n ,
+% \char_set_catcode_space:n ,
+% \char_set_catcode_letter:n ,
+% \char_set_catcode_other:n ,
+% \char_set_catcode_active:n ,
+% \char_set_catcode_comment:n ,
+% \char_set_catcode_invalid:n
+% }
+% \begin{macrocode}
+\cs_new_protected:Npn \char_set_catcode_escape:n #1
+ { \char_set_catcode:nn {#1} { 0 } }
+\cs_new_protected:Npn \char_set_catcode_group_begin:n #1
+ { \char_set_catcode:nn {#1} { 1 } }
+\cs_new_protected:Npn \char_set_catcode_group_end:n #1
+ { \char_set_catcode:nn {#1} { 2 } }
+\cs_new_protected:Npn \char_set_catcode_math_toggle:n #1
+ { \char_set_catcode:nn {#1} { 3 } }
+\cs_new_protected:Npn \char_set_catcode_alignment:n #1
+ { \char_set_catcode:nn {#1} { 4 } }
+\cs_new_protected:Npn \char_set_catcode_end_line:n #1
+ { \char_set_catcode:nn {#1} { 5 } }
+\cs_new_protected:Npn \char_set_catcode_parameter:n #1
+ { \char_set_catcode:nn {#1} { 6 } }
+\cs_new_protected:Npn \char_set_catcode_math_superscript:n #1
+ { \char_set_catcode:nn {#1} { 7 } }
+\cs_new_protected:Npn \char_set_catcode_math_subscript:n #1
+ { \char_set_catcode:nn {#1} { 8 } }
+\cs_new_protected:Npn \char_set_catcode_ignore:n #1
+ { \char_set_catcode:nn {#1} { 9 } }
+\cs_new_protected:Npn \char_set_catcode_space:n #1
+ { \char_set_catcode:nn {#1} { 10 } }
+\cs_new_protected:Npn \char_set_catcode_letter:n #1
+ { \char_set_catcode:nn {#1} { 11 } }
+\cs_new_protected:Npn \char_set_catcode_other:n #1
+ { \char_set_catcode:nn {#1} { 12 } }
+\cs_new_protected:Npn \char_set_catcode_active:n #1
+ { \char_set_catcode:nn {#1} { 13 } }
+\cs_new_protected:Npn \char_set_catcode_comment:n #1
+ { \char_set_catcode:nn {#1} { 14 } }
+\cs_new_protected:Npn \char_set_catcode_invalid:n #1
+ { \char_set_catcode:nn {#1} { 15 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\char_set_mathcode:nn}
+% \begin{macro}{\char_value_mathcode:n}
+% \begin{macro}{\char_show_value_mathcode:n}
+% \begin{macro}{\char_set_lccode:nn}
+% \begin{macro}{\char_value_lccode:n}
+% \begin{macro}{\char_show_value_lccode:n}
+% \begin{macro}{\char_set_uccode:nn}
+% \begin{macro}{\char_value_uccode:n}
+% \begin{macro}{\char_show_value_uccode:n}
+% \begin{macro}{\char_set_sfcode:nn}
+% \begin{macro}{\char_value_sfcode:n}
+% \begin{macro}{\char_show_value_sfcode:n}
+% Pretty repetitive, but necessary!
+% \begin{macrocode}
+\cs_new_protected:Npn \char_set_mathcode:nn #1#2
+ { \tex_mathcode:D \int_eval:n {#1} = \int_eval:n {#2} \exp_stop_f: }
+\cs_new:Npn \char_value_mathcode:n #1
+ { \tex_the:D \tex_mathcode:D \int_eval:n {#1} \exp_stop_f: }
+\cs_new_protected:Npn \char_show_value_mathcode:n #1
+ { \exp_args:Nf \tl_show:n { \char_value_mathcode:n {#1} } }
+\cs_new_protected:Npn \char_set_lccode:nn #1#2
+ { \tex_lccode:D \int_eval:n {#1} = \int_eval:n {#2} \exp_stop_f: }
+\cs_new:Npn \char_value_lccode:n #1
+ { \tex_the:D \tex_lccode:D \int_eval:n {#1} \exp_stop_f: }
+\cs_new_protected:Npn \char_show_value_lccode:n #1
+ { \exp_args:Nf \tl_show:n { \char_value_lccode:n {#1} } }
+\cs_new_protected:Npn \char_set_uccode:nn #1#2
+ { \tex_uccode:D \int_eval:n {#1} = \int_eval:n {#2} \exp_stop_f: }
+\cs_new:Npn \char_value_uccode:n #1
+ { \tex_the:D \tex_uccode:D \int_eval:n {#1} \exp_stop_f: }
+\cs_new_protected:Npn \char_show_value_uccode:n #1
+ { \exp_args:Nf \tl_show:n { \char_value_uccode:n {#1} } }
+\cs_new_protected:Npn \char_set_sfcode:nn #1#2
+ { \tex_sfcode:D \int_eval:n {#1} = \int_eval:n {#2} \exp_stop_f: }
+\cs_new:Npn \char_value_sfcode:n #1
+ { \tex_the:D \tex_sfcode:D \int_eval:n {#1} \exp_stop_f: }
+\cs_new_protected:Npn \char_show_value_sfcode:n #1
+ { \exp_args:Nf \tl_show:n { \char_value_sfcode:n {#1} } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\l_char_active_seq, \l_char_special_seq}
+% Two sequences for dealing with special characters. The first is characters
+% which may be active, the second longer list is for \enquote{special}
+% characters more generally. Both lists are escaped so that for example
+% bulk code assignments can be carried out. In both cases, the order is
+% by \textsc{ascii} character code (as is done in for example
+% \cs{ExplSyntaxOn}).
+% \begin{macrocode}
+\seq_new:N \l_char_special_seq
+\seq_set_split:Nnn \l_char_special_seq { }
+ { \ \" \# \$ \% \& \\ \^ \_ \{ \} \~ }
+\seq_new:N \l_char_active_seq
+\seq_set_split:Nnn \l_char_active_seq { }
+ { \" \$ \& \^ \_ \~ }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Creating character tokens}
+%
+% \begin{macro}
+% {
+% \char_set_active_eq:NN, \char_gset_active_eq:NN,
+% \char_set_active_eq:Nc, \char_gset_active_eq:Nc,
+% \char_set_active_eq:nN, \char_gset_active_eq:nN,
+% \char_set_active_eq:nc, \char_gset_active_eq:nc
+% }
+% Four simple functions with very similar definitions, so set up using
+% an auxiliary.
+% These are similar to \LuaTeX{}'s \tn{letcharcode} primitive.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_active:N \^^@
+ \cs_set_protected:Npn \@@_tmp:nN #1#2
+ {
+ \cs_new_protected:cpn { #1 :nN } ##1
+ {
+ \group_begin:
+ \char_set_lccode:nn { `\^^@ } { ##1 }
+ \tex_lowercase:D { \group_end: #2 ^^@ }
+ }
+ \cs_new_protected:cpe { #1 :NN } ##1
+ { \exp_not:c { #1 : nN } { `##1 } }
+ }
+ \@@_tmp:nN { char_set_active_eq } \cs_set_eq:NN
+ \@@_tmp:nN { char_gset_active_eq } \cs_gset_eq:NN
+\group_end:
+\cs_generate_variant:Nn \char_set_active_eq:NN { Nc }
+\cs_generate_variant:Nn \char_gset_active_eq:NN { Nc }
+\cs_generate_variant:Nn \char_set_active_eq:nN { nc }
+\cs_generate_variant:Nn \char_gset_active_eq:nN { nc }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_int_to_roman:w}
+% For efficiency in 8-bit engines, we use the faster primitive approach
+% to making roman numerals.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_int_to_roman:w \tex_romannumeral:D
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\char_generate:nn}
+% \begin{macro}[EXP]{\@@_generate_aux:nn}
+% \begin{macro}[EXP]{\@@_generate_aux:nnw, \@@_generate_auxii:nnw}
+% \begin{variable}{\l_@@_tmp_tl}
+% \begin{macro}[EXP]{\@@_generate_invalid_catcode:}
+% The aim here is to generate characters of (broadly) arbitrary category
+% code. Where possible, that is done using engine support (\XeTeX{},
+% \LuaTeX{}). There are though various issues which are covered below. At
+% the interface layer, turn the two arguments into integers up-front so
+% this is only done once.
+% \begin{macrocode}
+\cs_new:Npn \char_generate:nn #1#2
+ {
+ \exp:w \exp_after:wN \@@_generate_aux:w
+ \int_value:w \int_eval:n {#1} \exp_after:wN ;
+ \int_value:w \int_eval:n {#2} ;
+ }
+% \end{macrocode}
+% Before doing any actual conversion, first some special case filtering.
+% Spaces are out here as \LuaTeX{} emulation only makes normal (charcode
+% $32$ spaces). However, |^^@| is filtered out separately as that can't be
+% done with macro emulation either, so is treated separately. That
+% done, hand off to the engine-dependent part.
+% \begin{macrocode}
+\cs_new:Npn \@@_generate_aux:w #1 ; #2 ;
+ {
+ \if_int_odd:w 0
+ \if_int_compare:w #2 < 1 \exp_stop_f: 1 \fi:
+ \if_int_compare:w #2 = 5 \exp_stop_f: 1 \fi:
+ \if_int_compare:w #2 = 9 \exp_stop_f: 1 \fi:
+ \if_int_compare:w #2 > 13 \exp_stop_f: 1 \fi: \exp_stop_f:
+ \msg_expandable_error:nn { char }
+ { invalid-catcode }
+ \else:
+ \if_int_odd:w 0
+ \if_int_compare:w #1 < \c_zero_int 1 \fi:
+ \if_int_compare:w #1 > \c_max_char_int 1 \fi: \exp_stop_f:
+ \msg_expandable_error:nn { char }
+ { out-of-range }
+ \else:
+ \if_int_compare:w #2#1 = 100 \exp_stop_f:
+ \msg_expandable_error:nn { char } { null-space }
+ \else:
+ \@@_generate_aux:nnw {#1} {#2}
+ \fi:
+ \fi:
+ \fi:
+ \exp_end:
+ }
+\tl_new:N \l_@@_tmp_tl
+% \end{macrocode}
+% Engine-dependent definitions are now needed for the implementation. Recent
+% (u)p\TeX{} and the Unicode engines \LuaTeX{} and \XeTeX{} have engine-level
+% support for expandable character creation. \pdfTeX{} and older (u)p\TeX{}
+% releases do not. The branching here if low-level to avoid fixing
+% the category code of the null character used in the false branch.
+% The final level is the basic definition at the engine level: the arguments
+% here are integers so there is no need to worry about them too much.
+% Older versions of \XeTeX{} cannot generate active characters so we filter
+% that:
+% at some future stage that may change: the slightly odd ordering of
+% auxiliaries reflects that.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_active:N \^^L
+ \cs_set:Npn ^^L { }
+ \if_cs_exist:N \tex_Ucharcat:D
+ \cs_new:Npn \@@_generate_aux:nnw #1#2#3 \exp_end:
+ {
+ #3
+ \exp_after:wN \exp_end:
+ \tex_Ucharcat:D #1 \exp_stop_f: #2 \exp_stop_f:
+ }
+ \else:
+% \end{macrocode}
+% For engines where \tn{Ucharcat} isn't available or emulated, we have
+% to work in macros, and cover only the $8$-bit range. The first stage is
+% to build up a |tl| containing |^^@| with each category code that can
+% be accessed in this way, with an error set up for the other cases. This
+% is all done such that it can be quickly accessed using a |\if_case:w|
+% low-level conditional. The list is done in reverse as this puts the case
+% of an active token \emph{first}: that's needed to cover the possibility
+% that it is \tn{outer}. Getting the braces into the list is done using
+% some standard \cs{if_false:} manipulation, while all of the \cs{exp_not:N}
+% are required as there is an expansion in the setup.
+% \begin{macrocode}
+ \char_set_catcode_active:n { 0 }
+ \tl_set:Nn \l_@@_tmp_tl { \exp_not:N ^^@ \exp_not:N \or: }
+ \char_set_catcode_other:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { ^^@ \exp_not:N \or: }
+ \char_set_catcode_letter:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { ^^@ \exp_not:N \or: }
+% \end{macrocode}
+% For making spaces, there needs to be an |o|-type expansion of a |\use:n|
+% (or some other tokenization) to avoid dropping the space.
+% \begin{macrocode}
+ \tl_put_right:Nn \l_@@_tmp_tl { \use:n { ~ } \exp_not:N \or: }
+ \tl_put_right:Nn \l_@@_tmp_tl { \exp_not:N \or: }
+ \char_set_catcode_math_subscript:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { ^^@ \exp_not:N \or: }
+ \char_set_catcode_math_superscript:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { ^^@ \exp_not:N \or: }
+ \char_set_catcode_parameter:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { ^^@ \exp_not:N \or: }
+ \tl_put_right:Nn \l_@@_tmp_tl { { \if_false: } \fi: \exp_not:N \or: }
+ \char_set_catcode_alignment:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { ^^@ \exp_not:N \or: }
+ \char_set_catcode_math_toggle:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { ^^@ \exp_not:N \or: }
+ \char_set_catcode_group_end:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { \if_false: { \fi: ^^@ \exp_not:N \or: } % }
+ \char_set_catcode_group_begin:n { 0 } % {
+ \tl_put_right:Nn \l_@@_tmp_tl { ^^@ \exp_not:N \or: } }
+% \end{macrocode}
+% Convert the above temporary list into a series of constant token
+% lists, one for each character code, using \cs{tex_lowercase:D} to
+% convert |^^@| in each case. The \texttt{e}-type expansion ensures
+% that \cs{tex_lowercase:D} receives the contents of the token list.
+% \begin{macrocode}
+ \cs_set_protected:Npn \@@_tmp:n #1
+ {
+ \char_set_lccode:nn { 0 } {#1}
+ \char_set_lccode:nn { 32 } {#1}
+ \exp_args:Ne \tex_lowercase:D
+ {
+ \tl_const:Ne
+ \exp_not:c { c_@@_ \@@_int_to_roman:w #1 _tl }
+ { \exp_not:o \l_@@_tmp_tl }
+ }
+ }
+ \int_step_function:nnN { 0 } { 255 } \@@_tmp:n
+% \end{macrocode}
+% As \TeX{} is very unhappy if it finds an alignment character inside
+% a primitive \tn{halign} even when skipping false branches, some
+% precautions are required. \TeX{} is happy if the token is hidden
+% between braces within \cs{if_false:} \dots{} \cs{fi:}. The
+% rather low-level approach here expands in one step to the
+% \meta{target token} (\cs{or:} \dots{}), then \cs{exp_after:wN}
+% \meta{target token} (\cs{or:} \dots{}) expands in one step to
+% \meta{target token}. This means that \cs{exp_not:N} is applied to
+% a potentially-problematic active token.
+% \begin{macrocode}
+ \cs_new:Npn \@@_generate_aux:nnw #1#2#3 \exp_end:
+ {
+ #3
+ \if_false: { \fi:
+ \exp_after:wN \exp_after:wN \exp_after:wN \exp_end:
+ \exp_after:wN \exp_after:wN
+ \if_case:w \tex_numexpr:D 13 - #2
+ \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_after:wN \exp_after:wN \exp_after:wN \scan_stop:
+ \exp_after:wN \exp_after:wN \exp_after:wN \exp_not:N
+ \cs:w c_@@_ \@@_int_to_roman:w #1 _tl \cs_end:
+ }
+ \fi:
+ }
+ \fi:
+\group_end:
+% \end{macrocode}
+% \end{macro}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\c_catcode_active_space_tl}
+% While \cs{char_generate:nn} can produce active characters in some
+% engines it cannot in general. It would be possible to simply change
+% the catcode of space but then the code would need to avoid all
+% spaces, making it quite unreadable. Instead we use the primitive
+% \cs{tex_lowercase:D} trick.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_active:N *
+ \char_set_lccode:nn { `* } { `\ }
+ \tex_lowercase:D { \tl_const:Nn \c_catcode_active_space_tl { * } }
+\group_end:
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\c_catcode_other_space_tl}
+% Create a space with category code $12$: an \enquote{other} space.
+% \begin{macrocode}
+\tl_const:Ne \c_catcode_other_space_tl { \char_generate:nn { `\ } { 12 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Generic tokens}
+%
+% \begin{macrocode}
+%<@@=token>
+% \end{macrocode}
+%
+% \begin{variable}{\s_@@_mark, \s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\token_to_meaning:N, \token_to_meaning:c}
+% \begin{macro}{\token_to_str:N, \token_to_str:c}
+% These are all defined in \pkg{l3basics}, as they are needed
+% \enquote{early}. This is just a reminder!
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\token_to_catcode:N}
+% \begin{macro}{\@@_to_catcode:N}
+% The macro works by comparing the input token with \cs{if_catcode:w} with all
+% valid category codes. Since the most common tokens in an average argument
+% list are of category $11$ or $12$ those are tested first. And since a space
+% and braces are no ordinary |N|-type arguments, and only control sequences
+% let to those categories can match them they are tested last.
+% \begin{macrocode}
+\cs_new:Npn \token_to_catcode:N
+ { \int_value:w \group_align_safe_begin: \@@_to_catcode:N }
+\cs_new:Npn \@@_to_catcode:N #1
+ {
+ \if_catcode:w \exp_not:N #1 \c_catcode_letter_token
+ 11
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_catcode_other_token
+ 12
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_math_toggle_token
+ 3
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_alignment_token
+ 4
+ \else:
+ \if_catcode:w \exp_not:N #1 ##
+ 6
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_math_superscript_token
+ 7
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_math_subscript_token
+ 8
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_group_begin_token
+ 1
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_group_end_token
+ 2
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_space_token
+ 10
+ \else:
+ \token_if_cs:NTF #1 { 16 } { 13 }
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \group_align_safe_end:
+ \exp_stop_f:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \c_group_begin_token,
+% \c_group_end_token,
+% \c_math_toggle_token,
+% \c_alignment_token,
+% \c_parameter_token,
+% \c_math_superscript_token,
+% \c_math_subscript_token,
+% \c_space_token,
+% \c_catcode_letter_token,
+% \c_catcode_other_token
+% }
+% We define these useful tokens. For the brace and space tokens things have
+% to be done by hand: the formal argument spec.~for \cs{cs_new_eq:NN} does
+% not cover them so we do things by hand. (As currently coded it would
+% \emph{work} with \cs{cs_new_eq:NN} but that's not really a great idea to
+% show off: we want people to stick to the defined interfaces and that
+% includes us.) So that these few odd names go into the log when appropriate
+% there is a need to hand-apply the \cs{__kernel_chk_if_free_cs:N} check.
+% \begin{macrocode}
+\group_begin:
+ \__kernel_chk_if_free_cs:N \c_group_begin_token
+ \tex_global:D \tex_let:D \c_group_begin_token {
+ \__kernel_chk_if_free_cs:N \c_group_end_token
+ \tex_global:D \tex_let:D \c_group_end_token }
+ \char_set_catcode_math_toggle:N \*
+ \cs_new_eq:NN \c_math_toggle_token *
+ \char_set_catcode_alignment:N \*
+ \cs_new_eq:NN \c_alignment_token *
+ \cs_new_eq:NN \c_parameter_token #
+ \cs_new_eq:NN \c_math_superscript_token ^
+ \char_set_catcode_math_subscript:N \*
+ \cs_new_eq:NN \c_math_subscript_token *
+ \__kernel_chk_if_free_cs:N \c_space_token
+ \use:n { \tex_global:D \tex_let:D \c_space_token = ~ } ~
+ \cs_new_eq:NN \c_catcode_letter_token a
+ \cs_new_eq:NN \c_catcode_other_token 1
+\group_end:
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\c_catcode_active_tl}
+% Not an implicit token!
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_active:N \*
+ \tl_const:Nn \c_catcode_active_tl { \exp_not:N * }
+\group_end:
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Token conditionals}
+%
+% \begin{macro}[pTF]{\token_if_group_begin:N}
+% Check if token is a begin group token. We use the constant
+% \cs{c_group_begin_token} for this.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \token_if_group_begin:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_group_begin_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_group_end:N}
+% Check if token is a end group token. We use the constant
+% \cs{c_group_end_token} for this.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \token_if_group_end:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_group_end_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_math_toggle:N}
+% Check if token is a math shift token. We use the constant
+% \cs{c_math_toggle_token} for this.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \token_if_math_toggle:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_math_toggle_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_alignment:N}
+% Check if token is an alignment tab token. We use the constant
+% \cs{c_alignment_token} for this.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \token_if_alignment:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_alignment_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_parameter:N}
+% Check if token is a parameter token. We use the constant
+% \cs{c_parameter_token} for this. We have to trick \TeX{} a bit to
+% avoid an error message: within a group we prevent
+% \cs{c_parameter_token} from behaving like a macro parameter character.
+% The definitions of \cs{prg_new_conditional:Npnn} are global, so they
+% remain after the group.
+% \begin{macrocode}
+\group_begin:
+\cs_set_eq:NN \c_parameter_token \scan_stop:
+\prg_new_conditional:Npnn \token_if_parameter:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_parameter_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_math_superscript:N}
+% Check if token is a math superscript token. We use the constant
+% \cs{c_math_superscript_token} for this.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \token_if_math_superscript:N #1
+ { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_math_superscript_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_math_subscript:N}
+% Check if token is a math subscript token. We use the constant
+% \cs{c_math_subscript_token} for this.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \token_if_math_subscript:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_math_subscript_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_space:N}
+% Check if token is a space token. We use the constant
+% \cs{c_space_token} for this.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \token_if_space:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_space_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_letter:N}
+% Check if token is a letter token. We use the constant
+% \cs{c_catcode_letter_token} for this.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \token_if_letter:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_catcode_letter_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_other:N}
+% Check if token is an other char token. We use the constant
+% \cs{c_catcode_other_token} for this.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \token_if_other:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_catcode_other_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_active:N}
+% Check if token is an active char token. We use the constant
+% \cs{c_catcode_active_tl} for this. A technical point is that
+% \cs{c_catcode_active_tl} is in fact a macro expanding to
+% |\exp_not:N *|, where |*| is active.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \token_if_active:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_catcode_active_tl
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_eq_meaning:NN}
+% Check if the tokens |#1| and |#2| have same meaning.
+% \begin{macrocode}
+\prg_new_eq_conditional:NNn \token_if_eq_meaning:NN \cs_if_eq:NN
+ { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_eq_catcode:NN}
+% Check if the tokens |#1| and |#2| have same category code.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \token_if_eq_catcode:NN #1#2 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \exp_not:N #2
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_eq_charcode:NN}
+% Check if the tokens |#1| and |#2| have same character code.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \token_if_eq_charcode:NN #1#2 { p , T , F , TF }
+ {
+ \if_charcode:w \exp_not:N #1 \exp_not:N #2
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_macro:N}
+% \begin{macro}{\@@_if_macro_p:w}
+% When a token is a macro, \cs{token_to_meaning:N} always outputs
+% something like |\long macro:#1->#1| so we could naively check to
+% see if the meaning contains |->|. However, this can fail the five
+% \tn[no-index]{...mark} primitives, whose meaning has the form
+% |...mark:|\meta{user material}. The problem is that the
+% \meta{user material} can contain |->|.
+%
+% However, only characters, macros, and marks can contain the colon
+% character. The idea is thus to grab until the first |:|, and analyse
+% what is left. However, macros can have any combination of |\long|,
+% |\protected| or |\outer| (not used in \LaTeX3) before the string
+% |macro:|. We thus only select the part of the meaning between
+% the first |ma| and the first following |:|. If this string is
+% |cro|, then we have a macro. If the string is |rk|, then we have
+% a mark. The string can also be |cro parameter character | for a
+% colon with a weird category code (namely the usual category code
+% of |#|). Otherwise, it is empty.
+%
+% This relies on the fact that |\long|, |\protected|, |\outer|
+% cannot contain |ma|, regardless of the escape character, even if
+% the escape character is |m|\ldots{}
+%
+% Both |ma| and |:| must be of category code $12$ (other), so are
+% detokenized.
+%
+% \begin{macrocode}
+\use:e
+ {
+ \prg_new_conditional:Npnn \exp_not:N \token_if_macro:N #1
+ { p , T , F , TF }
+ {
+ \exp_not:N \exp_after:wN \exp_not:N \@@_if_macro_p:w
+ \exp_not:N \token_to_meaning:N #1 \tl_to_str:n { ma : }
+ \s_@@_stop
+ }
+ \cs_new:Npn \exp_not:N \@@_if_macro_p:w
+ #1 \tl_to_str:n { ma } #2 \c_colon_str #3 \s_@@_stop
+ }
+ {
+ \str_if_eq:nnTF { #2 } { cro }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_cs:N}
+% Check if token has same catcode as a control sequence. This
+% follows the same pattern as for \cs{token_if_letter:N} \emph{etc.}
+% We use \cs{scan_stop:} for this.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \token_if_cs:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \scan_stop:
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_expandable:N}
+% Check if token is expandable. We use the fact that \TeX{}
+% temporarily converts \cs{exp_not:N} \meta{token} into \cs{scan_stop:}
+% if \meta{token} is expandable. An \texttt{undefined} token is not
+% considered as expandable. No problem nesting the conditionals,
+% since the third |#1| is only skipped if it is non-expandable (hence
+% not part of \TeX{}'s conditional apparatus).
+% \begin{macrocode}
+\prg_new_conditional:Npnn \token_if_expandable:N #1 { p , T , F , TF }
+ {
+ \exp_after:wN \if_meaning:w \exp_not:N #1 #1
+ \prg_return_false:
+ \else:
+ \if_cs_exist:N #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_delimit_by_char":w,
+% \@@_delimit_by_count:w,
+% \@@_delimit_by_dimen:w,
+% \@@_delimit_by_ font:w,
+% \@@_delimit_by_macro:w,
+% \@@_delimit_by_muskip:w,
+% \@@_delimit_by_skip:w,
+% \@@_delimit_by_toks:w,
+% }
+% These auxiliary functions are used below to define some
+% conditionals which detect whether the \tn{meaning} of their
+% argument begins with a particular string. Each auxiliary takes an
+% argument delimited by a string, a second one delimited by
+% \cs{s_@@_stop}, and returns the first one and its delimiter.
+% This result is eventually compared to another string.
+% Note that the ``font'' auxiliary is delimited by a space followed by
+% ``\texttt{font}''. This avoids an unnecessary check for the
+% \tn{font} primitive below.
+% \begin{macrocode}
+\group_begin:
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \use:e
+ {
+ \cs_new:Npn \exp_not:c { @@_delimit_by_ #1 :w }
+ ##1 \tl_to_str:n {#1} ##2 \s_@@_stop
+ { ##1 \tl_to_str:n {#1} }
+ }
+ }
+\@@_tmp:w { char" }
+\@@_tmp:w { count }
+\@@_tmp:w { dimen }
+\@@_tmp:w { ~ font }
+\@@_tmp:w { macro }
+\@@_tmp:w { muskip }
+\@@_tmp:w { skip }
+\@@_tmp:w { toks }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]
+% {
+% \token_if_chardef:N, \token_if_mathchardef:N,
+% \token_if_long_macro:N,
+% \token_if_protected_macro:N, \token_if_protected_long_macro:N,
+% \token_if_font_selection:N,
+% \token_if_dim_register:N, \token_if_int_register:N,
+% \token_if_muskip_register:N,
+% \token_if_skip_register:N, \token_if_toks_register:N,
+% }
+% Each of these conditionals tests whether its argument's
+% \tn{meaning} starts with a given string. This is essentially done
+% by having an auxiliary grab an argument delimited by the string and
+% testing whether the argument was empty. Of course, a copy of this
+% string must first be added to the end of the \tn{meaning} to avoid
+% a runaway argument in case it does not contain the string. Two
+% complications arise. First, the escape character is not fixed, and
+% cannot be included in the delimiter of the auxiliary function (this
+% function cannot be defined on the fly because tests must remain
+% expandable): instead the first argument of the auxiliary (plus the
+% delimiter to avoid complications with trailing spaces) is compared
+% using \cs{str_if_eq:eeTF} to the result of applying
+% \cs{token_to_str:N} to a control sequence. Second, the
+% \tn{meaning} of primitives such as \tn{dimen} or \tn{dimendef}
+% starts in the same way as registers such as
+% \tn{dimen}\texttt{123}, so they must be tested for.
+%
+% Characters used as delimiters must have catcode~$12$
+% and are obtained through \cs{tl_to_str:n}. This requires doing all
+% definitions within \texttt{e}-expansion. The temporary function
+% \cs{@@_tmp:w} used to define each conditional receives three
+% arguments: the name of the conditional, the auxiliary's delimiter
+% (also used to name the auxiliary), and the string to which one
+% compares the auxiliary's result. Note that the \tn{meaning} of a
+% protected long macro starts with |\protected\long macro|, with no
+% space after |\protected| but a space after |\long|, hence the
+% mixture of \cs{token_to_str:N} and \cs{tl_to_str:n}.
+%
+% For the first six conditionals, \cs{cs_if_exist:cT} turns out to
+% be \texttt{false} (thanks to the leading space for \texttt{font}),
+% and the code boils down to a string comparison
+% between the result of the auxiliary on the \tn{meaning} of the
+% conditional's argument~|####1|, and~|#3|. Both are evaluated at
+% run-time, as this is important to get the correct escape character.
+%
+% The other five conditionals have additional code that compares the
+% argument~|####1| to two \TeX{} primitives which would wrongly be
+% recognized as registers otherwise. Despite using \TeX{}'s
+% primitive conditional construction, this does not break
+% when~|####1| is itself a conditional, because branches of the
+% conditionals are only skipped if |####1|~is one of the two
+% primitives that are tested for (which are not \TeX{} conditionals).
+% \begin{macrocode}
+\group_begin:
+\cs_set_protected:Npn \@@_tmp:w #1#2#3
+ {
+ \use:e
+ {
+ \prg_new_conditional:Npnn \exp_not:c { token_if_ #1 :N } ##1
+ { p , T , F , TF }
+ {
+ \cs_if_exist:cT { tex_ #2 :D }
+ {
+ \exp_not:N \if_meaning:w ##1 \exp_not:c { tex_ #2 :D }
+ \exp_not:N \prg_return_false:
+ \exp_not:N \else:
+ \exp_not:N \if_meaning:w ##1 \exp_not:c { tex_ #2 def:D }
+ \exp_not:N \prg_return_false:
+ \exp_not:N \else:
+ }
+ \exp_not:N \str_if_eq:eeTF
+ {
+ \exp_not:N \exp_after:wN
+ \exp_not:c { @@_delimit_by_ #2 :w }
+ \exp_not:N \token_to_meaning:N ##1
+ ? \tl_to_str:n {#2} \s_@@_stop
+ }
+ { \exp_not:n {#3} }
+ { \exp_not:N \prg_return_true: }
+ { \exp_not:N \prg_return_false: }
+ \cs_if_exist:cT { tex_ #2 :D }
+ {
+ \exp_not:N \fi:
+ \exp_not:N \fi:
+ }
+ }
+ }
+ }
+\@@_tmp:w { chardef } { char" } { \token_to_str:N \char" }
+\@@_tmp:w { mathchardef } { char" } { \token_to_str:N \mathchar" }
+\@@_tmp:w { long_macro } { macro } { \tl_to_str:n { \long } macro }
+\@@_tmp:w { protected_macro } { macro }
+ { \tl_to_str:n { \protected } macro }
+\@@_tmp:w { protected_long_macro } { macro }
+ { \token_to_str:N \protected \tl_to_str:n { \long } macro }
+\@@_tmp:w { font_selection } { ~ font } { select ~ font }
+\@@_tmp:w { dim_register } { dimen } { \token_to_str:N \dimen }
+\@@_tmp:w { int_register } { count } { \token_to_str:N \count }
+\@@_tmp:w { muskip_register } { muskip } { \token_to_str:N \muskip }
+\@@_tmp:w { skip_register } { skip } { \token_to_str:N \skip }
+\@@_tmp:w { toks_register } { toks } { \token_to_str:N \toks }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\token_if_primitive:N}
+% \begin{macro}{\@@_if_primitive:NNw,
+% \@@_if_primitive_space:w,
+% \@@_if_primitive_nullfont:N,
+% \@@_if_primitive_loop:N,
+% \@@_if_primitive:Nw,
+% \@@_if_primitive_undefined:N,
+% \@@_if_primitive_lua:N}
+%^^A See http://groups.google.com/group/comp.text.tex/browse_thread/thread/0a72666873f8753d#
+%
+% We filter out macros first, because they cause endless trouble later
+% otherwise.
+%
+% Primitives are almost distinguished by the fact that the result
+% of \cs{token_to_meaning:N} is formed from letters only. Every other
+% token has either a space (e.g., |the letter A|), a digit
+% (e.g., |\count123|) or a double quote (e.g., |\char"A|).
+%
+% Ten exceptions: on the one hand, \cs{tex_undefined:D} is not a
+% primitive, but its meaning is |undefined|, only letters;
+% on the other hand, \tn{space}, \tn{italiccorr},
+% \tn{hyphen}, \tn{firstmark}, \tn{topmark},
+% \tn{botmark}, \tn{splitfirstmark}, \tn{splitbotmark},
+% and \tn{nullfont} are primitives, but have non-letters
+% in their meaning.
+%
+% We start by removing the two first (non-space) characters from
+% the meaning. This removes the escape character (which may be
+% nonexistent depending on \tn{endlinechar}), and takes care
+% of three of the exceptions: \tn{space}, \tn{italiccorr}
+% and \tn{hyphen}, whose meaning is at most two characters.
+% This leaves a string terminated by some |:|, and \cs{s_@@_stop}.
+%
+% The meaning of each one of the five \tn[no-index]{...mark} primitives
+% has the form \meta{letters}|:|\meta{user material}. In other words,
+% the first non-letter is a colon. We remove everything after the first
+% colon.
+%
+% We are now left with a string, which we must analyze. For primitives,
+% it contains only letters. For non-primitives, it contains either
+% |"|, or a space, or a digit. Two exceptions remain: \cs{tex_undefined:D},
+% which is not a primitive, and \tn{nullfont}, which is a primitive.
+%
+% Spaces cannot be grabbed in an undelimited way, so we check them
+% separately. If there is a space, we test for \tn{nullfont}.
+% Otherwise, we go through characters one by one, and stop at the
+% first character less than |`A| (this is not quite a test for
+% \enquote{only letters}, but is close enough to work in this context).
+% If this first character is |:| then we have a primitive, or
+% \cs{tex_undefined:D}, and if it is |"| or a digit, then the token
+% is not a primitive.
+%
+% For \LuaTeX{} we use a different implementation which just looks at the
+% command code for the token and compares it to a list of non-primitives.
+% Again, \tn{nullfont} is a special case because it is the only primitive
+% with the normally non-primitive |set_font| command code.
+%
+% In LuaMeta\TeX{} some of the command names are different, so we check for
+% both versions. The first one is always the \LuaTeX{} version.
+%
+% \begin{macrocode}
+\sys_if_engine_luatex:TF
+ {
+%</tex>
+%<*lua>
+do
+ local get_next = token.get_next
+ local get_command = token.get_command
+ local get_index = token.get_index
+ local get_mode = token.get_mode or token.get_index
+ local cmd = command_id
+ local set_font = cmd'get_font'
+ local biggest_char = token.biggest_char and token.biggest_char()
+ or status.getconstants().max_character_code
+
+ local mode_below_biggest_char = {}
+ local index_not_nil = {}
+ local mode_not_null = {}
+ local non_primitive = {
+ [cmd'left_brace'] = true,
+ [cmd'right_brace'] = true,
+ [cmd'math_shift'] = true,
+ [cmd'mac_param' or cmd'parameter'] = mode_below_biggest_char,
+ [cmd'sup_mark' or cmd'superscript'] = true,
+ [cmd'sub_mark' or cmd'subscript'] = true,
+ [cmd'endv' or cmd'ignore'] = true,
+ [cmd'spacer'] = true,
+ [cmd'letter'] = true,
+ [cmd'other_char'] = true,
+ [cmd'tab_mark' or cmd'alignment_tab'] = mode_below_biggest_char,
+ [cmd'char_given'] = true,
+ [cmd'math_given' or 'math_char_given'] = true,
+ [cmd'xmath_given' or 'math_char_xgiven'] = true,
+ [cmd'set_font'] = mode_not_null,
+ [cmd'undefined_cs'] = true,
+ [cmd'call'] = true,
+ [cmd'long_call' or cmd'protected_call'] = true,
+ [cmd'outer_call' or cmd'tolerant_call'] = true,
+ [cmd'long_outer_call' or cmd'tolerant_protected_call'] = true,
+ [cmd'assign_glue' or cmd'register_glue'] = index_not_nil,
+ [cmd'assign_mu_glue' or cmd'register_mu_glue' or cmd'register_muglue'] = index_not_nil,
+ [cmd'assign_toks' or cmd'register_toks'] = index_not_nil,
+ [cmd'assign_int' or cmd'register_int' or cmd'register_integer'] = index_not_nil,
+ [cmd'assign_attr' or cmd'register_attribute'] = true,
+ [cmd'assign_dimen' or cmd'register_dimen' or cmd'register_dimension'] = index_not_nil,
+ }
+
+ luacmd("@@_if_primitive_lua:N", function()
+ local tok = get_next()
+ local is_non_primitive = non_primitive[get_command(tok)]
+ return put_next(
+ is_non_primitive == true
+ and false_tok
+ or is_non_primitive == nil
+ and true_tok
+ or is_non_primitive == mode_not_null
+ and (get_mode(tok) == 0 and true_tok or false_tok)
+ or is_non_primitive == index_not_nil
+ and (get_index(tok) and false_tok or true_tok)
+ or is_non_primitive == mode_below_biggest_char
+ and (get_mode(tok) > biggest_char and true_tok or false_tok))
+ end, "global")
+end
+%</lua>
+%<*tex>
+ \prg_new_conditional:Npnn \token_if_primitive:N #1 { p , T , F , TF }
+ {
+ \@@_if_primitive_lua:N #1
+ }
+ }
+ {
+ \tex_global:D \tex_chardef:D \c_@@_A_int = `A ~ %
+ \use:e
+ {
+ \prg_new_conditional:Npnn \exp_not:N \token_if_primitive:N #1
+ { p , T , F , TF }
+ {
+ \exp_not:N \token_if_macro:NTF #1
+ \exp_not:N \prg_return_false:
+ {
+ \exp_not:N \exp_after:wN \exp_not:N \@@_if_primitive:NNw
+ \exp_not:N \token_to_meaning:N #1
+ \tl_to_str:n { : : : } \s_@@_stop #1
+ }
+ }
+ \cs_new:Npn \exp_not:N \@@_if_primitive:NNw
+ #1#2 #3 \c_colon_str #4 \s_@@_stop
+ {
+ \exp_not:N \tl_if_empty:oTF
+ { \exp_not:N \@@_if_primitive_space:w #3 ~ }
+ {
+ \exp_not:N \@@_if_primitive_loop:N #3
+ \c_colon_str \s_@@_stop
+ }
+ { \exp_not:N \@@_if_primitive_nullfont:N }
+ }
+ }
+ \cs_new:Npn \@@_if_primitive_space:w #1 ~ { }
+ \cs_new:Npn \@@_if_primitive_nullfont:N #1
+ {
+ \if_meaning:w \tex_nullfont:D #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ \cs_new:Npn \@@_if_primitive_loop:N #1
+ {
+ \if_int_compare:w `#1 < \c_@@_A_int %
+ \exp_after:wN \@@_if_primitive:Nw
+ \exp_after:wN #1
+ \else:
+ \exp_after:wN \@@_if_primitive_loop:N
+ \fi:
+ }
+ \cs_new:Npn \@@_if_primitive:Nw #1 #2 \s_@@_stop
+ {
+ \if:w : #1
+ \exp_after:wN \@@_if_primitive_undefined:N
+ \else:
+ \prg_return_false:
+ \exp_after:wN \use_none:n
+ \fi:
+ }
+ \cs_new:Npn \@@_if_primitive_undefined:N #1
+ {
+ \if_cs_exist:N #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ }
+% \end{macrocode}
+% \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}
+%<@@=peek>
+% \end{macrocode}
+%
+% Peeking ahead is implemented using a two part mechanism. The
+% outer level provides a defined interface to the lower level material.
+% This allows a large amount of code to be shared. There are four
+% cases:
+% \begin{enumerate}
+% \item peek at the next token;
+% \item peek at the next non-space token;
+% \item peek at the next token and remove it;
+% \item peek at the next non-space token and remove it.
+% \end{enumerate}
+%
+% \begin{variable}{\l_peek_token}
+% \begin{variable}{\g_peek_token}
+% Storage tokens which are publicly documented: the token peeked.
+% \begin{macrocode}
+\cs_new_eq:NN \l_peek_token ?
+\cs_new_eq:NN \g_peek_token ?
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_search_token}
+% The token to search for as an implicit token:
+% \emph{cf.}~\cs{l_@@_search_tl}.
+% \begin{macrocode}
+\cs_new_eq:NN \l_@@_search_token ?
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_search_tl}
+% The token to search for as an explicit token:
+% \emph{cf.}~\cs{l_@@_search_token}.
+% \begin{macrocode}
+\tl_new:N \l_@@_search_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}
+% {\@@_true:w, \@@_true_aux:w, \@@_false:w, \@@_tmp:w}
+% Functions used by the branching and space-stripping code.
+% \begin{macrocode}
+\cs_new:Npn \@@_true:w { }
+\cs_new:Npn \@@_true_aux:w { }
+\cs_new:Npn \@@_false:w { }
+\cs_new:Npn \@@_tmp:w { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\s_@@_mark,\s_@@_stop}
+% Internal scan marks.
+% \begin{macrocode}
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_use_none_delimit_by_s_stop:w}
+% Functions to gobble up to a scan mark.
+% \begin{macrocode}
+\cs_new:Npn \@@_use_none_delimit_by_s_stop:w #1 \s_@@_stop { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\peek_after:Nw}
+% \begin{macro}{\peek_gafter:Nw}
+% Simple wrappers for \tn{futurelet}: no arguments absorbed
+% here.
+% \begin{macrocode}
+\cs_new_protected:Npn \peek_after:Nw
+ { \tex_futurelet:D \l_peek_token }
+\cs_new_protected:Npn \peek_gafter:Nw
+ { \tex_global:D \tex_futurelet:D \g_peek_token }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_true_remove:w}
+% A function to remove the next token and then regain control.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_true_remove:w
+ {
+ \tex_afterassignment:D \@@_true_aux:w
+ \cs_set_eq:NN \@@_tmp:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\peek_remove_spaces:n, \@@_remove_spaces:}
+% Repeatedly use \cs{@@_true_remove:w} to remove a space and call
+% \cs{@@_true_aux:w}.
+% \begin{macrocode}
+\cs_new_protected:Npn \peek_remove_spaces:n #1
+ {
+ \cs_set:Npe \@@_false:w { \exp_not:n {#1} }
+ \group_align_safe_begin:
+ \cs_set:Npn \@@_true_aux:w { \peek_after:Nw \@@_remove_spaces: }
+ \@@_true_aux:w
+ }
+\cs_new_protected:Npn \@@_remove_spaces:
+ {
+ \if_meaning:w \l_peek_token \c_space_token
+ \exp_after:wN \@@_true_remove:w
+ \else:
+ \group_align_safe_end:
+ \exp_after:wN \@@_false:w
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\peek_remove_filler:n}
+% \begin{macro}{\@@_remove_filler:w}
+% \begin{macro}{\@@_remove_filler:}
+% \begin{macro}{\@@_remove_filler_expand:w}
+% Here we expand the input, removing spaces and \cs{scan_stop:} tokens until
+% we reach a non-expandable token. At that stage we re-insert the payload.
+% To deal with the problem of |&| tokens, we have to put the align-safe
+% group in the correct place.
+% \begin{macrocode}
+\cs_new_protected:Npn \peek_remove_filler:n #1
+ {
+ \cs_set:Npn \@@_true_aux:w { \@@_remove_filler:w }
+ \cs_set:Npe \@@_false:w
+ {
+ \exp_not:N \group_align_safe_end:
+ \exp_not:n {#1}
+ }
+ \group_align_safe_begin:
+ \@@_remove_filler:w
+ }
+\cs_new_protected:Npn \@@_remove_filler:w
+ {
+ \exp_after:wN \peek_after:Nw \exp_after:wN \@@_remove_filler:
+ \exp:w \exp_end_continue_f:w
+ }
+% \end{macrocode}
+% Here we can nest conditionals as \cs{l_peek_token} is only skipped over in
+% the nested one if it's a space: no problems with conditionals or outer
+% tokens.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_remove_filler:
+ {
+ \if_catcode:w \exp_not:N \l_peek_token \c_space_token
+ \exp_after:wN \@@_true_remove:w
+ \else:
+ \if_meaning:w \l_peek_token \scan_stop:
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \@@_true_remove:w
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \@@_remove_filler_expand:w
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% To deal with undefined control sequences in the same way \TeX{} does,
+% we need to check for expansion manually.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_remove_filler_expand:w
+ {
+ \exp_after:wN \if_meaning:w \exp_not:N \l_peek_token \l_peek_token
+ \exp_after:wN \@@_false:w
+ \else:
+ \exp_after:wN \@@_remove_filler:w
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_token_generic_aux:NNNTF}
+% The generic functions store the test token in both implicit and
+% explicit modes, and the \texttt{true} and \texttt{false} code as
+% token lists, more or less. The two branches have to be absorbed here
+% as the input stream needs to be cleared for the peek function itself.
+% Here, |#1| is \cs{@@_true_remove:w} when removing the token and
+% \cs{@@_true_aux:w} otherwise.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_token_generic_aux:NNNTF #1#2#3#4#5
+ {
+ \group_align_safe_begin:
+ \cs_set_eq:NN \l_@@_search_token #3
+ \tl_set:Nn \l_@@_search_tl {#3}
+ \cs_set:Npe \@@_true_aux:w
+ {
+ \exp_not:N \group_align_safe_end:
+ \exp_not:n {#4}
+ }
+ \cs_set_eq:NN \@@_true:w #1
+ \cs_set:Npe \@@_false:w
+ {
+ \exp_not:N \group_align_safe_end:
+ \exp_not:n {#5}
+ }
+ \peek_after:Nw #2
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[TF]{\@@_token_generic:NN, \@@_token_remove_generic:NN}
+% For token removal there needs to be a call to the auxiliary
+% function which does the work.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_token_generic:NNTF
+ { \@@_token_generic_aux:NNNTF \@@_true_aux:w }
+\cs_new_protected:Npn \@@_token_generic:NNT #1#2#3
+ { \@@_token_generic:NNTF #1 #2 {#3} { } }
+\cs_new_protected:Npn \@@_token_generic:NNF #1#2#3
+ { \@@_token_generic:NNTF #1 #2 { } {#3} }
+\cs_new_protected:Npn \@@_token_remove_generic:NNTF
+ { \@@_token_generic_aux:NNNTF \@@_true_remove:w }
+\cs_new_protected:Npn \@@_token_remove_generic:NNT #1#2#3
+ { \@@_token_remove_generic:NNTF #1 #2 {#3} { } }
+\cs_new_protected:Npn \@@_token_remove_generic:NNF #1#2#3
+ { \@@_token_remove_generic:NNTF #1 #2 { } {#3} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_execute_branches_meaning:}
+% The meaning test is straight forward.
+% \begin{macrocode}
+\cs_new:Npn \@@_execute_branches_meaning:
+ {
+ \if_meaning:w \l_peek_token \l_@@_search_token
+ \exp_after:wN \@@_true:w
+ \else:
+ \exp_after:wN \@@_false:w
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_execute_branches_catcode:, \@@_execute_branches_charcode:}
+% \begin{macro}
+% {
+% \@@_execute_branches_catcode_aux: ,
+% \@@_execute_branches_catcode_auxii:N ,
+% \@@_execute_branches_catcode_auxiii:
+% }
+% The catcode and charcode tests are very similar, and in order to use
+% the same auxiliaries we do something a little bit odd, firing
+% \cs{if_catcode:w} and \cs{if_charcode:w} before finding the operands
+% for those tests, which are only given in the |auxii:N| and
+% |auxiii:| auxiliaries. For our purposes, three kinds of tokens may
+% follow the peeking function:
+% \begin{itemize}
+% \item control sequences which are not equal to a non-active
+% character token (\emph{e.g.}, macro, primitive);
+% \item active characters which are not equal to a non-active
+% character token (\emph{e.g.}, macro, primitive);
+% \item explicit non-active character tokens, or control sequences
+% or active characters set equal to a non-active character token.
+% \end{itemize}
+% The first two cases are not distinguishable simply using \TeX{}'s
+% \tn{futurelet}, because we can only access the \tn{meaning} of
+% tokens in that way. In those cases, detected thanks to a
+% comparison with \cs{scan_stop:}, we grab the following token, and
+% compare it explicitly with the explicit search token stored in
+% \cs{l_@@_search_tl}. The \cs{exp_not:N} prevents outer macros
+% (coming from non-\LaTeX3 code) from blowing up. In the third case,
+% \cs{l_peek_token} is good enough for the test, and we compare it
+% again with the explicit search token. Just like the peek token, the
+% search token may be of any of the three types above, hence the need
+% to use the explicit token that was given to the peek function.
+% \begin{macrocode}
+\cs_new:Npn \@@_execute_branches_catcode:
+ { \if_catcode:w \@@_execute_branches_catcode_aux: }
+\cs_new:Npn \@@_execute_branches_charcode:
+ { \if_charcode:w \@@_execute_branches_catcode_aux: }
+\cs_new:Npn \@@_execute_branches_catcode_aux:
+ {
+ \if_catcode:w \exp_not:N \l_peek_token \scan_stop:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_execute_branches_catcode_auxii:N
+ \exp_after:wN \exp_not:N
+ \else:
+ \exp_after:wN \@@_execute_branches_catcode_auxiii:
+ \fi:
+ }
+\cs_new:Npn \@@_execute_branches_catcode_auxii:N #1
+ {
+ \exp_not:N #1
+ \exp_after:wN \exp_not:N \l_@@_search_tl
+ \exp_after:wN \@@_true:w
+ \else:
+ \exp_after:wN \@@_false:w
+ \fi:
+ #1
+ }
+\cs_new:Npn \@@_execute_branches_catcode_auxiii:
+ {
+ \exp_not:N \l_peek_token
+ \exp_after:wN \exp_not:N \l_@@_search_tl
+ \exp_after:wN \@@_true:w
+ \else:
+ \exp_after:wN \@@_false:w
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[TF]
+% {
+% \peek_catcode:N,
+% \peek_catcode_remove:N,
+% \peek_charcode:N,
+% \peek_charcode_remove:N,
+% \peek_meaning:N,
+% \peek_meaning_remove:N,
+% }
+% The public functions themselves cannot be defined using
+% \cs{prg_new_conditional:Npnn}. Instead, the |TF|, |T|, |F| variants
+% are defined in terms of corresponding variants of
+% \cs{@@_token_generic:NNTF} or \cs{@@_token_remove_generic:NNTF},
+% with first argument one of \cs{@@_execute_branches_catcode:},
+% \cs{@@_execute_branches_charcode:}, or
+% \cs{@@_execute_branches_meaning:}.
+% \begin{macrocode}
+\tl_map_inline:nn { { catcode } { charcode } { meaning } }
+ {
+ \tl_map_inline:nn { { } { _remove } }
+ {
+ \tl_map_inline:nn { { TF } { T } { F } }
+ {
+ \cs_new_protected:cpe { peek_ #1 ##1 :N ####1 }
+ {
+ \exp_not:c { @@_token ##1 _generic:NN ####1 }
+ \exp_not:c { @@_execute_branches_ #1 : }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[TF]{\peek_N_type:}
+% \begin{macro}
+% {\@@_execute_branches_N_type:, \@@_N_type:w, \@@_N_type_aux:nnw}
+% All tokens are \texttt{N}-type tokens, except in four cases:
+% begin-group tokens, end-group tokens, space tokens with character
+% code~$32$, and outer tokens. Since \cs{l_peek_token} might be
+% outer, we cannot use the convenient \cs{bool_if:nTF} function, and
+% must resort to the old trick of using \tn{ifodd} to expand a set of
+% tests. The \texttt{false} branch of this test is taken if the token
+% is one of the first three kinds of non-\texttt{N}-type tokens
+% (explicit or implicit), thus we call \cs{@@_false:w}. In the
+% \texttt{true} branch, we must detect outer tokens, without impacting
+% performance too much for non-outer tokens. The first filter is to
+% search for \texttt{outer} in the \tn{meaning} of \cs{l_peek_token}.
+% If that is absent, \cs{@@_use_none_delimit_by_s_stop:w} cleans up, and
+% we call \cs{@@_true:w}. Otherwise, the token can be a non-outer
+% macro or a primitive mark whose parameter or replacement text
+% contains \texttt{outer}, it can be the primitive \tn{outer}, or it
+% can be an outer token. Macros and marks would have \texttt{ma} in
+% the part before the first occurrence of \texttt{outer}; the meaning
+% of \tn{outer} has nothing after \texttt{outer}, contrarily to outer
+% macros; and that covers all cases, calling \cs{@@_true:w} or
+% \cs{@@_false:w} as appropriate. Here, there is no \meta{search
+% token}, so we feed a dummy \cs{scan_stop:} to the
+% \cs{@@_token_generic:NNTF} function.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \@@_tmp:w #1 \s_@@_stop
+ {
+ \cs_new_protected:Npn \@@_execute_branches_N_type:
+ {
+ \if_int_odd:w
+ \if_catcode:w \exp_not:N \l_peek_token { \c_zero_int \fi:
+ \if_catcode:w \exp_not:N \l_peek_token } \c_zero_int \fi:
+ \if_meaning:w \l_peek_token \c_space_token \c_zero_int \fi:
+ \c_one_int
+ \exp_after:wN \@@_N_type:w
+ \token_to_meaning:N \l_peek_token
+ \s_@@_mark \@@_N_type_aux:nnw
+ #1 \s_@@_mark \@@_use_none_delimit_by_s_stop:w
+ \s_@@_stop
+ \exp_after:wN \@@_true:w
+ \else:
+ \exp_after:wN \@@_false:w
+ \fi:
+ }
+ \cs_new_protected:Npn \@@_N_type:w ##1 #1 ##2 \s_@@_mark ##3
+ { ##3 {##1} {##2} }
+ }
+ \exp_after:wN \@@_tmp:w \tl_to_str:n { outer } \s_@@_stop
+\group_end:
+\cs_new_protected:Npn \@@_N_type_aux:nnw #1 #2 #3 \fi:
+ {
+ \fi:
+ \tl_if_in:noTF {#1} { \tl_to_str:n {ma} }
+ { \@@_true:w }
+ { \tl_if_empty:nTF {#2} { \@@_true:w } { \@@_false:w } }
+ }
+\cs_new_protected:Npn \peek_N_type:TF
+ {
+ \@@_token_generic:NNTF
+ \@@_execute_branches_N_type: \scan_stop:
+ }
+\cs_new_protected:Npn \peek_N_type:T
+ { \@@_token_generic:NNT \@@_execute_branches_N_type: \scan_stop: }
+\cs_new_protected:Npn \peek_N_type:F
+ { \@@_token_generic:NNF \@@_execute_branches_N_type: \scan_stop: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</tex>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/source/latex-dev/l3kernel/l3unicode.dtx b/Master/texmf-dist/source/latex-dev/l3kernel/l3unicode.dtx
new file mode 100644
index 00000000000..0ee6858f5b9
--- /dev/null
+++ b/Master/texmf-dist/source/latex-dev/l3kernel/l3unicode.dtx
@@ -0,0 +1,1031 @@
+% \iffalse meta-comment
+%
+%% File: l3unicode.dtx
+%
+% Copyright (C) 2018-2024 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3unicode} module\\ Unicode support functions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX{} Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2024-04-11}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% This module provides Unicode-specific functions along with loading data
+% from a range of Unicode Consortium files. Most of the code here is
+% internal, but there are a small set of public functions. These work with
+% Unicode \meta{codepoints} and are designed to give useable results with
+% both Unicode-aware and $8$-bit engines.
+%
+% \begin{function}[EXP, added = 2022-10-09, updated = 2022-11-09]
+% {\codepoint_generate:nn}
+% \begin{syntax}
+% \cs{codepoint_generate:nn} \Arg{codepoint} \Arg{catcode}
+% \end{syntax}
+% Generates one or more character tokens representing the \meta{codepoint}.
+% With Unicode engines, exactly one character token will be generated, and
+% this will have the \meta{catcode} specified as the second argument:
+% \begin{itemize}
+% \item $1$ (begin group)
+% \item $2$ (end group)
+% \item $3$ (math toggle)
+% \item $4$ (alignment)
+% \item $6$ (parameter)
+% \item $7$ (math superscript)
+% \item $8$ (math subscript)
+% \item $10$ (space)
+% \item $11$ (letter)
+% \item $12$ (other)
+% \item $13$ (active)
+% \end{itemize}
+% For $8$-bit engines, between one and four character tokens will be
+% produced: these will be the bytes of the UTF-8 representation of the
+% \meta{codepoint}. For all codepoints outside of the classical ASCII
+% range, the generated character tokens will be active (category code
+% $13$); for codepoints in the ASCII range, the given \meta{catcode}
+% will be used. To allow the result of this function to be used
+% inside an expansion context, the result is protected by \cs{exp_not:n}.
+%
+% \begin{texnote}
+% Users of (u)p\TeX{} note that these engines are treated as $8$-bit in
+% this context. In particular, for up\TeX{}, irrespective of the
+% \tn{kcatcode} of the \meta{codepoint}, any value outside the ASCII range
+% will result in a series of active bytes being generated.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2022-10-09]
+% {\codepoint_str_generate:n}
+% \begin{syntax}
+% \cs{codepoint_str_generate:n} \Arg{codepoint}
+% \end{syntax}
+% Generates one or more character tokens representing the \meta{codepoint}.
+% With Unicode engines, exactly one character token will be generated.
+% For $8$-bit engines, between one and four character tokens will be
+% produced: these will be the bytes of the UTF-8 representation of the
+% \meta{codepoint}. All of the generated character tokens will be of
+% category code $12$, except any spaces (codepoint $32$), which will be
+% category code $10$.
+% \end{function}
+%
+% \begin{function}[added = 2023-06-19, EXP]{\codepoint_to_category:n}
+% \begin{syntax}
+% \cs{codepoint_to_category:n} \Arg{codepoint}
+% \end{syntax}
+% Expands to the Unicode general category identifier of the \meta{codepoint}.
+% The general category identifier is a string made up of two letter
+% characters, the first uppercase and the second lowercase. The uppercase
+% letters divide codepoints into broader groups, which are then refined
+% by the lowercase letter. For example, codepoints representing letters
+% all have identifiers starting \texttt{L}, for example \texttt{Lu}
+% (uppercase letter), \texttt{Lt} (titlecase letter), \emph{etc.}
+% Full details are available in the documentation provided by the Unicode
+% Consortium: see
+% \url{https://www.unicode.org/reports/tr44/#General_Category_Values}
+% \end{function}
+%
+% \begin{function}[added = 2022-10-09, EXP]{\codepoint_to_nfd:n}
+% \begin{syntax}
+% \cs{codepoint_to_nfd:n} \Arg{codepoint}
+% \end{syntax}
+% Converts the \meta{codepoint} to the Unicode Normalization
+% Form Canonical Decomposition. The generated character(s) will have
+% the current category code as they would if typed in directly for Unicode
+% engines; for $8$-bit engines, active characters are used for all codepoints
+% outside of the ASCII range.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3unicode} implementation}
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=codepoint>
+% \end{macrocode}
+%
+% \subsection{User functions}
+%
+% \begin{macro}[EXP]{\codepoint_str_generate:n}
+% \begin{macro}[EXP]{\@@_str_generate:nnnn}
+% \begin{macro}[EXP]{\codepoint_generate:nn}
+% \begin{macro}[EXP]{\@@_generate:nnnn}
+% \begin{macro}[EXP]{\@@_generate:n}
+% Conversion of a codepoint to a character (Unicode engines) or to one
+% or more bytes ($8$-bit engines) is required. For loading the data,
+% all that is needed is the form which creates strings: these are outside
+% the group as they will also be used when looking up data in the hash
+% table storage at point-of-use. Later, we will also need functions that
+% can generate character tokens for document use: those are defined below,
+% in the data recovery setup.
+% \begin{macrocode}
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_new:Npn \codepoint_str_generate:n #1
+ {
+ \int_compare:nNnTF {#1} = { `\ }
+ { ~ }
+ { \char_generate:nn {#1} { 12 } }
+ }
+ \cs_new:Npn \codepoint_generate:nn #1#2
+ {
+ \int_compare:nNnTF {#1} = { `\ }
+ { ~ }
+ {
+ \__kernel_exp_not:w \exp_after:wN \exp_after:wN \exp_after:wN
+ { \char_generate:nn {#1} {#2} }
+ }
+ }
+ }
+ {
+ \cs_new:Npn \codepoint_str_generate:n #1
+ {
+ \int_compare:nNnTF {#1} = { `\ }
+ { ~ }
+ {
+ \use:e
+ {
+ \exp_not:N \@@_str_generate:nnnn
+ \__kernel_codepoint_to_bytes:n {#1}
+ }
+ }
+ }
+ \cs_new:Npn \@@_str_generate:nnnn #1#2#3#4
+ {
+ \char_generate:nn {#1} { 12 }
+ \tl_if_blank:nF {#2}
+ {
+ \char_generate:nn {#2} { 12 }
+ \tl_if_blank:nF {#3}
+ {
+ \char_generate:nn {#3} { 12 }
+ \tl_if_blank:nF {#4}
+ { \char_generate:nn {#4} { 12 } }
+ }
+ }
+ }
+ \cs_new:Npn \codepoint_generate:nn #1#2
+ {
+ \int_compare:nNnTF {#1} = { `\ }
+ { ~ }
+ {
+ \int_compare:nNnTF {#1} < { "80 }
+ {
+ \__kernel_exp_not:w \exp_after:wN \exp_after:wN \exp_after:wN
+ { \char_generate:nn {#1} {#2} }
+ }
+ {
+ \use:e
+ {
+ \exp_not:N \@@_generate:nnnn
+ \__kernel_codepoint_to_bytes:n {#1}
+ }
+ }
+ }
+ }
+ \cs_new:Npn \@@_generate:nnnn #1#2#3#4
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \tex_expanded:D
+ {
+ \@@_generate:n {#1}
+ \@@_generate:n {#2}
+ \tl_if_blank:nF {#3}
+ {
+ \@@_generate:n {#3}
+ \tl_if_blank:nF {#4}
+ { \@@_generate:n {#4} }
+ }
+ }
+ }
+ }
+ \cs_new:Npn \@@_generate:n #1
+ {
+ \__kernel_exp_not:w \exp_after:wN \exp_after:wN \exp_after:wN
+ { \char_generate:nn {#1} { 13 } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\__kernel_codepoint_to_bytes:n}
+% \begin{macro}[EXP]{\@@_to_bytes_auxi:n}
+% \begin{macro}[EXP]{\@@_to_bytes_auxii:Nnn}
+% \begin{macro}[EXP]{\@@_to_bytes_auxiii:n}
+% \begin{macro}[EXP]
+% {
+% \@@_to_bytes_outputi:nw ,
+% \@@_to_bytes_outputii:nw ,
+% \@@_to_bytes_outputiii:nw ,
+% \@@_to_bytes_outputiv:nw
+% }
+% \begin{macro}[EXP]
+% {\@@_to_bytes_output:nnn, \@@_to_bytes_output:fnn}
+% \begin{macro}[EXP]{\@@_to_bytes_end:}
+% This code converts a codepoint into the correct UTF-8 representation.
+% In terms of the algorithm itself, see
+% \url{https://en.wikipedia.org/wiki/UTF-8} for the octet pattern.
+% \begin{macrocode}
+\cs_new:Npn \__kernel_codepoint_to_bytes:n #1
+ {
+ \exp_args:Nf \@@_to_bytes_auxi:n
+ { \int_eval:n {#1} }
+ }
+\cs_new:Npn \@@_to_bytes_auxi:n #1
+ {
+ \if_int_compare:w #1 > "80 \exp_stop_f:
+ \if_int_compare:w #1 < "800 \exp_stop_f:
+ \@@_to_bytes_outputi:nw
+ { \@@_to_bytes_auxii:Nnn C {#1} { 64 } }
+ \@@_to_bytes_outputii:nw
+ { \@@_to_bytes_auxiii:n {#1} }
+ \else:
+ \if_int_compare:w #1 < "10000 \exp_stop_f:
+ \@@_to_bytes_outputi:nw
+ { \@@_to_bytes_auxii:Nnn E {#1} { 64 * 64 } }
+ \@@_to_bytes_outputii:nw
+ {
+ \@@_to_bytes_auxiii:n
+ { \int_div_truncate:nn {#1} { 64 } }
+ }
+ \@@_to_bytes_outputiii:nw
+ { \@@_to_bytes_auxiii:n {#1} }
+ \else:
+ \@@_to_bytes_outputi:nw
+ {
+ \@@_to_bytes_auxii:Nnn F
+ {#1} { 64 * 64 * 64 }
+ }
+ \@@_to_bytes_outputii:nw
+ {
+ \@@_to_bytes_auxiii:n
+ { \int_div_truncate:nn {#1} { 64 * 64 } }
+ }
+ \@@_to_bytes_outputiii:nw
+ {
+ \@@_to_bytes_auxiii:n
+ { \int_div_truncate:nn {#1} { 64 } }
+ }
+ \@@_to_bytes_outputiv:nw
+ { \@@_to_bytes_auxiii:n {#1} }
+ \fi:
+ \fi:
+ \else:
+ \@@_to_bytes_outputi:nw {#1}
+ \fi:
+ \@@_to_bytes_end: { } { } { } { }
+ }
+\cs_new:Npn \@@_to_bytes_auxii:Nnn #1#2#3
+ { "#10 + \int_div_truncate:nn {#2} {#3} }
+\cs_new:Npn \@@_to_bytes_auxiii:n #1
+ { \int_mod:nn {#1} { 64 } + 128 }
+\cs_new:Npn \@@_to_bytes_outputi:nw
+ #1 #2 \@@_to_bytes_end: #3
+ { \@@_to_bytes_output:fnn { \int_eval:n {#1} } { } {#2} }
+\cs_new:Npn \@@_to_bytes_outputii:nw
+ #1 #2 \@@_to_bytes_end: #3#4
+ { \@@_to_bytes_output:fnn { \int_eval:n {#1} } { {#3} } {#2} }
+\cs_new:Npn \@@_to_bytes_outputiii:nw
+ #1 #2 \@@_to_bytes_end: #3#4#5
+ {
+ \@@_to_bytes_output:fnn
+ { \int_eval:n {#1} } { {#3} {#4} } {#2}
+ }
+\cs_new:Npn \@@_to_bytes_outputiv:nw
+ #1 #2 \@@_to_bytes_end: #3#4#5#6
+ {
+ \@@_to_bytes_output:fnn
+ { \int_eval:n {#1} } { {#3} {#4} {#5} } {#2}
+ }
+\cs_new:Npn \@@_to_bytes_output:nnn #1#2#3
+ {
+ #3
+ \@@_to_bytes_end: #2 {#1}
+ }
+\cs_generate_variant:Nn \@@_to_bytes_output:nnn { f }
+\cs_new:Npn \@@_to_bytes_end: { }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\codepoint_to_category:n}
+% Get the value and convert back to the string.
+% \begin{macrocode}
+\cs_new:Npn \codepoint_to_category:n #1
+ {
+ \cs:w
+ c_@@_category_
+ \tex_romannumeral:D
+ \__kernel_codepoint_data:nn { category } {#1}
+ _str
+ \cs_end:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\codepoint_to_nfd:n, \@@_to_nfd:n}
+% \begin{macro}[EXP]{\@@_to_nfd:nn}
+% \begin{macro}[EXP]{\@@_to_nfd:nnn}
+% \begin{macro}[EXP]{\@@_to_nfd:nnnn}
+% Converted to NFD is a potentially-recursive process: the key is to
+% check if we get the input codepoint back again. As far as possible,
+% we use the same path for all engines.
+% \begin{macrocode}
+\cs_new:Npn \codepoint_to_nfd:n #1
+ { \exp_args:Ne \@@_to_nfd:n { \int_eval:n {#1} } }
+\cs_new:Npn \@@_to_nfd:n #1
+ { \@@_to_nfd:nn {#1} { \char_value_catcode:n {#1} } }
+\bool_lazy_or:nnF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_gset:Npn \@@_to_nfd:n #1
+ {
+ \int_compare:nNnTF {#1} > { "80 }
+ { \@@_to_nfd:nn {#1} { 12 } }
+ { \@@_to_nfd:nn {#1} { \char_value_catcode:n {#1} } }
+ }
+ }
+\cs_new:Npn \@@_to_nfd:nn #1#2
+ {
+ \exp_args:Ne \@@_to_nfd:nnn
+ { \@@_nfd:n {#1} } {#1} {#2}
+ }
+\cs_new:Npn \@@_to_nfd:nnn #1#2#3 { \@@_to_nfd:nnnn #1 {#2} {#3} }
+\cs_new:Npn \@@_to_nfd:nnnn #1#2#3#4
+ {
+ \int_compare:nNnTF {#1} = {#3}
+ { \codepoint_generate:nn {#1} {#4} }
+ {
+ \@@_to_nfd:nn {#1} {#4}
+ \tl_if_blank:nF {#2}
+ { \@@_to_nfd:nn {#2} {#4} }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Data loader}
+%
+% Text operations requires data from the Unicode Consortium. Data read into
+% Unicode engine formats is at best a small part of what we need, so there
+% is a loader here to set up the appropriate data structures.
+%
+% Where we need data for most or all of the Unicode range, we use the two-stage
+% table approach recommended by the Unicode Consortium and demonstrated in a
+% model implementation in Python in
+% \url{https://www.strchr.com/multi-stage_tables}. This approach uses the
+% \texttt{intarray} (\texttt{fontdimen}-based) data type as it is fast for
+% random access and avoids significant hash table usage. In contrast, where
+% only a small subset of codepoints are required, storage as macros is
+% preferable. There is also some consideration of the effort needed to load
+% data: see for example the grapheme breaking information, which would be
+% problematic to convert into a two-stage table but which can be used with
+% reasonable performance in a small number of comma lists (at the cost that
+% breaking at higher codepoint Hangul characters will be slightly slow).
+%
+% \begin{variable}{\c_@@_block_size_int}
+% Choosing the block size for the blocks in the two-stage approach is
+% non-trivial: depending on the data stored, the optimal size for
+% memory usage will vary. At the same time, for us there is also the
+% question of load-time: larger blocks require longer comma lists
+% as intermediates, so are slower. As this is going to be needed
+% to use the data, we set it up outside of the group for clarity.
+% \begin{macrocode}
+\int_const:Nn \c_@@_block_size_int { 64 }
+% \end{macrocode}
+% \end{variable}
+%
+% Parsing the data files can be the same way for all engines, but where they
+% are stored as character tokens, the construction method depends on whether
+% they are Unicode or $8$-bit internally. Parsing is therefore done by common
+% functions, with some data storage using engine-specific auxiliaries.
+%
+% As only the data needs to remain at the end of this process, everything
+% is set up inside a group. The only thing that is outside is creating a
+% stream: they are global anyway and it is best to force a stream for
+% all engines.
+%
+% \begin{variable}{\g_@@_data_ior}
+% \begin{macrocode}
+\ior_new:N \g_@@_data_ior
+% \end{macrocode}
+% \end{variable}
+%
+% We need some setup for the two-part table approach. The number of blocks we
+% need will be variable, but the resulting size of the stage one table
+% is predictable. For performance reasons, we therefore create the stage one
+% tables now so they can be used immediately, and will later rename them as a
+% constant tables. For each two-stage table construction, we need a comma
+% list to hold the partial block and a couple of integers to track where
+% we are up to. To avoid burning registers, the latter are stored in macros
+% and are \enquote{fake} integers. We also avoid any \texttt{new} functions,
+% keeping as much as possible local.
+%
+% As we need both positive and negative values, case data requires one
+% two-stage table for each transformation. In contrasts, general Unicode
+% properties could be stored in one table with appropriate combination rules:
+% that is not done at present but is likely to be added over time. Here, all
+% that is needed is additional entries into the comma-list to create the
+% structures.
+%
+% Notice that in the standard \pkg{expl3} way we are indexes position not
+% offset: that does mean a little work later.
+% \begin{macrocode}
+\group_begin:
+ \clist_map_inline:nn
+ { category , uppercase , lowercase }
+ {
+ \cs_set_nopar:cpn { l_@@_ #1 _block_clist } { }
+ \cs_set_nopar:cpn { l_@@_ #1 _block_tl } { 1 }
+ \cs_set_nopar:cpn { l_@@_ #1 _pos_tl } { 0 }
+ \intarray_new:cn { g_@@_ #1 _index_intarray }
+ { \int_div_truncate:nn { "110000 } \c_@@_block_size_int }
+ }
+% \end{macrocode}
+% We need an integer value when matching the current block to those we have
+% already seen, and a way to track codepoints for handling ranges. Again,
+% we avoid using up registers or creating global names.
+% \begin{macrocode}
+ \cs_set_nopar:Npn \l_@@_next_codepoint_fint_tl { 0 }
+ \cs_set_nopar:Npn \l_@@_matched_block_tl { 0 }
+% \end{macrocode}
+% For Unicode general category, there needs to be numerical representation of
+% each possible value. As we need to go from string to number here, but the
+% other way elsewhere, we set up fast mappings both ways, but one set local
+% and the other as constants.
+% \begin{macrocode}
+ \cs_set_protected:Npn \@@_data_auxi:w #1#2
+ {
+ \quark_if_recursion_tail_stop:n {#2}
+ \cs_set_nopar:cpn { l_@@_category_ #2 _tl } {#1}
+ \str_const:cn { c_@@_category_ \tex_romannumeral:D #1 _str } {#2}
+ \exp_args:Ne \@@_data_auxi:w { \int_eval:n { #1 + 1 } }
+ }
+ \@@_data_auxi:w { 1 }
+ { Lu } { Ll } { Lt } { Lm } { Lo }
+ { Mn } { Me } { Mc }
+ { Nd } { Nl } { No }
+ { Zs } { Zl } { Zp }
+ { Cc } { Cf } { Co } { Cs } { Cn }
+ { Pd } { Ps } { Pe } { Pc } { Po } { Pi } { Pf }
+ { Sm } { Sc } { Sk } { So }
+ \q_recursion_tail
+ \q_recursion_stop
+% \end{macrocode}
+% Parse the main Unicode data file and pull out the NFD and case changing
+% data. The NFD data is stored on using the hash table approach and can yield
+% a predictable number of codepoints: one or two. We also need the case data,
+% which will be modified further below. To allow for finding ranges, the
+% description of the codepoint needs to be carried forward.
+% \begin{macrocode}
+ \cs_set_protected:Npn \@@_data_auxi:w
+ #1 ; #2 ; #3 ; #4 ; #5 ; #6 ; #7 ; #8 ; #9 ;
+ {
+ \tl_if_blank:nF {#6}
+ {
+ \tl_if_head_eq_charcode:nNF {#6} < % >
+ { \@@_data_auxii:w #1 ; #6 ~ \q_stop }
+ }
+ \@@_data_auxiii:w #1 ; #2 ; #3 ;
+ }
+ \cs_set_protected:Npn \@@_data_auxii:w #1 ; #2 ~ #3 \q_stop
+ {
+ \tl_const:ce
+ { c_@@_nfd_ \codepoint_str_generate:n {"#1} _tl }
+ {
+ {"#2}
+ { \tl_if_blank:nF {#3} {"#3} }
+ }
+ }
+% \end{macrocode}
+% The category data needs to be converted from a string to the numerical
+% equivalent: a simple operation.
+% The case data is going to be stored as an offset from the parent character,
+% rather than an absolute value. We therefore deal with that plus the situation
+% where a codepoint has no mapping data in one shot.
+% \begin{macrocode}
+ \cs_set_protected:Npn \@@_data_auxiii:w
+ #1 ; #2 ; #3 ; #4 ; #5 ; #6 ; #7 ; #8 ; #9 ~ \q_stop
+ {
+ \use:e
+ {
+ \@@_data_auxiv:w
+ #1 ; #2 ;
+ \@@_data_category:n {#3} ;
+ \@@_data_offset:nn {#1} {#7} ;
+ \@@_data_offset:nn {#1} {#8} ;
+ #9;
+ }
+ }
+ \cs_set:Npn \@@_data_category:n #1
+ { \use:c { l_@@_category_ #1 _tl } }
+ \cs_set:Npn \@@_data_offset:nn #1#2
+ {
+ \tl_if_blank:nTF {#2}
+ { 0 }
+ { \int_eval:n { "#2 - "#1 } }
+ }
+% \end{macrocode}
+% To deal with ranges, we track the position of the next codepoint expected.
+% If there is a gap, we deal with that separately: it could be a range or
+% an unused part of the Unicode space. As such, we deal with the current
+% codepoint here whether or not there is range to fill in. Upper- and
+% lowercase data go into the two-stage table, any titlecase exception is
+% just stored in a macro. The data for the codepoint is added to the current
+% block, and if that is now complete we move on to save the block. The
+% case exceptions are all stored as codepoints, with a fixed number of
+% balanced text as we know that there are never more than three.
+% \begin{macrocode}
+ \cs_set_protected:Npn \@@_data_auxiv:w #1 ; #2 ; #3 ; #4 ; #5 ; #6 ;
+ {
+ \int_compare:nNnT {"#1} > \l_@@_next_codepoint_fint_tl
+ {
+ \@@_data_auxv:nnnnw {#1} {#3} {#4} {#5}
+ #2 Last> \q_stop
+ }
+ \@@_add:nn { category } {#3}
+ \@@_add:nn { uppercase } {#4}
+ \@@_add:nn { lowercase } {#5}
+ \int_compare:nNnF {#4} = { \@@_data_offset:nn {#1} {#6} }
+ {
+ \tl_const:ce
+ { c_@@_titlecase_ \codepoint_str_generate:n {"#1} _tl }
+ { {"#6} { } { } }
+ }
+ \tl_set:Ne \l_@@_next_codepoint_fint_tl
+ { \int_eval:n { "#1 + 1 } }
+ }
+ \cs_set_protected:Npn \@@_add:nn #1#2
+ {
+ \clist_put_right:cn { l_@@_ #1 _block_clist } {#2}
+ \int_compare:nNnT { \clist_count:c { l_@@_ #1 _block_clist } }
+ = \c_@@_block_size_int
+ { \@@_save_blocks:nn {#1} { 1 } }
+ }
+% \end{macrocode}
+% Distinguish between a range and a gap, and pass on the appropriate value(s).
+% The general category for unassigned characters is \texttt{Cn}, so we
+% find the correct value once and then use that.
+% \begin{macrocode}
+ \cs_set_protected:Npe \@@_data_auxv:nnnnw #1#2#3#4#5 Last> #6 \q_stop
+ {
+ \exp_not:N \tl_if_blank:nTF {#6}
+ {
+ \exp_not:N \@@_range:nnn {#1} { category }
+ { \exp_not:V \l_@@_category_Cn_tl }
+ \exp_not:N \@@_range:nnn {#1} { uppercase } { 0 }
+ \exp_not:N \@@_range:nnn {#1} { lowercase } { 0 }
+ }
+ {
+ \exp_not:N \@@_range:nnn {#1} { category } {#2}
+ \exp_not:N \@@_range:nnn {#1} { uppercase } {#3}
+ \exp_not:N \@@_range:nnn {#1} { lowercase } {#4}
+ }
+ }
+% \end{macrocode}
+% Calculated the length of the range and the space remaining in the current
+% block.
+% \begin{macrocode}
+ \cs_set_protected:Npn \@@_range:nnn #1
+ {
+ \exp_args:Nf \@@_range_aux:nnn
+ { \int_eval:n { "#1 - \l_@@_next_codepoint_fint_tl } }
+ }
+ \cs_set_protected:Npn \@@_range_aux:nnn #1#2
+ {
+ \exp_args:Nf \@@_range:nnnn
+ {
+ \int_min:nn
+ {#1}
+ {
+ \c_@@_block_size_int
+ - \clist_count:c { l_@@_ #2 _block_clist }
+ }
+ }
+ {#1} {#2}
+ }
+% \end{macrocode}
+% Here we want to do three things: add to and possibly complete the current
+% block, add complete blocks quickly, then finish up the range in a final
+% open block. We need to avoid as far as possible avoid dealing with every
+% single codepoint, so the middle step is optimised.
+% \begin{macrocode}
+ \cs_set_protected:Npn \@@_range:nnnn #1#2#3#4
+ {
+ \prg_replicate:nn {#1}
+ { \clist_put_right:cn { l_@@_ #3 _block_clist } {#4} }
+ \int_compare:nNnT { \clist_count:c { l_@@_ #3 _block_clist } }
+ = \c_@@_block_size_int
+ { \@@_save_blocks:nn {#3} { 1 } }
+ \int_compare:nNnF
+ { \int_div_truncate:nn { #2 - #1 } \c_@@_block_size_int } = 0
+ {
+ \tl_set:ce { l_@@_ #3 _block_clist }
+ {
+ \exp_args:NNe \use:nn \use_none:n
+ { \prg_replicate:nn { \c_@@_block_size_int } { , #4 } }
+ }
+ \@@_save_blocks:nn {#3}
+ { \int_div_truncate:nn { (#2 - #1) } \c_@@_block_size_int }
+ }
+ \prg_replicate:nn
+ { \int_mod:nn { #2 - #1 } \c_@@_block_size_int }
+ { \clist_put_right:ce { l_@@_ #3 _block_clist } {#4} }
+ }
+% \end{macrocode}
+% To allow rapid comparison, each completed block is stored locally as a
+% comma list: once all of the blocks have been created, they are converted
+% into an \texttt{intarray} in one step. The aim here is to check the current
+% block against those we've already used, and either match to an existing
+% block or save a new block.
+% \begin{macrocode}
+ \cs_set_protected:Npn \@@_save_blocks:nn #1#2
+ {
+ \tl_set_eq:Nc \l_@@_matched_block_tl { l_@@_ #1 _block_tl }
+ \int_step_inline:nn { \tl_use:c { l_@@_ #1 _block_tl } - 1 }
+ {
+ \tl_if_eq:ccT { l_@@_ #1 _block_clist }
+ { l_@@_ #1 _block_ ##1 _clist }
+ { \tl_set:Nn \l_@@_matched_block_tl {##1} }
+ }
+ \int_compare:nNnT
+ { \tl_use:c { l_@@_ #1 _block_tl } } = \l_@@_matched_block_tl
+ {
+ \clist_set_eq:cc
+ {
+ l_@@_ #1 _block_
+ \tl_use:c { l_@@_ #1 _block_tl } _clist
+ }
+ { l_@@_ #1 _block_clist }
+ \tl_set:ce { l_@@_ #1 _block_tl }
+ { \int_eval:n { \tl_use:c { l_@@_ #1 _block_tl } + 1 } }
+ }
+ \prg_replicate:nn {#2}
+ {
+ \tl_set:ce { l_@@_ #1 _pos_tl }
+ { \int_eval:n { \tl_use:c { l_@@_ #1 _pos_tl } + 1 } }
+ \exp_args:Nc \__kernel_intarray_gset:Nnn
+ { g_@@_ #1 _index_intarray }
+ { \tl_use:c { l_@@_ #1 _pos_tl } }
+ \l_@@_matched_block_tl
+ }
+ \clist_clear:c { l_@@_ #1 _block_clist }
+ }
+% \end{macrocode}
+% Close out the final block, rename the first stage table, then combine all
+% of the block comma-lists into one large second-stage table with offsets.
+% As we use an index not an offset, there is a little back-and-forward to do.
+% \begin{macrocode}
+ \cs_set_protected:Npn \@@_finalise_blocks:
+ {
+ \clist_map_inline:nn { category , uppercase , lowercase }
+ {
+ \@@_range:nnn { 110000 } {##1} { 0 }
+ \@@_finalise_blocks:n {##1}
+ }
+ }
+ \cs_set_protected:Npn \@@_finalise_blocks:n #1
+ {
+ \cs_gset_eq:cc { c_@@_ #1 _index_intarray } { g_@@_ #1 _index_intarray }
+ \cs_undefine:c { g_@@_ #1 _index_intarray }
+ \intarray_new:cn { g_@@_ #1 _blocks_intarray }
+ { ( \tl_use:c { l_@@_ #1 _block_tl } - 1 ) * \c_@@_block_size_int }
+ \int_step_inline:nn { \tl_use:c { l_@@_ #1 _block_tl } - 1 }
+ {
+ \exp_args:Nv \@@_finalise_blocks:nnn
+ { l_@@_ #1 _block_ ##1 _clist }
+ {##1} {#1}
+ }
+ \cs_gset_eq:cc { c_@@_ #1 _blocks_intarray }
+ { g_@@_ #1 _blocks_intarray }
+ \cs_undefine:c { g_@@_ #1 _blocks_intarray }
+ }
+ \cs_set_protected:Npn \@@_finalise_blocks:nnn #1#2#3
+ {
+ \exp_args:Nnf \@@_finalise_blocks:nnnw { 1 }
+ { \int_eval:n { ( #2 - 1 ) * \c_@@_block_size_int } }
+ {#3}
+ #1 , \q_recursion_tail , \q_recursion_stop
+ }
+ \cs_set_protected:Npn \@@_finalise_blocks:nnnw #1#2#3#4 ,
+ {
+ \quark_if_recursion_tail_stop:n {#4}
+ \intarray_gset:cnn { g_@@_ #3 _blocks_intarray }
+ { #1 + #2 }
+ {#4}
+ \exp_args:Nf \@@_finalise_blocks:nnnw
+ { \int_eval:n { #1 + 1 } } {#2} {#3}
+ }
+% \end{macrocode}
+% With the setup done, read the main data file: it's easiest to do that as
+% a token list with spaces retained.
+% \begin{macrocode}
+ \ior_open:Nn \g_@@_data_ior { UnicodeData.txt }
+ \group_begin:
+ \char_set_catcode_space:n { `\ }%
+ \ior_map_variable:NNn \g_@@_data_ior \l_@@_tmpa_tl
+ {%
+ \if_meaning:w \l_@@_tmpa_tl \c_space_tl
+ \exp_after:wN \ior_map_break:
+ \fi:
+ \exp_after:wN \@@_data_auxi:w \l_@@_tmpa_tl \q_stop
+ }%
+ \@@_finalise_blocks:
+ \group_end:
+\group_end:
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\__kernel_codepoint_data:nn}
+% \begin{macro}[EXP]{\@@_data:nnn}
+% Recover data from a two-stage table: entirely generic as this applies to
+% all tables (as we use the same block size for all of them). Notice that
+% as we use indices not offsets we have to shuffle out-by-one issues. This
+% function is needed \emph{before} loading the special casing data, as there
+% we need to be able to check the standard case mappings.
+% \begin{macrocode}
+\cs_new:Npn \__kernel_codepoint_data:nn #1#2
+ {
+ \exp_args:Nf \@@_data:nnn
+ {
+ \int_eval:n
+ {
+ \c_@@_block_size_int *
+ (
+ \intarray_item:cn { c_@@_ #1 _index_intarray }
+ {
+ \int_div_truncate:nn {#2}
+ \c_@@_block_size_int
+ + 1
+ }
+ - 1
+ )
+ }
+ }
+ {#2} {#1}
+ }
+\cs_new:Npn \@@_data:nnn #1#2#3
+ {
+ \intarray_item:cn { c_@@_ #3 _blocks_intarray }
+ { #1 + \int_mod:nn {#2} \c_@@_block_size_int + 1 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% The other data files all use C-style comments so we have to worry about
+% |#| tokens (and reading as strings). The set up for case folding is in two
+% parts. For the basic (core) mappings, folding is the same as lower casing in
+% most positions so only store the differences. For the more complex foldings,
+% always store the result, splitting up the two or three code points in the input
+% as required.
+% \begin{macrocode}
+\group_begin:
+ \ior_open:Nn \g_@@_data_ior { CaseFolding.txt }
+ \cs_set_protected:Npn \@@_data_auxi:w #1 ;~ #2 ;~ #3 ; #4 \q_stop
+ {
+ \if:w \tl_head:n { #2 ? } C
+ \reverse_if:N \if_int_compare:w
+ \int_eval:n { \__kernel_codepoint_data:nn { lowercase } {"#1} + "#1 }
+ = "#3 ~
+ \tl_const:ce
+ { c_@@_casefold_ \codepoint_str_generate:n {"#1} _tl }
+ { {"#3} { } { } }
+ \fi:
+ \else:
+ \if:w \tl_head:n { #2 ? } F
+ \@@_data_auxii:w #1 ~ #3 ~ \q_stop
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% Here, |#4| can have a trailing space, so we tidy up a bit at the cost of
+% speed for these small number of cases it applies to.
+% \begin{macrocode}
+ \cs_set_protected:Npn \@@_data_auxii:w #1 ~ #2 ~ #3 ~ #4 \q_stop
+ {
+ \tl_const:ce { c_@@_casefold_ \codepoint_str_generate:n {"#1} _tl }
+ {
+ {"#2}
+ {"#3}
+ { \tl_if_blank:nF {#4} { " \int_to_Hex:n {"#4} } }
+ }
+ }
+ \ior_str_map_inline:Nn \g_@@_data_ior
+ {
+ \reverse_if:N \if:w \c_hash_str \tl_head:w #1 \c_hash_str \q_stop
+ \@@_data_auxi:w #1 \q_stop
+ \fi:
+ }
+ \ior_close:N \g_@@_data_ior
+% \end{macrocode}
+% For upper- and lowercasing special situations, there is a bit more to
+% do as we also have titlecasing to consider, plus we need to stop part-way
+% through the file.
+% \begin{macrocode}
+ \ior_open:Nn \g_@@_data_ior { SpecialCasing.txt }
+ \cs_set_protected:Npn \@@_data_auxi:w
+ #1 ;~ #2 ;~ #3 ;~ #4 ; #5 \q_stop
+ {
+ \use:n { \@@_data_auxii:w #1 ~ lower ~ #2 ~ } ~ \q_stop
+ \use:n { \@@_data_auxii:w #1 ~ upper ~ #4 ~ } ~ \q_stop
+ \str_if_eq:nnF {#3} {#4}
+ { \use:n { \@@_data_auxii:w #1 ~ title ~ #3 ~ } ~ \q_stop }
+ }
+ \cs_set_protected:Npn \@@_data_auxii:w
+ #1 ~ #2 ~ #3 ~ #4 ~ #5 \q_stop
+ {
+ \tl_if_empty:nF {#4}
+ {
+ \tl_const:ce { c_@@_ #2 case_ \codepoint_str_generate:n {"#1} _tl }
+ {
+ {"#3}
+ {"#4}
+ { \tl_if_blank:nF {#5} {"#5} }
+ }
+ }
+ }
+ \ior_str_map_inline:Nn \g_@@_data_ior
+ {
+ \str_if_eq:eeTF { \tl_head:w #1 \c_hash_str \q_stop } { \c_hash_str }
+ {
+ \str_if_eq:eeT
+ {#1}
+ { \c_hash_str \c_space_tl Conditional~Mappings }
+ { \ior_map_break: }
+ }
+ { \@@_data_auxi:w #1 \q_stop }
+ }
+ \ior_close:N \g_@@_data_ior
+\group_end:
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\__kernel_codepoint_case:nn}
+% \begin{macro}[EXP]{\@@_case:nnn}
+% \begin{macro}[EXP]
+% {\@@_uppercase:n, \@@_lowercase:n, \@@_titlecase:n, \@@_casefold:n}
+% \begin{macro}[EXP]{\@@_case:nn}
+% With the core data files loaded, there is now a need to provide access to
+% this information for other modules. That is done here such that case
+% folding can also be covered. At this level, all that needs to be returned
+% is the
+% \begin{macrocode}
+\cs_new:Npn \__kernel_codepoint_case:nn #1#2
+ {
+ \exp_args:Ne \@@_case:nnn
+ { \codepoint_str_generate:n {#2} } {#1} {#2}
+ }
+\cs_new:Npn \@@_case:nnn #1#2#3
+ {
+ \cs_if_exist:cTF { c_@@_ #2 _ #1 _tl }
+ {
+ \tl_use:c
+ { c_@@_ #2 _ #1 _tl }
+ }
+ { \use:c { @@_ #2 :n } {#3} }
+ }
+\cs_new:Npn \@@_uppercase:n { \@@_case:nn { uppercase } }
+\cs_new:Npn \@@_lowercase:n { \@@_case:nn { lowercase } }
+\cs_new:Npn \@@_titlecase:n { \@@_case:nn { uppercase } }
+\cs_new:Npn \@@_casefold:n { \@@_case:nn { lowercase } }
+\cs_new:Npn \@@_case:nn #1#2
+ {
+ { \int_eval:n { \__kernel_codepoint_data:nn {#1} {#2} + #2 } }
+ { }
+ { }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_nfd:n}
+% \begin{macro}[EXP]{\@@_nfd:nn}
+% A simple interface.
+% \begin{macrocode}
+\cs_new:Npn \@@_nfd:n #1
+ { \exp_args:Ne \@@_nfd:nn { \codepoint_str_generate:n {#1} } {#1} }
+\cs_new:Npn \@@_nfd:nn #1#2
+ {
+ \tl_if_exist:cTF { c_@@_nfd_ #1 _tl }
+ { \tl_use:c { c_@@_nfd_ #1 _tl } }
+ { {#2} { } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%<@@=text>
+% \end{macrocode}
+%
+% Read the Unicode grapheme data. This is quite easy to handle and we only need
+% codepoints, not characters, so there is no need to worry about the engine in use.
+% As reading as a string is most convenient, we have to do some work to remove
+% spaces: the hardest part of the entire process!
+% \begin{macrocode}
+\ior_new:N \g_@@_data_ior
+\group_begin:
+ \ior_open:Nn \g_@@_data_ior { GraphemeBreakProperty.txt }
+ \cs_set_nopar:Npn \l_@@_tmpa_str { }
+ \cs_set_nopar:Npn \l_@@_tmpb_str { }
+ \cs_set_protected:Npn \@@_data_auxi:w #1 ;~ #2 ~ #3 \q_stop
+ {
+ \str_if_eq:VnF \l_@@_tmpb_str {#2}
+ {
+ \str_if_empty:NF \l_@@_tmpb_str
+ {
+ \clist_const:ce { c_@@_grapheme_ \l_@@_tmpb_str _clist }
+ { \exp_after:wN \use_none:n \l_@@_tmpa_str }
+ \cs_set_nopar:Npn \l_@@_tmpa_str { }
+ }
+ \cs_set_nopar:Npn \l_@@_tmpb_str {#2}
+ }
+ \@@_data_auxii:w #1 .. #1 .. #1 \q_stop
+ }
+ \cs_set_protected:Npn \@@_data_auxii:w #1 .. #2 .. #3 \q_stop
+ {
+ \cs_set_nopar:Npe \l_@@_tmpa_str
+ {
+ \l_@@_tmpa_str ,
+ \tl_trim_spaces:n {#1} .. \tl_trim_spaces:n {#2}
+ }
+ }
+ \ior_str_map_inline:Nn \g_@@_data_ior
+ {
+ \str_if_eq:eeF { \tl_head:w #1 \c_hash_str \q_stop } { \c_hash_str }
+ {
+ \tl_if_blank:nF {#1}
+ { \@@_data_auxi:w #1 \q_stop }
+ }
+ }
+ \ior_close:N \g_@@_data_ior
+\group_end:
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/expl3-code.tex b/Master/texmf-dist/tex/latex-dev/l3kernel/expl3-code.tex
new file mode 100644
index 00000000000..8207f52f0fe
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/expl3-code.tex
@@ -0,0 +1,37498 @@
+%%
+%% This is file `expl3-code.tex',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% expl3.dtx (with options: `package')
+%% l3bootstrap.dtx (with options: `package')
+%% l3names.dtx (with options: `package,tex')
+%% l3basics.dtx (with options: `package')
+%% l3expan.dtx (with options: `package')
+%% l3quark.dtx (with options: `package')
+%% l3tl.dtx (with options: `package')
+%% l3tl-build.dtx (with options: `package')
+%% l3str.dtx (with options: `package')
+%% l3seq.dtx (with options: `package')
+%% l3int.dtx (with options: `package')
+%% l3flag.dtx (with options: `package')
+%% l3prg.dtx (with options: `package')
+%% l3sys.dtx (with options: `package,tex')
+%% l3clist.dtx (with options: `package')
+%% l3token.dtx (with options: `package,tex')
+%% l3prop.dtx (with options: `package')
+%% l3msg.dtx (with options: `package')
+%% l3file.dtx (with options: `package')
+%% l3skip.dtx (with options: `package')
+%% l3keys.dtx (with options: `package')
+%% l3intarray.dtx (with options: `package,tex')
+%% l3fp.dtx (with options: `package')
+%% l3fp-aux.dtx (with options: `package')
+%% l3fp-traps.dtx (with options: `package')
+%% l3fp-round.dtx (with options: `package')
+%% l3fp-parse.dtx (with options: `package')
+%% l3fp-assign.dtx (with options: `package')
+%% l3fp-logic.dtx (with options: `package')
+%% l3fp-basics.dtx (with options: `package')
+%% l3fp-extended.dtx (with options: `package')
+%% l3fp-expo.dtx (with options: `package')
+%% l3fp-trig.dtx (with options: `package')
+%% l3fp-convert.dtx (with options: `package')
+%% l3fp-random.dtx (with options: `package')
+%% l3fp-types.dtx (with options: `package')
+%% l3fp-symbolic.dtx (with options: `package')
+%% l3fp-functions.dtx (with options: `package')
+%% l3fparray.dtx (with options: `package')
+%% l3bitset.dtx (with options: `package')
+%% l3cctab.dtx (with options: `package')
+%% l3sort.dtx (with options: `package')
+%% l3str-convert.dtx (with options: `package')
+%% l3tl-analysis.dtx (with options: `package')
+%% l3regex.dtx (with options: `package')
+%% l3box.dtx (with options: `package')
+%% l3color.dtx (with options: `package')
+%% l3pdf.dtx (with options: `package,tex')
+%% l3coffins.dtx (with options: `package')
+%% l3luatex.dtx (with options: `package,tex')
+%% l3unicode.dtx (with options: `package')
+%% l3text.dtx (with options: `package')
+%% l3text-case.dtx (with options: `package')
+%% l3text-map.dtx (with options: `package')
+%% l3text-purify.dtx (with options: `package')
+%% l3legacy.dtx (with options: `package')
+%% l3deprecation.dtx (with options: `package')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: expl3.dtx
+\def\ExplFileDate{2024-04-11}%
+\begingroup
+ \def\next{\endgroup}%
+ \expandafter\ifx\csname PackageError\endcsname\relax
+ \begingroup
+ \def\next{\endgroup\endgroup}%
+ \def\PackageError#1#2#3%
+ {%
+ \endgroup
+ \errhelp{#3}%
+ \errmessage{#1 Error: #2!}%
+ }%
+ \fi
+ \expandafter\ifx\csname ExplLoaderFileDate\endcsname\relax
+ \def\next
+ {%
+ \PackageError{expl3}{No expl3 loader detected}
+ {%
+ You have attempted to use the expl3 code directly rather than using
+ the correct loader. Loading of expl3 will abort.
+ }%
+ \endgroup
+ \endinput
+ }
+ \else
+ \ifx\ExplLoaderFileDate\ExplFileDate
+ \else
+ \def\next
+ {%
+ \PackageError{expl3}{Mismatched expl3 files detected}
+ {%
+ You have attempted to load expl3 with mismatched files:
+ probably you have one or more files 'locally installed' which
+ are in conflict. Loading of expl3 will abort.
+ }%
+ \endgroup
+ \endinput
+ }%
+ \fi
+\fi
+\next
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname ver@expl3-code.tex\endcsname\relax
+ \expandafter\edef\csname ver@expl3-code.tex\endcsname
+ {%
+ \ExplFileDate\space
+ L3 programming layer
+ }%
+\else
+ \expandafter\endinput
+\fi
+\immediate\write-1 %
+ {%
+ Package: expl3
+ \ExplFileDate\space
+ L3 programming layer (code)%
+ }%
+%% File: l3bootstrap.dtx
+\begingroup\expandafter\expandafter\expandafter\endgroup
+ \expandafter\ifx\csname pdfstrcmp\endcsname\relax
+ \let\pdfstrcmp\strcmp
+\fi
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname directlua\endcsname\relax
+\else
+ \ifnum\luatexversion<110 %
+ \else
+ \begingroup\expandafter\expandafter\expandafter\endgroup
+ \expandafter\ifx\csname newcatcodetable\endcsname\relax
+ \input{ltluatex}%
+ \fi
+ \begingroup\expandafter\expandafter\expandafter\endgroup
+ \expandafter\ifx\csname newluabytecode\endcsname\relax
+ \else
+ \newluabytecode\@expl@luadata@bytecode
+ \fi
+ \directlua{require("expl3")}%
+ \ifnum 0%
+ \directlua{
+ if status.ini_version then
+ tex.write("1")
+ end
+ }>0 %
+ \everyjob\expandafter{%
+ \the\expandafter\everyjob
+ \csname\detokenize{lua_now:n}\endcsname{require("expl3")}%
+ }%
+ \fi
+ \fi
+\fi
+\begingroup
+ \def\next{\endgroup}%
+ \def\ShortText{Required primitives not found}%
+ \def\LongText%
+ {%
+ The L3 programming layer requires the e-TeX primitives and the
+ \LineBreak 'pdfTeX utilities' as described in the README file.
+ \LineBreak
+ These are available in the engines\LineBreak
+ - pdfTeX v1.40.20\LineBreak
+ - XeTeX v0.999991\LineBreak
+ - LuaTeX v1.10\LineBreak
+ - e-(u)pTeX v3.8.2\LineBreak
+ - Prote (2021)\LineBreak
+ or later.\LineBreak
+ \LineBreak
+ }%
+ \ifnum0%
+ \expandafter\ifx\csname luatexversion\endcsname\relax
+ \expandafter\ifx\csname expanded\endcsname\relax\else 1\fi
+ \else
+ \ifnum\luatexversion<110 \else 1\fi
+ \fi
+ =0 %
+ \newlinechar`\^^J %
+ \def\LineBreak{\noexpand\MessageBreak}%
+ \expandafter\ifx\csname PackageError\endcsname\relax
+ \def\LineBreak{^^J}%
+ \begingroup
+ \lccode`\~=`\ \lccode`\}=`\ %
+ \lccode`\T=`\T\lccode`\H=`\H%
+ \catcode`\ =11 %
+\lowercase{\endgroup\def\PackageError#1#2#3{%
+\begingroup\errorcontextlines-1\immediate\write0{}\errhelp{#3}\def%
+\ {#1 Error: #2.^^J^^J
+Type H <return> for immediate help}\def~{\errmessage{%
+\ }}~\endgroup}}%
+ \fi
+ \edef\next
+ {%
+ \noexpand\PackageError{expl3}{\ShortText}
+ {\LongText Loading of expl3 will abort!}%
+ \endgroup
+ \noexpand\endinput
+ }%
+ \fi
+\next
+\protected\edef\ExplSyntaxOff
+ {%
+ \protected\def\noexpand\ExplSyntaxOff{}%
+ \catcode 9 = \the\catcode 9\relax
+ \catcode 32 = \the\catcode 32\relax
+ \catcode 34 = \the\catcode 34\relax
+ \catcode 58 = \the\catcode 58\relax
+ \catcode 94 = \the\catcode 94\relax
+ \catcode 95 = \the\catcode 95\relax
+ \catcode 124 = \the\catcode 124\relax
+ \catcode 126 = \the\catcode 126\relax
+ \endlinechar = \the\endlinechar\relax
+ \chardef\csname\detokenize{l__kernel_expl_bool}\endcsname = 0\relax
+ }%
+\catcode 9 = 9\relax
+\catcode 32 = 9\relax
+\catcode 34 = 12\relax
+\catcode 58 = 11\relax
+\catcode 94 = 7\relax
+\catcode 95 = 11\relax
+\catcode 124 = 12\relax
+\catcode 126 = 10\relax
+\endlinechar = 32\relax
+\global\chardef\l__kernel_expl_bool = 1\relax
+\protected \def \ExplSyntaxOn
+ {
+ \bool_if:NF \l__kernel_expl_bool
+ {
+ \cs_set_protected:Npe \ExplSyntaxOff
+ {
+ \char_set_catcode:nn { 9 } { \char_value_catcode:n { 9 } }
+ \char_set_catcode:nn { 32 } { \char_value_catcode:n { 32 } }
+ \char_set_catcode:nn { 34 } { \char_value_catcode:n { 34 } }
+ \char_set_catcode:nn { 58 } { \char_value_catcode:n { 58 } }
+ \char_set_catcode:nn { 94 } { \char_value_catcode:n { 94 } }
+ \char_set_catcode:nn { 95 } { \char_value_catcode:n { 95 } }
+ \char_set_catcode:nn { 124 } { \char_value_catcode:n { 124 } }
+ \char_set_catcode:nn { 126 } { \char_value_catcode:n { 126 } }
+ \tex_endlinechar:D =
+ \tex_the:D \tex_endlinechar:D \scan_stop:
+ \bool_set_false:N \l__kernel_expl_bool
+ \cs_set_protected:Npn \ExplSyntaxOff { }
+ }
+ }
+ \char_set_catcode_ignore:n { 9 } % tab
+ \char_set_catcode_ignore:n { 32 } % space
+ \char_set_catcode_other:n { 34 } % double quote
+ \char_set_catcode_letter:n { 58 } % colon
+ \char_set_catcode_math_superscript:n { 94 } % circumflex
+ \char_set_catcode_letter:n { 95 } % underscore
+ \char_set_catcode_other:n { 124 } % pipe
+ \char_set_catcode_space:n { 126 } % tilde
+ \tex_endlinechar:D = 32 \scan_stop:
+ \bool_set_true:N \l__kernel_expl_bool
+ }
+%% File: l3names.dtx
+\let \tex_global:D \global
+\let \tex_let:D \let
+\begingroup
+ \long \def \__kernel_primitive:NN #1#2
+ { \tex_global:D \tex_let:D #2 #1 }
+ \__kernel_primitive:NN \ \tex_space:D
+ \__kernel_primitive:NN \/ \tex_italiccorrection:D
+ \__kernel_primitive:NN \- \tex_hyphen:D
+ \__kernel_primitive:NN \above \tex_above:D
+ \__kernel_primitive:NN \abovedisplayshortskip \tex_abovedisplayshortskip:D
+ \__kernel_primitive:NN \abovedisplayskip \tex_abovedisplayskip:D
+ \__kernel_primitive:NN \abovewithdelims \tex_abovewithdelims:D
+ \__kernel_primitive:NN \accent \tex_accent:D
+ \__kernel_primitive:NN \adjdemerits \tex_adjdemerits:D
+ \__kernel_primitive:NN \advance \tex_advance:D
+ \__kernel_primitive:NN \afterassignment \tex_afterassignment:D
+ \__kernel_primitive:NN \aftergroup \tex_aftergroup:D
+ \__kernel_primitive:NN \atop \tex_atop:D
+ \__kernel_primitive:NN \atopwithdelims \tex_atopwithdelims:D
+ \__kernel_primitive:NN \badness \tex_badness:D
+ \__kernel_primitive:NN \baselineskip \tex_baselineskip:D
+ \__kernel_primitive:NN \batchmode \tex_batchmode:D
+ \__kernel_primitive:NN \begingroup \tex_begingroup:D
+ \__kernel_primitive:NN \belowdisplayshortskip \tex_belowdisplayshortskip:D
+ \__kernel_primitive:NN \belowdisplayskip \tex_belowdisplayskip:D
+ \__kernel_primitive:NN \binoppenalty \tex_binoppenalty:D
+ \__kernel_primitive:NN \botmark \tex_botmark:D
+ \__kernel_primitive:NN \box \tex_box:D
+ \__kernel_primitive:NN \boxmaxdepth \tex_boxmaxdepth:D
+ \__kernel_primitive:NN \brokenpenalty \tex_brokenpenalty:D
+ \__kernel_primitive:NN \catcode \tex_catcode:D
+ \__kernel_primitive:NN \char \tex_char:D
+ \__kernel_primitive:NN \chardef \tex_chardef:D
+ \__kernel_primitive:NN \cleaders \tex_cleaders:D
+ \__kernel_primitive:NN \closein \tex_closein:D
+ \__kernel_primitive:NN \closeout \tex_closeout:D
+ \__kernel_primitive:NN \clubpenalty \tex_clubpenalty:D
+ \__kernel_primitive:NN \copy \tex_copy:D
+ \__kernel_primitive:NN \count \tex_count:D
+ \__kernel_primitive:NN \countdef \tex_countdef:D
+ \__kernel_primitive:NN \cr \tex_cr:D
+ \__kernel_primitive:NN \crcr \tex_crcr:D
+ \__kernel_primitive:NN \csname \tex_csname:D
+ \__kernel_primitive:NN \day \tex_day:D
+ \__kernel_primitive:NN \deadcycles \tex_deadcycles:D
+ \__kernel_primitive:NN \def \tex_def:D
+ \__kernel_primitive:NN \defaulthyphenchar \tex_defaulthyphenchar:D
+ \__kernel_primitive:NN \defaultskewchar \tex_defaultskewchar:D
+ \__kernel_primitive:NN \delcode \tex_delcode:D
+ \__kernel_primitive:NN \delimiter \tex_delimiter:D
+ \__kernel_primitive:NN \delimiterfactor \tex_delimiterfactor:D
+ \__kernel_primitive:NN \delimitershortfall \tex_delimitershortfall:D
+ \__kernel_primitive:NN \dimen \tex_dimen:D
+ \__kernel_primitive:NN \dimendef \tex_dimendef:D
+ \__kernel_primitive:NN \discretionary \tex_discretionary:D
+ \__kernel_primitive:NN \displayindent \tex_displayindent:D
+ \__kernel_primitive:NN \displaylimits \tex_displaylimits:D
+ \__kernel_primitive:NN \displaystyle \tex_displaystyle:D
+ \__kernel_primitive:NN \displaywidowpenalty \tex_displaywidowpenalty:D
+ \__kernel_primitive:NN \displaywidth \tex_displaywidth:D
+ \__kernel_primitive:NN \divide \tex_divide:D
+ \__kernel_primitive:NN \doublehyphendemerits \tex_doublehyphendemerits:D
+ \__kernel_primitive:NN \dp \tex_dp:D
+ \__kernel_primitive:NN \dump \tex_dump:D
+ \__kernel_primitive:NN \edef \tex_edef:D
+ \__kernel_primitive:NN \else \tex_else:D
+ \__kernel_primitive:NN \emergencystretch \tex_emergencystretch:D
+ \__kernel_primitive:NN \end \tex_end:D
+ \__kernel_primitive:NN \endcsname \tex_endcsname:D
+ \__kernel_primitive:NN \endgroup \tex_endgroup:D
+ \__kernel_primitive:NN \endinput \tex_endinput:D
+ \__kernel_primitive:NN \endlinechar \tex_endlinechar:D
+ \__kernel_primitive:NN \eqno \tex_eqno:D
+ \__kernel_primitive:NN \errhelp \tex_errhelp:D
+ \__kernel_primitive:NN \errmessage \tex_errmessage:D
+ \__kernel_primitive:NN \errorcontextlines \tex_errorcontextlines:D
+ \__kernel_primitive:NN \errorstopmode \tex_errorstopmode:D
+ \__kernel_primitive:NN \escapechar \tex_escapechar:D
+ \__kernel_primitive:NN \everycr \tex_everycr:D
+ \__kernel_primitive:NN \everydisplay \tex_everydisplay:D
+ \__kernel_primitive:NN \everyhbox \tex_everyhbox:D
+ \__kernel_primitive:NN \everyjob \tex_everyjob:D
+ \__kernel_primitive:NN \everymath \tex_everymath:D
+ \__kernel_primitive:NN \everypar \tex_everypar:D
+ \__kernel_primitive:NN \everyvbox \tex_everyvbox:D
+ \__kernel_primitive:NN \exhyphenpenalty \tex_exhyphenpenalty:D
+ \__kernel_primitive:NN \expandafter \tex_expandafter:D
+ \__kernel_primitive:NN \fam \tex_fam:D
+ \__kernel_primitive:NN \fi \tex_fi:D
+ \__kernel_primitive:NN \finalhyphendemerits \tex_finalhyphendemerits:D
+ \__kernel_primitive:NN \firstmark \tex_firstmark:D
+ \__kernel_primitive:NN \floatingpenalty \tex_floatingpenalty:D
+ \__kernel_primitive:NN \font \tex_font:D
+ \__kernel_primitive:NN \fontdimen \tex_fontdimen:D
+ \__kernel_primitive:NN \fontname \tex_fontname:D
+ \__kernel_primitive:NN \futurelet \tex_futurelet:D
+ \__kernel_primitive:NN \gdef \tex_gdef:D
+ \__kernel_primitive:NN \global \tex_global:D
+ \__kernel_primitive:NN \globaldefs \tex_globaldefs:D
+ \__kernel_primitive:NN \halign \tex_halign:D
+ \__kernel_primitive:NN \hangafter \tex_hangafter:D
+ \__kernel_primitive:NN \hangindent \tex_hangindent:D
+ \__kernel_primitive:NN \hbadness \tex_hbadness:D
+ \__kernel_primitive:NN \hbox \tex_hbox:D
+ \__kernel_primitive:NN \hfil \tex_hfil:D
+ \__kernel_primitive:NN \hfill \tex_hfill:D
+ \__kernel_primitive:NN \hfilneg \tex_hfilneg:D
+ \__kernel_primitive:NN \hfuzz \tex_hfuzz:D
+ \__kernel_primitive:NN \hoffset \tex_hoffset:D
+ \__kernel_primitive:NN \holdinginserts \tex_holdinginserts:D
+ \__kernel_primitive:NN \hrule \tex_hrule:D
+ \__kernel_primitive:NN \hsize \tex_hsize:D
+ \__kernel_primitive:NN \hskip \tex_hskip:D
+ \__kernel_primitive:NN \hss \tex_hss:D
+ \__kernel_primitive:NN \ht \tex_ht:D
+ \__kernel_primitive:NN \hyphenation \tex_hyphenation:D
+ \__kernel_primitive:NN \hyphenchar \tex_hyphenchar:D
+ \__kernel_primitive:NN \hyphenpenalty \tex_hyphenpenalty:D
+ \__kernel_primitive:NN \if \tex_if:D
+ \__kernel_primitive:NN \ifcase \tex_ifcase:D
+ \__kernel_primitive:NN \ifcat \tex_ifcat:D
+ \__kernel_primitive:NN \ifdim \tex_ifdim:D
+ \__kernel_primitive:NN \ifeof \tex_ifeof:D
+ \__kernel_primitive:NN \iffalse \tex_iffalse:D
+ \__kernel_primitive:NN \ifhbox \tex_ifhbox:D
+ \__kernel_primitive:NN \ifhmode \tex_ifhmode:D
+ \__kernel_primitive:NN \ifinner \tex_ifinner:D
+ \__kernel_primitive:NN \ifmmode \tex_ifmmode:D
+ \__kernel_primitive:NN \ifnum \tex_ifnum:D
+ \__kernel_primitive:NN \ifodd \tex_ifodd:D
+ \__kernel_primitive:NN \iftrue \tex_iftrue:D
+ \__kernel_primitive:NN \ifvbox \tex_ifvbox:D
+ \__kernel_primitive:NN \ifvmode \tex_ifvmode:D
+ \__kernel_primitive:NN \ifvoid \tex_ifvoid:D
+ \__kernel_primitive:NN \ifx \tex_ifx:D
+ \__kernel_primitive:NN \ignorespaces \tex_ignorespaces:D
+ \__kernel_primitive:NN \immediate \tex_immediate:D
+ \__kernel_primitive:NN \indent \tex_indent:D
+ \__kernel_primitive:NN \input \tex_input:D
+ \__kernel_primitive:NN \inputlineno \tex_inputlineno:D
+ \__kernel_primitive:NN \insert \tex_insert:D
+ \__kernel_primitive:NN \insertpenalties \tex_insertpenalties:D
+ \__kernel_primitive:NN \interlinepenalty \tex_interlinepenalty:D
+ \__kernel_primitive:NN \jobname \tex_jobname:D
+ \__kernel_primitive:NN \kern \tex_kern:D
+ \__kernel_primitive:NN \language \tex_language:D
+ \__kernel_primitive:NN \lastbox \tex_lastbox:D
+ \__kernel_primitive:NN \lastkern \tex_lastkern:D
+ \__kernel_primitive:NN \lastpenalty \tex_lastpenalty:D
+ \__kernel_primitive:NN \lastskip \tex_lastskip:D
+ \__kernel_primitive:NN \lccode \tex_lccode:D
+ \__kernel_primitive:NN \leaders \tex_leaders:D
+ \__kernel_primitive:NN \left \tex_left:D
+ \__kernel_primitive:NN \lefthyphenmin \tex_lefthyphenmin:D
+ \__kernel_primitive:NN \leftskip \tex_leftskip:D
+ \__kernel_primitive:NN \leqno \tex_leqno:D
+ \__kernel_primitive:NN \let \tex_let:D
+ \__kernel_primitive:NN \limits \tex_limits:D
+ \__kernel_primitive:NN \linepenalty \tex_linepenalty:D
+ \__kernel_primitive:NN \lineskip \tex_lineskip:D
+ \__kernel_primitive:NN \lineskiplimit \tex_lineskiplimit:D
+ \__kernel_primitive:NN \long \tex_long:D
+ \__kernel_primitive:NN \looseness \tex_looseness:D
+ \__kernel_primitive:NN \lower \tex_lower:D
+ \__kernel_primitive:NN \lowercase \tex_lowercase:D
+ \__kernel_primitive:NN \mag \tex_mag:D
+ \__kernel_primitive:NN \mark \tex_mark:D
+ \__kernel_primitive:NN \mathaccent \tex_mathaccent:D
+ \__kernel_primitive:NN \mathbin \tex_mathbin:D
+ \__kernel_primitive:NN \mathchar \tex_mathchar:D
+ \__kernel_primitive:NN \mathchardef \tex_mathchardef:D
+ \__kernel_primitive:NN \mathchoice \tex_mathchoice:D
+ \__kernel_primitive:NN \mathclose \tex_mathclose:D
+ \__kernel_primitive:NN \mathcode \tex_mathcode:D
+ \__kernel_primitive:NN \mathinner \tex_mathinner:D
+ \__kernel_primitive:NN \mathop \tex_mathop:D
+ \__kernel_primitive:NN \mathopen \tex_mathopen:D
+ \__kernel_primitive:NN \mathord \tex_mathord:D
+ \__kernel_primitive:NN \mathpunct \tex_mathpunct:D
+ \__kernel_primitive:NN \mathrel \tex_mathrel:D
+ \__kernel_primitive:NN \mathsurround \tex_mathsurround:D
+ \__kernel_primitive:NN \maxdeadcycles \tex_maxdeadcycles:D
+ \__kernel_primitive:NN \maxdepth \tex_maxdepth:D
+ \__kernel_primitive:NN \meaning \tex_meaning:D
+ \__kernel_primitive:NN \medmuskip \tex_medmuskip:D
+ \__kernel_primitive:NN \message \tex_message:D
+ \__kernel_primitive:NN \mkern \tex_mkern:D
+ \__kernel_primitive:NN \month \tex_month:D
+ \__kernel_primitive:NN \moveleft \tex_moveleft:D
+ \__kernel_primitive:NN \moveright \tex_moveright:D
+ \__kernel_primitive:NN \mskip \tex_mskip:D
+ \__kernel_primitive:NN \multiply \tex_multiply:D
+ \__kernel_primitive:NN \muskip \tex_muskip:D
+ \__kernel_primitive:NN \muskipdef \tex_muskipdef:D
+ \__kernel_primitive:NN \newlinechar \tex_newlinechar:D
+ \__kernel_primitive:NN \noalign \tex_noalign:D
+ \__kernel_primitive:NN \noboundary \tex_noboundary:D
+ \__kernel_primitive:NN \noexpand \tex_noexpand:D
+ \__kernel_primitive:NN \noindent \tex_noindent:D
+ \__kernel_primitive:NN \nolimits \tex_nolimits:D
+ \__kernel_primitive:NN \nonscript \tex_nonscript:D
+ \__kernel_primitive:NN \nonstopmode \tex_nonstopmode:D
+ \__kernel_primitive:NN \nulldelimiterspace \tex_nulldelimiterspace:D
+ \__kernel_primitive:NN \nullfont \tex_nullfont:D
+ \__kernel_primitive:NN \number \tex_number:D
+ \__kernel_primitive:NN \omit \tex_omit:D
+ \__kernel_primitive:NN \openin \tex_openin:D
+ \__kernel_primitive:NN \openout \tex_openout:D
+ \__kernel_primitive:NN \or \tex_or:D
+ \__kernel_primitive:NN \outer \tex_outer:D
+ \__kernel_primitive:NN \output \tex_output:D
+ \__kernel_primitive:NN \outputpenalty \tex_outputpenalty:D
+ \__kernel_primitive:NN \over \tex_over:D
+ \__kernel_primitive:NN \overfullrule \tex_overfullrule:D
+ \__kernel_primitive:NN \overline \tex_overline:D
+ \__kernel_primitive:NN \overwithdelims \tex_overwithdelims:D
+ \__kernel_primitive:NN \pagedepth \tex_pagedepth:D
+ \__kernel_primitive:NN \pagefilllstretch \tex_pagefilllstretch:D
+ \__kernel_primitive:NN \pagefillstretch \tex_pagefillstretch:D
+ \__kernel_primitive:NN \pagefilstretch \tex_pagefilstretch:D
+ \__kernel_primitive:NN \pagegoal \tex_pagegoal:D
+ \__kernel_primitive:NN \pageshrink \tex_pageshrink:D
+ \__kernel_primitive:NN \pagestretch \tex_pagestretch:D
+ \__kernel_primitive:NN \pagetotal \tex_pagetotal:D
+ \__kernel_primitive:NN \par \tex_par:D
+ \__kernel_primitive:NN \parfillskip \tex_parfillskip:D
+ \__kernel_primitive:NN \parindent \tex_parindent:D
+ \__kernel_primitive:NN \parshape \tex_parshape:D
+ \__kernel_primitive:NN \parskip \tex_parskip:D
+ \__kernel_primitive:NN \patterns \tex_patterns:D
+ \__kernel_primitive:NN \pausing \tex_pausing:D
+ \__kernel_primitive:NN \penalty \tex_penalty:D
+ \__kernel_primitive:NN \postdisplaypenalty \tex_postdisplaypenalty:D
+ \__kernel_primitive:NN \predisplaypenalty \tex_predisplaypenalty:D
+ \__kernel_primitive:NN \predisplaysize \tex_predisplaysize:D
+ \__kernel_primitive:NN \pretolerance \tex_pretolerance:D
+ \__kernel_primitive:NN \prevdepth \tex_prevdepth:D
+ \__kernel_primitive:NN \prevgraf \tex_prevgraf:D
+ \__kernel_primitive:NN \radical \tex_radical:D
+ \__kernel_primitive:NN \raise \tex_raise:D
+ \__kernel_primitive:NN \read \tex_read:D
+ \__kernel_primitive:NN \relax \tex_relax:D
+ \__kernel_primitive:NN \relpenalty \tex_relpenalty:D
+ \__kernel_primitive:NN \right \tex_right:D
+ \__kernel_primitive:NN \righthyphenmin \tex_righthyphenmin:D
+ \__kernel_primitive:NN \rightskip \tex_rightskip:D
+ \__kernel_primitive:NN \romannumeral \tex_romannumeral:D
+ \__kernel_primitive:NN \scriptfont \tex_scriptfont:D
+ \__kernel_primitive:NN \scriptscriptfont \tex_scriptscriptfont:D
+ \__kernel_primitive:NN \scriptscriptstyle \tex_scriptscriptstyle:D
+ \__kernel_primitive:NN \scriptspace \tex_scriptspace:D
+ \__kernel_primitive:NN \scriptstyle \tex_scriptstyle:D
+ \__kernel_primitive:NN \scrollmode \tex_scrollmode:D
+ \__kernel_primitive:NN \setbox \tex_setbox:D
+ \__kernel_primitive:NN \setlanguage \tex_setlanguage:D
+ \__kernel_primitive:NN \sfcode \tex_sfcode:D
+ \__kernel_primitive:NN \shipout \tex_shipout:D
+ \__kernel_primitive:NN \show \tex_show:D
+ \__kernel_primitive:NN \showbox \tex_showbox:D
+ \__kernel_primitive:NN \showboxbreadth \tex_showboxbreadth:D
+ \__kernel_primitive:NN \showboxdepth \tex_showboxdepth:D
+ \__kernel_primitive:NN \showlists \tex_showlists:D
+ \__kernel_primitive:NN \showthe \tex_showthe:D
+ \__kernel_primitive:NN \skewchar \tex_skewchar:D
+ \__kernel_primitive:NN \skip \tex_skip:D
+ \__kernel_primitive:NN \skipdef \tex_skipdef:D
+ \__kernel_primitive:NN \spacefactor \tex_spacefactor:D
+ \__kernel_primitive:NN \spaceskip \tex_spaceskip:D
+ \__kernel_primitive:NN \span \tex_span:D
+ \__kernel_primitive:NN \special \tex_special:D
+ \__kernel_primitive:NN \splitbotmark \tex_splitbotmark:D
+ \__kernel_primitive:NN \splitfirstmark \tex_splitfirstmark:D
+ \__kernel_primitive:NN \splitmaxdepth \tex_splitmaxdepth:D
+ \__kernel_primitive:NN \splittopskip \tex_splittopskip:D
+ \__kernel_primitive:NN \string \tex_string:D
+ \__kernel_primitive:NN \tabskip \tex_tabskip:D
+ \__kernel_primitive:NN \textfont \tex_textfont:D
+ \__kernel_primitive:NN \textstyle \tex_textstyle:D
+ \__kernel_primitive:NN \the \tex_the:D
+ \__kernel_primitive:NN \thickmuskip \tex_thickmuskip:D
+ \__kernel_primitive:NN \thinmuskip \tex_thinmuskip:D
+ \__kernel_primitive:NN \time \tex_time:D
+ \__kernel_primitive:NN \toks \tex_toks:D
+ \__kernel_primitive:NN \toksdef \tex_toksdef:D
+ \__kernel_primitive:NN \tolerance \tex_tolerance:D
+ \__kernel_primitive:NN \topmark \tex_topmark:D
+ \__kernel_primitive:NN \topskip \tex_topskip:D
+ \__kernel_primitive:NN \tracingcommands \tex_tracingcommands:D
+ \__kernel_primitive:NN \tracinglostchars \tex_tracinglostchars:D
+ \__kernel_primitive:NN \tracingmacros \tex_tracingmacros:D
+ \__kernel_primitive:NN \tracingonline \tex_tracingonline:D
+ \__kernel_primitive:NN \tracingoutput \tex_tracingoutput:D
+ \__kernel_primitive:NN \tracingpages \tex_tracingpages:D
+ \__kernel_primitive:NN \tracingparagraphs \tex_tracingparagraphs:D
+ \__kernel_primitive:NN \tracingrestores \tex_tracingrestores:D
+ \__kernel_primitive:NN \tracingstats \tex_tracingstats:D
+ \__kernel_primitive:NN \uccode \tex_uccode:D
+ \__kernel_primitive:NN \uchyph \tex_uchyph:D
+ \__kernel_primitive:NN \underline \tex_underline:D
+ \__kernel_primitive:NN \unhbox \tex_unhbox:D
+ \__kernel_primitive:NN \unhcopy \tex_unhcopy:D
+ \__kernel_primitive:NN \unkern \tex_unkern:D
+ \__kernel_primitive:NN \unpenalty \tex_unpenalty:D
+ \__kernel_primitive:NN \unskip \tex_unskip:D
+ \__kernel_primitive:NN \unvbox \tex_unvbox:D
+ \__kernel_primitive:NN \unvcopy \tex_unvcopy:D
+ \__kernel_primitive:NN \uppercase \tex_uppercase:D
+ \__kernel_primitive:NN \vadjust \tex_vadjust:D
+ \__kernel_primitive:NN \valign \tex_valign:D
+ \__kernel_primitive:NN \vbadness \tex_vbadness:D
+ \__kernel_primitive:NN \vbox \tex_vbox:D
+ \__kernel_primitive:NN \vcenter \tex_vcenter:D
+ \__kernel_primitive:NN \vfil \tex_vfil:D
+ \__kernel_primitive:NN \vfill \tex_vfill:D
+ \__kernel_primitive:NN \vfilneg \tex_vfilneg:D
+ \__kernel_primitive:NN \vfuzz \tex_vfuzz:D
+ \__kernel_primitive:NN \voffset \tex_voffset:D
+ \__kernel_primitive:NN \vrule \tex_vrule:D
+ \__kernel_primitive:NN \vsize \tex_vsize:D
+ \__kernel_primitive:NN \vskip \tex_vskip:D
+ \__kernel_primitive:NN \vsplit \tex_vsplit:D
+ \__kernel_primitive:NN \vss \tex_vss:D
+ \__kernel_primitive:NN \vtop \tex_vtop:D
+ \__kernel_primitive:NN \wd \tex_wd:D
+ \__kernel_primitive:NN \widowpenalty \tex_widowpenalty:D
+ \__kernel_primitive:NN \write \tex_write:D
+ \__kernel_primitive:NN \xdef \tex_xdef:D
+ \__kernel_primitive:NN \xleaders \tex_xleaders:D
+ \__kernel_primitive:NN \xspaceskip \tex_xspaceskip:D
+ \__kernel_primitive:NN \year \tex_year:D
+ \__kernel_primitive:NN \beginL \tex_beginL:D
+ \__kernel_primitive:NN \beginR \tex_beginR:D
+ \__kernel_primitive:NN \botmarks \tex_botmarks:D
+ \__kernel_primitive:NN \clubpenalties \tex_clubpenalties:D
+ \__kernel_primitive:NN \currentgrouplevel \tex_currentgrouplevel:D
+ \__kernel_primitive:NN \currentgrouptype \tex_currentgrouptype:D
+ \__kernel_primitive:NN \currentifbranch \tex_currentifbranch:D
+ \__kernel_primitive:NN \currentiflevel \tex_currentiflevel:D
+ \__kernel_primitive:NN \currentiftype \tex_currentiftype:D
+ \__kernel_primitive:NN \detokenize \tex_detokenize:D
+ \__kernel_primitive:NN \dimexpr \tex_dimexpr:D
+ \__kernel_primitive:NN \displaywidowpenalties \tex_displaywidowpenalties:D
+ \__kernel_primitive:NN \endL \tex_endL:D
+ \__kernel_primitive:NN \endR \tex_endR:D
+ \__kernel_primitive:NN \eTeXrevision \tex_eTeXrevision:D
+ \__kernel_primitive:NN \eTeXversion \tex_eTeXversion:D
+ \__kernel_primitive:NN \everyeof \tex_everyeof:D
+ \__kernel_primitive:NN \firstmarks \tex_firstmarks:D
+ \__kernel_primitive:NN \fontchardp \tex_fontchardp:D
+ \__kernel_primitive:NN \fontcharht \tex_fontcharht:D
+ \__kernel_primitive:NN \fontcharic \tex_fontcharic:D
+ \__kernel_primitive:NN \fontcharwd \tex_fontcharwd:D
+ \__kernel_primitive:NN \glueexpr \tex_glueexpr:D
+ \__kernel_primitive:NN \glueshrink \tex_glueshrink:D
+ \__kernel_primitive:NN \glueshrinkorder \tex_glueshrinkorder:D
+ \__kernel_primitive:NN \gluestretch \tex_gluestretch:D
+ \__kernel_primitive:NN \gluestretchorder \tex_gluestretchorder:D
+ \__kernel_primitive:NN \gluetomu \tex_gluetomu:D
+ \__kernel_primitive:NN \ifcsname \tex_ifcsname:D
+ \__kernel_primitive:NN \ifdefined \tex_ifdefined:D
+ \__kernel_primitive:NN \iffontchar \tex_iffontchar:D
+ \__kernel_primitive:NN \interactionmode \tex_interactionmode:D
+ \__kernel_primitive:NN \interlinepenalties \tex_interlinepenalties:D
+ \__kernel_primitive:NN \lastlinefit \tex_lastlinefit:D
+ \__kernel_primitive:NN \lastnodetype \tex_lastnodetype:D
+ \__kernel_primitive:NN \marks \tex_marks:D
+ \__kernel_primitive:NN \middle \tex_middle:D
+ \__kernel_primitive:NN \muexpr \tex_muexpr:D
+ \__kernel_primitive:NN \mutoglue \tex_mutoglue:D
+ \__kernel_primitive:NN \numexpr \tex_numexpr:D
+ \__kernel_primitive:NN \pagediscards \tex_pagediscards:D
+ \__kernel_primitive:NN \parshapedimen \tex_parshapedimen:D
+ \__kernel_primitive:NN \parshapeindent \tex_parshapeindent:D
+ \__kernel_primitive:NN \parshapelength \tex_parshapelength:D
+ \__kernel_primitive:NN \predisplaydirection \tex_predisplaydirection:D
+ \__kernel_primitive:NN \protected \tex_protected:D
+ \__kernel_primitive:NN \readline \tex_readline:D
+ \__kernel_primitive:NN \savinghyphcodes \tex_savinghyphcodes:D
+ \__kernel_primitive:NN \savingvdiscards \tex_savingvdiscards:D
+ \__kernel_primitive:NN \scantokens \tex_scantokens:D
+ \__kernel_primitive:NN \showgroups \tex_showgroups:D
+ \__kernel_primitive:NN \showifs \tex_showifs:D
+ \__kernel_primitive:NN \showtokens \tex_showtokens:D
+ \__kernel_primitive:NN \splitbotmarks \tex_splitbotmarks:D
+ \__kernel_primitive:NN \splitdiscards \tex_splitdiscards:D
+ \__kernel_primitive:NN \splitfirstmarks \tex_splitfirstmarks:D
+ \__kernel_primitive:NN \TeXXeTstate \tex_TeXXeTstate:D
+ \__kernel_primitive:NN \topmarks \tex_topmarks:D
+ \__kernel_primitive:NN \tracingassigns \tex_tracingassigns:D
+ \__kernel_primitive:NN \tracinggroups \tex_tracinggroups:D
+ \__kernel_primitive:NN \tracingifs \tex_tracingifs:D
+ \__kernel_primitive:NN \tracingnesting \tex_tracingnesting:D
+ \__kernel_primitive:NN \tracingscantokens \tex_tracingscantokens:D
+ \__kernel_primitive:NN \unexpanded \tex_unexpanded:D
+ \__kernel_primitive:NN \unless \tex_unless:D
+ \__kernel_primitive:NN \widowpenalties \tex_widowpenalties:D
+ \__kernel_primitive:NN \pdfannot \tex_pdfannot:D
+ \__kernel_primitive:NN \pdfcatalog \tex_pdfcatalog:D
+ \__kernel_primitive:NN \pdfcompresslevel \tex_pdfcompresslevel:D
+ \__kernel_primitive:NN \pdfcolorstack \tex_pdfcolorstack:D
+ \__kernel_primitive:NN \pdfcolorstackinit \tex_pdfcolorstackinit:D
+ \__kernel_primitive:NN \pdfdecimaldigits \tex_pdfdecimaldigits:D
+ \__kernel_primitive:NN \pdfdest \tex_pdfdest:D
+ \__kernel_primitive:NN \pdfdestmargin \tex_pdfdestmargin:D
+ \__kernel_primitive:NN \pdfendlink \tex_pdfendlink:D
+ \__kernel_primitive:NN \pdfendthread \tex_pdfendthread:D
+ \__kernel_primitive:NN \pdffakespace \tex_pdffakespace:D
+ \__kernel_primitive:NN \pdffontattr \tex_pdffontattr:D
+ \__kernel_primitive:NN \pdffontname \tex_pdffontname:D
+ \__kernel_primitive:NN \pdffontobjnum \tex_pdffontobjnum:D
+ \__kernel_primitive:NN \pdfgamma \tex_pdfgamma:D
+ \__kernel_primitive:NN \pdfgentounicode \tex_pdfgentounicode:D
+ \__kernel_primitive:NN \pdfglyphtounicode \tex_pdfglyphtounicode:D
+ \__kernel_primitive:NN \pdfhorigin \tex_pdfhorigin:D
+ \__kernel_primitive:NN \pdfimageapplygamma \tex_pdfimageapplygamma:D
+ \__kernel_primitive:NN \pdfimagegamma \tex_pdfimagegamma:D
+ \__kernel_primitive:NN \pdfimagehicolor \tex_pdfimagehicolor:D
+ \__kernel_primitive:NN \pdfimageresolution \tex_pdfimageresolution:D
+ \__kernel_primitive:NN \pdfincludechars \tex_pdfincludechars:D
+ \__kernel_primitive:NN \pdfinclusioncopyfonts \tex_pdfinclusioncopyfonts:D
+ \__kernel_primitive:NN \pdfinclusionerrorlevel
+ \tex_pdfinclusionerrorlevel:D
+ \__kernel_primitive:NN \pdfinfo \tex_pdfinfo:D
+ \__kernel_primitive:NN \pdfinfoomitdate \tex_pdfinfoomitdate:D
+ \__kernel_primitive:NN \pdfinterwordspaceoff \tex_pdfinterwordspaceoff:D
+ \__kernel_primitive:NN \pdfinterwordspaceon \tex_pdfinterwordspaceon:D
+ \__kernel_primitive:NN \pdflastannot \tex_pdflastannot:D
+ \__kernel_primitive:NN \pdflastlink \tex_pdflastlink:D
+ \__kernel_primitive:NN \pdflastobj \tex_pdflastobj:D
+ \__kernel_primitive:NN \pdflastxform \tex_pdflastxform:D
+ \__kernel_primitive:NN \pdflastximage \tex_pdflastximage:D
+ \__kernel_primitive:NN \pdflastximagecolordepth
+ \tex_pdflastximagecolordepth:D
+ \__kernel_primitive:NN \pdflastximagepages \tex_pdflastximagepages:D
+ \__kernel_primitive:NN \pdflinkmargin \tex_pdflinkmargin:D
+ \__kernel_primitive:NN \pdfliteral \tex_pdfliteral:D
+ \__kernel_primitive:NN \pdfmapfile \tex_pdfmapfile:D
+ \__kernel_primitive:NN \pdfmapline \tex_pdfmapline:D
+ \__kernel_primitive:NN \pdfmajorversion \tex_pdfmajorversion:D
+ \__kernel_primitive:NN \pdfminorversion \tex_pdfminorversion:D
+ \__kernel_primitive:NN \pdfnames \tex_pdfnames:D
+ \__kernel_primitive:NN \pdfnobuiltintounicode \tex_pdfnobuiltintounicode:D
+ \__kernel_primitive:NN \pdfobj \tex_pdfobj:D
+ \__kernel_primitive:NN \pdfobjcompresslevel \tex_pdfobjcompresslevel:D
+ \__kernel_primitive:NN \pdfomitcharset \tex_pdfomitcharset:D
+ \__kernel_primitive:NN \pdfoutline \tex_pdfoutline:D
+ \__kernel_primitive:NN \pdfoutput \tex_pdfoutput:D
+ \__kernel_primitive:NN \pdfpageattr \tex_pdfpageattr:D
+ \__kernel_primitive:NN \pdfpagesattr \tex_pdfpagesattr:D
+ \__kernel_primitive:NN \pdfpagebox \tex_pdfpagebox:D
+ \__kernel_primitive:NN \pdfpageref \tex_pdfpageref:D
+ \__kernel_primitive:NN \pdfpageresources \tex_pdfpageresources:D
+ \__kernel_primitive:NN \pdfpagesattr \tex_pdfpagesattr:D
+ \__kernel_primitive:NN \pdfrefobj \tex_pdfrefobj:D
+ \__kernel_primitive:NN \pdfrefxform \tex_pdfrefxform:D
+ \__kernel_primitive:NN \pdfrefximage \tex_pdfrefximage:D
+ \__kernel_primitive:NN \pdfrestore \tex_pdfrestore:D
+ \__kernel_primitive:NN \pdfretval \tex_pdfretval:D
+ \__kernel_primitive:NN \pdfrunninglinkoff \tex_pdfrunninglinkoff:D
+ \__kernel_primitive:NN \pdfrunninglinkon \tex_pdfrunninglinkon:D
+ \__kernel_primitive:NN \pdfsave \tex_pdfsave:D
+ \__kernel_primitive:NN \pdfsetmatrix \tex_pdfsetmatrix:D
+ \__kernel_primitive:NN \pdfstartlink \tex_pdfstartlink:D
+ \__kernel_primitive:NN \pdfstartthread \tex_pdfstartthread:D
+ \__kernel_primitive:NN \pdfsuppressptexinfo \tex_pdfsuppressptexinfo:D
+ \__kernel_primitive:NN \pdfsuppresswarningdupdest
+ \tex_pdfsuppresswarningdupdest:D
+ \__kernel_primitive:NN \pdfsuppresswarningdupmap
+ \tex_pdfsuppresswarningdupmap:D
+ \__kernel_primitive:NN \pdfsuppresswarningpagegroup
+ \tex_pdfsuppresswarningpagegroup:D
+ \__kernel_primitive:NN \pdfthread \tex_pdfthread:D
+ \__kernel_primitive:NN \pdfthreadmargin \tex_pdfthreadmargin:D
+ \__kernel_primitive:NN \pdftrailer \tex_pdftrailer:D
+ \__kernel_primitive:NN \pdftrailerid \tex_pdftrailerid:D
+ \__kernel_primitive:NN \pdfuniqueresname \tex_pdfuniqueresname:D
+ \__kernel_primitive:NN \pdfvorigin \tex_pdfvorigin:D
+ \__kernel_primitive:NN \pdfxform \tex_pdfxform:D
+ \__kernel_primitive:NN \pdfxformname \tex_pdfxformname:D
+ \__kernel_primitive:NN \pdfximage \tex_pdfximage:D
+ \__kernel_primitive:NN \pdfximagebbox \tex_pdfximagebbox:D
+ \__kernel_primitive:NN \ifpdfabsdim \tex_ifabsdim:D
+ \__kernel_primitive:NN \ifpdfabsnum \tex_ifabsnum:D
+ \__kernel_primitive:NN \ifpdfprimitive \tex_ifprimitive:D
+ \__kernel_primitive:NN \pdfadjustinterwordglue
+ \tex_adjustinterwordglue:D
+ \__kernel_primitive:NN \pdfadjustspacing \tex_adjustspacing:D
+ \__kernel_primitive:NN \pdfappendkern \tex_appendkern:D
+ \__kernel_primitive:NN \pdfcopyfont \tex_copyfont:D
+ \__kernel_primitive:NN \pdfcreationdate \tex_creationdate:D
+ \__kernel_primitive:NN \pdfdraftmode \tex_draftmode:D
+ \__kernel_primitive:NN \pdfeachlinedepth \tex_eachlinedepth:D
+ \__kernel_primitive:NN \pdfeachlineheight \tex_eachlineheight:D
+ \__kernel_primitive:NN \pdfelapsedtime \tex_elapsedtime:D
+ \__kernel_primitive:NN \pdfescapehex \tex_escapehex:D
+ \__kernel_primitive:NN \pdfescapename \tex_escapename:D
+ \__kernel_primitive:NN \pdfescapestring \tex_escapestring:D
+ \__kernel_primitive:NN \pdffirstlineheight \tex_firstlineheight:D
+ \__kernel_primitive:NN \pdffontexpand \tex_fontexpand:D
+ \__kernel_primitive:NN \pdffontsize \tex_fontsize:D
+ \__kernel_primitive:NN \pdfignoreddimen \tex_ignoreddimen:D
+ \__kernel_primitive:NN \pdfinsertht \tex_insertht:D
+ \__kernel_primitive:NN \pdflastlinedepth \tex_lastlinedepth:D
+ \__kernel_primitive:NN \pdflastmatch \tex_lastmatch:D
+ \__kernel_primitive:NN \pdflastxpos \tex_lastxpos:D
+ \__kernel_primitive:NN \pdflastypos \tex_lastypos:D
+ \__kernel_primitive:NN \pdfmatch \tex_match:D
+ \__kernel_primitive:NN \pdfnoligatures \tex_noligatures:D
+ \__kernel_primitive:NN \pdfnormaldeviate \tex_normaldeviate:D
+ \__kernel_primitive:NN \pdfpageheight \tex_pageheight:D
+ \__kernel_primitive:NN \pdfpagewidth \tex_pagewidth:D
+ \__kernel_primitive:NN \pdfpkmode \tex_pkmode:D
+ \__kernel_primitive:NN \pdfpkresolution \tex_pkresolution:D
+ \__kernel_primitive:NN \pdfprimitive \tex_primitive:D
+ \__kernel_primitive:NN \pdfprependkern \tex_prependkern:D
+ \__kernel_primitive:NN \pdfprotrudechars \tex_protrudechars:D
+ \__kernel_primitive:NN \pdfpxdimen \tex_pxdimen:D
+ \__kernel_primitive:NN \pdfrandomseed \tex_randomseed:D
+ \__kernel_primitive:NN \pdfresettimer \tex_resettimer:D
+ \__kernel_primitive:NN \pdfsavepos \tex_savepos:D
+ \__kernel_primitive:NN \pdfsetrandomseed \tex_setrandomseed:D
+ \__kernel_primitive:NN \pdfshellescape \tex_shellescape:D
+ \__kernel_primitive:NN \pdftracingfonts \tex_tracingfonts:D
+ \__kernel_primitive:NN \pdfunescapehex \tex_unescapehex:D
+ \__kernel_primitive:NN \pdfuniformdeviate \tex_uniformdeviate:D
+ \__kernel_primitive:NN \pdftexbanner \tex_pdftexbanner:D
+ \__kernel_primitive:NN \pdftexrevision \tex_pdftexrevision:D
+ \__kernel_primitive:NN \pdftexversion \tex_pdftexversion:D
+ \__kernel_primitive:NN \efcode \tex_efcode:D
+ \__kernel_primitive:NN \ifincsname \tex_ifincsname:D
+ \__kernel_primitive:NN \knaccode \tex_knaccode:D
+ \__kernel_primitive:NN \knbccode \tex_knbccode:D
+ \__kernel_primitive:NN \knbscode \tex_knbscode:D
+ \__kernel_primitive:NN \leftmarginkern \tex_leftmarginkern:D
+ \__kernel_primitive:NN \letterspacefont \tex_letterspacefont:D
+ \__kernel_primitive:NN \lpcode \tex_lpcode:D
+ \__kernel_primitive:NN \quitvmode \tex_quitvmode:D
+ \__kernel_primitive:NN \rightmarginkern \tex_rightmarginkern:D
+ \__kernel_primitive:NN \rpcode \tex_rpcode:D
+ \__kernel_primitive:NN \shbscode \tex_shbscode:D
+ \__kernel_primitive:NN \stbscode \tex_stbscode:D
+ \__kernel_primitive:NN \synctex \tex_synctex:D
+ \__kernel_primitive:NN \tagcode \tex_tagcode:D
+ \tex_long:D \tex_def:D \use_ii:nn #1#2 {#2}
+ \tex_long:D \tex_def:D \use_none:n #1 { }
+ \tex_long:D \tex_def:D \__kernel_primitive:NN #1#2
+ {
+ \tex_ifdefined:D #1
+ \tex_expandafter:D \use_ii:nn
+ \tex_fi:D
+ \use_none:n { \tex_global:D \tex_let:D #2 #1 }
+ }
+ \__kernel_primitive:NN \pdfstrcmp \tex_strcmp:D
+ \__kernel_primitive:NN \pdffilesize \tex_filesize:D
+ \__kernel_primitive:NN \pdfmdfivesum \tex_mdfivesum:D
+ \__kernel_primitive:NN \pdffilemoddate \tex_filemoddate:D
+ \__kernel_primitive:NN \pdffiledump \tex_filedump:D
+ \__kernel_primitive:NN \suppressfontnotfounderror
+ \tex_suppressfontnotfounderror:D
+ \__kernel_primitive:NN \XeTeXcharclass \tex_XeTeXcharclass:D
+ \__kernel_primitive:NN \XeTeXcharglyph \tex_XeTeXcharglyph:D
+ \__kernel_primitive:NN \XeTeXcountfeatures \tex_XeTeXcountfeatures:D
+ \__kernel_primitive:NN \XeTeXcountglyphs \tex_XeTeXcountglyphs:D
+ \__kernel_primitive:NN \XeTeXcountselectors \tex_XeTeXcountselectors:D
+ \__kernel_primitive:NN \XeTeXcountvariations \tex_XeTeXcountvariations:D
+ \__kernel_primitive:NN \XeTeXdefaultencoding \tex_XeTeXdefaultencoding:D
+ \__kernel_primitive:NN \XeTeXdashbreakstate \tex_XeTeXdashbreakstate:D
+ \__kernel_primitive:NN \XeTeXfeaturecode \tex_XeTeXfeaturecode:D
+ \__kernel_primitive:NN \XeTeXfeaturename \tex_XeTeXfeaturename:D
+ \__kernel_primitive:NN \XeTeXfindfeaturebyname
+ \tex_XeTeXfindfeaturebyname:D
+ \__kernel_primitive:NN \XeTeXfindselectorbyname
+ \tex_XeTeXfindselectorbyname:D
+ \__kernel_primitive:NN \XeTeXfindvariationbyname
+ \tex_XeTeXfindvariationbyname:D
+ \__kernel_primitive:NN \XeTeXfirstfontchar \tex_XeTeXfirstfontchar:D
+ \__kernel_primitive:NN \XeTeXfonttype \tex_XeTeXfonttype:D
+ \__kernel_primitive:NN \XeTeXgenerateactualtext
+ \tex_XeTeXgenerateactualtext:D
+ \__kernel_primitive:NN \XeTeXglyph \tex_XeTeXglyph:D
+ \__kernel_primitive:NN \XeTeXglyphbounds \tex_XeTeXglyphbounds:D
+ \__kernel_primitive:NN \XeTeXglyphindex \tex_XeTeXglyphindex:D
+ \__kernel_primitive:NN \XeTeXglyphname \tex_XeTeXglyphname:D
+ \__kernel_primitive:NN \XeTeXinputencoding \tex_XeTeXinputencoding:D
+ \__kernel_primitive:NN \XeTeXinputnormalization
+ \tex_XeTeXinputnormalization:D
+ \__kernel_primitive:NN \XeTeXinterchartokenstate
+ \tex_XeTeXinterchartokenstate:D
+ \__kernel_primitive:NN \XeTeXinterchartoks \tex_XeTeXinterchartoks:D
+ \__kernel_primitive:NN \XeTeXisdefaultselector
+ \tex_XeTeXisdefaultselector:D
+ \__kernel_primitive:NN \XeTeXisexclusivefeature
+ \tex_XeTeXisexclusivefeature:D
+ \__kernel_primitive:NN \XeTeXlastfontchar \tex_XeTeXlastfontchar:D
+ \__kernel_primitive:NN \XeTeXlinebreakskip \tex_XeTeXlinebreakskip:D
+ \__kernel_primitive:NN \XeTeXlinebreaklocale \tex_XeTeXlinebreaklocale:D
+ \__kernel_primitive:NN \XeTeXlinebreakpenalty \tex_XeTeXlinebreakpenalty:D
+ \__kernel_primitive:NN \XeTeXOTcountfeatures \tex_XeTeXOTcountfeatures:D
+ \__kernel_primitive:NN \XeTeXOTcountlanguages \tex_XeTeXOTcountlanguages:D
+ \__kernel_primitive:NN \XeTeXOTcountscripts \tex_XeTeXOTcountscripts:D
+ \__kernel_primitive:NN \XeTeXOTfeaturetag \tex_XeTeXOTfeaturetag:D
+ \__kernel_primitive:NN \XeTeXOTlanguagetag \tex_XeTeXOTlanguagetag:D
+ \__kernel_primitive:NN \XeTeXOTscripttag \tex_XeTeXOTscripttag:D
+ \__kernel_primitive:NN \XeTeXpdffile \tex_XeTeXpdffile:D
+ \__kernel_primitive:NN \XeTeXpdfpagecount \tex_XeTeXpdfpagecount:D
+ \__kernel_primitive:NN \XeTeXpicfile \tex_XeTeXpicfile:D
+ \__kernel_primitive:NN \XeTeXrevision \tex_XeTeXrevision:D
+ \__kernel_primitive:NN \XeTeXselectorname \tex_XeTeXselectorname:D
+ \__kernel_primitive:NN \XeTeXtracingfonts \tex_XeTeXtracingfonts:D
+ \__kernel_primitive:NN \XeTeXupwardsmode \tex_XeTeXupwardsmode:D
+ \__kernel_primitive:NN \XeTeXuseglyphmetrics \tex_XeTeXuseglyphmetrics:D
+ \__kernel_primitive:NN \XeTeXvariation \tex_XeTeXvariation:D
+ \__kernel_primitive:NN \XeTeXvariationdefault \tex_XeTeXvariationdefault:D
+ \__kernel_primitive:NN \XeTeXvariationmax \tex_XeTeXvariationmax:D
+ \__kernel_primitive:NN \XeTeXvariationmin \tex_XeTeXvariationmin:D
+ \__kernel_primitive:NN \XeTeXvariationname \tex_XeTeXvariationname:D
+ \__kernel_primitive:NN \XeTeXversion \tex_XeTeXversion:D
+ \__kernel_primitive:NN \XeTeXselectorcode \tex_XeTeXselectorcode:D
+ \__kernel_primitive:NN \XeTeXinterwordspaceshaping
+ \tex_XeTeXinterwordspaceshaping:D
+ \__kernel_primitive:NN \XeTeXhyphenatablelength
+ \tex_XeTeXhyphenatablelength:D
+ \__kernel_primitive:NN \creationdate \tex_creationdate:D
+ \__kernel_primitive:NN \elapsedtime \tex_elapsedtime:D
+ \__kernel_primitive:NN \filedump \tex_filedump:D
+ \__kernel_primitive:NN \filemoddate \tex_filemoddate:D
+ \__kernel_primitive:NN \filesize \tex_filesize:D
+ \__kernel_primitive:NN \mdfivesum \tex_mdfivesum:D
+ \__kernel_primitive:NN \ifprimitive \tex_ifprimitive:D
+ \__kernel_primitive:NN \primitive \tex_primitive:D
+ \__kernel_primitive:NN \resettimer \tex_resettimer:D
+ \__kernel_primitive:NN \shellescape \tex_shellescape:D
+ \__kernel_primitive:NN \XeTeXprotrudechars \tex_protrudechars:D
+ \__kernel_primitive:NN \alignmark \tex_alignmark:D
+ \__kernel_primitive:NN \aligntab \tex_aligntab:D
+ \__kernel_primitive:NN \attribute \tex_attribute:D
+ \__kernel_primitive:NN \attributedef \tex_attributedef:D
+ \__kernel_primitive:NN \automaticdiscretionary
+ \tex_automaticdiscretionary:D
+ \__kernel_primitive:NN \automatichyphenmode \tex_automatichyphenmode:D
+ \__kernel_primitive:NN \automatichyphenpenalty
+ \tex_automatichyphenpenalty:D
+ \__kernel_primitive:NN \begincsname \tex_begincsname:D
+ \__kernel_primitive:NN \bodydir \tex_bodydir:D
+ \__kernel_primitive:NN \bodydirection \tex_bodydirection:D
+ \__kernel_primitive:NN \boundary \tex_boundary:D
+ \__kernel_primitive:NN \boxdir \tex_boxdir:D
+ \__kernel_primitive:NN \boxdirection \tex_boxdirection:D
+ \__kernel_primitive:NN \breakafterdirmode \tex_breakafterdirmode:D
+ \__kernel_primitive:NN \catcodetable \tex_catcodetable:D
+ \__kernel_primitive:NN \clearmarks \tex_clearmarks:D
+ \__kernel_primitive:NN \crampeddisplaystyle \tex_crampeddisplaystyle:D
+ \__kernel_primitive:NN \crampedscriptscriptstyle
+ \tex_crampedscriptscriptstyle:D
+ \__kernel_primitive:NN \crampedscriptstyle \tex_crampedscriptstyle:D
+ \__kernel_primitive:NN \crampedtextstyle \tex_crampedtextstyle:D
+ \__kernel_primitive:NN \csstring \tex_csstring:D
+ \__kernel_primitive:NN \deferred \tex_deferred:D
+ \__kernel_primitive:NN \discretionaryligaturemode
+ \tex_discretionaryligaturemode:D
+ \__kernel_primitive:NN \directlua \tex_directlua:D
+ \__kernel_primitive:NN \dviextension \tex_dviextension:D
+ \__kernel_primitive:NN \dvifeedback \tex_dvifeedback:D
+ \__kernel_primitive:NN \dvivariable \tex_dvivariable:D
+ \__kernel_primitive:NN \eTeXglueshrinkorder \tex_eTeXglueshrinkorder:D
+ \__kernel_primitive:NN \eTeXgluestretchorder \tex_eTeXgluestretchorder:D
+ \__kernel_primitive:NN \endlocalcontrol \tex_endlocalcontrol:D
+ \__kernel_primitive:NN \etoksapp \tex_etoksapp:D
+ \__kernel_primitive:NN \etokspre \tex_etokspre:D
+ \__kernel_primitive:NN \exceptionpenalty \tex_exceptionpenalty:D
+ \__kernel_primitive:NN \exhyphenchar \tex_exhyphenchar:D
+ \__kernel_primitive:NN \explicithyphenpenalty \tex_explicithyphenpenalty:D
+ \__kernel_primitive:NN \expanded \tex_expanded:D
+ \__kernel_primitive:NN \explicitdiscretionary \tex_explicitdiscretionary:D
+ \__kernel_primitive:NN \firstvalidlanguage \tex_firstvalidlanguage:D
+ \__kernel_primitive:NN \fontid \tex_fontid:D
+ \__kernel_primitive:NN \formatname \tex_formatname:D
+ \__kernel_primitive:NN \hjcode \tex_hjcode:D
+ \__kernel_primitive:NN \hpack \tex_hpack:D
+ \__kernel_primitive:NN \hyphenationbounds \tex_hyphenationbounds:D
+ \__kernel_primitive:NN \hyphenationmin \tex_hyphenationmin:D
+ \__kernel_primitive:NN \hyphenpenaltymode \tex_hyphenpenaltymode:D
+ \__kernel_primitive:NN \gleaders \tex_gleaders:D
+ \__kernel_primitive:NN \glet \tex_glet:D
+ \__kernel_primitive:NN \glyphdimensionsmode \tex_glyphdimensionsmode:D
+ \__kernel_primitive:NN \gtoksapp \tex_gtoksapp:D
+ \__kernel_primitive:NN \gtokspre \tex_gtokspre:D
+ \__kernel_primitive:NN \ifcondition \tex_ifcondition:D
+ \__kernel_primitive:NN \immediateassigned \tex_immediateassigned:D
+ \__kernel_primitive:NN \immediateassignment \tex_immediateassignment:D
+ \__kernel_primitive:NN \initcatcodetable \tex_initcatcodetable:D
+ \__kernel_primitive:NN \lastnamedcs \tex_lastnamedcs:D
+ \__kernel_primitive:NN \latelua \tex_latelua:D
+ \__kernel_primitive:NN \lateluafunction \tex_lateluafunction:D
+ \__kernel_primitive:NN \leftghost \tex_leftghost:D
+ \__kernel_primitive:NN \letcharcode \tex_letcharcode:D
+ \__kernel_primitive:NN \linedir \tex_linedir:D
+ \__kernel_primitive:NN \linedirection \tex_linedirection:D
+ \__kernel_primitive:NN \localbrokenpenalty \tex_localbrokenpenalty:D
+ \__kernel_primitive:NN \localinterlinepenalty \tex_localinterlinepenalty:D
+ \__kernel_primitive:NN \luabytecode \tex_luabytecode:D
+ \__kernel_primitive:NN \luabytecodecall \tex_luabytecodecall:D
+ \__kernel_primitive:NN \luacopyinputnodes \tex_luacopyinputnodes:D
+ \__kernel_primitive:NN \luadef \tex_luadef:D
+ \__kernel_primitive:NN \localleftbox \tex_localleftbox:D
+ \__kernel_primitive:NN \localrightbox \tex_localrightbox:D
+ \__kernel_primitive:NN \luaescapestring \tex_luaescapestring:D
+ \__kernel_primitive:NN \luafunction \tex_luafunction:D
+ \__kernel_primitive:NN \luafunctioncall \tex_luafunctioncall:D
+ \__kernel_primitive:NN \luatexbanner \tex_luatexbanner:D
+ \__kernel_primitive:NN \luatexrevision \tex_luatexrevision:D
+ \__kernel_primitive:NN \luatexversion \tex_luatexversion:D
+ \__kernel_primitive:NN \mathdefaultsmode \tex_mathdefaultsmode:D
+ \__kernel_primitive:NN \mathdelimitersmode \tex_mathdelimitersmode:D
+ \__kernel_primitive:NN \mathdir \tex_mathdir:D
+ \__kernel_primitive:NN \mathdirection \tex_mathdirection:D
+ \__kernel_primitive:NN \mathdisplayskipmode \tex_mathdisplayskipmode:D
+ \__kernel_primitive:NN \matheqdirmode \tex_matheqdirmode:D
+ \__kernel_primitive:NN \matheqnogapstep \tex_matheqnogapstep:D
+ \__kernel_primitive:NN \mathflattenmode \tex_mathflattenmode:D
+ \__kernel_primitive:NN \mathitalicsmode \tex_mathitalicsmode:D
+ \__kernel_primitive:NN \mathnolimitsmode \tex_mathnolimitsmode:D
+ \__kernel_primitive:NN \mathoption \tex_mathoption:D
+ \__kernel_primitive:NN \mathpenaltiesmode \tex_mathpenaltiesmode:D
+ \__kernel_primitive:NN \mathrulesfam \tex_mathrulesfam:D
+ \__kernel_primitive:NN \mathscriptsmode \tex_mathscriptsmode:D
+ \__kernel_primitive:NN \mathscriptboxmode \tex_mathscriptboxmode:D
+ \__kernel_primitive:NN \mathscriptcharmode \tex_mathscriptcharmode:D
+ \__kernel_primitive:NN \mathstyle \tex_mathstyle:D
+ \__kernel_primitive:NN \mathsurroundmode \tex_mathsurroundmode:D
+ \__kernel_primitive:NN \mathsurroundskip \tex_mathsurroundskip:D
+ \__kernel_primitive:NN \nohrule \tex_nohrule:D
+ \__kernel_primitive:NN \nokerns \tex_nokerns:D
+ \__kernel_primitive:NN \noligs \tex_noligs:D
+ \__kernel_primitive:NN \nospaces \tex_nospaces:D
+ \__kernel_primitive:NN \novrule \tex_novrule:D
+ \__kernel_primitive:NN \outputbox \tex_outputbox:D
+ \__kernel_primitive:NN \pagebottomoffset \tex_pagebottomoffset:D
+ \__kernel_primitive:NN \pagedir \tex_pagedir:D
+ \__kernel_primitive:NN \pagedirection \tex_pagedirection:D
+ \__kernel_primitive:NN \pageleftoffset \tex_pageleftoffset:D
+ \__kernel_primitive:NN \pagerightoffset \tex_pagerightoffset:D
+ \__kernel_primitive:NN \pagetopoffset \tex_pagetopoffset:D
+ \__kernel_primitive:NN \pardir \tex_pardir:D
+ \__kernel_primitive:NN \pardirection \tex_pardirection:D
+ \__kernel_primitive:NN \pdfextension \tex_pdfextension:D
+ \__kernel_primitive:NN \pdffeedback \tex_pdffeedback:D
+ \__kernel_primitive:NN \pdfvariable \tex_pdfvariable:D
+ \__kernel_primitive:NN \postexhyphenchar \tex_postexhyphenchar:D
+ \__kernel_primitive:NN \posthyphenchar \tex_posthyphenchar:D
+ \__kernel_primitive:NN \prebinoppenalty \tex_prebinoppenalty:D
+ \__kernel_primitive:NN \predisplaygapfactor \tex_predisplaygapfactor:D
+ \__kernel_primitive:NN \preexhyphenchar \tex_preexhyphenchar:D
+ \__kernel_primitive:NN \prehyphenchar \tex_prehyphenchar:D
+ \__kernel_primitive:NN \prerelpenalty \tex_prerelpenalty:D
+ \__kernel_primitive:NN \protrusionboundary \tex_protrusionboundary:D
+ \__kernel_primitive:NN \rightghost \tex_rightghost:D
+ \__kernel_primitive:NN \savecatcodetable \tex_savecatcodetable:D
+ \__kernel_primitive:NN \scantextokens \tex_scantextokens:D
+ \__kernel_primitive:NN \setfontid \tex_setfontid:D
+ \__kernel_primitive:NN \shapemode \tex_shapemode:D
+ \__kernel_primitive:NN \suppressifcsnameerror \tex_suppressifcsnameerror:D
+ \__kernel_primitive:NN \suppresslongerror \tex_suppresslongerror:D
+ \__kernel_primitive:NN \suppressmathparerror \tex_suppressmathparerror:D
+ \__kernel_primitive:NN \suppressoutererror \tex_suppressoutererror:D
+ \__kernel_primitive:NN \suppressprimitiveerror
+ \tex_suppressprimitiveerror:D
+ \__kernel_primitive:NN \textdir \tex_textdir:D
+ \__kernel_primitive:NN \textdirection \tex_textdirection:D
+ \__kernel_primitive:NN \toksapp \tex_toksapp:D
+ \__kernel_primitive:NN \tokspre \tex_tokspre:D
+ \__kernel_primitive:NN \tpack \tex_tpack:D
+ \__kernel_primitive:NN \variablefam \tex_variablefam:D
+ \__kernel_primitive:NN \vpack \tex_vpack:D
+ \__kernel_primitive:NN \wordboundary \tex_wordboundary:D
+ \__kernel_primitive:NN \xtoksapp \tex_xtoksapp:D
+ \__kernel_primitive:NN \xtokspre \tex_xtokspre:D
+ \__kernel_primitive:NN \adjustspacing \tex_adjustspacing:D
+ \__kernel_primitive:NN \copyfont \tex_copyfont:D
+ \__kernel_primitive:NN \draftmode \tex_draftmode:D
+ \__kernel_primitive:NN \expandglyphsinfont \tex_fontexpand:D
+ \__kernel_primitive:NN \ifabsdim \tex_ifabsdim:D
+ \__kernel_primitive:NN \ifabsnum \tex_ifabsnum:D
+ \__kernel_primitive:NN \ignoreligaturesinfont \tex_ignoreligaturesinfont:D
+ \__kernel_primitive:NN \insertht \tex_insertht:D
+ \__kernel_primitive:NN \lastsavedboxresourceindex
+ \tex_pdflastxform:D
+ \__kernel_primitive:NN \lastsavedimageresourceindex
+ \tex_pdflastximage:D
+ \__kernel_primitive:NN \lastsavedimageresourcepages
+ \tex_pdflastximagepages:D
+ \__kernel_primitive:NN \lastxpos \tex_lastxpos:D
+ \__kernel_primitive:NN \lastypos \tex_lastypos:D
+ \__kernel_primitive:NN \normaldeviate \tex_normaldeviate:D
+ \__kernel_primitive:NN \outputmode \tex_pdfoutput:D
+ \__kernel_primitive:NN \pageheight \tex_pageheight:D
+ \__kernel_primitive:NN \pagewidth \tex_pagewidth:D
+ \__kernel_primitive:NN \protrudechars \tex_protrudechars:D
+ \__kernel_primitive:NN \pxdimen \tex_pxdimen:D
+ \__kernel_primitive:NN \randomseed \tex_randomseed:D
+ \__kernel_primitive:NN \useboxresource \tex_pdfrefxform:D
+ \__kernel_primitive:NN \useimageresource \tex_pdfrefximage:D
+ \__kernel_primitive:NN \savepos \tex_savepos:D
+ \__kernel_primitive:NN \saveboxresource \tex_pdfxform:D
+ \__kernel_primitive:NN \saveimageresource \tex_pdfximage:D
+ \__kernel_primitive:NN \setrandomseed \tex_setrandomseed:D
+ \__kernel_primitive:NN \tracingfonts \tex_tracingfonts:D
+ \__kernel_primitive:NN \uniformdeviate \tex_uniformdeviate:D
+ \__kernel_primitive:NN \Uchar \tex_Uchar:D
+ \__kernel_primitive:NN \Ucharcat \tex_Ucharcat:D
+ \__kernel_primitive:NN \Udelcode \tex_Udelcode:D
+ \__kernel_primitive:NN \Udelcodenum \tex_Udelcodenum:D
+ \__kernel_primitive:NN \Udelimiter \tex_Udelimiter:D
+ \__kernel_primitive:NN \Udelimiterover \tex_Udelimiterover:D
+ \__kernel_primitive:NN \Udelimiterunder \tex_Udelimiterunder:D
+ \__kernel_primitive:NN \Uhextensible \tex_Uhextensible:D
+ \__kernel_primitive:NN \Uleft \tex_Uleft:D
+ \__kernel_primitive:NN \Umathaccent \tex_Umathaccent:D
+ \__kernel_primitive:NN \Umathaxis \tex_Umathaxis:D
+ \__kernel_primitive:NN \Umathbinbinspacing \tex_Umathbinbinspacing:D
+ \__kernel_primitive:NN \Umathbinclosespacing \tex_Umathbinclosespacing:D
+ \__kernel_primitive:NN \Umathbininnerspacing \tex_Umathbininnerspacing:D
+ \__kernel_primitive:NN \Umathbinopenspacing \tex_Umathbinopenspacing:D
+ \__kernel_primitive:NN \Umathbinopspacing \tex_Umathbinopspacing:D
+ \__kernel_primitive:NN \Umathbinordspacing \tex_Umathbinordspacing:D
+ \__kernel_primitive:NN \Umathbinpunctspacing \tex_Umathbinpunctspacing:D
+ \__kernel_primitive:NN \Umathbinrelspacing \tex_Umathbinrelspacing:D
+ \__kernel_primitive:NN \Umathchar \tex_Umathchar:D
+ \__kernel_primitive:NN \Umathcharclass \tex_Umathcharclass:D
+ \__kernel_primitive:NN \Umathchardef \tex_Umathchardef:D
+ \__kernel_primitive:NN \Umathcharfam \tex_Umathcharfam:D
+ \__kernel_primitive:NN \Umathcharnum \tex_Umathcharnum:D
+ \__kernel_primitive:NN \Umathcharnumdef \tex_Umathcharnumdef:D
+ \__kernel_primitive:NN \Umathcharslot \tex_Umathcharslot:D
+ \__kernel_primitive:NN \Umathclosebinspacing \tex_Umathclosebinspacing:D
+ \__kernel_primitive:NN \Umathcloseclosespacing
+ \tex_Umathcloseclosespacing:D
+ \__kernel_primitive:NN \Umathcloseinnerspacing
+ \tex_Umathcloseinnerspacing:D
+ \__kernel_primitive:NN \Umathcloseopenspacing \tex_Umathcloseopenspacing:D
+ \__kernel_primitive:NN \Umathcloseopspacing \tex_Umathcloseopspacing:D
+ \__kernel_primitive:NN \Umathcloseordspacing \tex_Umathcloseordspacing:D
+ \__kernel_primitive:NN \Umathclosepunctspacing
+ \tex_Umathclosepunctspacing:D
+ \__kernel_primitive:NN \Umathcloserelspacing \tex_Umathcloserelspacing:D
+ \__kernel_primitive:NN \Umathcode \tex_Umathcode:D
+ \__kernel_primitive:NN \Umathcodenum \tex_Umathcodenum:D
+ \__kernel_primitive:NN \Umathconnectoroverlapmin
+ \tex_Umathconnectoroverlapmin:D
+ \__kernel_primitive:NN \Umathfractiondelsize \tex_Umathfractiondelsize:D
+ \__kernel_primitive:NN \Umathfractiondenomdown
+ \tex_Umathfractiondenomdown:D
+ \__kernel_primitive:NN \Umathfractiondenomvgap
+ \tex_Umathfractiondenomvgap:D
+ \__kernel_primitive:NN \Umathfractionnumup \tex_Umathfractionnumup:D
+ \__kernel_primitive:NN \Umathfractionnumvgap \tex_Umathfractionnumvgap:D
+ \__kernel_primitive:NN \Umathfractionrule \tex_Umathfractionrule:D
+ \__kernel_primitive:NN \Umathinnerbinspacing \tex_Umathinnerbinspacing:D
+ \__kernel_primitive:NN \Umathinnerclosespacing
+ \tex_Umathinnerclosespacing:D
+ \__kernel_primitive:NN \Umathinnerinnerspacing
+ \tex_Umathinnerinnerspacing:D
+ \__kernel_primitive:NN \Umathinneropenspacing \tex_Umathinneropenspacing:D
+ \__kernel_primitive:NN \Umathinneropspacing \tex_Umathinneropspacing:D
+ \__kernel_primitive:NN \Umathinnerordspacing \tex_Umathinnerordspacing:D
+ \__kernel_primitive:NN \Umathinnerpunctspacing
+ \tex_Umathinnerpunctspacing:D
+ \__kernel_primitive:NN \Umathinnerrelspacing \tex_Umathinnerrelspacing:D
+ \__kernel_primitive:NN \Umathlimitabovebgap \tex_Umathlimitabovebgap:D
+ \__kernel_primitive:NN \Umathlimitabovekern \tex_Umathlimitabovekern:D
+ \__kernel_primitive:NN \Umathlimitabovevgap \tex_Umathlimitabovevgap:D
+ \__kernel_primitive:NN \Umathlimitbelowbgap \tex_Umathlimitbelowbgap:D
+ \__kernel_primitive:NN \Umathlimitbelowkern \tex_Umathlimitbelowkern:D
+ \__kernel_primitive:NN \Umathlimitbelowvgap \tex_Umathlimitbelowvgap:D
+ \__kernel_primitive:NN \Umathnolimitsubfactor \tex_Umathnolimitsubfactor:D
+ \__kernel_primitive:NN \Umathnolimitsupfactor \tex_Umathnolimitsupfactor:D
+ \__kernel_primitive:NN \Umathopbinspacing \tex_Umathopbinspacing:D
+ \__kernel_primitive:NN \Umathopclosespacing \tex_Umathopclosespacing:D
+ \__kernel_primitive:NN \Umathopenbinspacing \tex_Umathopenbinspacing:D
+ \__kernel_primitive:NN \Umathopenclosespacing \tex_Umathopenclosespacing:D
+ \__kernel_primitive:NN \Umathopeninnerspacing \tex_Umathopeninnerspacing:D
+ \__kernel_primitive:NN \Umathopenopenspacing \tex_Umathopenopenspacing:D
+ \__kernel_primitive:NN \Umathopenopspacing \tex_Umathopenopspacing:D
+ \__kernel_primitive:NN \Umathopenordspacing \tex_Umathopenordspacing:D
+ \__kernel_primitive:NN \Umathopenpunctspacing \tex_Umathopenpunctspacing:D
+ \__kernel_primitive:NN \Umathopenrelspacing \tex_Umathopenrelspacing:D
+ \__kernel_primitive:NN \Umathoperatorsize \tex_Umathoperatorsize:D
+ \__kernel_primitive:NN \Umathopinnerspacing \tex_Umathopinnerspacing:D
+ \__kernel_primitive:NN \Umathopopenspacing \tex_Umathopopenspacing:D
+ \__kernel_primitive:NN \Umathopopspacing \tex_Umathopopspacing:D
+ \__kernel_primitive:NN \Umathopordspacing \tex_Umathopordspacing:D
+ \__kernel_primitive:NN \Umathoppunctspacing \tex_Umathoppunctspacing:D
+ \__kernel_primitive:NN \Umathoprelspacing \tex_Umathoprelspacing:D
+ \__kernel_primitive:NN \Umathordbinspacing \tex_Umathordbinspacing:D
+ \__kernel_primitive:NN \Umathordclosespacing \tex_Umathordclosespacing:D
+ \__kernel_primitive:NN \Umathordinnerspacing \tex_Umathordinnerspacing:D
+ \__kernel_primitive:NN \Umathordopenspacing \tex_Umathordopenspacing:D
+ \__kernel_primitive:NN \Umathordopspacing \tex_Umathordopspacing:D
+ \__kernel_primitive:NN \Umathordordspacing \tex_Umathordordspacing:D
+ \__kernel_primitive:NN \Umathordpunctspacing \tex_Umathordpunctspacing:D
+ \__kernel_primitive:NN \Umathordrelspacing \tex_Umathordrelspacing:D
+ \__kernel_primitive:NN \Umathoverbarkern \tex_Umathoverbarkern:D
+ \__kernel_primitive:NN \Umathoverbarrule \tex_Umathoverbarrule:D
+ \__kernel_primitive:NN \Umathoverbarvgap \tex_Umathoverbarvgap:D
+ \__kernel_primitive:NN \Umathoverdelimiterbgap
+ \tex_Umathoverdelimiterbgap:D
+ \__kernel_primitive:NN \Umathoverdelimitervgap
+ \tex_Umathoverdelimitervgap:D
+ \__kernel_primitive:NN \Umathpunctbinspacing \tex_Umathpunctbinspacing:D
+ \__kernel_primitive:NN \Umathpunctclosespacing
+ \tex_Umathpunctclosespacing:D
+ \__kernel_primitive:NN \Umathpunctinnerspacing
+ \tex_Umathpunctinnerspacing:D
+ \__kernel_primitive:NN \Umathpunctopenspacing \tex_Umathpunctopenspacing:D
+ \__kernel_primitive:NN \Umathpunctopspacing \tex_Umathpunctopspacing:D
+ \__kernel_primitive:NN \Umathpunctordspacing \tex_Umathpunctordspacing:D
+ \__kernel_primitive:NN \Umathpunctpunctspacing
+ \tex_Umathpunctpunctspacing:D
+ \__kernel_primitive:NN \Umathpunctrelspacing \tex_Umathpunctrelspacing:D
+ \__kernel_primitive:NN \Umathquad \tex_Umathquad:D
+ \__kernel_primitive:NN \Umathradicaldegreeafter
+ \tex_Umathradicaldegreeafter:D
+ \__kernel_primitive:NN \Umathradicaldegreebefore
+ \tex_Umathradicaldegreebefore:D
+ \__kernel_primitive:NN \Umathradicaldegreeraise
+ \tex_Umathradicaldegreeraise:D
+ \__kernel_primitive:NN \Umathradicalkern \tex_Umathradicalkern:D
+ \__kernel_primitive:NN \Umathradicalrule \tex_Umathradicalrule:D
+ \__kernel_primitive:NN \Umathradicalvgap \tex_Umathradicalvgap:D
+ \__kernel_primitive:NN \Umathrelbinspacing \tex_Umathrelbinspacing:D
+ \__kernel_primitive:NN \Umathrelclosespacing \tex_Umathrelclosespacing:D
+ \__kernel_primitive:NN \Umathrelinnerspacing \tex_Umathrelinnerspacing:D
+ \__kernel_primitive:NN \Umathrelopenspacing \tex_Umathrelopenspacing:D
+ \__kernel_primitive:NN \Umathrelopspacing \tex_Umathrelopspacing:D
+ \__kernel_primitive:NN \Umathrelordspacing \tex_Umathrelordspacing:D
+ \__kernel_primitive:NN \Umathrelpunctspacing \tex_Umathrelpunctspacing:D
+ \__kernel_primitive:NN \Umathrelrelspacing \tex_Umathrelrelspacing:D
+ \__kernel_primitive:NN \Umathskewedfractionhgap
+ \tex_Umathskewedfractionhgap:D
+ \__kernel_primitive:NN \Umathskewedfractionvgap
+ \tex_Umathskewedfractionvgap:D
+ \__kernel_primitive:NN \Umathspaceafterscript \tex_Umathspaceafterscript:D
+ \__kernel_primitive:NN \Umathstackdenomdown \tex_Umathstackdenomdown:D
+ \__kernel_primitive:NN \Umathstacknumup \tex_Umathstacknumup:D
+ \__kernel_primitive:NN \Umathstackvgap \tex_Umathstackvgap:D
+ \__kernel_primitive:NN \Umathsubshiftdown \tex_Umathsubshiftdown:D
+ \__kernel_primitive:NN \Umathsubshiftdrop \tex_Umathsubshiftdrop:D
+ \__kernel_primitive:NN \Umathsubsupshiftdown \tex_Umathsubsupshiftdown:D
+ \__kernel_primitive:NN \Umathsubsupvgap \tex_Umathsubsupvgap:D
+ \__kernel_primitive:NN \Umathsubtopmax \tex_Umathsubtopmax:D
+ \__kernel_primitive:NN \Umathsupbottommin \tex_Umathsupbottommin:D
+ \__kernel_primitive:NN \Umathsupshiftdrop \tex_Umathsupshiftdrop:D
+ \__kernel_primitive:NN \Umathsupshiftup \tex_Umathsupshiftup:D
+ \__kernel_primitive:NN \Umathsupsubbottommax \tex_Umathsupsubbottommax:D
+ \__kernel_primitive:NN \Umathunderbarkern \tex_Umathunderbarkern:D
+ \__kernel_primitive:NN \Umathunderbarrule \tex_Umathunderbarrule:D
+ \__kernel_primitive:NN \Umathunderbarvgap \tex_Umathunderbarvgap:D
+ \__kernel_primitive:NN \Umathunderdelimiterbgap
+ \tex_Umathunderdelimiterbgap:D
+ \__kernel_primitive:NN \Umathunderdelimitervgap
+ \tex_Umathunderdelimitervgap:D
+ \__kernel_primitive:NN \Umiddle \tex_Umiddle:D
+ \__kernel_primitive:NN \Unosubscript \tex_Unosubscript:D
+ \__kernel_primitive:NN \Unosuperscript \tex_Unosuperscript:D
+ \__kernel_primitive:NN \Uoverdelimiter \tex_Uoverdelimiter:D
+ \__kernel_primitive:NN \Uradical \tex_Uradical:D
+ \__kernel_primitive:NN \Uright \tex_Uright:D
+ \__kernel_primitive:NN \Uroot \tex_Uroot:D
+ \__kernel_primitive:NN \Uskewed \tex_Uskewed:D
+ \__kernel_primitive:NN \Uskewedwithdelims \tex_Uskewedwithdelims:D
+ \__kernel_primitive:NN \Ustack \tex_Ustack:D
+ \__kernel_primitive:NN \Ustartdisplaymath \tex_Ustartdisplaymath:D
+ \__kernel_primitive:NN \Ustartmath \tex_Ustartmath:D
+ \__kernel_primitive:NN \Ustopdisplaymath \tex_Ustopdisplaymath:D
+ \__kernel_primitive:NN \Ustopmath \tex_Ustopmath:D
+ \__kernel_primitive:NN \Usubscript \tex_Usubscript:D
+ \__kernel_primitive:NN \Usuperscript \tex_Usuperscript:D
+ \__kernel_primitive:NN \Uunderdelimiter \tex_Uunderdelimiter:D
+ \__kernel_primitive:NN \Uvextensible \tex_Uvextensible:D
+ \__kernel_primitive:NN \autospacing \tex_autospacing:D
+ \__kernel_primitive:NN \autoxspacing \tex_autoxspacing:D
+ \__kernel_primitive:NN \currentcjktoken \tex_currentcjktoken:D
+ \__kernel_primitive:NN \currentspacingmode \tex_currentspacingmode:D
+ \__kernel_primitive:NN \currentxspacingmode \tex_currentxspacingmode:D
+ \__kernel_primitive:NN \disinhibitglue \tex_disinhibitglue:D
+ \__kernel_primitive:NN \dtou \tex_dtou:D
+ \__kernel_primitive:NN \epTeXinputencoding \tex_epTeXinputencoding:D
+ \__kernel_primitive:NN \epTeXversion \tex_epTeXversion:D
+ \__kernel_primitive:NN \euc \tex_euc:D
+ \__kernel_primitive:NN \hfi \tex_hfi:D
+ \__kernel_primitive:NN \ifdbox \tex_ifdbox:D
+ \__kernel_primitive:NN \ifddir \tex_ifddir:D
+ \__kernel_primitive:NN \ifjfont \tex_ifjfont:D
+ \__kernel_primitive:NN \ifmbox \tex_ifmbox:D
+ \__kernel_primitive:NN \ifmdir \tex_ifmdir:D
+ \__kernel_primitive:NN \iftbox \tex_iftbox:D
+ \__kernel_primitive:NN \iftfont \tex_iftfont:D
+ \__kernel_primitive:NN \iftdir \tex_iftdir:D
+ \__kernel_primitive:NN \ifybox \tex_ifybox:D
+ \__kernel_primitive:NN \ifydir \tex_ifydir:D
+ \__kernel_primitive:NN \inhibitglue \tex_inhibitglue:D
+ \__kernel_primitive:NN \inhibitxspcode \tex_inhibitxspcode:D
+ \__kernel_primitive:NN \jcharwidowpenalty \tex_jcharwidowpenalty:D
+ \__kernel_primitive:NN \jfam \tex_jfam:D
+ \__kernel_primitive:NN \jfont \tex_jfont:D
+ \__kernel_primitive:NN \jis \tex_jis:D
+ \__kernel_primitive:NN \kanjiskip \tex_kanjiskip:D
+ \__kernel_primitive:NN \kansuji \tex_kansuji:D
+ \__kernel_primitive:NN \kansujichar \tex_kansujichar:D
+ \__kernel_primitive:NN \kcatcode \tex_kcatcode:D
+ \__kernel_primitive:NN \kuten \tex_kuten:D
+ \__kernel_primitive:NN \lastnodechar \tex_lastnodechar:D
+ \__kernel_primitive:NN \lastnodefont \tex_lastnodefont:D
+ \__kernel_primitive:NN \lastnodesubtype \tex_lastnodesubtype:D
+ \__kernel_primitive:NN \noautospacing \tex_noautospacing:D
+ \__kernel_primitive:NN \noautoxspacing \tex_noautoxspacing:D
+ \__kernel_primitive:NN \pagefistretch \tex_pagefistretch:D
+ \__kernel_primitive:NN \postbreakpenalty \tex_postbreakpenalty:D
+ \__kernel_primitive:NN \prebreakpenalty \tex_prebreakpenalty:D
+ \__kernel_primitive:NN \ptexfontname \tex_ptexfontname:D
+ \__kernel_primitive:NN \ptexlineendmode \tex_lineendmode:D
+ \__kernel_primitive:NN \ptexminorversion \tex_ptexminorversion:D
+ \__kernel_primitive:NN \ptexrevision \tex_ptexrevision:D
+ \__kernel_primitive:NN \ptextracingfonts \tex_ptextracingfonts:D
+ \__kernel_primitive:NN \ptexversion \tex_ptexversion:D
+ \__kernel_primitive:NN \readpapersizespecial \tex_readpapersizespecial:D
+ \__kernel_primitive:NN \scriptbaselineshiftfactor
+ \tex_scriptbaselineshiftfactor:D
+ \__kernel_primitive:NN \scriptscriptbaselineshiftfactor
+ \tex_scriptscriptbaselineshiftfactor:D
+ \__kernel_primitive:NN \showmode \tex_showmode:D
+ \__kernel_primitive:NN \sjis \tex_sjis:D
+ \__kernel_primitive:NN \tate \tex_tate:D
+ \__kernel_primitive:NN \tbaselineshift \tex_tbaselineshift:D
+ \__kernel_primitive:NN \textbaselineshiftfactor
+ \tex_textbaselineshiftfactor:D
+ \__kernel_primitive:NN \tfont \tex_tfont:D
+ \__kernel_primitive:NN \tojis \tex_tojis:D
+ \__kernel_primitive:NN \toucs \tex_toucs:D
+ \__kernel_primitive:NN \ucs \tex_ucs:D
+ \__kernel_primitive:NN \xkanjiskip \tex_xkanjiskip:D
+ \__kernel_primitive:NN \xspcode \tex_xspcode:D
+ \__kernel_primitive:NN \ybaselineshift \tex_ybaselineshift:D
+ \__kernel_primitive:NN \yoko \tex_yoko:D
+ \__kernel_primitive:NN \vfi \tex_vfi:D
+ \__kernel_primitive:NN \currentcjktoken \tex_currentcjktoken:D
+ \__kernel_primitive:NN \disablecjktoken \tex_disablecjktoken:D
+ \__kernel_primitive:NN \enablecjktoken \tex_enablecjktoken:D
+ \__kernel_primitive:NN \forcecjktoken \tex_forcecjktoken:D
+ \__kernel_primitive:NN \kchar \tex_kchar:D
+ \__kernel_primitive:NN \kchardef \tex_kchardef:D
+ \__kernel_primitive:NN \kuten \tex_kuten:D
+ \__kernel_primitive:NN \uptexrevision \tex_uptexrevision:D
+ \__kernel_primitive:NN \uptexversion \tex_uptexversion:D
+ \__kernel_primitive:NN \odelcode \tex_odelcode:D
+ \__kernel_primitive:NN \odelimiter \tex_odelimiter:D
+ \__kernel_primitive:NN \omathaccent \tex_omathaccent:D
+ \__kernel_primitive:NN \omathchar \tex_omathchar:D
+ \__kernel_primitive:NN \omathchardef \tex_omathchardef:D
+ \__kernel_primitive:NN \omathcode \tex_omathcode:D
+ \__kernel_primitive:NN \oradical \tex_oradical:D
+ \__kernel_primitive:NN \partokencontext \tex_partokencontext:D
+ \__kernel_primitive:NN \partokenname \tex_partokenname:D
+ \__kernel_primitive:NN \showstream \tex_showstream:D
+ \__kernel_primitive:NN \tracingstacklevels \tex_tracingstacklevels:D
+\tex_endgroup:D
+\tex_ifdefined:D \@@end
+ \tex_let:D \tex_end:D \@@end
+ \tex_let:D \tex_input:D \@@input
+\tex_fi:D
+\tex_ifdefined:D \@@hyph
+ \tex_let:D \tex_everydisplay:D \frozen@everydisplay
+ \tex_let:D \tex_everymath:D \frozen@everymath
+ \tex_let:D \tex_hyphen:D \@@hyph
+ \tex_let:D \tex_italiccorrection:D \@@italiccorr
+ \tex_let:D \tex_underline:D \@@underline
+ \tex_ifdefined:D \@@shipout
+ \tex_let:D \tex_shipout:D \@@shipout
+ \tex_fi:D
+ \tex_begingroup:D
+ \tex_edef:D \l_tmpa_tl { \tex_string:D \shipout }
+ \tex_edef:D \l_tmpb_tl { \tex_meaning:D \shipout }
+ \tex_ifx:D \l_tmpa_tl \l_tmpb_tl
+ \tex_else:D
+ \tex_expandafter:D \@tfor \tex_expandafter:D \@tempa \tex_string:D :=
+ \CROP@shipout
+ \dup@shipout
+ \GPTorg@shipout
+ \LL@shipout
+ \mem@oldshipout
+ \opem@shipout
+ \pgfpages@originalshipout
+ \pr@shipout
+ \Shipout
+ \verso@orig@shipout
+ \do
+ {
+ \tex_edef:D \l_tmpb_tl
+ { \tex_expandafter:D \tex_meaning:D \@tempa }
+ \tex_ifx:D \l_tmpa_tl \l_tmpb_tl
+ \tex_global:D \tex_expandafter:D \tex_let:D
+ \tex_expandafter:D \tex_shipout:D \@tempa
+ \tex_fi:D
+ }
+ \tex_fi:D
+ \tex_endgroup:D
+ \tex_let:D \tex_tracingfonts:D \tex_undefined:D
+ \tex_ifdefined:D \pdftracingfonts
+ \tex_let:D \tex_tracingfonts:D \pdftracingfonts
+ \tex_else:D
+ \tex_ifdefined:D \tex_directlua:D
+ \tex_directlua:D { tex.enableprimitives("@@", {"tracingfonts"}) }
+ \tex_let:D \tex_tracingfonts:D \@@tracingfonts
+ \tex_fi:D
+ \tex_fi:D
+\tex_fi:D
+\tex_ifnum:D 0
+ \tex_ifdefined:D \tex_pdftexversion:D 1 \tex_fi:D
+ \tex_ifdefined:D \tex_luatexversion:D 1 \tex_fi:D
+ = 0 %
+ \tex_let:D \tex_pdfmapfile:D \tex_undefined:D
+ \tex_let:D \tex_pdfmapline:D \tex_undefined:D
+\tex_fi:D
+\tex_begingroup:D
+ \tex_edef:D \l_tmpa_tl { \tex_meaning:D \tex_time:D }
+ \tex_edef:D \l_tmpb_tl { \tex_string:D \time }
+ \tex_ifx:D \l_tmpa_tl \l_tmpb_tl
+ \tex_else:D
+ \tex_global:D \tex_let:D \tex_time:D \tex_undefined:D
+ \tex_fi:D
+ \tex_edef:D \l_tmpa_tl { \tex_meaning:D \tex_day:D }
+ \tex_edef:D \l_tmpb_tl { \tex_string:D \day }
+ \tex_ifx:D \l_tmpa_tl \l_tmpb_tl
+ \tex_else:D
+ \tex_global:D \tex_let:D \tex_day:D \tex_undefined:D
+ \tex_fi:D
+ \tex_edef:D \l_tmpa_tl { \tex_meaning:D \tex_month:D }
+ \tex_edef:D \l_tmpb_tl { \tex_string:D \month }
+ \tex_ifx:D \l_tmpa_tl \l_tmpb_tl
+ \tex_else:D
+ \tex_global:D \tex_let:D \tex_month:D \tex_undefined:D
+ \tex_fi:D
+ \tex_edef:D \l_tmpa_tl { \tex_meaning:D \tex_year:D }
+ \tex_edef:D \l_tmpb_tl { \tex_string:D \year }
+ \tex_ifx:D \l_tmpa_tl \l_tmpb_tl
+ \tex_else:D
+ \tex_global:D \tex_let:D \tex_year:D \tex_undefined:D
+ \tex_fi:D
+\tex_endgroup:D
+\tex_ifdefined:D \orieveryjob
+ \tex_let:D \tex_everyjob:D \orieveryjob
+\tex_fi:D
+\tex_ifdefined:D \oripdfoutput
+ \tex_let:D \tex_pdfoutput:D \oripdfoutput
+\tex_fi:D
+\tex_ifdefined:D \normalend
+ \tex_let:D \tex_end:D \normalend
+ \tex_let:D \tex_everyjob:D \normaleveryjob
+ \tex_let:D \tex_input:D \normalinput
+ \tex_let:D \tex_language:D \normallanguage
+ \tex_let:D \tex_mathop:D \normalmathop
+ \tex_let:D \tex_month:D \normalmonth
+ \tex_let:D \tex_outer:D \normalouter
+ \tex_let:D \tex_over:D \normalover
+ \tex_let:D \tex_vcenter:D \normalvcenter
+ \tex_let:D \tex_unexpanded:D \normalunexpanded
+ \tex_let:D \tex_expanded:D \normalexpanded
+\tex_fi:D
+\tex_ifdefined:D \normalitaliccorrection
+ \tex_let:D \tex_hoffset:D \normalhoffset
+ \tex_let:D \tex_italiccorrection:D \normalitaliccorrection
+ \tex_let:D \tex_voffset:D \normalvoffset
+ \tex_let:D \tex_showtokens:D \normalshowtokens
+ \tex_let:D \tex_bodydir:D \spac_directions_normal_body_dir
+ \tex_let:D \tex_pagedir:D \spac_directions_normal_page_dir
+\tex_fi:D
+\tex_ifdefined:D \normalleft
+ \tex_let:D \tex_left:D \normalleft
+ \tex_let:D \tex_middle:D \normalmiddle
+ \tex_let:D \tex_right:D \normalright
+\tex_fi:D
+%% File: l3basics.dtx
+\tex_global:D \tex_let:D \if_true: \tex_iftrue:D
+\tex_global:D \tex_let:D \if_false: \tex_iffalse:D
+\tex_global:D \tex_let:D \or: \tex_or:D
+\tex_global:D \tex_let:D \else: \tex_else:D
+\tex_global:D \tex_let:D \fi: \tex_fi:D
+\tex_global:D \tex_let:D \reverse_if:N \tex_unless:D
+\tex_global:D \tex_let:D \if:w \tex_if:D
+\tex_global:D \tex_let:D \if_charcode:w \tex_if:D
+\tex_global:D \tex_let:D \if_catcode:w \tex_ifcat:D
+\tex_global:D \tex_let:D \if_meaning:w \tex_ifx:D
+\tex_global:D \tex_let:D \if_bool:N \tex_ifodd:D
+\tex_global:D \tex_let:D \if_mode_math: \tex_ifmmode:D
+\tex_global:D \tex_let:D \if_mode_horizontal: \tex_ifhmode:D
+\tex_global:D \tex_let:D \if_mode_vertical: \tex_ifvmode:D
+\tex_global:D \tex_let:D \if_mode_inner: \tex_ifinner:D
+\tex_global:D \tex_let:D \if_cs_exist:N \tex_ifdefined:D
+\tex_global:D \tex_let:D \if_cs_exist:w \tex_ifcsname:D
+\tex_global:D \tex_let:D \cs:w \tex_csname:D
+\tex_global:D \tex_let:D \cs_end: \tex_endcsname:D
+\tex_global:D \tex_let:D \exp_after:wN \tex_expandafter:D
+\tex_global:D \tex_let:D \exp_not:N \tex_noexpand:D
+\tex_global:D \tex_let:D \exp_not:n \tex_unexpanded:D
+\tex_global:D \tex_let:D \exp:w \tex_romannumeral:D
+\tex_global:D \tex_chardef:D \exp_end: = 0 ~
+\tex_global:D \tex_let:D \token_to_meaning:N \tex_meaning:D
+\tex_global:D \tex_let:D \cs_meaning:N \tex_meaning:D
+\tex_global:D \tex_let:D \tl_to_str:n \tex_detokenize:D
+\tex_global:D \tex_let:D \token_to_str:N \tex_string:D
+\tex_global:D \tex_let:D \__kernel_tl_to_str:w \tex_detokenize:D
+\tex_global:D \tex_let:D \scan_stop: \tex_relax:D
+\tex_global:D \tex_let:D \group_begin: \tex_begingroup:D
+\tex_global:D \tex_let:D \group_end: \tex_endgroup:D
+\tex_global:D \tex_let:D \if_int_compare:w \tex_ifnum:D
+\tex_global:D \tex_let:D \__int_to_roman:w \tex_romannumeral:D
+\tex_global:D \tex_let:D \group_insert_after:N \tex_aftergroup:D
+\tex_long:D \tex_gdef:D \exp_args:Nc #1#2
+ { \exp_after:wN #1 \cs:w #2 \cs_end: }
+\tex_long:D \tex_gdef:D \exp_args:cc #1#2
+ { \cs:w #1 \exp_after:wN \cs_end: \cs:w #2 \cs_end: }
+\tex_gdef:D \token_to_str:c { \exp_args:Nc \token_to_str:N }
+\tex_long:D \tex_gdef:D \cs_meaning:c #1
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ { \exp_args:Nc \cs_meaning:N {#1} }
+ { \tl_to_str:n {undefined} }
+ }
+\tex_global:D \tex_let:D \token_to_meaning:c = \cs_meaning:c
+\tex_global:D \tex_chardef:D \c_zero_int = 0 ~
+\tex_ifdefined:D \tex_luatexversion:D
+ \tex_global:D \tex_chardef:D \c_max_register_int = 65 535 ~
+\tex_else:D
+ \tex_ifdefined:D \tex_omathchardef:D
+ \tex_global:D \tex_omathchardef:D \c_max_register_int = 65535 ~
+ \tex_else:D
+ \tex_global:D \tex_mathchardef:D \c_max_register_int = 32767 ~
+ \tex_fi:D
+\tex_fi:D
+\tex_global:D \tex_let:D \cs_gset_nopar:Npn \tex_gdef:D
+\tex_global:D \tex_let:D \cs_gset_nopar:Npe \tex_xdef:D
+\tex_global:D \tex_let:D \cs_gset_nopar:Npx \tex_xdef:D
+\tex_protected:D \tex_long:D \tex_gdef:D \cs_gset:Npn
+ { \tex_long:D \tex_gdef:D }
+\tex_protected:D \tex_long:D \tex_gdef:D \cs_gset:Npe
+ { \tex_long:D \tex_xdef:D }
+\tex_global:D \tex_let:D \cs_gset:Npx \cs_gset:Npe
+\tex_protected:D \tex_long:D \tex_gdef:D \cs_gset_protected_nopar:Npn
+ { \tex_protected:D \tex_gdef:D }
+\tex_protected:D \tex_long:D \tex_gdef:D \cs_gset_protected_nopar:Npe
+ { \tex_protected:D \tex_xdef:D }
+\tex_global:D \tex_let:D \cs_gset_protected_nopar:Npx \cs_gset_protected_nopar:Npe
+\tex_protected:D \tex_long:D \tex_gdef:D \cs_gset_protected:Npn
+ { \tex_protected:D \tex_long:D \tex_gdef:D }
+\tex_protected:D \tex_long:D \tex_gdef:D \cs_gset_protected:Npe
+ { \tex_protected:D \tex_long:D \tex_xdef:D }
+\tex_global:D \tex_let:D \cs_gset_protected:Npx \cs_gset_protected:Npe
+\tex_global:D \tex_let:D \cs_set_nopar:Npn \tex_def:D
+\tex_global:D \tex_let:D \cs_set_nopar:Npe \tex_edef:D
+\tex_global:D \tex_let:D \cs_set_nopar:Npx \tex_edef:D
+\cs_gset_protected:Npn \cs_set:Npn
+ { \tex_long:D \tex_def:D }
+\cs_gset_protected:Npn \cs_set:Npe
+ { \tex_long:D \tex_edef:D }
+\tex_global:D \tex_let:D \cs_set:Npx \cs_set:Npe
+\cs_gset_protected:Npn \cs_set_protected_nopar:Npn
+ { \tex_protected:D \tex_def:D }
+\cs_gset_protected:Npn \cs_set_protected_nopar:Npe
+ { \tex_protected:D \tex_edef:D }
+\tex_global:D \tex_let:D \cs_set_protected_nopar:Npx \cs_set_protected_nopar:Npe
+\cs_gset_protected:Npn \cs_set_protected:Npn
+ { \tex_protected:D \tex_long:D \tex_def:D }
+\cs_gset_protected:Npn \cs_set_protected:Npe
+ { \tex_protected:D \tex_long:D \tex_edef:D }
+\tex_global:D \tex_let:D \cs_set_protected:Npx \cs_set_protected:Npe
+\cs_gset_nopar:Npn \l__exp_internal_tl { }
+\cs_gset:Npn \use:c #1 { \cs:w #1 \cs_end: }
+\cs_gset_protected:Npn \use:x #1
+ {
+ \cs_set_nopar:Npx \l__exp_internal_tl {#1}
+ \l__exp_internal_tl
+ }
+\cs_gset:Npn \use:e #1 { \tex_expanded:D {#1} }
+\cs_gset:Npn \use:n #1 {#1}
+\cs_gset:Npn \use:nn #1#2 {#1#2}
+\cs_gset:Npn \use:nnn #1#2#3 {#1#2#3}
+\cs_gset:Npn \use:nnnn #1#2#3#4 {#1#2#3#4}
+\cs_gset:Npn \use_i:nn #1#2 {#1}
+\cs_gset:Npn \use_ii:nn #1#2 {#2}
+\cs_gset:Npn \use_i:nnn #1#2#3 {#1}
+\cs_gset:Npn \use_ii:nnn #1#2#3 {#2}
+\cs_gset:Npn \use_iii:nnn #1#2#3 {#3}
+\cs_gset:Npn \use_i:nnnn #1#2#3#4 {#1}
+\cs_gset:Npn \use_ii:nnnn #1#2#3#4 {#2}
+\cs_gset:Npn \use_iii:nnnn #1#2#3#4 {#3}
+\cs_gset:Npn \use_iv:nnnn #1#2#3#4 {#4}
+\cs_gset:Npn \use_i:nnnnn #1#2#3#4#5 {#1}
+\cs_gset:Npn \use_ii:nnnnn #1#2#3#4#5 {#2}
+\cs_gset:Npn \use_iii:nnnnn #1#2#3#4#5 {#3}
+\cs_gset:Npn \use_iv:nnnnn #1#2#3#4#5 {#4}
+\cs_gset:Npn \use_v:nnnnn #1#2#3#4#5 {#5}
+\cs_gset:Npn \use_i:nnnnnn #1#2#3#4#5#6 {#1}
+\cs_gset:Npn \use_ii:nnnnnn #1#2#3#4#5#6 {#2}
+\cs_gset:Npn \use_iii:nnnnnn #1#2#3#4#5#6 {#3}
+\cs_gset:Npn \use_iv:nnnnnn #1#2#3#4#5#6 {#4}
+\cs_gset:Npn \use_v:nnnnnn #1#2#3#4#5#6 {#5}
+\cs_gset:Npn \use_vi:nnnnnn #1#2#3#4#5#6 {#6}
+\cs_gset:Npn \use_i:nnnnnnn #1#2#3#4#5#6#7 {#1}
+\cs_gset:Npn \use_ii:nnnnnnn #1#2#3#4#5#6#7 {#2}
+\cs_gset:Npn \use_iii:nnnnnnn #1#2#3#4#5#6#7 {#3}
+\cs_gset:Npn \use_iv:nnnnnnn #1#2#3#4#5#6#7 {#4}
+\cs_gset:Npn \use_v:nnnnnnn #1#2#3#4#5#6#7 {#5}
+\cs_gset:Npn \use_vi:nnnnnnn #1#2#3#4#5#6#7 {#6}
+\cs_gset:Npn \use_vii:nnnnnnn #1#2#3#4#5#6#7 {#7}
+\cs_gset:Npn \use_i:nnnnnnnn #1#2#3#4#5#6#7#8 {#1}
+\cs_gset:Npn \use_ii:nnnnnnnn #1#2#3#4#5#6#7#8 {#2}
+\cs_gset:Npn \use_iii:nnnnnnnn #1#2#3#4#5#6#7#8 {#3}
+\cs_gset:Npn \use_iv:nnnnnnnn #1#2#3#4#5#6#7#8 {#4}
+\cs_gset:Npn \use_v:nnnnnnnn #1#2#3#4#5#6#7#8 {#5}
+\cs_gset:Npn \use_vi:nnnnnnnn #1#2#3#4#5#6#7#8 {#6}
+\cs_gset:Npn \use_vii:nnnnnnnn #1#2#3#4#5#6#7#8 {#7}
+\cs_gset:Npn \use_viii:nnnnnnnn #1#2#3#4#5#6#7#8 {#8}
+\cs_gset:Npn \use_i:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#1}
+\cs_gset:Npn \use_ii:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#2}
+\cs_gset:Npn \use_iii:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#3}
+\cs_gset:Npn \use_iv:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#4}
+\cs_gset:Npn \use_v:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#5}
+\cs_gset:Npn \use_vi:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#6}
+\cs_gset:Npn \use_vii:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#7}
+\cs_gset:Npn \use_viii:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#8}
+\cs_gset:Npn \use_ix:nnnnnnnnn #1#2#3#4#5#6#7#8#9 {#9}
+\cs_gset:Npn \use_i_ii:nnn #1#2#3 {#1#2}
+\cs_gset:Npn \use_ii_i:nn #1#2 { #2 #1 }
+\cs_gset:Npn \use_none_delimit_by_q_nil:w #1 \q_nil { }
+\cs_gset:Npn \use_none_delimit_by_q_stop:w #1 \q_stop { }
+\cs_gset:Npn \use_none_delimit_by_q_recursion_stop:w #1 \q_recursion_stop { }
+\cs_gset:Npn \use_i_delimit_by_q_nil:nw #1#2 \q_nil {#1}
+\cs_gset:Npn \use_i_delimit_by_q_stop:nw #1#2 \q_stop {#1}
+\cs_gset:Npn \use_i_delimit_by_q_recursion_stop:nw
+ #1#2 \q_recursion_stop {#1}
+\cs_gset:Npn \use_none:n #1 { }
+\cs_gset:Npn \use_none:nn #1#2 { }
+\cs_gset:Npn \use_none:nnn #1#2#3 { }
+\cs_gset:Npn \use_none:nnnn #1#2#3#4 { }
+\cs_gset:Npn \use_none:nnnnn #1#2#3#4#5 { }
+\cs_gset:Npn \use_none:nnnnnn #1#2#3#4#5#6 { }
+\cs_gset:Npn \use_none:nnnnnnn #1#2#3#4#5#6#7 { }
+\cs_gset:Npn \use_none:nnnnnnnn #1#2#3#4#5#6#7#8 { }
+\cs_gset:Npn \use_none:nnnnnnnnn #1#2#3#4#5#6#7#8#9 { }
+\cs_gset_protected:Npn \__kernel_if_debug:TF #1#2 {#2}
+\cs_gset_protected:Npn \debug_on:n #1
+ {
+ \sys_load_debug:
+ \cs_if_exist:NT \__debug_all_on:
+ { \debug_on:n {#1} }
+ }
+\cs_gset_protected:Npn \debug_off:n #1
+ {
+ \sys_load_debug:
+ \cs_if_exist:NT \__debug_all_on:
+ { \debug_off:n {#1} }
+ }
+\cs_gset_protected:Npn \debug_suspend: { }
+\cs_gset_protected:Npn \debug_resume: { }
+\cs_gset_nopar:Npn \g__debug_deprecation_on_tl { }
+\cs_gset_nopar:Npn \g__debug_deprecation_off_tl { }
+\cs_gset_protected:Npn \__kernel_deprecation_code:nn #1#2
+ {
+ \tl_gput_right:Nn \g__debug_deprecation_on_tl {#1}
+ \tl_gput_right:Nn \g__debug_deprecation_off_tl {#2}
+ }
+\cs_gset:Npn \prg_return_true:
+ { \exp_after:wN \use_i:nn \exp:w }
+\cs_gset:Npn \prg_return_false:
+ { \exp_after:wN \use_ii:nn \exp:w}
+\cs_gset:Npn \__prg_use_none_delimit_by_q_recursion_stop:w
+ #1 \q__prg_recursion_stop { }
+\cs_gset_protected:Npn \prg_set_conditional:Npnn
+ { \__prg_generate_conditional_parm:NNNpnn \cs_set:Npn e }
+\cs_gset_protected:Npn \prg_gset_conditional:Npnn
+ { \__prg_generate_conditional_parm:NNNpnn \cs_gset:Npn e }
+\cs_gset_protected:Npn \prg_new_conditional:Npnn
+ { \__prg_generate_conditional_parm:NNNpnn \cs_new:Npn e }
+\cs_gset_protected:Npn \prg_set_protected_conditional:Npnn
+ { \__prg_generate_conditional_parm:NNNpnn \cs_set_protected:Npn p }
+\cs_gset_protected:Npn \prg_gset_protected_conditional:Npnn
+ { \__prg_generate_conditional_parm:NNNpnn \cs_gset_protected:Npn p }
+\cs_gset_protected:Npn \prg_new_protected_conditional:Npnn
+ { \__prg_generate_conditional_parm:NNNpnn \cs_new_protected:Npn p }
+\cs_gset_protected:Npn \__prg_generate_conditional_parm:NNNpnn #1#2#3#4#
+ {
+ \use:e
+ {
+ \__prg_generate_conditional:nnNNNnnn
+ \cs_split_function:N #3
+ }
+ #1 #2 {#4}
+ }
+\cs_gset_protected:Npn \prg_set_conditional:Nnn
+ { \__prg_generate_conditional_count:NNNnn \cs_set:Npn e }
+\cs_gset_protected:Npn \prg_gset_conditional:Nnn
+ { \__prg_generate_conditional_count:NNNnn \cs_set:Npn e }
+\cs_gset_protected:Npn \prg_new_conditional:Nnn
+ { \__prg_generate_conditional_count:NNNnn \cs_new:Npn e }
+\cs_gset_protected:Npn \prg_set_protected_conditional:Nnn
+ { \__prg_generate_conditional_count:NNNnn \cs_set_protected:Npn p }
+\cs_gset_protected:Npn \prg_gset_protected_conditional:Nnn
+ { \__prg_generate_conditional_count:NNNnn \cs_gset_protected:Npn p }
+\cs_gset_protected:Npn \prg_new_protected_conditional:Nnn
+ { \__prg_generate_conditional_count:NNNnn \cs_new_protected:Npn p }
+\cs_gset_protected:Npn \__prg_generate_conditional_count:NNNnn #1#2#3
+ {
+ \use:e
+ {
+ \__prg_generate_conditional_count:nnNNNnn
+ \cs_split_function:N #3
+ }
+ #1 #2
+ }
+\cs_gset_protected:Npn \__prg_generate_conditional_count:nnNNNnn #1#2#3#4#5
+ {
+ \__kernel_cs_parm_from_arg_count:nnF
+ { \__prg_generate_conditional:nnNNNnnn {#1} {#2} #3 #4 #5 }
+ { \tl_count:n {#2} }
+ {
+ \msg_error:nnee { kernel } { bad-number-of-arguments }
+ { \token_to_str:c { #1 : #2 } }
+ { \tl_count:n {#2} }
+ \use_none:nn
+ }
+ }
+\cs_gset_protected:Npn \__prg_generate_conditional:nnNNNnnn #1#2#3#4#5#6#7#8
+ {
+ \if_meaning:w \c_false_bool #3
+ \msg_error:nne { kernel } { missing-colon }
+ { \token_to_str:c {#1} }
+ \exp_after:wN \use_none:nn
+ \fi:
+ \use:e
+ {
+ \exp_not:N \__prg_generate_conditional:NNnnnnNw
+ \exp_not:n { #4 #5 {#1} {#2} {#6} }
+ \__prg_generate_conditional_test:w
+ #8 \s__prg_mark
+ \__prg_generate_conditional_fast:nw
+ \prg_return_true: \else: \prg_return_false: \fi: \s__prg_mark
+ \use_none:n
+ \exp_not:n { {#8} \use_i_ii:nnn }
+ \tl_to_str:n {#7}
+ \exp_not:n { , \q__prg_recursion_tail , \q__prg_recursion_stop }
+ }
+ }
+\cs_gset:Npn \__prg_generate_conditional_test:w
+ #1 \prg_return_true: \else: \prg_return_false: \fi: \s__prg_mark #2
+ { #2 {#1} }
+\cs_gset:Npn \__prg_generate_conditional_fast:nw #1#2 \exp_not:n #3
+ { \exp_not:n { {#1} \use_i:nn } }
+\cs_gset_protected:Npn \__prg_generate_conditional:NNnnnnNw #1#2#3#4#5#6#7#8 ,
+ {
+ \if_meaning:w \q__prg_recursion_tail #8
+ \exp_after:wN \__prg_use_none_delimit_by_q_recursion_stop:w
+ \fi:
+ \use:c { __prg_generate_ #8 _form:wNNnnnnN }
+ \tl_if_empty:nF {#8}
+ {
+ \msg_error:nnee
+ { kernel } { conditional-form-unknown }
+ {#8} { \token_to_str:c { #3 : #4 } }
+ }
+ \use_none:nnnnnnnn
+ \s__prg_stop
+ #1 #2 {#3} {#4} {#5} {#6} #7
+ \__prg_generate_conditional:NNnnnnNw #1 #2 {#3} {#4} {#5} {#6} #7
+ }
+\cs_gset_protected:Npn \__prg_generate_p_form:wNNnnnnN
+ #1 \s__prg_stop #2#3#4#5#6#7#8
+ {
+ \if_meaning:w e #3
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ {
+ #8
+ { \exp_args:Nc #2 { #4 _p: #5 } #6 }
+ { { #7 \exp_end: \c_true_bool \c_false_bool } }
+ { #7 \__prg_p_true:w \fi: \c_false_bool }
+ }
+ {
+ \msg_error:nne { kernel } { protected-predicate }
+ { \token_to_str:c { #4 _p: #5 } }
+ }
+ }
+\cs_gset_protected:Npn \__prg_generate_T_form:wNNnnnnN
+ #1 \s__prg_stop #2#3#4#5#6#7#8
+ {
+ #8
+ { \exp_args:Nc #2 { #4 : #5 T } #6 }
+ { { #7 \exp_end: \use:n \use_none:n } }
+ { #7 \__prg_T_true:w \fi: \use_none:n }
+ }
+\cs_gset_protected:Npn \__prg_generate_F_form:wNNnnnnN
+ #1 \s__prg_stop #2#3#4#5#6#7#8
+ {
+ #8
+ { \exp_args:Nc #2 { #4 : #5 F } #6 }
+ { { #7 \exp_end: { } } }
+ { #7 \__prg_F_true:w \fi: \use:n }
+ }
+\cs_gset_protected:Npn \__prg_generate_TF_form:wNNnnnnN
+ #1 \s__prg_stop #2#3#4#5#6#7#8
+ {
+ #8
+ { \exp_args:Nc #2 { #4 : #5 TF } #6 }
+ { { #7 \exp_end: } }
+ { #7 \__prg_TF_true:w \fi: \use_ii:nn }
+ }
+\cs_gset:Npn \__prg_p_true:w \fi: \c_false_bool { \fi: \c_true_bool }
+\cs_gset:Npn \__prg_T_true:w \fi: \use_none:n { \fi: \use:n }
+\cs_gset:Npn \__prg_F_true:w \fi: \use:n { \fi: \use_none:n }
+\cs_gset:Npn \__prg_TF_true:w \fi: \use_ii:nn { \fi: \use_i:nn }
+\cs_gset_protected:Npn \prg_set_eq_conditional:NNn
+ { \__prg_set_eq_conditional:NNNn \cs_set_eq:cc }
+\cs_gset_protected:Npn \prg_gset_eq_conditional:NNn
+ { \__prg_set_eq_conditional:NNNn \cs_gset_eq:cc }
+\cs_gset_protected:Npn \prg_new_eq_conditional:NNn
+ { \__prg_set_eq_conditional:NNNn \cs_new_eq:cc }
+\cs_gset_protected:Npn \__prg_set_eq_conditional:NNNn #1#2#3#4
+ {
+ \use:e
+ {
+ \exp_not:N \__prg_set_eq_conditional:nnNnnNNw
+ \cs_split_function:N #2
+ \cs_split_function:N #3
+ \exp_not:N #1
+ \tl_to_str:n {#4}
+ \exp_not:n { , \q__prg_recursion_tail , \q__prg_recursion_stop }
+ }
+ }
+\cs_gset_protected:Npn \__prg_set_eq_conditional:nnNnnNNw #1#2#3#4#5#6
+ {
+ \if_meaning:w \c_false_bool #3
+ \msg_error:nne { kernel } { missing-colon }
+ { \token_to_str:c {#1} }
+ \exp_after:wN \__prg_use_none_delimit_by_q_recursion_stop:w
+ \fi:
+ \if_meaning:w \c_false_bool #6
+ \msg_error:nne { kernel } { missing-colon }
+ { \token_to_str:c {#4} }
+ \exp_after:wN \__prg_use_none_delimit_by_q_recursion_stop:w
+ \fi:
+ \__prg_set_eq_conditional_loop:nnnnNw {#1} {#2} {#4} {#5}
+ }
+\cs_gset_protected:Npn \__prg_set_eq_conditional_loop:nnnnNw #1#2#3#4#5#6 ,
+ {
+ \if_meaning:w \q__prg_recursion_tail #6
+ \exp_after:wN \__prg_use_none_delimit_by_q_recursion_stop:w
+ \fi:
+ \use:c { __prg_set_eq_conditional_ #6 _form:wNnnnn }
+ \tl_if_empty:nF {#6}
+ {
+ \msg_error:nnee
+ { kernel } { conditional-form-unknown }
+ {#6} { \token_to_str:c { #1 : #2 } }
+ }
+ \use_none:nnnnnn
+ \s__prg_stop
+ #5 {#1} {#2} {#3} {#4}
+ \__prg_set_eq_conditional_loop:nnnnNw {#1} {#2} {#3} {#4} #5
+ }
+\cs_gset:Npn \__prg_set_eq_conditional_p_form:wNnnnn #1 \s__prg_stop #2#3#4#5#6
+ { #2 { #3 _p : #4 } { #5 _p : #6 } }
+\cs_gset:Npn \__prg_set_eq_conditional_TF_form:wNnnnn #1 \s__prg_stop #2#3#4#5#6
+ { #2 { #3 : #4 TF } { #5 : #6 TF } }
+\cs_gset:Npn \__prg_set_eq_conditional_T_form:wNnnnn #1 \s__prg_stop #2#3#4#5#6
+ { #2 { #3 : #4 T } { #5 : #6 T } }
+\cs_gset:Npn \__prg_set_eq_conditional_F_form:wNnnnn #1 \s__prg_stop #2#3#4#5#6
+ { #2 { #3 : #4 F } { #5 : #6 F } }
+\tex_global:D \tex_chardef:D \c_true_bool = 1 ~
+\tex_global:D \tex_chardef:D \c_false_bool = 0 ~
+\cs_gset:Npn \cs_to_str:N
+ {
+ \tex_romannumeral:D
+ \if:w \token_to_str:N \ \__cs_to_str:w \fi:
+ \exp_after:wN \__cs_to_str:N \token_to_str:N
+ }
+\cs_gset:Npn \__cs_to_str:N #1 { \c_zero_int }
+\cs_gset:Npn \__cs_to_str:w #1 \__cs_to_str:N
+ { - \int_value:w \fi: \exp_after:wN \c_zero_int }
+\cs_gset_protected:Npn \__cs_tmp:w #1
+ {
+ \cs_gset:Npn \cs_split_function:N ##1
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \__cs_split_function_auxi:w
+ \cs_to_str:N ##1 \s__cs_mark \c_true_bool
+ #1 \s__cs_mark \c_false_bool \s__cs_stop
+ }
+ \cs_gset:Npn \__cs_split_function_auxi:w
+ ##1 #1 ##2 \s__cs_mark ##3##4 \s__cs_stop
+ { \__cs_split_function_auxii:w ##1 \s__cs_mark \s__cs_stop {##2} ##3 }
+ \cs_gset:Npn \__cs_split_function_auxii:w ##1 \s__cs_mark ##2 \s__cs_stop
+ { {##1} }
+ }
+\exp_after:wN \__cs_tmp:w \token_to_str:N :
+\prg_gset_conditional:Npnn \cs_if_exist:N #1 { p , T , F , TF }
+ {
+ \if_meaning:w #1 \scan_stop:
+ \use_i:nnnn
+ \else:
+ \fi:
+ \if_cs_exist:N #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_if_exist:NTF \tex_lastnamedcs:D
+ {
+ \prg_gset_conditional:Npnn \cs_if_exist:c #1 { p , T , F , TF }
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \__cs_if_exist_c_aux:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ \cs_gset:Npn \__cs_if_exist_c_aux:
+ { \fi: \exp_after:wN \if_meaning:w \tex_lastnamedcs:D \scan_stop: \else: }
+ }
+ {
+ \prg_gset_conditional:Npnn \cs_if_exist:c #1 { p , T , F , TF }
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \__cs_if_exist_c_aux:w
+ \fi:
+ \use_none:n {#1}
+ \if_false:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ \cs_gset:Npn \__cs_if_exist_c_aux:w \fi: \use_none:n #1 \if_false:
+ { \fi: \exp_after:wN \if_meaning:w \cs:w #1 \cs_end: \scan_stop: \else: }
+ }
+\prg_gset_conditional:Npnn \cs_if_free:N #1 { p , T , F , TF }
+ {
+ \if_cs_exist:N #1
+ \else:
+ \use_none:nnnn
+ \fi:
+ \if_meaning:w #1 \scan_stop:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_if_exist:NTF \tex_lastnamedcs:D
+ {
+ \prg_gset_conditional:Npnn \cs_if_free:c #1 { p , T , F , TF }
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \__cs_if_free_c_aux:w
+ \fi:
+ \if_true:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ \cs_gset:Npn \__cs_if_free_c_aux:w \fi: \if_true:
+ { \fi: \exp_after:wN \if_meaning:w \tex_lastnamedcs:D \scan_stop: }
+ }
+ {
+ \prg_gset_conditional:Npnn \cs_if_free:c #1 { p , T , F , TF }
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \__cs_if_free_c_aux:w
+ \fi:
+ \use_none:n {#1}
+ \if_true:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ \cs_gset:Npn \__cs_if_free_c_aux:w \fi: \use_none:n #1 \if_true:
+ { \fi: \exp_after:wN \if_meaning:w \cs:w #1 \cs_end: \scan_stop: }
+ }
+\cs_gset:Npn \cs_if_exist_use:NTF #1#2
+ { \cs_if_exist:NTF #1 { #1 #2 } }
+\cs_gset:Npn \cs_if_exist_use:NF #1
+ { \cs_if_exist:NTF #1 #1 }
+\cs_gset:Npn \cs_if_exist_use:NT #1 #2
+ { \cs_if_exist:NT #1 { #1 #2 } }
+\cs_gset:Npn \cs_if_exist_use:N #1
+ { \cs_if_exist:NT #1 #1 }
+\cs_if_exist:NTF \tex_lastnamedcs:D
+ {
+ \cs_gset:Npn \cs_if_exist_use:cTF #1
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \__cs_if_exist_use_aux:w
+ \fi:
+ \use_ii:nn
+ }
+ \cs_gset:Npn \__cs_if_exist_use_aux:w \fi: \use_ii:nn
+ { \fi: \exp_after:wN \__cs_if_exist_use_aux:Nnn \tex_lastnamedcs:D }
+ }
+ {
+ \cs_gset:Npn \cs_if_exist_use:cTF #1
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \__cs_if_exist_use_aux:w
+ \fi:
+ \use_iii:nnn {#1}
+ }
+ \cs_gset:Npn \__cs_if_exist_use_aux:w \fi: \use_iii:nnn #1
+ { \fi: \exp_after:wN \__cs_if_exist_use_aux:Nnn \cs:w #1 \cs_end: }
+ }
+\cs_gset:Npn \__cs_if_exist_use_aux:Nnn #1#2
+ {
+ \if_meaning:w #1 \scan_stop:
+ \exp_after:wN \use_iii:nnn
+ \fi:
+ \use_i:nn { #1 #2 }
+ }
+\cs_gset:Npn \cs_if_exist_use:cF #1
+ { \cs_if_exist_use:cTF {#1} {} }
+\cs_gset:Npn \cs_if_exist_use:cT #1#2
+ { \cs_if_exist_use:cTF {#1} {#2} {} }
+\cs_gset:Npn \cs_if_exist_use:c #1
+ { \cs_if_exist_use:cTF {#1} {} {} }
+\cs_gset_protected:Npn \msg_error:nnee #1#2#3#4
+ {
+ \tex_newlinechar:D = `\^^J \scan_stop:
+ \tex_errmessage:D
+ {
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!~! ^^J
+ Argh,~internal~LaTeX3~error! ^^J ^^J
+ Module ~ #1 , ~ message~name~"#2": ^^J
+ Arguments~'#3'~and~'#4' ^^J ^^J
+ This~is~one~for~The~LaTeX3~Project:~bailing~out
+ }
+ \tex_end:D
+ }
+\cs_gset_protected:Npn \msg_error:nne #1#2#3
+ { \msg_error:nnee {#1} {#2} {#3} { } }
+\cs_gset_protected:Npn \msg_error:nn #1#2
+ { \msg_error:nnee {#1} {#2} { } { } }
+\cs_gset:Npn \msg_line_context:
+ { on~line~ \tex_the:D \tex_inputlineno:D }
+\cs_gset_protected:Npn \iow_log:e
+ { \tex_immediate:D \tex_write:D -1 }
+\cs_gset_protected:Npn \iow_term:e
+ { \tex_immediate:D \tex_write:D 16 }
+\cs_gset_protected:Npn \__kernel_chk_if_free_cs:N #1
+ {
+ \cs_if_free:NF #1
+ {
+ \msg_error:nnee { kernel } { command-already-defined }
+ { \token_to_str:N #1 } { \token_to_meaning:N #1 }
+ }
+ }
+\cs_gset_protected:Npn \__kernel_chk_if_free_cs:c
+ { \exp_args:Nc \__kernel_chk_if_free_cs:N }
+\cs_set:Npn \__cs_tmp:w #1#2
+ {
+ \cs_gset_protected:Npn #1 ##1
+ {
+ \__kernel_chk_if_free_cs:N ##1
+ #2 ##1
+ }
+ }
+\__cs_tmp:w \cs_new_nopar:Npn \cs_gset_nopar:Npn
+\__cs_tmp:w \cs_new_nopar:Npe \cs_gset_nopar:Npe
+\__cs_tmp:w \cs_new_nopar:Npx \cs_gset_nopar:Npx
+\__cs_tmp:w \cs_new:Npn \cs_gset:Npn
+\__cs_tmp:w \cs_new:Npe \cs_gset:Npe
+\__cs_tmp:w \cs_new:Npx \cs_gset:Npx
+\__cs_tmp:w \cs_new_protected_nopar:Npn \cs_gset_protected_nopar:Npn
+\__cs_tmp:w \cs_new_protected_nopar:Npe \cs_gset_protected_nopar:Npe
+\__cs_tmp:w \cs_new_protected_nopar:Npx \cs_gset_protected_nopar:Npx
+\__cs_tmp:w \cs_new_protected:Npn \cs_gset_protected:Npn
+\__cs_tmp:w \cs_new_protected:Npe \cs_gset_protected:Npe
+\__cs_tmp:w \cs_new_protected:Npx \cs_gset_protected:Npx
+\cs_set:Npn \__cs_tmp:w #1#2
+ { \cs_new_protected_nopar:Npn #1 { \exp_args:Nc #2 } }
+\__cs_tmp:w \cs_set_nopar:cpn \cs_set_nopar:Npn
+\__cs_tmp:w \cs_set_nopar:cpe \cs_set_nopar:Npe
+\__cs_tmp:w \cs_set_nopar:cpx \cs_set_nopar:Npx
+\__cs_tmp:w \cs_gset_nopar:cpn \cs_gset_nopar:Npn
+\__cs_tmp:w \cs_gset_nopar:cpe \cs_gset_nopar:Npe
+\__cs_tmp:w \cs_gset_nopar:cpx \cs_gset_nopar:Npx
+\__cs_tmp:w \cs_new_nopar:cpn \cs_new_nopar:Npn
+\__cs_tmp:w \cs_new_nopar:cpe \cs_new_nopar:Npe
+\__cs_tmp:w \cs_new_nopar:cpx \cs_new_nopar:Npx
+\__cs_tmp:w \cs_set:cpn \cs_set:Npn
+\__cs_tmp:w \cs_set:cpe \cs_set:Npe
+\__cs_tmp:w \cs_set:cpx \cs_set:Npx
+\__cs_tmp:w \cs_gset:cpn \cs_gset:Npn
+\__cs_tmp:w \cs_gset:cpe \cs_gset:Npe
+\__cs_tmp:w \cs_gset:cpx \cs_gset:Npx
+\__cs_tmp:w \cs_new:cpn \cs_new:Npn
+\__cs_tmp:w \cs_new:cpe \cs_new:Npe
+\__cs_tmp:w \cs_new:cpx \cs_new:Npx
+\__cs_tmp:w \cs_set_protected_nopar:cpn \cs_set_protected_nopar:Npn
+\__cs_tmp:w \cs_set_protected_nopar:cpe \cs_set_protected_nopar:Npe
+\__cs_tmp:w \cs_set_protected_nopar:cpx \cs_set_protected_nopar:Npx
+\__cs_tmp:w \cs_gset_protected_nopar:cpn \cs_gset_protected_nopar:Npn
+\__cs_tmp:w \cs_gset_protected_nopar:cpe \cs_gset_protected_nopar:Npe
+\__cs_tmp:w \cs_gset_protected_nopar:cpx \cs_gset_protected_nopar:Npx
+\__cs_tmp:w \cs_new_protected_nopar:cpn \cs_new_protected_nopar:Npn
+\__cs_tmp:w \cs_new_protected_nopar:cpe \cs_new_protected_nopar:Npe
+\__cs_tmp:w \cs_new_protected_nopar:cpx \cs_new_protected_nopar:Npx
+\__cs_tmp:w \cs_set_protected:cpn \cs_set_protected:Npn
+\__cs_tmp:w \cs_set_protected:cpe \cs_set_protected:Npe
+\__cs_tmp:w \cs_set_protected:cpx \cs_set_protected:Npx
+\__cs_tmp:w \cs_gset_protected:cpn \cs_gset_protected:Npn
+\__cs_tmp:w \cs_gset_protected:cpe \cs_gset_protected:Npe
+\__cs_tmp:w \cs_gset_protected:cpx \cs_gset_protected:Npx
+\__cs_tmp:w \cs_new_protected:cpn \cs_new_protected:Npn
+\__cs_tmp:w \cs_new_protected:cpe \cs_new_protected:Npe
+\__cs_tmp:w \cs_new_protected:cpx \cs_new_protected:Npx
+\cs_new_protected:Npn \cs_set_eq:NN #1 { \tex_let:D #1 =~ }
+\cs_new_protected:Npn \cs_set_eq:cN { \exp_args:Nc \cs_set_eq:NN }
+\cs_new_protected:Npn \cs_set_eq:Nc { \exp_args:NNc \cs_set_eq:NN }
+\cs_new_protected:Npn \cs_set_eq:cc { \exp_args:Ncc \cs_set_eq:NN }
+\cs_new_protected:Npn \cs_gset_eq:NN { \tex_global:D \cs_set_eq:NN }
+\cs_new_protected:Npn \cs_gset_eq:Nc { \exp_args:NNc \cs_gset_eq:NN }
+\cs_new_protected:Npn \cs_gset_eq:cN { \exp_args:Nc \cs_gset_eq:NN }
+\cs_new_protected:Npn \cs_gset_eq:cc { \exp_args:Ncc \cs_gset_eq:NN }
+\cs_new_protected:Npn \cs_new_eq:NN #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \tex_global:D \cs_set_eq:NN #1
+ }
+\cs_new_protected:Npn \cs_new_eq:cN { \exp_args:Nc \cs_new_eq:NN }
+\cs_new_protected:Npn \cs_new_eq:Nc { \exp_args:NNc \cs_new_eq:NN }
+\cs_new_protected:Npn \cs_new_eq:cc { \exp_args:Ncc \cs_new_eq:NN }
+\cs_new_protected:Npn \cs_undefine:N #1
+ { \cs_gset_eq:NN #1 \tex_undefined:D }
+\cs_new_protected:Npn \cs_undefine:c #1
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \else:
+ \use_i:nnnn
+ \fi:
+ \exp_args:Nc \cs_undefine:N {#1}
+ }
+\cs_new_protected:Npn \__kernel_cs_parm_from_arg_count:nnF #1#2
+ {
+ \exp_args:Ne \__cs_parm_from_arg_count_test:nnF
+ {
+ \exp_after:wN \exp_not:n
+ \if_case:w \int_eval:n {#2}
+ { }
+ \or: { ##1 }
+ \or: { ##1##2 }
+ \or: { ##1##2##3 }
+ \or: { ##1##2##3##4 }
+ \or: { ##1##2##3##4##5 }
+ \or: { ##1##2##3##4##5##6 }
+ \or: { ##1##2##3##4##5##6##7 }
+ \or: { ##1##2##3##4##5##6##7##8 }
+ \or: { ##1##2##3##4##5##6##7##8##9 }
+ \else: { \c_false_bool }
+ \fi:
+ }
+ {#1}
+ }
+\cs_new_protected:Npn \__cs_parm_from_arg_count_test:nnF #1#2
+ {
+ \if_meaning:w \c_false_bool #1
+ \exp_after:wN \use_ii:nn
+ \else:
+ \exp_after:wN \use_i:nn
+ \fi:
+ { #2 {#1} }
+ }
+\cs_new:Npn \__cs_count_signature:N #1
+ { \exp_args:Nf \__cs_count_signature:n { \cs_split_function:N #1 } }
+\cs_new:Npn \__cs_count_signature:n #1
+ { \int_eval:n { \__cs_count_signature:nnN #1 } }
+\cs_new:Npn \__cs_count_signature:nnN #1#2#3
+ {
+ \if_meaning:w \c_true_bool #3
+ \tl_count:n {#2}
+ \else:
+ -1
+ \fi:
+ }
+\cs_new:Npn \__cs_count_signature:c
+ { \exp_args:Nc \__cs_count_signature:N }
+\cs_new_protected:Npn \cs_generate_from_arg_count:NNnn #1#2#3#4
+ {
+ \__kernel_cs_parm_from_arg_count:nnF { \use:nnn #2 #1 } {#3}
+ {
+ \msg_error:nnee { kernel } { bad-number-of-arguments }
+ { \token_to_str:N #1 } { \int_eval:n {#3} }
+ \use_none:n
+ }
+ {#4}
+ }
+\cs_new_protected:Npn \cs_generate_from_arg_count:cNnn
+ { \exp_args:Nc \cs_generate_from_arg_count:NNnn }
+\cs_new_protected:Npn \cs_generate_from_arg_count:Ncnn
+ { \exp_args:NNc \cs_generate_from_arg_count:NNnn }
+\cs_set:Npn \__cs_tmp:w #1#2#3
+ {
+ \cs_new_protected:cpx { cs_ #1 : #2 }
+ {
+ \exp_not:N \__cs_generate_from_signature:NNn
+ \exp_after:wN \exp_not:N \cs:w cs_ #1 : #3 \cs_end:
+ }
+ }
+\cs_new_protected:Npn \__cs_generate_from_signature:NNn #1#2
+ {
+ \use:e
+ {
+ \__cs_generate_from_signature:nnNNNn
+ \cs_split_function:N #2
+ }
+ #1 #2
+ }
+\cs_new_protected:Npn \__cs_generate_from_signature:nnNNNn #1#2#3#4#5#6
+ {
+ \bool_if:NTF #3
+ {
+ \cs_set_nopar:Npx \__cs_tmp:w
+ { \tl_map_function:nN {#2} \__cs_generate_from_signature:n }
+ \tl_if_empty:oF \__cs_tmp:w
+ {
+ \msg_error:nneee { kernel } { non-base-function }
+ { \token_to_str:N #5 } {#2} { \__cs_tmp:w }
+ }
+ \cs_generate_from_arg_count:NNnn
+ #5 #4 { \tl_count:n {#2} } {#6}
+ }
+ {
+ \msg_error:nne { kernel } { missing-colon }
+ { \token_to_str:N #5 }
+ }
+ }
+\cs_new:Npn \__cs_generate_from_signature:n #1
+ {
+ \if:w n #1 \else: \if:w N #1 \else:
+ \if:w T #1 \else: \if:w F #1 \else: #1 \fi: \fi: \fi: \fi:
+ }
+\__cs_tmp:w { set } { Nn } { Npn }
+\__cs_tmp:w { set } { Ne } { Npe }
+\__cs_tmp:w { set } { Nx } { Npx }
+\__cs_tmp:w { set_nopar } { Nn } { Npn }
+\__cs_tmp:w { set_nopar } { Ne } { Npe }
+\__cs_tmp:w { set_nopar } { Nx } { Npx }
+\__cs_tmp:w { set_protected } { Nn } { Npn }
+\__cs_tmp:w { set_protected } { Ne } { Npe }
+\__cs_tmp:w { set_protected } { Nx } { Npx }
+\__cs_tmp:w { set_protected_nopar } { Nn } { Npn }
+\__cs_tmp:w { set_protected_nopar } { Ne } { Npe }
+\__cs_tmp:w { set_protected_nopar } { Nx } { Npx }
+\__cs_tmp:w { gset } { Nn } { Npn }
+\__cs_tmp:w { gset } { Ne } { Npe }
+\__cs_tmp:w { gset } { Nx } { Npx }
+\__cs_tmp:w { gset_nopar } { Nn } { Npn }
+\__cs_tmp:w { gset_nopar } { Ne } { Npe }
+\__cs_tmp:w { gset_nopar } { Nx } { Npx }
+\__cs_tmp:w { gset_protected } { Nn } { Npn }
+\__cs_tmp:w { gset_protected } { Ne } { Npe }
+\__cs_tmp:w { gset_protected } { Nx } { Npx }
+\__cs_tmp:w { gset_protected_nopar } { Nn } { Npn }
+\__cs_tmp:w { gset_protected_nopar } { Ne } { Npe }
+\__cs_tmp:w { gset_protected_nopar } { Nx } { Npx }
+\__cs_tmp:w { new } { Nn } { Npn }
+\__cs_tmp:w { new } { Ne } { Npe }
+\__cs_tmp:w { new } { Nx } { Npx }
+\__cs_tmp:w { new_nopar } { Nn } { Npn }
+\__cs_tmp:w { new_nopar } { Ne } { Npe }
+\__cs_tmp:w { new_nopar } { Nx } { Npx }
+\__cs_tmp:w { new_protected } { Nn } { Npn }
+\__cs_tmp:w { new_protected } { Ne } { Npe }
+\__cs_tmp:w { new_protected } { Nx } { Npx }
+\__cs_tmp:w { new_protected_nopar } { Nn } { Npn }
+\__cs_tmp:w { new_protected_nopar } { Ne } { Npe }
+\__cs_tmp:w { new_protected_nopar } { Nx } { Npx }
+\cs_set:Npn \__cs_tmp:w #1#2
+ {
+ \cs_new_protected:cpx { cs_ #1 : c #2 }
+ {
+ \exp_not:N \exp_args:Nc
+ \exp_after:wN \exp_not:N \cs:w cs_ #1 : N #2 \cs_end:
+ }
+ }
+\__cs_tmp:w { set } { n }
+\__cs_tmp:w { set } { e }
+\__cs_tmp:w { set } { x }
+\__cs_tmp:w { set_nopar } { n }
+\__cs_tmp:w { set_nopar } { e }
+\__cs_tmp:w { set_nopar } { x }
+\__cs_tmp:w { set_protected } { n }
+\__cs_tmp:w { set_protected } { e }
+\__cs_tmp:w { set_protected } { x }
+\__cs_tmp:w { set_protected_nopar } { n }
+\__cs_tmp:w { set_protected_nopar } { e }
+\__cs_tmp:w { set_protected_nopar } { x }
+\__cs_tmp:w { gset } { n }
+\__cs_tmp:w { gset } { e }
+\__cs_tmp:w { gset } { x }
+\__cs_tmp:w { gset_nopar } { n }
+\__cs_tmp:w { gset_nopar } { e }
+\__cs_tmp:w { gset_nopar } { x }
+\__cs_tmp:w { gset_protected } { n }
+\__cs_tmp:w { gset_protected } { e }
+\__cs_tmp:w { gset_protected } { x }
+\__cs_tmp:w { gset_protected_nopar } { n }
+\__cs_tmp:w { gset_protected_nopar } { e }
+\__cs_tmp:w { gset_protected_nopar } { x }
+\__cs_tmp:w { new } { n }
+\__cs_tmp:w { new } { e }
+\__cs_tmp:w { new } { x }
+\__cs_tmp:w { new_nopar } { n }
+\__cs_tmp:w { new_nopar } { e }
+\__cs_tmp:w { new_nopar } { x }
+\__cs_tmp:w { new_protected } { n }
+\__cs_tmp:w { new_protected } { e }
+\__cs_tmp:w { new_protected } { x }
+\__cs_tmp:w { new_protected_nopar } { n }
+\__cs_tmp:w { new_protected_nopar } { e }
+\__cs_tmp:w { new_protected_nopar } { x }
+\prg_new_conditional:Npnn \cs_if_eq:NN #1#2 { p , T , F , TF }
+ {
+ \if_meaning:w #1#2
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\cs_new:Npn \cs_if_eq_p:cN { \exp_args:Nc \cs_if_eq_p:NN }
+\cs_new:Npn \cs_if_eq:cNTF { \exp_args:Nc \cs_if_eq:NNTF }
+\cs_new:Npn \cs_if_eq:cNT { \exp_args:Nc \cs_if_eq:NNT }
+\cs_new:Npn \cs_if_eq:cNF { \exp_args:Nc \cs_if_eq:NNF }
+\cs_new:Npn \cs_if_eq_p:Nc { \exp_args:NNc \cs_if_eq_p:NN }
+\cs_new:Npn \cs_if_eq:NcTF { \exp_args:NNc \cs_if_eq:NNTF }
+\cs_new:Npn \cs_if_eq:NcT { \exp_args:NNc \cs_if_eq:NNT }
+\cs_new:Npn \cs_if_eq:NcF { \exp_args:NNc \cs_if_eq:NNF }
+\cs_new:Npn \cs_if_eq_p:cc { \exp_args:Ncc \cs_if_eq_p:NN }
+\cs_new:Npn \cs_if_eq:ccTF { \exp_args:Ncc \cs_if_eq:NNTF }
+\cs_new:Npn \cs_if_eq:ccT { \exp_args:Ncc \cs_if_eq:NNT }
+\cs_new:Npn \cs_if_eq:ccF { \exp_args:Ncc \cs_if_eq:NNF }
+\cs_new_protected:Npn \__kernel_chk_defined:NT #1#2
+ {
+ \cs_if_exist:NTF #1
+ {#2}
+ {
+ \msg_error:nne { kernel } { variable-not-defined }
+ { \token_to_str:N #1 }
+ }
+ }
+\cs_new_protected:Npn \__kernel_register_show:N
+ { \__kernel_register_show_aux:NN \tl_show:n }
+\cs_new_protected:Npn \__kernel_register_show:c
+ { \exp_args:Nc \__kernel_register_show:N }
+\cs_new_protected:Npn \__kernel_register_log:N
+ { \__kernel_register_show_aux:NN \tl_log:n }
+\cs_new_protected:Npn \__kernel_register_log:c
+ { \exp_args:Nc \__kernel_register_log:N }
+\cs_new_protected:Npn \__kernel_register_show_aux:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ \exp_args:No \__kernel_register_show_aux:nNN
+ { \tex_the:D #2 } #2 #1
+ }
+ }
+\cs_new_protected:Npn \__kernel_register_show_aux:nNN #1#2#3
+ { \exp_args:No #3 { \token_to_str:N #2 = #1 } }
+\cs_new_protected:Npn \cs_show:N { \__kernel_show:NN \tl_show:n }
+\cs_new_protected:Npn \cs_show:c
+ { \group_begin: \exp_args:NNc \group_end: \cs_show:N }
+\cs_new_protected:Npn \cs_log:N { \__kernel_show:NN \tl_log:n }
+\cs_new_protected:Npn \cs_log:c
+ { \group_begin: \exp_args:NNc \group_end: \cs_log:N }
+\cs_new_protected:Npn \__kernel_show:NN #1#2
+ {
+ \group_begin:
+ \int_set:Nn \tex_escapechar:D { `\\ }
+ \exp_args:NNe
+ \group_end:
+ #1 { \token_to_str:N #2 = \cs_meaning:N #2 }
+ }
+\cs_new_protected:Npn \group_show_list:
+ { \__kernel_group_show:NN \use_none:n 1 }
+\cs_new_protected:Npn \group_log_list:
+ { \__kernel_group_show:NN \int_gzero:N 0 }
+\cs_new_protected:Npn \__kernel_group_show:NN #1#2
+ {
+ \use:e
+ {
+ #1 \tex_interactionmode:D
+ \int_set:Nn \tex_tracingonline:D {#2}
+ \int_set:Nn \tex_errorcontextlines:D { -1 }
+ \exp_not:N \exp_after:wN \scan_stop:
+ \tex_showgroups:D
+ \int_gset:Nn \tex_interactionmode:D
+ { \int_use:N \tex_interactionmode:D }
+ \int_set:Nn \tex_tracingonline:D
+ { \int_use:N \tex_tracingonline:D }
+ \int_set:Nn \tex_errorcontextlines:D
+ { \int_use:N \tex_errorcontextlines:D }
+ }
+ }
+\use:e
+ {
+ \exp_not:n { \cs_new:Npn \__kernel_prefix_arg_replacement:wN #1 }
+ \tl_to_str:n { macro : } \exp_not:n { #2 -> #3 \s__kernel_stop #4 }
+ }
+ { #4 {#1} {#2} {#3} }
+\cs_new:Npn \cs_prefix_spec:N #1
+ {
+ \token_if_macro:NTF #1
+ {
+ \exp_after:wN \__kernel_prefix_arg_replacement:wN
+ \token_to_meaning:N #1 \s__kernel_stop \use_i:nnn
+ }
+ { \scan_stop: }
+ }
+\cs_new:Npn \cs_parameter_spec:N #1
+ {
+ \token_if_macro:NTF #1
+ {
+ \exp_after:wN \__kernel_prefix_arg_replacement:wN
+ \token_to_meaning:N #1 \s__kernel_stop \use_ii:nnn
+ }
+ { \scan_stop: }
+ }
+\cs_new:Npn \cs_replacement_spec:N #1
+ {
+ \token_if_macro:NTF #1
+ {
+ \exp_after:wN \__kernel_prefix_arg_replacement:wN
+ \token_to_meaning:N #1 \s__kernel_stop \use_iii:nnn
+ }
+ { \scan_stop: }
+ }
+\cs_new:Npn \prg_do_nothing: { }
+\cs_new_eq:NN \prg_break_point:Nn \use_ii:nn
+\cs_new:Npn \prg_map_break:Nn #1#2#3 \prg_break_point:Nn #4#5
+ {
+ #5
+ \if_meaning:w #1 #4
+ \exp_after:wN \use_iii:nnn
+ \fi:
+ \prg_map_break:Nn #1 {#2}
+ }
+\cs_new_eq:NN \prg_break_point: \prg_do_nothing:
+\cs_new:Npn \prg_break: #1 \prg_break_point: { }
+\cs_new:Npn \prg_break:n #1#2 \prg_break_point: {#1}
+\cs_new_protected:Npn \mode_leave_vertical:
+ {
+ \if_mode_vertical:
+ \exp_after:wN \tex_indent:D
+ \fi:
+ }
+%% File: l3expan.dtx
+\cs_new:Npn \__exp_arg_next:nnn #1#2#3 { #2 \::: { #3 {#1} } }
+\cs_new:Npn \__exp_arg_next:Nnn #1#2#3 { #2 \::: { #3 #1 } }
+\cs_new:Npn \::: #1 {#1}
+\cs_new:Npn \::n #1 \::: #2#3 { #1 \::: { #2 {#3} } }
+\cs_new:Npn \::N #1 \::: #2#3 { #1 \::: {#2#3} }
+\cs_new:Npn \::p #1 \::: #2#3# { #1 \::: {#2#3} }
+\cs_new:Npn \::c #1 \::: #2#3
+ { \exp_after:wN \__exp_arg_next:Nnn \cs:w #3 \cs_end: {#1} {#2} }
+\cs_new:Npn \::o #1 \::: #2#3
+ { \exp_after:wN \__exp_arg_next:nnn \exp_after:wN {#3} {#1} {#2} }
+\cs_new:Npn \::e #1 \::: #2#3
+ { \tex_expanded:D { \exp_not:n { #1 \::: } { \exp_not:n {#2} {#3} } } }
+\cs_new:Npn \::f #1 \::: #2#3
+ {
+ \exp_after:wN \__exp_arg_next:nnn
+ \exp_after:wN { \exp:w \exp_end_continue_f:w #3 }
+ {#1} {#2}
+ }
+\use:nn { \cs_new_eq:NN \exp_stop_f: } { ~ }
+\cs_new_protected:Npn \::x #1 \::: #2#3
+ {
+ \cs_set_nopar:Npe \l__exp_internal_tl
+ { \exp_not:n { #1 \::: } { \exp_not:n {#2} {#3} } }
+ \l__exp_internal_tl
+ }
+\cs_new:Npn \::V #1 \::: #2#3
+ {
+ \exp_after:wN \__exp_arg_next:nnn
+ \exp_after:wN { \exp:w \__exp_eval_register:N #3 }
+ {#1} {#2}
+}
+\cs_new:Npn \::v #1 \::: #2#3
+ {
+ \exp_after:wN \__exp_arg_next:nnn
+ \exp_after:wN { \exp:w \__exp_eval_register:c {#3} }
+ {#1} {#2}
+ }
+\cs_new:Npn \__exp_eval_register:N #1
+ {
+ \exp_after:wN \if_meaning:w \exp_not:N #1 #1
+ \if_meaning:w \scan_stop: #1
+ \__exp_eval_error_msg:w
+ \fi:
+ \else:
+ \exp_after:wN \use_i_ii:nnn
+ \fi:
+ \exp_after:wN \exp_end: \tex_the:D #1
+ }
+\cs_new:Npn \__exp_eval_register:c #1
+ { \exp_after:wN \__exp_eval_register:N \cs:w #1 \cs_end: }
+\cs_new:Npn \__exp_eval_error_msg:w #1 \tex_the:D #2
+ {
+ \fi:
+ \fi:
+ \msg_expandable_error:nnn { kernel } { bad-variable } {#2}
+ \exp_end:
+ }
+\cs_new:Npn \exp_args:NNc #1#2#3
+ { \exp_after:wN #1 \exp_after:wN #2 \cs:w # 3\cs_end: }
+\cs_new:Npn \exp_args:Ncc #1#2#3
+ { \exp_after:wN #1 \cs:w #2 \exp_after:wN \cs_end: \cs:w #3 \cs_end: }
+\cs_new:Npn \exp_args:Nccc #1#2#3#4
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \cs:w #3 \exp_after:wN \cs_end:
+ \cs:w #4 \cs_end:
+ }
+\cs_new:Npn \exp_args:No #1#2 { \exp_after:wN #1 \exp_after:wN {#2} }
+\cs_new:Npn \exp_args:NNo #1#2#3
+ { \exp_after:wN #1 \exp_after:wN #2 \exp_after:wN {#3} }
+\cs_new:Npn \exp_args:NNNo #1#2#3#4
+ { \exp_after:wN #1 \exp_after:wN#2 \exp_after:wN #3 \exp_after:wN {#4} }
+\cs_new:Npn \exp_args:Ne #1#2
+ { \exp_after:wN #1 \tex_expanded:D { {#2} } }
+\cs_new:Npn \exp_args:Nf #1#2
+ { \exp_after:wN #1 \exp_after:wN { \exp:w \exp_end_continue_f:w #2 } }
+\cs_new:Npn \exp_args:Nv #1#2
+ {
+ \exp_after:wN #1 \exp_after:wN
+ { \exp:w \__exp_eval_register:c {#2} }
+ }
+\cs_new:Npn \exp_args:NV #1#2
+ {
+ \exp_after:wN #1 \exp_after:wN
+ { \exp:w \__exp_eval_register:N #2 }
+ }
+\cs_new:Npn \exp_args:NNV #1#2#3
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN { \exp:w \__exp_eval_register:N #3 }
+ }
+\cs_new:Npn \exp_args:NNv #1#2#3
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN { \exp:w \__exp_eval_register:c {#3} }
+ }
+\cs_new:Npn \exp_args:NNe #1#2#3
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \tex_expanded:D { {#3} }
+ }
+\cs_new:Npn \exp_args:NNf #1#2#3
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN { \exp:w \exp_end_continue_f:w #3 }
+ }
+\cs_new:Npn \exp_args:Nco #1#2#3
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \exp_after:wN {#3}
+ }
+\cs_new:Npn \exp_args:NcV #1#2#3
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \exp_after:wN { \exp:w \__exp_eval_register:N #3 }
+ }
+\cs_new:Npn \exp_args:Ncv #1#2#3
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \exp_after:wN { \exp:w \__exp_eval_register:c {#3} }
+ }
+\cs_new:Npn \exp_args:Ncf #1#2#3
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \exp_after:wN { \exp:w \exp_end_continue_f:w #3 }
+ }
+\cs_new:Npn \exp_args:NVV #1#2#3
+ {
+ \exp_after:wN #1
+ \exp_after:wN { \exp:w \exp_after:wN
+ \__exp_eval_register:N \exp_after:wN #2 \exp_after:wN }
+ \exp_after:wN { \exp:w \__exp_eval_register:N #3 }
+ }
+\cs_new:Npn \exp_args:NNNV #1#2#3#4
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN { \exp:w \__exp_eval_register:N #4 }
+ }
+\cs_new:Npn \exp_args:NNNv #1#2#3#4
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN { \exp:w \__exp_eval_register:c {#4} }
+ }
+\cs_new:Npn \exp_args:NNNe #1#2#3#4
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \tex_expanded:D { {#4} }
+ }
+\cs_new:Npn \exp_args:NcNc #1#2#3#4
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \exp_after:wN #3
+ \cs:w #4 \cs_end:
+ }
+\cs_new:Npn \exp_args:NcNo #1#2#3#4
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \exp_after:wN #3
+ \exp_after:wN {#4}
+ }
+\cs_new:Npn \exp_args:Ncco #1#2#3#4
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \cs:w #3 \exp_after:wN \cs_end:
+ \exp_after:wN {#4}
+ }
+\cs_new_protected:Npn \exp_args:Nx #1#2
+ { \use:x { \exp_not:N #1 {#2} } }
+\cs_new:Npn \__exp_arg_last_unbraced:nn #1#2 { #2#1 }
+\cs_new:Npn \::o_unbraced \::: #1#2
+ { \exp_after:wN \__exp_arg_last_unbraced:nn \exp_after:wN {#2} {#1} }
+\cs_new:Npn \::V_unbraced \::: #1#2
+ {
+ \exp_after:wN \__exp_arg_last_unbraced:nn
+ \exp_after:wN { \exp:w \__exp_eval_register:N #2 } {#1}
+ }
+\cs_new:Npn \::v_unbraced \::: #1#2
+ {
+ \exp_after:wN \__exp_arg_last_unbraced:nn
+ \exp_after:wN { \exp:w \__exp_eval_register:c {#2} } {#1}
+ }
+\cs_new:Npn \::e_unbraced \::: #1#2
+ { \tex_expanded:D { \exp_not:n {#1} #2 } }
+\cs_new:Npn \::f_unbraced \::: #1#2
+ {
+ \exp_after:wN \__exp_arg_last_unbraced:nn
+ \exp_after:wN { \exp:w \exp_end_continue_f:w #2 } {#1}
+ }
+\cs_new_protected:Npn \::x_unbraced \::: #1#2
+ {
+ \cs_set_nopar:Npe \l__exp_internal_tl { \exp_not:n {#1} #2 }
+ \l__exp_internal_tl
+ }
+\cs_new:Npn \exp_last_unbraced:No #1#2 { \exp_after:wN #1 #2 }
+\cs_new:Npn \exp_last_unbraced:NV #1#2
+ { \exp_after:wN #1 \exp:w \__exp_eval_register:N #2 }
+\cs_new:Npn \exp_last_unbraced:Nv #1#2
+ { \exp_after:wN #1 \exp:w \__exp_eval_register:c {#2} }
+\cs_new:Npn \exp_last_unbraced:Ne #1#2
+ { \exp_after:wN #1 \tex_expanded:D {#2} }
+\cs_new:Npn \exp_last_unbraced:Nf #1#2
+ { \exp_after:wN #1 \exp:w \exp_end_continue_f:w #2 }
+\cs_new:Npn \exp_last_unbraced:NNo #1#2#3
+ { \exp_after:wN #1 \exp_after:wN #2 #3 }
+\cs_new:Npn \exp_last_unbraced:NNV #1#2#3
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp:w \__exp_eval_register:N #3
+ }
+\cs_new:Npn \exp_last_unbraced:NNf #1#2#3
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp:w \exp_end_continue_f:w #3
+ }
+\cs_new:Npn \exp_last_unbraced:Nco #1#2#3
+ { \exp_after:wN #1 \cs:w #2 \exp_after:wN \cs_end: #3 }
+\cs_new:Npn \exp_last_unbraced:NcV #1#2#3
+ {
+ \exp_after:wN #1
+ \cs:w #2 \exp_after:wN \cs_end:
+ \exp:w \__exp_eval_register:N #3
+ }
+\cs_new:Npn \exp_last_unbraced:NNNo #1#2#3#4
+ { \exp_after:wN #1 \exp_after:wN #2 \exp_after:wN #3 #4 }
+\cs_new:Npn \exp_last_unbraced:NNNV #1#2#3#4
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp:w \__exp_eval_register:N #4
+ }
+\cs_new:Npn \exp_last_unbraced:NNNf #1#2#3#4
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp:w \exp_end_continue_f:w #4
+ }
+\cs_new:Npn \exp_last_unbraced:Nno { \::n \::o_unbraced \::: }
+\cs_new:Npn \exp_last_unbraced:Nnf { \::n \::f_unbraced \::: }
+\cs_new:Npn \exp_last_unbraced:Noo { \::o \::o_unbraced \::: }
+\cs_new:Npn \exp_last_unbraced:Nfo { \::f \::o_unbraced \::: }
+\cs_new:Npn \exp_last_unbraced:NnNo { \::n \::N \::o_unbraced \::: }
+\cs_new:Npn \exp_last_unbraced:NNNNo #1#2#3#4#5
+ { \exp_after:wN #1 \exp_after:wN #2 \exp_after:wN #3 \exp_after:wN #4 #5 }
+\cs_new:Npn \exp_last_unbraced:NNNNf #1#2#3#4#5
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN #4
+ \exp:w \exp_end_continue_f:w #5
+ }
+\cs_new_protected:Npn \exp_last_unbraced:Nx { \::x_unbraced \::: }
+\cs_new:Npn \exp_last_two_unbraced:Noo #1#2#3
+ { \exp_after:wN \__exp_last_two_unbraced:noN \exp_after:wN {#3} {#2} #1 }
+\cs_new:Npn \__exp_last_two_unbraced:noN #1#2#3
+ { \exp_after:wN #3 #2 #1 }
+\cs_new_eq:NN \__kernel_exp_not:w \tex_unexpanded:D
+\cs_new:Npn \exp_not:c #1 { \exp_after:wN \exp_not:N \cs:w #1 \cs_end: }
+\cs_new:Npn \exp_not:o #1 { \__kernel_exp_not:w \exp_after:wN {#1} }
+\cs_new:Npn \exp_not:e #1
+ { \__kernel_exp_not:w \tex_expanded:D { {#1} } }
+\cs_new:Npn \exp_not:f #1
+ { \__kernel_exp_not:w \exp_after:wN { \exp:w \exp_end_continue_f:w #1 } }
+\cs_new:Npn \exp_not:V #1
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ { \exp:w \__exp_eval_register:N #1 }
+ }
+\cs_new:Npn \exp_not:v #1
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ { \exp:w \__exp_eval_register:c {#1} }
+ }
+\group_begin:
+ \tex_catcode:D `\^^@ = 13
+ \cs_new_protected:Npn \exp_end_continue_f:w { `^^@ }
+ \if_cs_exist:N ^^@
+ \else:
+ \cs_new:Npn ^^@
+ { \msg_expandable_error:nn { kernel } { bad-exp-end-f } }
+ \fi:
+ \cs_new:Npn \exp_end_continue_f:nw #1 { `^^@ #1 }
+\group_end:
+\cs_new_eq:NN \s__cs_mark \scan_stop:
+\cs_new_eq:NN \s__cs_stop \scan_stop:
+\cs_new:Npn \q__cs_recursion_stop { \q__cs_recursion_stop }
+\cs_new:Npn \__cs_use_none_delimit_by_s_stop:w #1 \s__cs_stop { }
+\cs_new:Npn \__cs_use_i_delimit_by_s_stop:nw #1 #2 \s__cs_stop {#1}
+\cs_new:Npn \__cs_use_none_delimit_by_q_recursion_stop:w
+ #1 \q__cs_recursion_stop { }
+\cs_new_protected:Npn \cs_generate_variant:Nn #1#2
+ {
+ \__cs_generate_variant:N #1
+ \use:e
+ {
+ \__cs_generate_variant:nnNN
+ \cs_split_function:N #1
+ \exp_not:N #1
+ \tl_to_str:n {#2} ,
+ \exp_not:N \scan_stop: ,
+ \exp_not:N \q__cs_recursion_stop
+ }
+ }
+\cs_new_protected:Npn \cs_generate_variant:cn
+ { \exp_args:Nc \cs_generate_variant:Nn }
+\cs_new_protected:Npe \__cs_generate_variant:N #1
+ {
+ \exp_not:N \exp_after:wN \exp_not:N \if_meaning:w
+ \exp_not:N \exp_not:N #1 #1
+ \cs_set_eq:NN \exp_not:N \__cs_tmp:w \cs_new_protected:Npe
+ \exp_not:N \else:
+ \exp_not:N \exp_after:wN \exp_not:N \__cs_generate_variant:ww
+ \exp_not:N \token_to_meaning:N #1 \tl_to_str:n { ma }
+ \s__cs_mark
+ \s__cs_mark \cs_new_protected:Npe
+ \tl_to_str:n { pr }
+ \s__cs_mark \cs_new:Npe
+ \s__cs_stop
+ \exp_not:N \fi:
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new_protected:Npn \__cs_generate_variant:ww
+ #1 { \tl_to_str:n { ma } } #2 \s__cs_mark
+ { \__cs_generate_variant:wwNw #1 }
+\exp_last_unbraced:NNNNo
+ \cs_new_protected:Npn \__cs_generate_variant:wwNw
+ #1 { \tl_to_str:n { pr } } #2 \s__cs_mark #3 #4 \s__cs_stop
+ { \cs_set_eq:NN \__cs_tmp:w #3 }
+\cs_new_protected:Npn \__cs_generate_variant:nnNN #1#2#3#4
+ {
+ \if_meaning:w \c_false_bool #3
+ \msg_error:nne { kernel } { missing-colon }
+ { \token_to_str:c {#1} }
+ \exp_after:wN \__cs_use_none_delimit_by_q_recursion_stop:w
+ \fi:
+ \__cs_generate_variant:Nnnw #4 {#1}{#2}
+ }
+\cs_new_protected:Npn \__cs_generate_variant:Nnnw #1#2#3#4 ,
+ {
+ \if_meaning:w \scan_stop: #4
+ \exp_after:wN \__cs_use_none_delimit_by_q_recursion_stop:w
+ \fi:
+ \use:e
+ {
+ \exp_not:N \__cs_generate_variant:wwNN
+ \__cs_generate_variant_loop:nNwN { }
+ #4
+ \__cs_generate_variant_loop_end:nwwwNNnn
+ \s__cs_mark
+ #3 ~
+ { ~ { } \fi: \__cs_generate_variant_loop_long:wNNnn } ~
+ { }
+ \s__cs_stop
+ \exp_not:N #1 {#2} {#4}
+ }
+ \__cs_generate_variant:Nnnw #1 {#2} {#3}
+ }
+\cs_new:Npn \__cs_generate_variant_loop:nNwN #1#2#3 \s__cs_mark #4
+ {
+ \if:w #2 #4
+ \exp_after:wN \__cs_generate_variant_loop_same:w
+ \else:
+ \if:w #4 \__cs_generate_variant_loop_base:N #2 \else:
+ \if:w 0
+ \if:w N #4 \else: \if:w n #4 \else: 1 \fi: \fi:
+ \if:w \scan_stop: \__cs_generate_variant_loop_base:N #2 1 \fi:
+ 0
+ \__cs_generate_variant_loop_special:NNwNNnn #4#2
+ \else:
+ \__cs_generate_variant_loop_invalid:NNwNNnn #4#2
+ \fi:
+ \fi:
+ \fi:
+ #1
+ \prg_do_nothing:
+ #2
+ \__cs_generate_variant_loop:nNwN { } #3 \s__cs_mark
+ }
+\cs_new:Npn \__cs_generate_variant_loop_base:N #1
+ {
+ \if:w c #1 N \else:
+ \if:w o #1 n \else:
+ \if:w V #1 n \else:
+ \if:w v #1 n \else:
+ \if:w f #1 n \else:
+ \if:w e #1 n \else:
+ \if:w x #1 n \else:
+ \if:w n #1 n \else:
+ \if:w N #1 N \else:
+ \scan_stop:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ }
+\cs_new:Npn \__cs_generate_variant_loop_same:w
+ #1 \prg_do_nothing: #2#3#4
+ { #3 { #1 \__cs_generate_variant_same:N #2 } }
+\cs_new:Npn \__cs_generate_variant_loop_end:nwwwNNnn
+ #1#2 \s__cs_mark #3 ~ #4 \s__cs_stop #5#6#7#8
+ {
+ \scan_stop: \scan_stop: \fi:
+ \s__cs_mark \s__cs_stop
+ \exp_not:N #6
+ \exp_not:c { #7 : #8 #1 #3 }
+ }
+\cs_new:Npn \__cs_generate_variant_loop_long:wNNnn #1 \s__cs_stop #2#3#4#5
+ {
+ \exp_not:n
+ {
+ \s__cs_mark
+ \msg_error:nnee { kernel } { variant-too-long }
+ {#5} { \token_to_str:N #3 }
+ \use_none:nnn
+ \s__cs_stop
+ #3
+ #3
+ }
+ }
+\cs_new:Npn \__cs_generate_variant_loop_invalid:NNwNNnn
+ #1#2 \fi: \fi: \fi: #3 \s__cs_stop #4#5#6#7
+ {
+ \fi: \fi: \fi:
+ \exp_not:n
+ {
+ \s__cs_mark
+ \msg_error:nneeee { kernel } { invalid-variant }
+ {#7} { \token_to_str:N #5 } {#1} {#2}
+ \use_none:nnn
+ \s__cs_stop
+ #5
+ #5
+ }
+ }
+\cs_new:Npn \__cs_generate_variant_loop_special:NNwNNnn
+ #1#2#3 \s__cs_stop #4#5#6#7
+ {
+ #3 \s__cs_stop #4 #5 {#6} {#7}
+ \exp_not:n
+ {
+ \msg_error:nneeee
+ { kernel } { deprecated-variant }
+ {#7} { \token_to_str:N #5 } {#1} {#2}
+ }
+ }
+\cs_new:Npn \__cs_generate_variant_same:N #1
+ {
+ \if:w N #1 #1 \else:
+ \if:w p #1 #1 \else:
+ \token_to_str:N n
+ \if:w n #1 \else:
+ \__cs_generate_variant_loop_special:NNwNNnn #1#1
+ \fi:
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \__cs_generate_variant:wwNN
+ #1 \s__cs_mark #2 \s__cs_stop #3#4
+ {
+ #2
+ \cs_if_free:NT #4
+ {
+ \group_begin:
+ \__cs_generate_internal_variant:n {#1}
+ \__cs_tmp:w #4 { \exp_not:c { exp_args:N #1 } \exp_not:N #3 }
+ \group_end:
+ }
+ }
+\cs_new_protected:Npe \__cs_generate_internal_variant:n #1
+ {
+ \exp_not:N \__cs_generate_internal_variant:wwnNwn
+ #1 \s__cs_mark
+ { \cs_set_eq:NN \exp_not:N \__cs_tmp:w \cs_new_protected:Npe }
+ \cs_new_protected:cpn
+ \use:x
+ \token_to_str:N x \s__cs_mark
+ { }
+ \cs_new:cpn
+ \exp_not:N \tex_expanded:D
+ \s__cs_stop
+ {#1}
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new_protected:Npn \__cs_generate_internal_variant:wwnNwn #1
+ { \token_to_str:N x } #2 \s__cs_mark #3#4#5#6 \s__cs_stop #7
+ {
+ #3
+ \cs_if_free:cT { exp_args:N #7 }
+ { \__cs_generate_internal_variant:NNn #4 #5 {#7} }
+ }
+\cs_set_protected:Npn \__cs_tmp:w #1
+ {
+ \cs_new_protected:Npn \__cs_generate_internal_variant:NNn ##1##2##3
+ {
+ \if_catcode:w X \use_none:nnnnnnnn ##3
+ \prg_do_nothing: \prg_do_nothing: \prg_do_nothing:
+ \prg_do_nothing: \prg_do_nothing: \prg_do_nothing:
+ \prg_do_nothing: \prg_do_nothing: X
+ \exp_after:wN \__cs_generate_internal_test:Nw \exp_after:wN ##2
+ \else:
+ \exp_after:wN \__cs_generate_internal_test_aux:w \exp_after:wN #1
+ \fi:
+ ##3
+ \s__cs_mark
+ {
+ \use:e
+ {
+ ##1 { exp_args:N ##3 }
+ { \__cs_generate_internal_variant_loop:n ##3 { : \use_i:nn } }
+ }
+ }
+ #1
+ \s__cs_mark
+ { \exp_not:n { \__cs_generate_internal_one_go:NNn ##1 ##2 {##3} } }
+ \s__cs_stop
+ }
+ \cs_new_protected:Npn \__cs_generate_internal_test_aux:w
+ ##1 #1 ##2 \s__cs_mark ##3 ##4 \s__cs_stop {##3}
+ \cs_new_eq:NN \__cs_generate_internal_test:Nw
+ \__cs_generate_internal_test_aux:w
+ }
+\exp_args:No \__cs_tmp:w { \token_to_str:N p }
+\cs_new_protected:Npn \__cs_generate_internal_one_go:NNn #1#2#3
+ {
+ \__cs_generate_internal_loop:nwnnw
+ { \exp_not:N ##1 } 1 . { } { }
+ #3 { ? \__cs_generate_internal_end:w } X ;
+ 23456789 { ? \__cs_generate_internal_long:w } ;
+ #1 #2 {#3}
+ }
+\cs_new_protected:Npn \__cs_generate_internal_loop:nwnnw #1#2 . #3#4#5#6 ; #7
+ {
+ \use_none:n #5
+ \use_none:n #7
+ \cs_if_exist_use:cF { __cs_generate_internal_#5:NN }
+ { \__cs_generate_internal_other:NN }
+ #5 #7
+ #7 .
+ { #3 #1 } { #4 ## #2 }
+ #6 ;
+ }
+\cs_new_protected:Npn \__cs_generate_internal_N:NN #1#2
+ { \__cs_generate_internal_loop:nwnnw { \exp_not:N ###2 } }
+\cs_new_protected:Npn \__cs_generate_internal_c:NN #1#2
+ { \exp_args:No \__cs_generate_internal_loop:nwnnw { \exp_not:c {###2} } }
+\cs_new_protected:Npn \__cs_generate_internal_n:NN #1#2
+ { \__cs_generate_internal_loop:nwnnw { { \exp_not:n {###2} } } }
+\cs_new_protected:Npn \__cs_generate_internal_x:NN #1#2
+ { \__cs_generate_internal_loop:nwnnw { {###2} } }
+\cs_new_protected:Npn \__cs_generate_internal_other:NN #1#2
+ {
+ \exp_args:No \__cs_generate_internal_loop:nwnnw
+ {
+ \exp_after:wN
+ {
+ \exp:w \exp_args:NNc \exp_after:wN \exp_end:
+ { exp_not:#1 } {###2}
+ }
+ }
+ }
+\cs_new_protected:Npn \__cs_generate_internal_end:w #1 . #2#3#4 ; #5 ; #6#7#8
+ { #6 { exp_args:N #8 } #3 { #7 {#2} } }
+\cs_new_protected:Npn \__cs_generate_internal_long:w #1 N #2#3 . #4#5#6#
+ {
+ \exp_args:Nx \__cs_generate_internal_long:nnnNNn
+ { \__cs_generate_internal_variant_loop:n #2 #6 { : \use_i:nn } }
+ {#4} {#5}
+ }
+\cs_new:Npn \__cs_generate_internal_long:nnnNNn #1#2#3#4 ; ; #5#6#7
+ { #5 { exp_args:N #7 } #3 { #6 { \exp_not:n {#1} {#2} } } }
+\cs_new:Npn \__cs_generate_internal_variant_loop:n #1
+ {
+ \exp_after:wN \exp_not:N \cs:w :: #1 \cs_end:
+ \__cs_generate_internal_variant_loop:n
+ }
+\cs_new_protected:Npn \prg_generate_conditional_variant:Nnn #1
+ {
+ \use:e
+ {
+ \__cs_generate_variant:nnNnn
+ \cs_split_function:N #1
+ }
+ }
+\cs_new_protected:Npn \__cs_generate_variant:nnNnn #1#2#3#4#5
+ {
+ \if_meaning:w \c_false_bool #3
+ \msg_error:nne { kernel } { missing-colon }
+ { \token_to_str:c {#1} }
+ \__cs_use_i_delimit_by_s_stop:nw
+ \fi:
+ \exp_after:wN \__cs_generate_variant:w
+ \tl_to_str:n {#5} , \scan_stop: , \q__cs_recursion_stop
+ \__cs_use_none_delimit_by_s_stop:w \s__cs_mark {#1} {#2} {#4} \s__cs_stop
+ }
+\cs_new_protected:Npn \__cs_generate_variant:w
+ #1 , #2 \s__cs_mark #3#4#5
+ {
+ \if_meaning:w \scan_stop: #1 \scan_stop:
+ \if_meaning:w \q__cs_nil #1 \q__cs_nil
+ \use_i:nnn
+ \fi:
+ \exp_after:wN \__cs_use_none_delimit_by_q_recursion_stop:w
+ \else:
+ \cs_if_exist_use:cTF { __cs_generate_variant_#1_form:nnn }
+ { {#3} {#4} {#5} }
+ {
+ \msg_error:nnee
+ { kernel } { conditional-form-unknown }
+ {#1} { \token_to_str:c { #3 : #4 } }
+ }
+ \fi:
+ \__cs_generate_variant:w #2 \s__cs_mark {#3} {#4} {#5}
+ }
+\cs_new_protected:Npn \__cs_generate_variant_p_form:nnn #1#2
+ { \cs_generate_variant:cn { #1 _p : #2 } }
+\cs_new_protected:Npn \__cs_generate_variant_T_form:nnn #1#2
+ { \cs_generate_variant:cn { #1 : #2 T } }
+\cs_new_protected:Npn \__cs_generate_variant_F_form:nnn #1#2
+ { \cs_generate_variant:cn { #1 : #2 F } }
+\cs_new_protected:Npn \__cs_generate_variant_TF_form:nnn #1#2
+ { \cs_generate_variant:cn { #1 : #2 TF } }
+\cs_new_protected:Npn \exp_args_generate:n #1
+ {
+ \exp_args:No \clist_map_inline:nn { \tl_to_str:n {#1} }
+ {
+ \str_map_inline:nn {##1}
+ {
+ \str_if_in:nnF { NnpcofeVvx } {####1}
+ {
+ \msg_error:nnnn { kernel } { invalid-exp-args }
+ {####1} {##1}
+ \str_map_break:n { \use_none:nn }
+ }
+ }
+ \__cs_generate_internal_variant:n {##1}
+ }
+ }
+\cs_set_protected:Npn \__cs_tmp:w #1
+ {
+ \group_begin:
+ \exp_args:No \__cs_generate_internal_variant:n
+ { \tl_to_str:n {#1} }
+ \group_end:
+ }
+\__cs_tmp:w { nc }
+\__cs_tmp:w { no }
+\__cs_tmp:w { nV }
+\__cs_tmp:w { nv }
+\__cs_tmp:w { ne }
+\__cs_tmp:w { nf }
+\__cs_tmp:w { oc }
+\__cs_tmp:w { oo }
+\__cs_tmp:w { of }
+\__cs_tmp:w { Vo }
+\__cs_tmp:w { fo }
+\__cs_tmp:w { ff }
+\__cs_tmp:w { ee }
+\__cs_tmp:w { Nx }
+\__cs_tmp:w { cx }
+\__cs_tmp:w { nx }
+\__cs_tmp:w { ox }
+\__cs_tmp:w { xo }
+\__cs_tmp:w { xx }
+\__cs_tmp:w { Ncf }
+\__cs_tmp:w { Nno }
+\__cs_tmp:w { NnV }
+\__cs_tmp:w { Noo }
+\__cs_tmp:w { NVV }
+\__cs_tmp:w { cno }
+\__cs_tmp:w { cnV }
+\__cs_tmp:w { coo }
+\__cs_tmp:w { cVV }
+\__cs_tmp:w { nnc }
+\__cs_tmp:w { nno }
+\__cs_tmp:w { nnf }
+\__cs_tmp:w { nff }
+\__cs_tmp:w { ooo }
+\__cs_tmp:w { oof }
+\__cs_tmp:w { ffo }
+\__cs_tmp:w { eee }
+\__cs_tmp:w { NNx }
+\__cs_tmp:w { Nnx }
+\__cs_tmp:w { Nox }
+\__cs_tmp:w { nnx }
+\__cs_tmp:w { nox }
+\__cs_tmp:w { ccx }
+\__cs_tmp:w { cnx }
+\__cs_tmp:w { oox }
+\cs_generate_variant:Nn \cs_generate_from_arg_count:NNnn { NNno }
+\cs_generate_variant:Nn \cs_replacement_spec:N { c }
+%% File: l3quark.dtx
+\cs_new_protected:Npn \quark_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_nopar:Npn #1 {#1}
+ }
+\quark_new:N \q_nil
+\quark_new:N \q_mark
+\quark_new:N \q_no_value
+\quark_new:N \q_stop
+\quark_new:N \q_recursion_tail
+\quark_new:N \q_recursion_stop
+\cs_new_eq:NN \s__quark \scan_stop:
+\quark_new:N \q__quark_nil
+\cs_new:Npn \quark_if_recursion_tail_stop:N #1
+ {
+ \if_meaning:w \q_recursion_tail #1
+ \exp_after:wN \use_none_delimit_by_q_recursion_stop:w
+ \fi:
+ }
+\cs_new:Npn \quark_if_recursion_tail_stop_do:Nn #1
+ {
+ \if_meaning:w \q_recursion_tail #1
+ \exp_after:wN \use_i_delimit_by_q_recursion_stop:nw
+ \else:
+ \exp_after:wN \use_none:n
+ \fi:
+ }
+\cs_new:Npn \quark_if_recursion_tail_stop:n #1
+ {
+ \tl_if_empty:oTF
+ { \__quark_if_recursion_tail:w {} #1 {} ?! \q_recursion_tail ??! }
+ { \use_none_delimit_by_q_recursion_stop:w }
+ { }
+ }
+\cs_new:Npn \quark_if_recursion_tail_stop_do:nn #1
+ {
+ \tl_if_empty:oTF
+ { \__quark_if_recursion_tail:w {} #1 {} ?! \q_recursion_tail ??! }
+ { \use_i_delimit_by_q_recursion_stop:nw }
+ { \use_none:n }
+ }
+\cs_new:Npn \__quark_if_recursion_tail:w
+ #1 \q_recursion_tail #2 ? #3 ?! { #1 #2 }
+\cs_generate_variant:Nn \quark_if_recursion_tail_stop:n { o }
+\cs_generate_variant:Nn \quark_if_recursion_tail_stop_do:nn { o }
+\cs_new:Npn \quark_if_recursion_tail_break:NN #1#2
+ {
+ \if_meaning:w \q_recursion_tail #1
+ \exp_after:wN #2
+ \fi:
+ }
+\cs_new:Npn \quark_if_recursion_tail_break:nN #1#2
+ {
+ \tl_if_empty:oT
+ { \__quark_if_recursion_tail:w {} #1 {} ?! \q_recursion_tail ??! }
+ {#2}
+ }
+\prg_new_conditional:Npnn \quark_if_nil:N #1 { p, T , F , TF }
+ {
+ \if_meaning:w \q_nil #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_new_conditional:Npnn \quark_if_no_value:N #1 { p, T , F , TF }
+ {
+ \if_meaning:w \q_no_value #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \quark_if_no_value:N
+ { c } { p , T , F , TF }
+\prg_new_conditional:Npnn \quark_if_nil:n #1 { p, T , F , TF }
+ {
+ \__quark_if_empty_if:o
+ { \__quark_if_nil:w {} #1 {} ? ! \q_nil ? ? ! }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \__quark_if_nil:w #1 \q_nil #2 ? #3 ? ! { #1 #2 }
+\prg_new_conditional:Npnn \quark_if_no_value:n #1 { p, T , F , TF }
+ {
+ \__quark_if_empty_if:o
+ { \__quark_if_no_value:w {} #1 {} ? ! \q_no_value ? ? ! }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \__quark_if_no_value:w #1 \q_no_value #2 ? #3 ? ! { #1 #2 }
+\prg_generate_conditional_variant:Nnn \quark_if_nil:n
+ { V , o } { p , TF , T , F }
+\cs_new:Npn \__quark_if_empty_if:o #1
+ {
+ \exp_after:wN \if_meaning:w \exp_after:wN \q_nil
+ \__kernel_tl_to_str:w \exp_after:wN {#1} \q_nil
+ }
+\cs_new_protected:Npn \__kernel_quark_new_test:N #1
+ { \__quark_new_test_aux:Ne #1 { \__quark_module_name:N #1 } }
+\cs_new_protected:Npn \__quark_new_test_aux:Nn #1 #2
+ {
+ \if_meaning:w \q_nil #2 \q_nil
+ \msg_error:nne { quark } { invalid-function }
+ { \token_to_str:N #1 }
+ \else:
+ \__quark_new_test:Nccn #1
+ { q__#2_recursion_tail } { q__#2_recursion_stop } { __#2 }
+ \fi:
+ }
+\cs_generate_variant:Nn \__quark_new_test_aux:Nn { Ne }
+\cs_new_protected:Npn \__quark_new_test:NNNn #1
+ {
+ \exp_last_unbraced:Nf \__quark_new_test_aux:nnNNnnnn
+ { \cs_split_function:N #1 }
+ #1 { test }
+ }
+\cs_generate_variant:Nn \__quark_new_test:NNNn { Ncc }
+\cs_new_protected:Npn \__kernel_quark_new_conditional:Nn #1
+ {
+ \__quark_new_conditional:Neen #1
+ { \__quark_quark_conditional_name:N #1 }
+ { \__quark_module_name:N #1 }
+ }
+\cs_new_protected:Npn \__quark_new_conditional:Nnnn #1#2#3#4
+ {
+ \if_meaning:w \q_nil #2 \q_nil
+ \msg_error:nne { quark } { invalid-function }
+ { \token_to_str:N #1 }
+ \else:
+ \if_meaning:w \q_nil #3 \q_nil
+ \msg_error:nne { quark } { invalid-function }
+ { \token_to_str:N #1 }
+ \else:
+ \exp_last_unbraced:Nf \__quark_new_test_aux:nnNNnnnn
+ { \cs_split_function:N #1 }
+ #1 { conditional }
+ {#2} {#3} {#4}
+ \fi:
+ \fi:
+ }
+\cs_generate_variant:Nn \__quark_new_conditional:Nnnn { Nee }
+\cs_new_protected:Npn \__quark_new_test_aux:nnNNnnnn #1 #2 #3 #4 #5
+ {
+ \cs_if_exist_use:cTF { __quark_new_#5_#2:Nnnn } { #4 }
+ {
+ \msg_error:nnee { quark } { invalid-function }
+ { \token_to_str:N #4 } {#2}
+ \use_none:nnn
+ }
+ }
+\cs_new_protected:Npn \__quark_new_test_n:Nnnn #1 #2 #3 #4
+ {
+ \__quark_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { none } { } { } { }
+ \__quark_new_test_define_tl:nNnNNn #1 { }
+ }
+\cs_new_protected:Npn \__quark_new_test_nn:Nnnn #1 #2 #3 #4
+ {
+ \__quark_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { i } { n } {##1} {##2}
+ \__quark_new_test_define_tl:nNnNNn #1 { \use_none:n }
+ }
+\cs_new_protected:Npn \__quark_new_test_nN:Nnnn #1 #2 #3 #4
+ {
+ \__quark_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { i } { n } {##1} {##2}
+ \__quark_new_test_define_break_tl:nNNNNn #1 { }
+ }
+\cs_new_protected:Npn \__quark_new_test_N:Nnnn #1 #2 #3 #4
+ {
+ \__quark_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { none } { } { } { }
+ \__quark_new_test_define_ifx:nNnNNn #1 { }
+ }
+\cs_new_protected:Npn \__quark_new_test_Nn:Nnnn #1 #2 #3 #4
+ {
+ \__quark_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { i } { n } {##1} {##2}
+ \__quark_new_test_define_ifx:nNnNNn #1
+ { \else: \exp_after:wN \use_none:n }
+ }
+\cs_new_protected:Npn \__quark_new_test_NN:Nnnn #1 #2 #3 #4
+ {
+ \__quark_new_test_aux_do:nNNnnnnNNn {#4} #2 #3 { i } { n } {##1} {##2}
+ \__quark_new_test_define_break_ifx:nNNNNn #1 { }
+ }
+\cs_new_protected:Npn \__quark_new_test_aux_do:nNNnnnnNNn #1 #2 #3 #4 #5
+ {
+ \exp_args:Ncc \__quark_test_define_aux:NNNNnnNNn
+ { #1 _quark_recursion_tail:w }
+ { #1 _use_ #4 _delimit_by_q_recursion_stop: #5 w }
+ #2 #3
+ }
+\cs_new_protected:Npn \__quark_test_define_aux:NNNNnnNNn #1 #2 #3 #4 #5 #6 #7
+ {
+ \cs_gset:Npn #1 ##1 #3 ##2 ? ##3 ?! { ##1 ##2 }
+ \cs_gset:Npn #2 ##1 #6 #4 {#5}
+ #7 {##1} #1 #2 #3
+ }
+\cs_new_protected:Npn \__quark_new_test_define_tl:nNnNNn #1 #2 #3 #4 #5 #6
+ {
+ \cs_new:Npn #5 #1
+ {
+ \tl_if_empty:oTF
+ { #2 {} ##1 {} ?! #4 ??! }
+ {#3} {#6}
+ }
+ }
+\cs_new_protected:Npn \__quark_new_test_define_ifx:nNnNNn #1 #2 #3 #4 #5 #6
+ {
+ \cs_new:Npn #5 #1
+ {
+ \if_meaning:w #4 ##1
+ \exp_after:wN #3
+ #6
+ \fi:
+ }
+ }
+\cs_new_protected:Npn \__quark_new_test_define_break_tl:nNNNNn #1 #2 #3
+ { \__quark_new_test_define_tl:nNnNNn {##1##2} #2 {##2} }
+\cs_new_protected:Npn \__quark_new_test_define_break_ifx:nNNNNn #1 #2 #3
+ { \__quark_new_test_define_ifx:nNnNNn {##1##2} #2 {##2} }
+\cs_new_protected:Npn \__quark_new_conditional_n:Nnnn #1 #2 #3
+ {
+ \exp_args:Ncc \__quark_new_conditional_n_aux:NNNn
+ { __ #3 _if_quark_ #2 :w } { q__ #3 _ #2 } #1
+ }
+\cs_new_protected:Npn \__quark_new_conditional_N:Nnnn #1 #2 #3
+ {
+ \exp_args:NNc \__quark_new_conditional_N_aux:NNNn
+ \prg_do_nothing: { q__ #3 _ #2 } #1
+ }
+\cs_new_protected:Npn \__quark_new_conditional_n_aux:NNNn #1 #2 #3 #4
+ {
+ \cs_gset:Npn #1 ##1 #2 ##2 ? ##3 ?! { ##1##2 }
+ \prg_new_conditional:Npnn #3 ##1 {#4}
+ {
+ \__quark_if_empty_if:o { #1 {} ##1 {} ?! #2 ??! }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ }
+\cs_new_protected:Npn \__quark_new_conditional_N_aux:NNNn #1 #2 #3 #4
+ {
+ \prg_new_conditional:Npnn #3 ##1 {#4}
+ {
+ \if_meaning:w #2 ##1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ }
+\cs_set:Npn \__quark_tmp:w #1#2
+ {
+ \cs_new:Npn \__quark_module_name:N ##1
+ {
+ \exp_last_unbraced:Nf \__quark_module_name:w
+ { \cs_to_str:N ##1 } #1 \s__quark
+ }
+ \cs_new:Npn \__quark_module_name:w ##1 #1 ##2 \s__quark
+ { \__quark_module_name_loop:w ##1 #2 \use_none:n { } #2 \s__quark }
+ \cs_new:Npn \__quark_module_name_loop:w ##1 #2
+ {
+ \use_i_ii:nnn \if_meaning:w \prg_do_nothing:
+ ##1 \prg_do_nothing: \prg_do_nothing:
+ \exp_after:wN \__quark_module_name_loop:w
+ \else:
+ \__quark_module_name_end:w ##1
+ \fi:
+ }
+ \cs_new:Npn \__quark_module_name_end:w
+ ##1 \fi: ##2 \s__quark { \fi: ##1 }
+ }
+\exp_after:wN \__quark_tmp:w \tl_to_str:n { : _ }
+\cs_set:Npn \__quark_tmp:w #1 #2 \s__quark
+ {
+ \cs_new:Npn \__quark_quark_conditional_name:N ##1
+ {
+ \exp_last_unbraced:Nf \__quark_quark_conditional_name:w
+ { \cs_to_str:N ##1 } #1 #2 #1 \s__quark
+ }
+ \cs_new:Npn \__quark_quark_conditional_name:w
+ ##1 #2 ##2 #1 ##3 \s__quark {##2}
+ }
+\exp_after:wN \__quark_tmp:w \tl_to_str:n { : _quark_if_ } \s__quark
+\cs_new_protected:Npn \scan_new:N #1
+ {
+ \tl_if_in:NnTF \g__scan_marks_tl { #1 }
+ {
+ \msg_error:nne { scanmark } { already-defined }
+ { \token_to_str:N #1 }
+ }
+ {
+ \tl_gput_right:Nn \g__scan_marks_tl {#1}
+ \cs_new_eq:NN #1 \scan_stop:
+ }
+ }
+\cs_new_eq:NN \s_stop \scan_stop:
+\cs_gset_nopar:Npn \g__scan_marks_tl
+ {
+ \s_stop
+ \s__quark
+ \s__cs_mark
+ \s__cs_stop
+ }
+\cs_new:Npn \use_none_delimit_by_s_stop:w #1 \s_stop { }
+%% File: l3tl.dtx
+\cs_new_eq:NN \__kernel_tl_set:Nx \cs_set_nopar:Npe
+\cs_new_eq:NN \__kernel_tl_gset:Nx \cs_gset_nopar:Npe
+\cs_new_protected:Npn \tl_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_eq:NN #1 \c_empty_tl
+ }
+\cs_generate_variant:Nn \tl_new:N { c }
+\cs_new_protected:Npn \tl_const:Nn #1#2
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_nopar:Npe #1 { \__kernel_exp_not:w {#2} }
+ }
+\cs_generate_variant:Nn \tl_const:Nn { Ne , c , ce }
+\cs_generate_variant:Nn \tl_const:Nn { Nx , cx }
+\cs_new_protected:Npn \tl_clear:N #1
+ { \tex_let:D #1 = ~ \c_empty_tl }
+\cs_new_protected:Npn \tl_gclear:N #1
+ { \tex_global:D \tex_let:D #1 ~ \c_empty_tl }
+\cs_generate_variant:Nn \tl_clear:N { c }
+\cs_generate_variant:Nn \tl_gclear:N { c }
+\cs_new_protected:Npn \tl_clear_new:N #1
+ { \tl_if_exist:NTF #1 { \tl_clear:N #1 } { \tl_new:N #1 } }
+\cs_new_protected:Npn \tl_gclear_new:N #1
+ { \tl_if_exist:NTF #1 { \tl_gclear:N #1 } { \tl_new:N #1 } }
+\cs_generate_variant:Nn \tl_clear_new:N { c }
+\cs_generate_variant:Nn \tl_gclear_new:N { c }
+\cs_new_protected:Npn \tl_set_eq:NN #1#2
+ { \tex_let:D #1 = ~ #2 }
+\cs_new_protected:Npn \tl_gset_eq:NN #1#2
+ { \tex_global:D \tex_let:D #1 = ~ #2 }
+\cs_generate_variant:Nn \tl_set_eq:NN { cN, Nc, cc }
+\cs_generate_variant:Nn \tl_gset_eq:NN { cN, Nc, cc }
+\cs_new_protected:Npn \tl_concat:NNN #1#2#3
+ {
+ \__kernel_tl_set:Nx #1
+ {
+ \__kernel_exp_not:w \exp_after:wN {#2}
+ \__kernel_exp_not:w \exp_after:wN {#3}
+ }
+ }
+\cs_new_protected:Npn \tl_gconcat:NNN #1#2#3
+ {
+ \__kernel_tl_gset:Nx #1
+ {
+ \__kernel_exp_not:w \exp_after:wN {#2}
+ \__kernel_exp_not:w \exp_after:wN {#3}
+ }
+ }
+\cs_generate_variant:Nn \tl_concat:NNN { ccc }
+\cs_generate_variant:Nn \tl_gconcat:NNN { ccc }
+\prg_new_eq_conditional:NNn \tl_if_exist:N \cs_if_exist:N { TF , T , F , p }
+\prg_new_eq_conditional:NNn \tl_if_exist:c \cs_if_exist:c { TF , T , F , p }
+\tl_const:Nn \c_empty_tl { }
+\group_begin:
+\tex_catcode:D `- = 11 ~
+\tl_const:Ne \c_novalue_tl { - NoValue \token_to_str:N - }
+\group_end:
+\tl_const:Nn \c_space_tl { ~ }
+\cs_new_protected:Npn \tl_set:Nn #1#2
+ { \__kernel_tl_set:Nx #1 { \__kernel_exp_not:w {#2} } }
+\cs_new_protected:Npn \tl_set:No #1#2
+ { \__kernel_tl_set:Nx #1 { \__kernel_exp_not:w \exp_after:wN {#2} } }
+\cs_new_protected:Npn \tl_gset:Nn #1#2
+ { \__kernel_tl_gset:Nx #1 { \__kernel_exp_not:w {#2} } }
+\cs_new_protected:Npn \tl_gset:No #1#2
+ { \__kernel_tl_gset:Nx #1 { \__kernel_exp_not:w \exp_after:wN {#2} } }
+\cs_generate_variant:Nn \tl_set:Nn { NV , Nv , Ne , Nf }
+\cs_generate_variant:Nn \tl_set:Nn { c, cV , cv , ce , cf }
+\cs_generate_variant:Nn \tl_set:No { c }
+\cs_generate_variant:Nn \tl_set:Nn { Nx , cx }
+\cs_generate_variant:Nn \tl_gset:Nn { NV , Nv , Ne , Nf }
+\cs_generate_variant:Nn \tl_gset:Nn { c, cV , cv , ce , cf }
+\cs_generate_variant:Nn \tl_gset:No { c }
+\cs_generate_variant:Nn \tl_gset:Nn { Nx , cx }
+\cs_new_protected:Npn \tl_put_left:Nn #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \__kernel_exp_not:w {#2} \__kernel_exp_not:w \exp_after:wN {#1} }
+ }
+\cs_new_protected:Npn \tl_put_left:NV #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \exp_not:V #2 \__kernel_exp_not:w \exp_after:wN {#1} }
+ }
+\cs_new_protected:Npn \tl_put_left:Nv #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \exp_not:v {#2} \__kernel_exp_not:w \exp_after:wN {#1} }
+ }
+\cs_new_protected:Npn \tl_put_left:Ne #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ {
+ \__kernel_exp_not:w \tex_expanded:D { {#2} }
+ \__kernel_exp_not:w \exp_after:wN {#1}
+ }
+ }
+\cs_new_protected:Npn \tl_put_left:No #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ {
+ \__kernel_exp_not:w \exp_after:wN {#2}
+ \__kernel_exp_not:w \exp_after:wN {#1}
+ }
+ }
+\cs_new_protected:Npn \tl_gput_left:Nn #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \__kernel_exp_not:w {#2} \__kernel_exp_not:w \exp_after:wN {#1} }
+ }
+\cs_new_protected:Npn \tl_gput_left:NV #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \exp_not:V #2 \__kernel_exp_not:w \exp_after:wN {#1} }
+ }
+\cs_new_protected:Npn \tl_gput_left:Nv #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \exp_not:v {#2} \__kernel_exp_not:w \exp_after:wN {#1} }
+ }
+\cs_new_protected:Npn \tl_gput_left:Ne #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ {
+ \__kernel_exp_not:w \tex_expanded:D { {#2} }
+ \__kernel_exp_not:w \exp_after:wN {#1}
+ }
+ }
+\cs_new_protected:Npn \tl_gput_left:No #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ {
+ \__kernel_exp_not:w \exp_after:wN {#2}
+ \__kernel_exp_not:w \exp_after:wN {#1}
+ }
+ }
+\cs_generate_variant:Nn \tl_put_left:Nn { c }
+\cs_generate_variant:Nn \tl_put_left:NV { c }
+\cs_generate_variant:Nn \tl_put_left:Nv { c }
+\cs_generate_variant:Nn \tl_put_left:Ne { c }
+\cs_generate_variant:Nn \tl_put_left:No { c }
+\cs_generate_variant:Nn \tl_put_left:Nn { Nx, cx }
+\cs_generate_variant:Nn \tl_gput_left:Nn { c }
+\cs_generate_variant:Nn \tl_gput_left:NV { c }
+\cs_generate_variant:Nn \tl_gput_left:Nv { c }
+\cs_generate_variant:Nn \tl_gput_left:Ne { c }
+\cs_generate_variant:Nn \tl_gput_left:No { c }
+\cs_generate_variant:Nn \tl_gput_left:Nn { Nx , cx }
+\cs_new_protected:Npn \tl_put_right:Nn #1#2
+ { \__kernel_tl_set:Nx #1 { \__kernel_exp_not:w \exp_after:wN { #1 #2 } } }
+\cs_new_protected:Npn \tl_put_right:NV #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \__kernel_exp_not:w \exp_after:wN {#1} \exp_not:V #2 }
+ }
+\cs_new_protected:Npn \tl_put_right:Nv #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \__kernel_exp_not:w \exp_after:wN {#1} \exp_not:v {#2} }
+ }
+\cs_new_protected:Npn \tl_put_right:Ne #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ {
+ \__kernel_exp_not:w \exp_after:wN {#1}
+ \__kernel_exp_not:w \tex_expanded:D { {#2} }
+ }
+ }
+\cs_new_protected:Npn \tl_put_right:No #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ {
+ \__kernel_exp_not:w \exp_after:wN {#1}
+ \__kernel_exp_not:w \exp_after:wN {#2}
+ }
+ }
+\cs_new_protected:Npn \tl_gput_right:Nn #1#2
+ { \__kernel_tl_gset:Nx #1 { \__kernel_exp_not:w \exp_after:wN { #1 #2 } } }
+\cs_new_protected:Npn \tl_gput_right:NV #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \__kernel_exp_not:w \exp_after:wN {#1} \exp_not:V #2 }
+ }
+\cs_new_protected:Npn \tl_gput_right:Nv #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \__kernel_exp_not:w \exp_after:wN {#1} \exp_not:v {#2} }
+ }
+\cs_new_protected:Npn \tl_gput_right:Ne #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ {
+ \__kernel_exp_not:w \exp_after:wN {#1}
+ \__kernel_exp_not:w \tex_expanded:D { {#2} }
+ }
+ }
+\cs_new_protected:Npn \tl_gput_right:No #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ {
+ \__kernel_exp_not:w \exp_after:wN {#1}
+ \__kernel_exp_not:w \exp_after:wN {#2}
+ }
+ }
+\cs_generate_variant:Nn \tl_put_right:Nn { c }
+\cs_generate_variant:Nn \tl_put_right:NV { c }
+\cs_generate_variant:Nn \tl_put_right:Nv { c }
+\cs_generate_variant:Nn \tl_put_right:Ne { c }
+\cs_generate_variant:Nn \tl_put_right:No { c }
+\cs_generate_variant:Nn \tl_put_right:Nn { Nx , cx }
+\cs_generate_variant:Nn \tl_gput_right:Nn { c }
+\cs_generate_variant:Nn \tl_gput_right:NV { c }
+\cs_generate_variant:Nn \tl_gput_right:Nv { c }
+\cs_generate_variant:Nn \tl_gput_right:Ne { c }
+\cs_generate_variant:Nn \tl_gput_right:No { c }
+\cs_generate_variant:Nn \tl_gput_right:Nn { Nx, cx }
+\quark_new:N \q__tl_nil
+\quark_new:N \q__tl_mark
+\quark_new:N \q__tl_stop
+\quark_new:N \q__tl_recursion_tail
+\quark_new:N \q__tl_recursion_stop
+\__kernel_quark_new_test:N \__tl_if_recursion_tail_break:nN
+\__kernel_quark_new_conditional:Nn \__tl_quark_if_nil:n { TF }
+\tl_const:Ne \c__tl_rescan_marker_tl { : \token_to_str:N : }
+\cs_new_protected:Npn \tl_rescan:nn #1#2
+ {
+ \tl_set_rescan:Nnn \l__tl_internal_a_tl {#1} {#2}
+ \exp_after:wN \__tl_rescan_aux:
+ \l__tl_internal_a_tl
+ }
+\cs_generate_variant:Nn \tl_rescan:nn { nV }
+\exp_args:NNo \cs_new_protected:Npn \__tl_rescan_aux:
+ { \tl_clear:N \l__tl_internal_a_tl }
+\cs_new_protected:Npn \tl_set_rescan:Nnn
+ { \__tl_set_rescan:NNnn \tl_set:No }
+\cs_new_protected:Npn \tl_gset_rescan:Nnn
+ { \__tl_set_rescan:NNnn \tl_gset:No }
+\cs_new_protected:Npn \__tl_set_rescan:NNnn #1#2#3#4
+ {
+ \group_begin:
+ \if_false: { \fi:
+ \int_set_eq:NN \tex_tracingnesting:D \c_zero_int
+ \int_compare:nNnT \tex_endlinechar:D = { 32 }
+ { \int_set:Nn \tex_endlinechar:D { -1 } }
+ \int_set_eq:NN \tex_newlinechar:D \tex_endlinechar:D
+ #3 \scan_stop:
+ \exp_args:No \__tl_set_rescan:nNN { \tl_to_str:n {#4} } #1 #2
+ \if_false: } \fi:
+ }
+\cs_new_protected:Npn \__tl_set_rescan_multi:nNN #1#2#3
+ {
+ \tex_everyeof:D \exp_after:wN { \c__tl_rescan_marker_tl }
+ \exp_after:wN \__tl_rescan:NNw
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN \prg_do_nothing:
+ \tex_scantokens:D {#1}
+ }
+\exp_args:Nno \use:nn
+ { \cs_new:Npn \__tl_rescan:NNw #1#2#3 } \c__tl_rescan_marker_tl
+ {
+ \group_end:
+ #1 #2 {#3}
+ }
+\cs_generate_variant:Nn \tl_set_rescan:Nnn { NnV , Nne , c , cnV , cne }
+\cs_generate_variant:Nn \tl_set_rescan:Nnn { Nno , Nnx , cno , cnx }
+\cs_generate_variant:Nn \tl_gset_rescan:Nnn { NnV , Nne , c , cnV , cne }
+\cs_generate_variant:Nn \tl_gset_rescan:Nnn { Nno , Nnx , cno , cnx }
+\cs_new_protected:Npn \__tl_set_rescan:nNN #1
+ {
+ \int_compare:nNnTF \tex_newlinechar:D < 0
+ { \use_ii:nn }
+ {
+ \exp_args:Nnf \tl_if_in:nnTF {#1}
+ { \char_generate:nn { \tex_newlinechar:D } { 12 } }
+ }
+ { \__tl_set_rescan_multi:nNN }
+ {
+ \int_set:Nn \tex_endlinechar:D { -1 }
+ \__tl_set_rescan_single:nnNN { `' }
+ }
+ {#1}
+ }
+\cs_new_protected:Npn \__tl_set_rescan_single:nnNN #1
+ {
+ \int_compare:nNnTF
+ { \char_value_catcode:n {#1} / 2 } = 6
+ {
+ \exp_args:Nof \__tl_set_rescan_single_aux:nnnNN
+ \c__tl_rescan_marker_tl
+ { \char_generate:nn {#1} { \char_value_catcode:n {#1} } }
+ }
+ {
+ \int_compare:nNnTF {#1} < { `\~ }
+ {
+ \exp_args:Nf \__tl_set_rescan_single:nnNN
+ { \int_eval:n { #1 + 1 } }
+ }
+ { \__tl_set_rescan_multi:nNN }
+ }
+ }
+\cs_new_protected:Npn \__tl_set_rescan_single_aux:nnnNN #1#2#3#4#5
+ {
+ \tex_everyeof:D
+ {
+ #1 \use_none:n
+ #2 #1 { \exp:w \__tl_set_rescan_single_aux:w }
+ \s__tl_stop
+ }
+ \cs_set:Npn \__tl_rescan:NNw ##1##2##3 #2 #1 ##4 ##5 \s__tl_stop
+ {
+ \group_end:
+ ##1 ##2 { ##4 ##3 }
+ }
+ \exp_after:wN \__tl_rescan:NNw
+ \exp_after:wN #4
+ \exp_after:wN #5
+ \tex_scantokens:D { #2 #3 #2 }
+ }
+\exp_args:Nno \use:nn
+ { \cs_new:Npn \__tl_set_rescan_single_aux:w #1 }
+ \c__tl_rescan_marker_tl #2
+ { \use_i:nn \exp_end: #1 }
+\cs_new_protected:Npn \tl_replace_once:Nnn
+ { \__tl_replace:NnNNNnn \q__tl_mark ? \__tl_replace_wrap:w \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \tl_greplace_once:Nnn
+ { \__tl_replace:NnNNNnn \q__tl_mark ? \__tl_replace_wrap:w \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \tl_replace_all:Nnn
+ { \__tl_replace:NnNNNnn \q__tl_mark ? \__tl_replace_next:w \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \tl_greplace_all:Nnn
+ { \__tl_replace:NnNNNnn \q__tl_mark ? \__tl_replace_next:w \__kernel_tl_gset:Nx }
+\cs_generate_variant:Nn \tl_replace_once:Nnn
+ { NnV , Nne , NV , Ne , Nee , c , cnV , cne , cV , ce , cee }
+\cs_generate_variant:Nn \tl_replace_once:Nnn
+ { Nx , Nnx , Nxx , cxn , cnx , cxx }
+\cs_generate_variant:Nn \tl_greplace_once:Nnn
+ { NnV , Nne , NV , Ne , Nee , c , cnV , cne , cV , ce , cee }
+\cs_generate_variant:Nn \tl_greplace_once:Nnn
+ { Nx , Nnx , Nxx , cxn , cnx , cxx }
+\cs_generate_variant:Nn \tl_replace_all:Nnn
+ { NnV , Nne , NV , Ne , Nee , c , cnV , cne , cV , ce , cee }
+\cs_generate_variant:Nn \tl_replace_all:Nnn
+ { Nx , Nnx , Nxx , cxn , cnx , cxx }
+\cs_generate_variant:Nn \tl_greplace_all:Nnn
+ { NnV , Nne , NV , Ne , Nee , c , cnV , cne , cV , ce , cee }
+\cs_generate_variant:Nn \tl_greplace_all:Nnn
+ { Nx , Nnx , Nxx , cxn , cnx , cxx }
+\cs_new_protected:Npn \__tl_replace:NnNNNnn #1#2#3#4#5#6#7
+ {
+ \tl_if_empty:nTF {#6}
+ {
+ \msg_error:nne { kernel } { empty-search-pattern }
+ { \tl_to_str:n {#7} }
+ }
+ {
+ \tl_if_in:onTF { #5 #6 } {#1}
+ {
+ \tl_if_in:nnTF {#6} {#1}
+ { \exp_args:Nc \__tl_replace:NnNNNnn {#2} {#2?} }
+ {
+ \__tl_quark_if_nil:nTF {#6}
+ { \__tl_replace_auxi:NnnNNNnn #5 {#1} { #1 \q__tl_stop } }
+ { \__tl_replace_auxi:NnnNNNnn #5 {#1} { #1 \q__tl_nil } }
+ }
+ }
+ { \__tl_replace_auxii:nNNNnn {#1} }
+ #3#4#5 {#6} {#7}
+ }
+ }
+\cs_new_protected:Npn \__tl_replace_auxi:NnnNNNnn #1#2#3
+ {
+ \tl_if_in:NnTF #1 { #2 #3 #3 }
+ { \__tl_replace_auxi:NnnNNNnn #1 { #2 #3 } {#2} }
+ { \__tl_replace_auxii:nNNNnn { #2 #3 #3 } }
+ }
+\cs_new_protected:Npn \__tl_replace_auxii:nNNNnn #1#2#3#4#5#6
+ {
+ \group_align_safe_begin:
+ \cs_set:Npn \__tl_replace_wrap:w ##1 #1 ##2
+ { \__kernel_exp_not:w \exp_after:wN { \use_none:nn ##1 } ##2 }
+ \cs_set:Npe \__tl_replace_next:w ##1 #5
+ {
+ \exp_not:N \__tl_replace_wrap:w ##1
+ \exp_not:n { #1 }
+ \exp_not:n { \exp_not:n {#6} }
+ \exp_not:n { #2 { } { } }
+ }
+ #3 #4
+ {
+ \exp_after:wN \__tl_replace_next_aux:w
+ #4
+ #1
+ {
+ \if_false: { \fi: }
+ \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi:
+ }
+ #5
+ }
+ \group_align_safe_end:
+ }
+\cs_new:Npn \__tl_replace_next_aux:w { \__tl_replace_next:w { } { } }
+\cs_new_eq:NN \__tl_replace_wrap:w ?
+\cs_new_eq:NN \__tl_replace_next:w ?
+\cs_new_protected:Npn \tl_remove_once:Nn #1#2
+ { \tl_replace_once:Nnn #1 {#2} { } }
+\cs_new_protected:Npn \tl_gremove_once:Nn #1#2
+ { \tl_greplace_once:Nnn #1 {#2} { } }
+\cs_generate_variant:Nn \tl_remove_once:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \tl_gremove_once:Nn { NV , Ne , c , cV , ce }
+\cs_new_protected:Npn \tl_remove_all:Nn #1#2
+ { \tl_replace_all:Nnn #1 {#2} { } }
+\cs_new_protected:Npn \tl_gremove_all:Nn #1#2
+ { \tl_greplace_all:Nnn #1 {#2} { } }
+\cs_generate_variant:Nn \tl_remove_all:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \tl_remove_all:Nn { Nx , cx }
+\cs_generate_variant:Nn \tl_gremove_all:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \tl_gremove_all:Nn { Nx , cx }
+\prg_new_conditional:Npnn \tl_if_empty:N #1 { p , T , F , TF }
+ {
+ \if_meaning:w #1 \c_empty_tl
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_empty:N
+ { c } { p , T , F , TF }
+\prg_new_conditional:Npnn \tl_if_empty:n #1 { p , TF , T , F }
+ {
+ \if:w \scan_stop: \tl_to_str:n {#1} \scan_stop:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_empty:n
+ { V , e } { p , TF , T , F }
+\cs_new:Npn \__tl_if_empty_if:o #1
+ {
+ \if:w \scan_stop: \__kernel_tl_to_str:w \exp_after:wN {#1} \scan_stop:
+ }
+\exp_args:Nno \use:n
+ { \prg_new_conditional:Npnn \tl_if_empty:o #1 { p , TF , T , F } }
+ {
+ \__tl_if_empty_if:o {#1}
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\exp_args:Nno \use:n
+ { \prg_new_conditional:Npnn \tl_if_blank:n #1 { p , T , F , TF } }
+ {
+ \__tl_if_empty_if:o { \use_none:n #1 ? }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_blank:n
+ { e , V , o } { p , T , F , TF }
+\prg_new_eq_conditional:NNn \tl_if_eq:NN \cs_if_eq:NN { p , T , F , TF }
+\prg_generate_conditional_variant:Nnn \tl_if_eq:NN
+ { Nc , c , cc } { p , TF , T , F }
+\tl_new:N \l__tl_internal_a_tl
+\tl_new:N \l__tl_internal_b_tl
+\prg_new_protected_conditional:Npnn \tl_if_eq:Nn #1#2 { T , F , TF }
+ {
+ \group_begin:
+ \tl_set:Nn \l__tl_internal_b_tl {#2}
+ \exp_after:wN
+ \group_end:
+ \if_meaning:w #1 \l__tl_internal_b_tl
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_eq:Nn { c } { TF , T , F }
+\prg_new_protected_conditional:Npnn \tl_if_eq:nn #1#2 { T , F , TF }
+ {
+ \group_begin:
+ \tl_set:Nn \l__tl_internal_a_tl {#1}
+ \tl_set:Nn \l__tl_internal_b_tl {#2}
+ \exp_after:wN
+ \group_end:
+ \if_meaning:w \l__tl_internal_a_tl \l__tl_internal_b_tl
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_eq:nn
+ { nV , ne , nx , V, e , ee , x , xx }
+ { TF , T , F }
+\cs_new_protected:Npn \tl_if_in:NnT { \exp_args:No \tl_if_in:nnT }
+\cs_new_protected:Npn \tl_if_in:NnF { \exp_args:No \tl_if_in:nnF }
+\cs_new_protected:Npn \tl_if_in:NnTF { \exp_args:No \tl_if_in:nnTF }
+\prg_generate_conditional_variant:Nnn \tl_if_in:Nn
+ { NV , No , c , cV , co } { T , F , TF }
+\prg_new_protected_conditional:Npnn \tl_if_in:nn #1#2 { T , F , TF }
+ {
+ \scan_stop:
+ \if_false: { \fi:
+ \cs_set:Npn \__tl_tmp:w ##1 #2 { }
+ \tl_if_empty:oTF { \__tl_tmp:w #1 {} {} #2 }
+ { \prg_return_false: } { \prg_return_true: }
+ \if_false: } \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_in:nn
+ { V , VV , o , oo , nV , no } { T , F , TF }
+\cs_set_protected:Npn \__tl_tmp:w #1
+ {
+ \prg_new_conditional:Npnn \tl_if_novalue:n ##1
+ { p , T , F , TF }
+ {
+ \__tl_if_empty_if:o { \__tl_if_novalue:w {} ##1 {} ? ! #1 ? ? ! }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ \cs_new:Npn \__tl_if_novalue:w ##1 #1 ##2 ? ##3 ? ! { ##1 ##2 }
+ }
+\exp_args:No \__tl_tmp:w { \c_novalue_tl }
+\cs_new:Npn \tl_if_single_p:N { \exp_args:No \tl_if_single_p:n }
+\cs_new:Npn \tl_if_single:NT { \exp_args:No \tl_if_single:nT }
+\cs_new:Npn \tl_if_single:NF { \exp_args:No \tl_if_single:nF }
+\cs_new:Npn \tl_if_single:NTF { \exp_args:No \tl_if_single:nTF }
+\prg_generate_conditional_variant:Nnn \tl_if_single:N {c} { p , T , F , TF }
+\prg_new_conditional:Npnn \tl_if_single:n #1 { p , T , F , TF }
+ {
+ \if:w \scan_stop: \exp_after:wN \__tl_if_single:nnw
+ \__kernel_tl_to_str:w
+ \exp_after:wN { \use_none:nn #1 ?? } \scan_stop: ? \s__tl_stop
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \__tl_if_single:nnw #1#2#3 \s__tl_stop {#2}
+\prg_new_conditional:Npnn \tl_if_single_token:n #1 { p , T , F , TF }
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \__tl_if_empty_if:o { \use_none:n #1 } }
+ {
+ \tl_if_empty:nTF {#1}
+ { \if_false: }
+ { \__tl_if_empty_if:o { \exp:w \exp_end_continue_f:w #1 } }
+ }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \tl_map_function:nN #1#2
+ {
+ \__tl_map_function:Nnnnnnnnn #2 #1
+ \s__tl_stop \s__tl_stop \s__tl_stop \s__tl_stop
+ \s__tl_stop \s__tl_stop \s__tl_stop \s__tl_stop
+ \prg_break_point:Nn \tl_map_break: { }
+ }
+\cs_new:Npn \tl_map_function:NN
+ { \exp_args:No \tl_map_function:nN }
+\cs_generate_variant:Nn \tl_map_function:NN { c }
+\cs_new:Npn \__tl_map_function:Nnnnnnnnn #1#2#3#4#5#6#7#8#9
+ {
+ \__tl_use_none_delimit_by_s_stop:w
+ #9 \__tl_map_function_end:w \s__tl_stop
+ #1 {#2} #1 {#3} #1 {#4} #1 {#5} #1 {#6} #1 {#7} #1 {#8} #1 {#9}
+ \__tl_map_function:Nnnnnnnnn #1
+ }
+\cs_new:Npn \__tl_map_function_end:w \s__tl_stop #1#2
+ {
+ \__tl_use_none_delimit_by_s_stop:w #2 \tl_map_break: \s__tl_stop
+ #1 {#2}
+ \__tl_map_function_end:w \s__tl_stop
+ }
+\cs_new:Npn \__tl_use_none_delimit_by_s_stop:w #1 \s__tl_stop { }
+\cs_new_protected:Npn \tl_map_inline:nn #1#2
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \cs_gset_protected:cpn
+ { __tl_map_ \int_use:N \g__kernel_prg_map_int :w } ##1 {#2}
+ \exp_args:Nc \__tl_map_function:Nnnnnnnnn
+ { __tl_map_ \int_use:N \g__kernel_prg_map_int :w }
+ #1
+ \s__tl_stop \s__tl_stop \s__tl_stop \s__tl_stop
+ \s__tl_stop \s__tl_stop \s__tl_stop \s__tl_stop
+ \prg_break_point:Nn \tl_map_break:
+ { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+\cs_new_protected:Npn \tl_map_inline:Nn
+ { \exp_args:No \tl_map_inline:nn }
+\cs_generate_variant:Nn \tl_map_inline:Nn { c }
+\cs_new:Npn \tl_map_tokens:nn #1#2
+ {
+ \__tl_map_tokens:nnnnnnnnn {#2} #1
+ \s__tl_stop \s__tl_stop \s__tl_stop \s__tl_stop
+ \s__tl_stop \s__tl_stop \s__tl_stop \s__tl_stop
+ \prg_break_point:Nn \tl_map_break: { }
+ }
+\cs_new:Npn \tl_map_tokens:Nn
+ { \exp_args:No \tl_map_tokens:nn }
+\cs_generate_variant:Nn \tl_map_tokens:Nn { c }
+\cs_new:Npn \__tl_map_tokens:nnnnnnnnn #1#2#3#4#5#6#7#8#9
+ {
+ \__tl_use_none_delimit_by_s_stop:w
+ #9 \__tl_map_tokens_end:w \s__tl_stop
+ \use:n {#1} {#2} \use:n {#1} {#3} \use:n {#1} {#4} \use:n {#1} {#5}
+ \use:n {#1} {#6} \use:n {#1} {#7} \use:n {#1} {#8} \use:n {#1} {#9}
+ \__tl_map_tokens:nnnnnnnnn {#1}
+ }
+\cs_new:Npn \__tl_map_tokens_end:w \s__tl_stop \use:n #1#2
+ {
+ \__tl_use_none_delimit_by_s_stop:w #2 \tl_map_break: \s__tl_stop
+ #1 {#2}
+ \__tl_map_tokens_end:w \s__tl_stop
+ }
+\cs_new_protected:Npn \tl_map_variable:nNn #1#2#3
+ { \tl_map_tokens:nn {#1} { \__tl_map_variable:Nnn #2 {#3} } }
+\cs_new_protected:Npn \__tl_map_variable:Nnn #1#2#3
+ { \tl_set:Nn #1 {#3} #2 }
+\cs_new_protected:Npn \tl_map_variable:NNn
+ { \exp_args:No \tl_map_variable:nNn }
+\cs_generate_variant:Nn \tl_map_variable:NNn { c }
+\cs_new:Npn \tl_map_break:
+ { \prg_map_break:Nn \tl_map_break: { } }
+\cs_new:Npn \tl_map_break:n
+ { \prg_map_break:Nn \tl_map_break: }
+\cs_generate_variant:Nn \tl_to_str:n { o , V , v , e }
+\cs_new:Npn \tl_to_str:N #1 { \__kernel_tl_to_str:w \exp_after:wN {#1} }
+\cs_generate_variant:Nn \tl_to_str:N { c }
+\cs_new:Npn \tl_use:N #1
+ {
+ \tl_if_exist:NTF #1 {#1}
+ {
+ \msg_expandable_error:nnn
+ { kernel } { bad-variable } {#1}
+ }
+ }
+\cs_generate_variant:Nn \tl_use:N { c }
+\cs_new:Npn \tl_count:n #1
+ {
+ \int_eval:n
+ { 0 \tl_map_function:nN {#1} \__tl_count:n }
+ }
+\cs_new:Npn \tl_count:N #1
+ {
+ \int_eval:n
+ { 0 \tl_map_function:NN #1 \__tl_count:n }
+ }
+\cs_new:Npn \__tl_count:n #1 { + 1 }
+\cs_generate_variant:Nn \tl_count:n { V , v , e , o }
+\cs_generate_variant:Nn \tl_count:N { c }
+\cs_new:Npn \tl_count_tokens:n #1
+ {
+ \int_eval:n
+ {
+ \__tl_act:NNNn
+ \__tl_act_count_normal:N
+ \__tl_act_count_group:n
+ \__tl_act_count_space:
+ {#1}
+ }
+ }
+\cs_new:Npn \__tl_act_count_normal:N #1 { 1 + }
+\cs_new:Npn \__tl_act_count_space: { 1 + }
+\cs_new:Npn \__tl_act_count_group:n #1 { 2 + \tl_count_tokens:n {#1} + }
+\cs_new:Npn \tl_reverse_items:n #1
+ {
+ \__tl_reverse_items:nwNwn #1 ?
+ \s__tl_mark \__tl_reverse_items:nwNwn
+ \s__tl_mark \__tl_reverse_items:wn
+ \s__tl_stop { }
+ }
+\cs_new:Npn \__tl_reverse_items:nwNwn #1 #2 \s__tl_mark #3 #4 \s__tl_stop #5
+ {
+ #3 #2
+ \s__tl_mark \__tl_reverse_items:nwNwn
+ \s__tl_mark \__tl_reverse_items:wn
+ \s__tl_stop { {#1} #5 }
+ }
+\cs_new:Npn \__tl_reverse_items:wn #1 \s__tl_stop #2
+ { \__kernel_exp_not:w \exp_after:wN { \use_none:nn #2 } }
+\cs_new:Npn \tl_trim_spaces:n #1
+ {
+ \__tl_trim_spaces:nn
+ { \__tl_trim_mark: #1 }
+ { \__kernel_exp_not:w \exp_after:wN }
+ }
+\cs_generate_variant:Nn \tl_trim_spaces:n { V , v , e , o }
+\cs_new:Npn \tl_trim_spaces_apply:nN #1#2
+ { \__tl_trim_spaces:nn { \__tl_trim_mark: #1 } { \exp_args:No #2 } }
+\cs_generate_variant:Nn \tl_trim_spaces_apply:nN { o }
+\cs_new_protected:Npn \tl_trim_spaces:N #1
+ { \__kernel_tl_set:Nx #1 { \exp_args:No \tl_trim_spaces:n {#1} } }
+\cs_new_protected:Npn \tl_gtrim_spaces:N #1
+ { \__kernel_tl_gset:Nx #1 { \exp_args:No \tl_trim_spaces:n {#1} } }
+\cs_generate_variant:Nn \tl_trim_spaces:N { c }
+\cs_generate_variant:Nn \tl_gtrim_spaces:N { c }
+\cs_set_protected:Npn \__tl_tmp:w #1
+ {
+ \cs_new:Npn \__tl_trim_spaces:nn ##1
+ {
+ \__tl_trim_spaces_auxi:w
+ ##1
+ \s__tl_nil
+ \__tl_trim_mark: #1 { }
+ \__tl_trim_mark: \__tl_trim_spaces_auxii:w
+ \__tl_trim_spaces_auxiii:w
+ #1 \s__tl_nil
+ \__tl_trim_spaces_auxiv:w
+ \s__tl_stop
+ }
+ \cs_new:Npn
+ \__tl_trim_spaces_auxi:w ##1 \__tl_trim_mark: #1 ##2 \__tl_trim_mark: ##3
+ {
+ ##3
+ \__tl_trim_spaces_auxi:w
+ \__tl_trim_mark:
+ ##2
+ \__tl_trim_mark: #1 {##1}
+ }
+ \cs_new:Npn \__tl_trim_spaces_auxii:w
+ \__tl_trim_spaces_auxi:w \__tl_trim_mark: \__tl_trim_mark: ##1
+ {
+ \__tl_trim_spaces_auxiii:w
+ ##1
+ }
+ \cs_new:Npn \__tl_trim_spaces_auxiii:w ##1 #1 \s__tl_nil ##2
+ {
+ ##2
+ ##1 \s__tl_nil
+ \__tl_trim_spaces_auxiii:w
+ }
+ \cs_new:Npn \__tl_trim_spaces_auxiv:w ##1 \s__tl_nil ##2 \s__tl_stop ##3
+ { ##3 { ##1 } }
+ \cs_new:Npn \__tl_trim_mark: {}
+ }
+\__tl_tmp:w { ~ }
+\cs_new:Npn \tl_head:n #1
+ {
+ \__kernel_exp_not:w \tex_expanded:D
+ { { \if_false: { \fi: \__tl_head_aux:n #1 { } } } }
+ }
+\cs_new:Npn \__tl_head_aux:n #1
+ {
+ \__kernel_exp_not:w {#1}
+ \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi:
+ }
+\cs_generate_variant:Nn \tl_head:n { V , v , f }
+\cs_new:Npn \tl_head:w #1#2 \q_stop {#1}
+\cs_new:Npn \__tl_tl_head:w #1#2 \s__tl_stop {#1}
+\cs_new:Npn \tl_head:N { \exp_args:No \tl_head:n }
+\exp_args:Nno \use:n { \cs_new:Npn \tl_tail:n #1 }
+ {
+ \exp_after:wN \__kernel_exp_not:w
+ \tl_if_blank:nTF {#1}
+ { { } }
+ { \exp_after:wN { \use_none:n #1 } }
+ }
+\cs_generate_variant:Nn \tl_tail:n { V , v , f }
+\cs_new:Npn \tl_tail:N { \exp_args:No \tl_tail:n }
+\prg_new_conditional:Npnn \tl_if_head_eq_charcode:nN #1#2 { p , T , F , TF }
+ {
+ \if_charcode:w
+ \tl_if_head_is_N_type:nTF { #1 ? }
+ { \__tl_head_exp_not:w #1 { ^ \__tl_if_head_eq_empty_arg:w } \s__tl_stop }
+ { \str_head:n {#1} }
+ \exp_not:N #2
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_head_eq_charcode:nN
+ { V , e , f } { p , TF , T , F }
+\prg_new_conditional:Npnn \tl_if_head_eq_catcode:nN #1 #2 { p , T , F , TF }
+ {
+ \if_catcode:w
+ \tl_if_head_is_N_type:nTF { #1 ? }
+ { \__tl_head_exp_not:w #1 { ^ \__tl_if_head_eq_empty_arg:w } \s__tl_stop }
+ {
+ \tl_if_head_is_group:nTF {#1}
+ \c_group_begin_token
+ \c_space_token
+ }
+ \exp_not:N #2
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_head_eq_catcode:nN
+ { V , e , o } { p , TF , T , F }
+\prg_new_conditional:Npnn \tl_if_head_eq_meaning:nN #1#2 { p , T , F , TF }
+ {
+ \tl_if_head_is_N_type:nTF { #1 ? }
+ \__tl_if_head_eq_meaning_normal:nN
+ \__tl_if_head_eq_meaning_special:nN
+ {#1} #2
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_head_eq_meaning:nN
+ { V , e } { p , TF , T , F }
+\cs_new:Npn \__tl_if_head_eq_meaning_normal:nN #1 #2
+ {
+ \exp_after:wN \if_meaning:w
+ \__tl_tl_head:w #1 { ?? \use_none:nnn } \s__tl_stop #2
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \__tl_if_head_eq_meaning_special:nN #1 #2
+ {
+ \if_charcode:w \str_head:n {#1} \exp_not:N #2
+ \exp_after:wN \use_ii:nn
+ \else:
+ \prg_return_false:
+ \fi:
+ \use_none:n
+ {
+ \if_catcode:w \exp_not:N #2
+ \tl_if_head_is_group:nTF {#1}
+ { \c_group_begin_token }
+ { \c_space_token }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ }
+\cs_new:Npn \__tl_head_exp_not:w #1 #2 \s__tl_stop
+ { \exp_not:N #1 }
+\cs_new:Npn \__tl_if_head_eq_empty_arg:w \exp_not:N #1
+ { ? }
+\prg_new_conditional:Npnn \tl_if_head_is_N_type:n #1 { p , T , F , TF }
+ {
+ \if:w
+ \if_false: { \fi: \__tl_if_head_is_N_type_auxi:w #1 ~ }
+ { \exp_after:wN { \token_to_str:N #1 } }
+ \scan_stop: \scan_stop:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\exp_args:Nno \use:n { \cs_new:Npn \__tl_if_head_is_N_type_auxi:w #1 ~ }
+ {
+ \tl_if_empty:nTF {#1}
+ { f \exp_after:wN \use_none:nn }
+ { \exp_after:wN \__tl_if_head_is_N_type_auxii:n }
+ \exp_after:wN { \if_false: } \fi:
+ }
+\cs_new:Npn \__tl_if_head_is_N_type_auxii:n #1
+ { \exp_after:wN \use_none:n \exp_after:wN }
+\prg_new_conditional:Npnn \tl_if_head_is_group:n #1 { p , T , F , TF }
+ {
+ \if:w
+ \exp_after:wN \use_none:n
+ \exp_after:wN { \exp_after:wN { \token_to_str:N #1 ? } }
+ \scan_stop: \scan_stop:
+ \__tl_if_head_is_group_fi_false:w
+ \fi:
+ \if_true:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \__tl_if_head_is_group_fi_false:w \fi: \if_true: { \fi: \if_false: }
+\prg_new_conditional:Npnn \tl_if_head_is_space:n #1 { p , T , F , TF }
+ {
+ \if:w
+ \if_false: { \fi: \__tl_if_head_is_space:w \prg_do_nothing: #1 ? ~ }
+ \scan_stop: \scan_stop:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\exp_args:Nno \use:n { \cs_new:Npn \__tl_if_head_is_space:w #1 ~ }
+ {
+ \__tl_if_empty_if:o {#1} \else: f \fi:
+ \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi:
+ }
+\scan_new:N \s__tl_act_stop
+\cs_set_protected:Npn \__tl_tmp:w #1
+ {
+ \cs_new:Npn \__tl_act_if_head_is_space:nTF ##1
+ {
+ \__tl_act_if_head_is_space:w
+ \s__tl_act_stop ##1 \s__tl_act_stop \__tl_act_if_head_is_space_true:w
+ \s__tl_act_stop #1 \s__tl_act_stop \use_ii:nn
+ }
+ \cs_new:Npn \__tl_act_if_head_is_space:w
+ ##1 \s__tl_act_stop #1 ##2 \s__tl_act_stop
+ {}
+ \cs_new:Npn \__tl_act_if_head_is_space_true:w
+ \s__tl_act_stop #1 \s__tl_act_stop \use_ii:nn ##1 ##2
+ {##1}
+ }
+\__tl_tmp:w { ~ }
+\exp_args:Nne \use:n { \cs_new:Npn \__tl_act_loop:w #1 \s__tl_act_stop }
+ {
+ \exp_not:o { \__tl_act_if_head_is_space:nTF {#1} }
+ \exp_not:N \__tl_act_space:wwNNN
+ {
+ \exp_not:o { \tl_if_head_is_group:nTF {#1} }
+ \exp_not:N \__tl_act_group:nwNNN
+ \exp_not:N \__tl_act_normal:NwNNN
+ }
+ \exp_not:n {#1} \s__tl_act_stop
+ }
+\cs_undefine:N \__tl_act_if_head_is_space:nTF
+\cs_new:Npn \__tl_act_normal:NwNNN #1 #2 \s__tl_act_stop #3
+ {
+ #3 #1
+ \__tl_act_loop:w #2 \s__tl_act_stop
+ #3
+ }
+\cs_new:Npn \__tl_use_none_delimit_by_s_act_stop:w #1 \s__tl_act_stop { }
+\cs_new:Npn \__tl_act_end:wn #1 \__tl_act_result:n #2
+ { \group_align_safe_end: \exp_end: #2 }
+\cs_new:Npn \__tl_act_group:nwNNN #1 #2 \s__tl_act_stop #3#4#5
+ {
+ \__tl_use_none_delimit_by_s_act_stop:w #1 \__tl_act_end:wn \s__tl_act_stop
+ #5 {#1}
+ \__tl_act_loop:w #2 \s__tl_act_stop
+ #3 #4 #5
+ }
+\exp_last_unbraced:NNo
+ \cs_new:Npn \__tl_act_space:wwNNN \c_space_tl #1 \s__tl_act_stop #2#3
+ {
+ #3
+ \__tl_act_loop:w #1 \s__tl_act_stop
+ #2 #3
+ }
+\cs_new:Npn \__tl_act:NNNn #1#2#3#4
+ {
+ \group_align_safe_begin:
+ \__tl_act_loop:w #4 { \s__tl_act_stop } ? \s__tl_act_stop
+ #1 #3 #2
+ \__tl_act_result:n { }
+ }
+\cs_new:Npn \__tl_act_output:n #1 #2 \__tl_act_result:n #3
+ { #2 \__tl_act_result:n { #3 #1 } }
+\cs_new:Npn \__tl_act_reverse_output:n #1 #2 \__tl_act_result:n #3
+ { #2 \__tl_act_result:n { #1 #3 } }
+\cs_new:Npn \tl_reverse:n #1
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \exp:w
+ \__tl_act:NNNn
+ \__tl_reverse_normal:N
+ \__tl_reverse_group_preserve:n
+ \__tl_reverse_space:
+ {#1}
+ }
+ }
+\cs_generate_variant:Nn \tl_reverse:n { o , V , f , e }
+\cs_new:Npn \__tl_reverse_normal:N
+ { \__tl_act_reverse_output:n }
+\cs_new:Npn \__tl_reverse_group_preserve:n #1
+ { \__tl_act_reverse_output:n { {#1} } }
+\cs_new:Npn \__tl_reverse_space:
+ { \__tl_act_reverse_output:n { ~ } }
+\cs_new_protected:Npn \tl_reverse:N #1
+ { \__kernel_tl_set:Nx #1 { \exp_args:No \tl_reverse:n { #1 } } }
+\cs_new_protected:Npn \tl_greverse:N #1
+ { \__kernel_tl_gset:Nx #1 { \exp_args:No \tl_reverse:n { #1 } } }
+\cs_generate_variant:Nn \tl_reverse:N { c }
+\cs_generate_variant:Nn \tl_greverse:N { c }
+\cs_new:Npn \tl_item:nn #1#2
+ {
+ \exp_args:Nf \__tl_item:nn
+ { \exp_args:Nf \__tl_item_aux:nn { \int_eval:n {#2} } {#1} }
+ #1
+ \q__tl_recursion_tail
+ \prg_break_point:
+ }
+\cs_new:Npn \__tl_item_aux:nn #1#2
+ {
+ \int_compare:nNnTF {#1} < 0
+ { \int_eval:n { \tl_count:n {#2} + 1 + #1 } }
+ {#1}
+ }
+\cs_new:Npn \__tl_item:nn #1#2
+ {
+ \__tl_if_recursion_tail_break:nN {#2} \prg_break:
+ \int_compare:nNnTF {#1} = 1
+ { \prg_break:n { \exp_not:n {#2} } }
+ { \exp_args:Nf \__tl_item:nn { \int_eval:n { #1 - 1 } } }
+ }
+\cs_new:Npn \tl_item:Nn { \exp_args:No \tl_item:nn }
+\cs_generate_variant:Nn \tl_item:Nn { c }
+\cs_new:Npn \tl_rand_item:n #1
+ {
+ \tl_if_blank:nF {#1}
+ { \tl_item:nn {#1} { \int_rand:nn { 1 } { \tl_count:n {#1} } } }
+ }
+\cs_new:Npn \tl_rand_item:N { \exp_args:No \tl_rand_item:n }
+\cs_generate_variant:Nn \tl_rand_item:N { c }
+\cs_new:Npn \tl_range:Nnn { \exp_args:No \tl_range:nnn }
+\cs_generate_variant:Nn \tl_range:Nnn { c }
+\cs_new:Npn \tl_range:nnn { \__tl_range:Nnnn \__tl_range:w }
+\cs_new:Npn \__tl_range:Nnnn #1#2#3#4
+ {
+ \tl_head:f
+ {
+ \exp_args:Nf \__tl_range:nnnNn
+ { \tl_count:n {#2} } {#3} {#4} #1 {#2}
+ }
+ }
+\cs_new:Npn \__tl_range:nnnNn #1#2#3
+ {
+ \exp_args:Nff \__tl_range:nnNn
+ {
+ \exp_args:Nf \__tl_range_normalize:nn
+ { \int_eval:n { #2 - 1 } } {#1}
+ }
+ {
+ \exp_args:Nf \__tl_range_normalize:nn
+ { \int_eval:n {#3} } {#1}
+ }
+ }
+\cs_new:Npn \__tl_range:nnNn #1#2#3#4
+ {
+ \if_int_compare:w #2 > #1 \exp_stop_f: \else:
+ \exp_after:wN { \exp_after:wN }
+ \fi:
+ \exp_after:wN #3
+ \int_value:w \int_eval:n { #2 - #1 } \exp_after:wN ;
+ \exp_after:wN { \exp:w \__tl_range_skip:w #1 ; { } #4 }
+ }
+\cs_new:Npn \__tl_range_skip:w #1 ; #2
+ {
+ \if_int_compare:w #1 > \c_zero_int
+ \exp_after:wN \__tl_range_skip:w
+ \int_value:w \int_eval:n { #1 - 1 } \exp_after:wN ;
+ \else:
+ \exp_after:wN \exp_end:
+ \fi:
+ }
+\cs_new:Npn \__tl_range:w #1 ; #2
+ {
+ \exp_args:Nf \__tl_range_collect:nn
+ { \__tl_range_skip_spaces:n {#2} } {#1}
+ }
+\cs_new:Npn \__tl_range_skip_spaces:n #1
+ {
+ \tl_if_head_is_space:nTF {#1}
+ { \exp_args:Nf \__tl_range_skip_spaces:n {#1} }
+ { { } #1 }
+ }
+\cs_new:Npn \__tl_range_collect:nn #1#2
+ {
+ \int_compare:nNnTF {#2} = 0
+ {#1}
+ {
+ \exp_args:No \tl_if_head_is_space:nTF { \use_none:n #1 }
+ {
+ \exp_args:Nf \__tl_range_collect:nn
+ { \__tl_range_collect_space:nw #1 }
+ {#2}
+ }
+ {
+ \__tl_range_collect:ff
+ {
+ \exp_args:No \tl_if_head_is_N_type:nTF { \use_none:n #1 }
+ { \__tl_range_collect_N:nN }
+ { \__tl_range_collect_group:nn }
+ #1
+ }
+ { \int_eval:n { #2 - 1 } }
+ }
+ }
+ }
+\cs_new:Npn \__tl_range_collect_space:nw #1 ~ { { #1 ~ } }
+\cs_new:Npn \__tl_range_collect_N:nN #1#2 { { #1 #2 } }
+\cs_new:Npn \__tl_range_collect_group:nn #1#2 { { #1 {#2} } }
+\cs_generate_variant:Nn \__tl_range_collect:nn { ff }
+\cs_new:Npn \__tl_range_normalize:nn #1#2
+ {
+ \int_eval:n
+ {
+ \if_int_compare:w #1 < \c_zero_int
+ \if_int_compare:w #1 < -#2 \exp_stop_f:
+ 0
+ \else:
+ #1 + #2 + 1
+ \fi:
+ \else:
+ \if_int_compare:w #1 < #2 \exp_stop_f:
+ #1
+ \else:
+ #2
+ \fi:
+ \fi:
+ }
+ }
+\cs_new_protected:Npn \tl_show:N { \__tl_show:NN \tl_show:n }
+\cs_generate_variant:Nn \tl_show:N { c }
+\cs_new_protected:Npn \tl_log:N { \__tl_show:NN \tl_log:n }
+\cs_generate_variant:Nn \tl_log:N { c }
+\cs_new_protected:Npn \__tl_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ \exp_args:Nf \tl_if_empty:nTF
+ { \cs_prefix_spec:N #2 \cs_parameter_spec:N #2 }
+ {
+ \exp_args:Ne #1
+ { \token_to_str:N #2 = \__kernel_exp_not:w \exp_after:wN {#2} }
+ }
+ {
+ \msg_error:nneee { kernel } { bad-type }
+ { \token_to_str:N #2 } { \token_to_meaning:N #2 } { tl }
+ }
+ }
+ }
+\cs_new_protected:Npn \tl_show:n #1
+ { \iow_wrap:nnnN { >~ \tl_to_str:n {#1} . } { } { } \__tl_show:n }
+\cs_generate_variant:Nn \tl_show:n { e , x }
+\cs_new_protected:Npn \__tl_show:n #1
+ {
+ \tl_set:Nf \l__tl_internal_a_tl { \__tl_show:w #1 \s__tl_stop }
+ \__kernel_iow_with:Nnn \tex_newlinechar:D { 10 }
+ {
+ \__kernel_iow_with:Nnn \tex_errorcontextlines:D { -1 }
+ {
+ \tex_showtokens:D \exp_after:wN \exp_after:wN \exp_after:wN
+ { \exp_after:wN \l__tl_internal_a_tl }
+ }
+ }
+ }
+\cs_new:Npn \__tl_show:w #1 > #2 . \s__tl_stop {#2}
+\cs_new_protected:Npn \tl_log:n #1
+ { \iow_wrap:nnnN { > ~ \tl_to_str:n {#1} . } { } { } \iow_log:n }
+\cs_generate_variant:Nn \tl_log:n { e , x }
+\cs_new_protected:Npn \__kernel_chk_tl_type:NnnT #1#2#3#4
+ {
+ \__kernel_chk_defined:NT #1
+ {
+ \exp_args:Nf \tl_if_empty:nTF
+ { \cs_prefix_spec:N #1 \cs_parameter_spec:N #1 }
+ {
+ \tl_set:Ne \l__tl_internal_a_tl {#3}
+ \tl_if_eq:NNTF #1 \l__tl_internal_a_tl
+ {#4}
+ {
+ \msg_error:nneeee { kernel } { bad-type }
+ { \token_to_str:N #1 } { \tl_to_str:N #1 }
+ {#2} { \tl_to_str:N \l__tl_internal_a_tl }
+ }
+ }
+ {
+ \msg_error:nneee { kernel } { bad-type }
+ { \token_to_str:N #1 } { \token_to_meaning:N #1 } {#2}
+ }
+ }
+ }
+\scan_new:N \s__tl_nil
+\scan_new:N \s__tl_mark
+\scan_new:N \s__tl_stop
+\tl_new:N \g_tmpa_tl
+\tl_new:N \g_tmpb_tl
+\tl_new:N \l_tmpa_tl
+\tl_new:N \l_tmpb_tl
+\cs_undefine:N \__tl_tmp:w
+%% File: l3tl-build.dtx
+\cs_new_protected:Npn \tl_build_begin:N #1
+ { \__tl_build_begin:NN \cs_set_nopar:Npe #1 }
+\cs_new_protected:Npn \tl_build_gbegin:N #1
+ { \__tl_build_begin:NN \cs_gset_nopar:Npe #1 }
+\cs_new_protected:Npn \__tl_build_begin:NN #1#2
+ { \exp_args:Nc \__tl_build_begin:NNN { \cs_to_str:N #2 ' } #2 #1 }
+\cs_new_protected:Npn \__tl_build_begin:NNN #1#2#3
+ {
+ #3 #1 { }
+ #3 #2
+ {
+ \exp_not:n { \exp_end: \exp_end: \exp_end: \exp_end: }
+ \exp_not:n { \__tl_build_last:NNn #3 #1 { } }
+ }
+ }
+\cs_new_protected:Npn \tl_build_put_right:Nn #1#2
+ {
+ \cs_set_nopar:Npe #1
+ { \__kernel_exp_not:w \exp_after:wN { \exp:w #1 #2 } }
+ }
+\cs_generate_variant:Nn \tl_build_put_right:Nn { Ne , Nx }
+\cs_new_protected:Npn \tl_build_gput_right:Nn #1#2
+ {
+ \cs_gset_nopar:Npe #1
+ { \__kernel_exp_not:w \exp_after:wN { \exp:w #1 #2 } }
+ }
+\cs_generate_variant:Nn \tl_build_gput_right:Nn { Ne , Nx }
+\cs_new_protected:Npn \__tl_build_last:NNn #1#2
+ {
+ \if_false: { { \fi:
+ \exp_end: \exp_end: \exp_end: \exp_end: \exp_end:
+ \__tl_build_last:NNn #1 #2 { }
+ }
+ }
+ \if_meaning:w \c_empty_tl #2
+ \__tl_build_begin:NN #1 #2
+ \fi:
+ #1 #2
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \exp:w \if_false: } } \fi:
+ \exp_after:wN \__tl_build_put:nn \exp_after:wN {#2}
+ }
+\cs_new_protected:Npn \__tl_build_put:nn #1#2 { \__tl_build_put:nw {#2} #1 }
+\cs_new_protected:Npn \__tl_build_put:nw #1#2 \__tl_build_last:NNn #3#4#5
+ { #2 \__tl_build_last:NNn #3 #4 { #1 #5 } }
+\cs_new_protected:Npn \tl_build_put_left:Nn #1
+ { \__tl_build_put_left:NNn \cs_set_nopar:Npe #1 }
+\cs_generate_variant:Nn \tl_build_put_left:Nn { Ne , Nx }
+\cs_new_protected:Npn \tl_build_gput_left:Nn #1
+ { \__tl_build_put_left:NNn \cs_gset_nopar:Npe #1 }
+\cs_generate_variant:Nn \tl_build_gput_left:Nn { Ne , Nx }
+\cs_new_protected:Npn \__tl_build_put_left:NNn #1#2#3
+ {
+ #1 #2
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \exp:w \exp_after:wN \__tl_build_put:nn
+ \exp_after:wN {#2} {#3}
+ }
+ }
+ }
+\cs_new_protected:Npn \tl_build_end:N #1
+ {
+ \__tl_build_get:NNN \__kernel_tl_set:Nx #1 #1
+ \exp_args:Nc \__tl_build_end_loop:NN { \cs_to_str:N #1 ' } \tl_clear:N
+ }
+\cs_new_protected:Npn \tl_build_gend:N #1
+ {
+ \__tl_build_get:NNN \__kernel_tl_gset:Nx #1 #1
+ \exp_args:Nc \__tl_build_end_loop:NN { \cs_to_str:N #1 ' } \tl_gclear:N
+ }
+\cs_new_protected:Npn \__tl_build_end_loop:NN #1#2
+ {
+ \if_meaning:w \c_empty_tl #1
+ \exp_after:wN \use_none:nnnnnn
+ \fi:
+ #2 #1
+ \exp_args:Nc \__tl_build_end_loop:NN { \cs_to_str:N #1 ' } #2
+ }
+\cs_new_protected:Npn \tl_build_get_intermediate:NN
+ { \__tl_build_get:NNN \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \__tl_build_get:NNN #1#2#3
+ { #1 #3 { \if_false: { \fi: \exp_after:wN \__tl_build_get:w #2 } } }
+\cs_new:Npn \__tl_build_get:w #1 \__tl_build_last:NNn #2#3#4
+ {
+ \exp_not:n {#4}
+ \if_meaning:w \c_empty_tl #3
+ \exp_after:wN \__tl_build_get_end:w
+ \fi:
+ \exp_after:wN \__tl_build_get:w #3
+ }
+\cs_new:Npn \__tl_build_get_end:w #1#2#3
+ { \__kernel_exp_not:w \exp_after:wN { \if_false: } \fi: }
+%% File: l3str.dtx
+\scan_new:N \s__str_mark
+\scan_new:N \s__str_stop
+\cs_new:Npn \__str_use_none_delimit_by_s_stop:w #1 \s__str_stop { }
+\cs_new:Npn \__str_use_i_delimit_by_s_stop:nw #1 #2 \s__str_stop {#1}
+\quark_new:N \q__str_recursion_tail
+\quark_new:N \q__str_recursion_stop
+\__kernel_quark_new_test:N \__str_if_recursion_tail_break:NN
+\__kernel_quark_new_test:N \__str_if_recursion_tail_stop_do:Nn
+\group_begin:
+ \cs_set_protected:Npn \__str_tmp:n #1
+ {
+ \tl_if_blank:nF {#1}
+ {
+ \cs_new_eq:cc { str_ #1 :N } { tl_ #1 :N }
+ \exp_args:Nc \cs_generate_variant:Nn { str_ #1 :N } { c }
+ \__str_tmp:n
+ }
+ }
+ \__str_tmp:n
+ { new }
+ { use }
+ { clear }
+ { gclear }
+ { clear_new }
+ { gclear_new }
+ { }
+\group_end:
+\cs_new_eq:NN \str_set_eq:NN \tl_set_eq:NN
+\cs_new_eq:NN \str_gset_eq:NN \tl_gset_eq:NN
+\cs_generate_variant:Nn \str_set_eq:NN { c , Nc , cc }
+\cs_generate_variant:Nn \str_gset_eq:NN { c , Nc , cc }
+\cs_new_eq:NN \str_concat:NNN \tl_concat:NNN
+\cs_new_eq:NN \str_gconcat:NNN \tl_gconcat:NNN
+\cs_generate_variant:Nn \str_concat:NNN { ccc }
+\cs_generate_variant:Nn \str_gconcat:NNN { ccc }
+\cs_new_protected:Npn \str_set:Nn #1#2
+ { \__kernel_tl_set:Nx #1 { \__kernel_tl_to_str:w {#2} } }
+\cs_gset_protected:Npn \str_gset:Nn #1#2
+ { \__kernel_tl_gset:Nx #1 { \__kernel_tl_to_str:w {#2} } }
+\cs_new_protected:Npn \str_const:Nn #1#2
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_nopar:Npe #1 { \__kernel_tl_to_str:w {#2} }
+ }
+\cs_new_protected:Npn \str_put_left:Nn #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \__kernel_tl_to_str:w {#2} \__kernel_exp_not:w \exp_after:wN {#1} }
+ }
+\cs_new_protected:Npn \str_gput_left:Nn #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \__kernel_tl_to_str:w {#2} \__kernel_exp_not:w \exp_after:wN {#1} }
+ }
+\cs_new_protected:Npn \str_put_right:Nn #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \__kernel_exp_not:w \exp_after:wN {#1} \__kernel_tl_to_str:w {#2} }
+ }
+\cs_new_protected:Npn \str_gput_right:Nn #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \__kernel_exp_not:w \exp_after:wN {#1} \__kernel_tl_to_str:w {#2} }
+ }
+\cs_generate_variant:Nn \str_set:Nn { NV , Ne , Nx , c , cV , ce , cx }
+\cs_generate_variant:Nn \str_gset:Nn { NV , Ne , Nx , c , cV , ce , cx }
+\cs_generate_variant:Nn \str_const:Nn { NV , Ne , Nx , c , cV , ce , cx }
+\cs_generate_variant:Nn \str_put_left:Nn { NV , Ne , Nx , c , cV , ce , cx }
+\cs_generate_variant:Nn \str_gput_left:Nn { NV , Ne , Nx , c , cV , ce , cx }
+\cs_generate_variant:Nn \str_put_right:Nn { NV , Ne , Nx , c , cV , ce , cx }
+\cs_generate_variant:Nn \str_gput_right:Nn { NV , Ne , Nx , c , cV , ce , cx }
+\cs_new_protected:Npn \str_replace_once:Nnn
+ { \__str_replace:NNNnn \prg_do_nothing: \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \str_greplace_once:Nnn
+ { \__str_replace:NNNnn \prg_do_nothing: \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \str_replace_all:Nnn
+ { \__str_replace:NNNnn \__str_replace_next:w \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \str_greplace_all:Nnn
+ { \__str_replace:NNNnn \__str_replace_next:w \__kernel_tl_gset:Nx }
+\cs_generate_variant:Nn \str_replace_once:Nnn { c }
+\cs_generate_variant:Nn \str_greplace_once:Nnn { c }
+\cs_generate_variant:Nn \str_replace_all:Nnn { c }
+\cs_generate_variant:Nn \str_greplace_all:Nnn { c }
+\cs_new_protected:Npn \__str_replace:NNNnn #1#2#3#4#5
+ {
+ \tl_if_empty:nTF {#4}
+ {
+ \msg_error:nne { kernel } { empty-search-pattern } {#5}
+ }
+ {
+ \use:e
+ {
+ \exp_not:n { \__str_replace_aux:NNNnnn #1 #2 #3 }
+ { \tl_to_str:N #3 }
+ { \tl_to_str:n {#4} } { \tl_to_str:n {#5} }
+ }
+ }
+ }
+\cs_new_protected:Npn \__str_replace_aux:NNNnnn #1#2#3#4#5#6
+ {
+ \cs_set:Npn \__str_replace_next:w ##1 #5 { ##1 #6 #1 }
+ #2 #3
+ {
+ \__str_replace_next:w
+ #4
+ \__str_use_none_delimit_by_s_stop:w
+ #5
+ \s__str_stop
+ }
+ }
+\cs_new_eq:NN \__str_replace_next:w ?
+\cs_new_protected:Npn \str_remove_once:Nn #1#2
+ { \str_replace_once:Nnn #1 {#2} { } }
+\cs_new_protected:Npn \str_gremove_once:Nn #1#2
+ { \str_greplace_once:Nnn #1 {#2} { } }
+\cs_generate_variant:Nn \str_remove_once:Nn { c }
+\cs_generate_variant:Nn \str_gremove_once:Nn { c }
+\cs_new_protected:Npn \str_remove_all:Nn #1#2
+ { \str_replace_all:Nnn #1 {#2} { } }
+\cs_new_protected:Npn \str_gremove_all:Nn #1#2
+ { \str_greplace_all:Nnn #1 {#2} { } }
+\cs_generate_variant:Nn \str_remove_all:Nn { c }
+\cs_generate_variant:Nn \str_gremove_all:Nn { c }
+\prg_new_eq_conditional:NNn \str_if_exist:N \tl_if_exist:N
+ { p , T , F , TF }
+\prg_new_eq_conditional:NNn \str_if_exist:c \tl_if_exist:c
+ { p , T , F , TF }
+\prg_new_eq_conditional:NNn \str_if_empty:N \tl_if_empty:N
+ { p , T , F , TF }
+\prg_new_eq_conditional:NNn \str_if_empty:c \tl_if_empty:c
+ { p , T , F , TF }
+\prg_new_eq_conditional:NNn \str_if_empty:n \tl_if_empty:n
+ { p , T , F , TF }
+\cs_new_eq:NN \__str_if_eq:nn \tex_strcmp:D
+\prg_new_conditional:Npnn \str_compare:nNn #1#2#3 { p , T , F , TF }
+ {
+ \if_int_compare:w
+ \__str_if_eq:nn { \exp_not:n {#1} } { \exp_not:n {#3} }
+ #2 \c_zero_int
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_new_conditional:Npnn \str_compare:eNe #1#2#3 { p , T , F , TF }
+ {
+ \if_int_compare:w \__str_if_eq:nn {#1} {#3} #2 \c_zero_int
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_new_conditional:Npnn \str_if_eq:nn #1#2 { p , T , F , TF }
+ {
+ \if:w 0 \__str_if_eq:nn { \exp_not:n {#1} } { \exp_not:n {#2} }
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_generate_conditional_variant:Nnn \str_if_eq:nn
+ { V , v , o , nV , no , VV , nv } { p , T , F , TF }
+\prg_new_conditional:Npnn \str_if_eq:ee #1#2 { p , T , F , TF }
+ {
+ \if:w 0 \__str_if_eq:nn {#1} {#2}
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_new_conditional:Npnn \str_if_eq:NN #1#2 { p , TF , T , F }
+ {
+ \if:w 0 \__str_if_eq:nn { \tl_to_str:N #1 } { \tl_to_str:N #2 }
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_generate_conditional_variant:Nnn \str_if_eq:NN
+ { c , Nc , cc } { T , F , TF , p }
+\prg_new_protected_conditional:Npnn \str_if_in:Nn #1#2 { T , F , TF }
+ {
+ \use:e
+ { \tl_if_in:nnTF { \tl_to_str:N #1 } { \tl_to_str:n {#2} } }
+ { \prg_return_true: } { \prg_return_false: }
+ }
+\prg_generate_conditional_variant:Nnn \str_if_in:Nn
+ { c } { T , F , TF }
+\prg_new_protected_conditional:Npnn \str_if_in:nn #1#2 { T , F , TF }
+ {
+ \use:e
+ { \tl_if_in:nnTF { \tl_to_str:n {#1} } { \tl_to_str:n {#2} } }
+ { \prg_return_true: } { \prg_return_false: }
+ }
+\cs_new:Npn \str_case:nn #1#2
+ {
+ \exp:w
+ \__str_case:nnTF {#1} {#2} { } { }
+ }
+\cs_new:Npn \str_case:nnT #1#2#3
+ {
+ \exp:w
+ \__str_case:nnTF {#1} {#2} {#3} { }
+ }
+\cs_new:Npn \str_case:nnF #1#2
+ {
+ \exp:w
+ \__str_case:nnTF {#1} {#2} { }
+ }
+\cs_new:Npn \str_case:nnTF #1#2
+ {
+ \exp:w
+ \__str_case:nnTF {#1} {#2}
+ }
+\cs_new:Npn \__str_case:nnTF #1#2#3#4
+ { \__str_case:nw {#1} #2 {#1} { } \s__str_mark {#3} \s__str_mark {#4} \s__str_stop }
+\cs_generate_variant:Nn \str_case:nn { V , o , e , nV , nv }
+\prg_generate_conditional_variant:Nnn \str_case:nn
+ { V , o , e , nV , nv } { T , F , TF }
+\cs_new_eq:NN \str_case:Nn \str_case:Vn
+\cs_new_eq:NN \str_case:NnT \str_case:VnT
+\cs_new_eq:NN \str_case:NnF \str_case:VnF
+\cs_new_eq:NN \str_case:NnTF \str_case:VnTF
+\cs_new:Npn \__str_case:nw #1#2#3
+ {
+ \str_if_eq:nnTF {#1} {#2}
+ { \__str_case_end:nw {#3} }
+ { \__str_case:nw {#1} }
+ }
+\cs_new:Npn \str_case_e:nn #1#2
+ {
+ \exp:w
+ \__str_case_e:nnTF {#1} {#2} { } { }
+ }
+\cs_new:Npn \str_case_e:nnT #1#2#3
+ {
+ \exp:w
+ \__str_case_e:nnTF {#1} {#2} {#3} { }
+ }
+\cs_new:Npn \str_case_e:nnF #1#2
+ {
+ \exp:w
+ \__str_case_e:nnTF {#1} {#2} { }
+ }
+\cs_new:Npn \str_case_e:nnTF #1#2
+ {
+ \exp:w
+ \__str_case_e:nnTF {#1} {#2}
+ }
+\cs_new:Npn \__str_case_e:nnTF #1#2#3#4
+ { \__str_case_e:nw {#1} #2 {#1} { } \s__str_mark {#3} \s__str_mark {#4} \s__str_stop }
+\cs_generate_variant:Nn \str_case_e:nn { e }
+\prg_generate_conditional_variant:Nnn \str_case_e:nn { e } { T , F , TF }
+\cs_new:Npn \__str_case_e:nw #1#2#3
+ {
+ \str_if_eq:eeTF {#1} {#2}
+ { \__str_case_end:nw {#3} }
+ { \__str_case_e:nw {#1} }
+ }
+\cs_new:Npn \__str_case_end:nw #1#2#3 \s__str_mark #4#5 \s__str_stop
+ { \exp_end: #1 #4 }
+\cs_new:Npn \str_map_function:nN #1#2
+ {
+ \exp_after:wN \__str_map_function:w
+ \exp_after:wN \__str_map_function:nn \exp_after:wN #2
+ \__kernel_tl_to_str:w {#1}
+ \q__str_recursion_tail ? ~
+ \prg_break_point:Nn \str_map_break: { }
+ }
+\cs_new:Npn \str_map_function:NN
+ { \exp_args:No \str_map_function:nN }
+\cs_new:Npn \__str_map_function:w #1 ~
+ { #1 { ~ { ~ } \__str_map_function:w } }
+\cs_new:Npn \__str_map_function:nn #1#2
+ {
+ \if_meaning:w \q__str_recursion_tail #2
+ \exp_after:wN \str_map_break:
+ \fi:
+ #1 #2 \__str_map_function:nn {#1}
+ }
+\cs_generate_variant:Nn \str_map_function:NN { c }
+\cs_new_protected:Npn \str_map_inline:nn #1#2
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \cs_gset_protected:cpn
+ { __str_map_ \int_use:N \g__kernel_prg_map_int :w } ##1 {#2}
+ \use:e
+ {
+ \exp_not:N \__str_map_inline:NN
+ \exp_not:c { __str_map_ \int_use:N \g__kernel_prg_map_int :w }
+ \__kernel_str_to_other_fast:n {#1}
+ }
+ \q__str_recursion_tail
+ \prg_break_point:Nn \str_map_break:
+ { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+\cs_new_protected:Npn \str_map_inline:Nn
+ { \exp_args:No \str_map_inline:nn }
+\cs_generate_variant:Nn \str_map_inline:Nn { c }
+\cs_new:Npn \__str_map_inline:NN #1#2
+ {
+ \__str_if_recursion_tail_break:NN #2 \str_map_break:
+ \exp_args:No #1 { \token_to_str:N #2 }
+ \__str_map_inline:NN #1
+ }
+\cs_new_protected:Npn \str_map_variable:nNn #1#2#3
+ {
+ \use:e
+ {
+ \exp_not:n { \__str_map_variable:NnN #2 {#3} }
+ \__kernel_str_to_other_fast:n {#1}
+ }
+ \q__str_recursion_tail
+ \prg_break_point:Nn \str_map_break: { }
+ }
+\cs_new_protected:Npn \str_map_variable:NNn
+ { \exp_args:No \str_map_variable:nNn }
+\cs_new_protected:Npn \__str_map_variable:NnN #1#2#3
+ {
+ \__str_if_recursion_tail_break:NN #3 \str_map_break:
+ \str_set:Nn #1 {#3}
+ \use:n {#2}
+ \__str_map_variable:NnN #1 {#2}
+ }
+\cs_generate_variant:Nn \str_map_variable:NNn { c }
+\cs_new:Npn \str_map_break:
+ { \prg_map_break:Nn \str_map_break: { } }
+\cs_new:Npn \str_map_break:n
+ { \prg_map_break:Nn \str_map_break: }
+\cs_new:Npn \str_map_tokens:nn #1#2
+ {
+ \exp_args:Nno \use:nn
+ { \__str_map_function:w \__str_map_function:nn {#2} }
+ { \__kernel_tl_to_str:w {#1} }
+ \q__str_recursion_tail ? ~
+ \prg_break_point:Nn \str_map_break: { }
+ }
+\cs_new:Npn \str_map_tokens:Nn { \exp_args:No \str_map_tokens:nn }
+\cs_generate_variant:Nn \str_map_tokens:Nn { c }
+\cs_new:Npn \__kernel_str_to_other:n #1
+ {
+ \exp_after:wN \__str_to_other_loop:w
+ \tl_to_str:n {#1} ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ \s__str_mark \s__str_stop
+ }
+\group_begin:
+\tex_lccode:D `\* = `\ %
+\tex_lccode:D `\A = `\A %
+\tex_lowercase:D
+ {
+ \group_end:
+ \cs_new:Npn \__str_to_other_loop:w
+ #1 ~ #2 ~ #3 ~ #4 ~ #5 ~ #6 ~ #7 ~ #8 ~ #9 \s__str_stop
+ {
+ \if_meaning:w A #8
+ \__str_to_other_end:w
+ \fi:
+ \__str_to_other_loop:w
+ #9 #1 * #2 * #3 * #4 * #5 * #6 * #7 * #8 * \s__str_stop
+ }
+ \cs_new:Npn \__str_to_other_end:w \fi: #1 \s__str_mark #2 * A #3 \s__str_stop
+ { \fi: #2 }
+ }
+\cs_new:Npn \__kernel_str_to_other_fast:n #1
+ {
+ \exp_after:wN \__str_to_other_fast_loop:w \tl_to_str:n {#1} ~
+ A ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ \s__str_stop
+ }
+\group_begin:
+\tex_lccode:D `\* = `\ %
+\tex_lccode:D `\A = `\A %
+\tex_lowercase:D
+ {
+ \group_end:
+ \cs_new:Npn \__str_to_other_fast_loop:w
+ #1 ~ #2 ~ #3 ~ #4 ~ #5 ~ #6 ~ #7 ~ #8 ~ #9 ~
+ {
+ \if_meaning:w A #9
+ \__str_to_other_fast_end:w
+ \fi:
+ #1 * #2 * #3 * #4 * #5 * #6 * #7 * #8 * #9
+ \__str_to_other_fast_loop:w *
+ }
+ \cs_new:Npn \__str_to_other_fast_end:w #1 * A #2 \s__str_stop {#1}
+ }
+\cs_new:Npn \str_item:Nn { \exp_args:No \str_item:nn }
+\cs_generate_variant:Nn \str_item:Nn { c }
+\cs_new:Npn \str_item:nn #1#2
+ {
+ \exp_args:Nf \tl_to_str:n
+ {
+ \exp_args:Nf \__str_item:nn
+ { \__kernel_str_to_other:n {#1} } {#2}
+ }
+ }
+\cs_new:Npn \str_item_ignore_spaces:nn #1
+ { \exp_args:No \__str_item:nn { \tl_to_str:n {#1} } }
+\cs_new:Npn \__str_item:nn #1#2
+ {
+ \exp_after:wN \__str_item:w
+ \int_value:w \int_eval:n {#2} \exp_after:wN ;
+ \int_value:w \__str_count:n {#1} ;
+ #1 \s__str_stop
+ }
+\cs_new:Npn \__str_item:w #1; #2;
+ {
+ \int_compare:nNnTF {#1} < 0
+ {
+ \int_compare:nNnTF {#1} < {-#2}
+ { \__str_use_none_delimit_by_s_stop:w }
+ {
+ \exp_after:wN \__str_use_i_delimit_by_s_stop:nw
+ \exp:w \exp_after:wN \__str_skip_exp_end:w
+ \int_value:w \int_eval:n { #1 + #2 } ;
+ }
+ }
+ {
+ \int_compare:nNnTF {#1} > {#2}
+ { \__str_use_none_delimit_by_s_stop:w }
+ {
+ \exp_after:wN \__str_use_i_delimit_by_s_stop:nw
+ \exp:w \__str_skip_exp_end:w #1 ; { }
+ }
+ }
+ }
+\cs_new:Npn \__str_skip_exp_end:w #1;
+ {
+ \if_int_compare:w #1 > 8 \exp_stop_f:
+ \exp_after:wN \__str_skip_loop:wNNNNNNNN
+ \else:
+ \exp_after:wN \__str_skip_end:w
+ \int_value:w \int_eval:w
+ \fi:
+ #1 ;
+ }
+\cs_new:Npn \__str_skip_loop:wNNNNNNNN #1; #2#3#4#5#6#7#8#9
+ {
+ \exp_after:wN \__str_skip_exp_end:w
+ \int_value:w \int_eval:n { #1 - 8 } ;
+ }
+\cs_new:Npn \__str_skip_end:w #1 ;
+ {
+ \exp_after:wN \__str_skip_end:NNNNNNNN
+ \if_case:w #1 \exp_stop_f: \or: \or: \or: \or: \or: \or: \or: \or:
+ }
+\cs_new:Npn \__str_skip_end:NNNNNNNN #1#2#3#4#5#6#7#8 { \fi: \exp_end: }
+\cs_new:Npn \str_range:Nnn { \exp_args:No \str_range:nnn }
+\cs_generate_variant:Nn \str_range:Nnn { c }
+\cs_new:Npn \str_range:nnn #1#2#3
+ {
+ \exp_args:Nf \tl_to_str:n
+ {
+ \exp_args:Nf \__str_range:nnn
+ { \__kernel_str_to_other:n {#1} } {#2} {#3}
+ }
+ }
+\cs_new:Npn \str_range_ignore_spaces:nnn #1
+ { \exp_args:No \__str_range:nnn { \tl_to_str:n {#1} } }
+\cs_new:Npn \__str_range:nnn #1#2#3
+ {
+ \exp_after:wN \__str_range:w
+ \int_value:w \__str_count:n {#1} \exp_after:wN ;
+ \int_value:w \int_eval:n { (#2) - 1 } \exp_after:wN ;
+ \int_value:w \int_eval:n {#3} ;
+ #1 \s__str_stop
+ }
+\cs_new:Npn \__str_range:w #1; #2; #3;
+ {
+ \exp_args:Nf \__str_range:nnw
+ { \__str_range_normalize:nn {#2} {#1} }
+ { \__str_range_normalize:nn {#3} {#1} }
+ }
+\cs_new:Npn \__str_range:nnw #1#2
+ {
+ \exp_after:wN \__str_collect_delimit_by_q_stop:w
+ \int_value:w \int_eval:n { #2 - #1 } \exp_after:wN ;
+ \exp:w \__str_skip_exp_end:w #1 ;
+ }
+\cs_new:Npn \__str_range_normalize:nn #1#2
+ {
+ \int_eval:n
+ {
+ \if_int_compare:w #1 < \c_zero_int
+ \if_int_compare:w #1 < -#2 \exp_stop_f:
+ 0
+ \else:
+ #1 + #2 + 1
+ \fi:
+ \else:
+ \if_int_compare:w #1 < #2 \exp_stop_f:
+ #1
+ \else:
+ #2
+ \fi:
+ \fi:
+ }
+ }
+\cs_new:Npn \__str_collect_delimit_by_q_stop:w #1;
+ { \__str_collect_loop:wn #1 ; { } }
+\cs_new:Npn \__str_collect_loop:wn #1 ;
+ {
+ \if_int_compare:w #1 > 7 \exp_stop_f:
+ \exp_after:wN \__str_collect_loop:wnNNNNNNN
+ \else:
+ \exp_after:wN \__str_collect_end:wn
+ \fi:
+ #1 ;
+ }
+\cs_new:Npn \__str_collect_loop:wnNNNNNNN #1; #2 #3#4#5#6#7#8#9
+ {
+ \exp_after:wN \__str_collect_loop:wn
+ \int_value:w \int_eval:n { #1 - 7 } ;
+ { #2 #3#4#5#6#7#8#9 }
+ }
+\cs_new:Npn \__str_collect_end:wn #1 ;
+ {
+ \exp_after:wN \__str_collect_end:nnnnnnnnw
+ \if_case:w \if_int_compare:w #1 > \c_zero_int
+ #1 \else: 0 \fi: \exp_stop_f:
+ \or: \or: \or: \or: \or: \or: \fi:
+ }
+\cs_new:Npn \__str_collect_end:nnnnnnnnw #1#2#3#4#5#6#7#8 #9 \s__str_stop
+ { #1#2#3#4#5#6#7#8 }
+\cs_new:Npn \str_count_spaces:N
+ { \exp_args:No \str_count_spaces:n }
+\cs_generate_variant:Nn \str_count_spaces:N { c }
+\cs_new:Npn \str_count_spaces:n #1
+ {
+ \int_eval:n
+ {
+ \exp_after:wN \__str_count_spaces_loop:w
+ \tl_to_str:n {#1} ~
+ X 7 ~ X 6 ~ X 5 ~ X 4 ~ X 3 ~ X 2 ~ X 1 ~ X 0 ~ X -1 ~
+ \s__str_stop
+ }
+ }
+\cs_new:Npn \__str_count_spaces_loop:w #1~#2~#3~#4~#5~#6~#7~#8~#9~
+ {
+ \if_meaning:w X #9
+ \__str_use_i_delimit_by_s_stop:nw
+ \fi:
+ 9 + \__str_count_spaces_loop:w
+ }
+\cs_new:Npn \str_count:N { \exp_args:No \str_count:n }
+\cs_generate_variant:Nn \str_count:N { c }
+\cs_new:Npn \str_count:n #1
+ {
+ \__str_count_aux:n
+ {
+ \str_count_spaces:n {#1}
+ + \exp_after:wN \__str_count_loop:NNNNNNNNN \tl_to_str:n {#1}
+ }
+ }
+\cs_new:Npn \__str_count:n #1
+ {
+ \__str_count_aux:n
+ { \__str_count_loop:NNNNNNNNN #1 }
+ }
+\cs_new:Npn \str_count_ignore_spaces:n #1
+ {
+ \__str_count_aux:n
+ { \exp_after:wN \__str_count_loop:NNNNNNNNN \tl_to_str:n {#1} }
+ }
+\cs_new:Npn \__str_count_aux:n #1
+ {
+ \int_eval:n
+ {
+ #1
+ { X 8 } { X 7 } { X 6 }
+ { X 5 } { X 4 } { X 3 }
+ { X 2 } { X 1 } { X 0 }
+ \s__str_stop
+ }
+ }
+\cs_new:Npn \__str_count_loop:NNNNNNNNN #1#2#3#4#5#6#7#8#9
+ {
+ \if_meaning:w X #9
+ \exp_after:wN \__str_use_none_delimit_by_s_stop:w
+ \fi:
+ 9 + \__str_count_loop:NNNNNNNNN
+ }
+\cs_new:Npn \str_head:N { \exp_args:No \str_head:n }
+\cs_generate_variant:Nn \str_head:N { c }
+\cs_new:Npn \str_head:n #1
+ {
+ \exp_after:wN \__str_head:w
+ \tl_to_str:n {#1}
+ { { } } ~ \s__str_stop
+ }
+\cs_new:Npn \__str_head:w #1 ~ %
+ { \__str_use_i_delimit_by_s_stop:nw #1 { ~ } }
+\cs_new:Npn \str_head_ignore_spaces:n #1
+ {
+ \exp_after:wN \__str_use_i_delimit_by_s_stop:nw
+ \tl_to_str:n {#1} { } \s__str_stop
+ }
+\cs_new:Npn \str_tail:N { \exp_args:No \str_tail:n }
+\cs_generate_variant:Nn \str_tail:N { c }
+\cs_new:Npn \str_tail:n #1
+ {
+ \exp_after:wN \__str_tail_auxi:w
+ \reverse_if:N \if_charcode:w
+ \scan_stop: \tl_to_str:n {#1} X X \s__str_stop
+ }
+\cs_new:Npn \__str_tail_auxi:w #1 X #2 \s__str_stop { \fi: #1 }
+\cs_new:Npn \str_tail_ignore_spaces:n #1
+ {
+ \exp_after:wN \__str_tail_auxii:w
+ \tl_to_str:n {#1} \s__str_mark \s__str_mark \s__str_stop
+ }
+\cs_new:Npn \__str_tail_auxii:w #1 #2 \s__str_mark #3 \s__str_stop { #2 }
+\cs_new:Npn \str_casefold:n #1 { \__str_change_case:nn {#1} { casefold } }
+\cs_new:Npn \str_lowercase:n #1 { \__str_change_case:nn {#1} { lowercase } }
+\cs_new:Npn \str_uppercase:n #1 { \__str_change_case:nn {#1} { uppercase } }
+\cs_generate_variant:Nn \str_casefold:n { V }
+\cs_generate_variant:Nn \str_lowercase:n { f }
+\cs_generate_variant:Nn \str_uppercase:n { f }
+\cs_new:Npn \__str_change_case:nn #1
+ {
+ \exp_after:wN \__str_change_case_aux:nn \exp_after:wN
+ { \tl_to_str:n {#1} }
+ }
+\cs_new:Npn \__str_change_case_aux:nn #1#2
+ {
+ \__str_change_case_loop:nw {#2} #1 \q__str_recursion_tail \q__str_recursion_stop
+ \__str_change_case_result:n { }
+ }
+\cs_new:Npn \__str_change_case_output:nw #1#2 \__str_change_case_result:n #3
+ { #2 \__str_change_case_result:n { #3 #1 } }
+\cs_generate_variant:Nn \__str_change_case_output:nw { f }
+\cs_new:Npn \__str_change_case_end:wn #1 \__str_change_case_result:n #2
+ { \tl_to_str:n {#2} }
+\cs_new:Npn \__str_change_case_loop:nw #1#2 \q__str_recursion_stop
+ {
+ \tl_if_head_is_space:nTF {#2}
+ { \__str_change_case_space:n }
+ { \__str_change_case_char:nN }
+ {#1} #2 \q__str_recursion_stop
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new:Npn \__str_change_case_space:n #1 \c_space_tl
+ {
+ \__str_change_case_output:nw { ~ }
+ \__str_change_case_loop:nw {#1}
+ }
+\cs_new:Npn \__str_change_case_char:nN #1#2
+ {
+ \__str_if_recursion_tail_stop_do:Nn #2
+ { \__str_change_case_end:wn }
+ \__str_change_case_codepoint:nN {#1} #2
+ }
+\if_int_compare:w 0
+ \cs_if_exist:NT \tex_XeTeXversion:D { 1 }
+ \cs_if_exist:NT \tex_luatexversion:D { 1 }
+ > 0 \exp_stop_f:
+ \cs_new:Npn \__str_change_case_codepoint:nN #1#2
+ { \__str_change_case_char:fnn { \int_eval:n {`#2} } {#1} {#2} }
+\else:
+ \cs_new:Npe \__str_change_case_codepoint:nN #1#2
+ {
+ \exp_not:N \int_compare:nNnTF {`#2} > { "80 }
+ {
+ \cs_if_exist:NTF \tex_pdftexversion:D
+ { \exp_not:N \__str_change_case_char_auxi:nN }
+ {
+ \exp_not:N \int_compare:nNnTF {`#2} > { "FF }
+ { \exp_not:N \__str_change_case_char_auxii:nN }
+ { \exp_not:N \__str_change_case_char_auxi:nN }
+ }
+ }
+ { \exp_not:N \__str_change_case_char_auxii:nN }
+ {#1} #2
+ }
+ \cs_new:Npn \__str_change_case_char_auxi:nN #1#2
+ {
+ \int_compare:nNnTF {`#2} < { "E0 }
+ { \__str_change_case_codepoint:nNN }
+ {
+ \int_compare:nNnTF {`#2} < { "F0 }
+ { \__str_change_case_codepoint:nNNN }
+ { \__str_change_case_codepoint:nNNNNN }
+ }
+ {#1} #2
+ }
+ \cs_new:Npn \__str_change_case_char_auxii:nN #1#2
+ { \__str_change_case_char:fnn { \int_eval:n {`#2} } {#1} {#2} }
+ \cs_new:Npn \__str_change_case_codepoint:nNN #1#2#3
+ {
+ \__str_change_case_char:fnn
+ { \int_eval:n { (`#2 - "C0) * "40 + `#3 - "80 } }
+ {#1} {#2#3}
+ }
+ \cs_new:Npn \__str_change_case_codepoint:nNNN #1#2#3#4
+ {
+ \__str_change_case_char:fnn
+ {
+ \int_eval:n
+ { (`#2 - "E0) * "1000 + (`#3 - "80) * "40 + `#4 - "80 }
+ }
+ {#1} {#2#3#4}
+ }
+ \cs_new:Npn \__str_change_case_codepoint:nNNNN #1#2#3#4#5
+ {
+ \__str_change_case_char:fnn
+ {
+ \int_eval:n
+ {
+ (`#2 - "F0) * "40000
+ + (`#3 - "80) * "1000
+ + (`#4 - "80) * "40
+ + `#5 - "80
+ }
+ }
+ {#1} {#2#3#4#5}
+ }
+\fi:
+\cs_new:Npn \__str_change_case_char:nnn #1#2#3
+ {
+ \__str_change_case_output:fw
+ {
+ \exp_args:Ne \__str_change_case_char_aux:nnn
+ { \__kernel_codepoint_case:nn {#2} {#1} } {#1} {#3}
+ }
+ \__str_change_case_loop:nw {#2}
+ }
+\cs_generate_variant:Nn \__str_change_case_char:nnn { f }
+\cs_new:Npn \__str_change_case_char_aux:nnn #1#2#3
+ {
+ \use:e { \__str_change_case_char:nnnnn #1 {#2} {#3} }
+ }
+\cs_new:Npn \__str_change_case_char:nnnnn #1#2#3#4#5
+ {
+ \int_compare:nNnTF {#1} = {#4}
+ { \tl_to_str:n {#5} }
+ {
+ \codepoint_str_generate:n {#1}
+ \tl_if_blank:nF {#2}
+ {
+ \codepoint_str_generate:n {#2}
+ \tl_if_blank:nF {#3}
+ { \codepoint_str_generate:n {#3} }
+ }
+ }
+ }
+\cs_new:Npn \str_mdfive_hash:n #1 { \tex_mdfivesum:D { \tl_to_str:n {#1} } }
+\cs_new:Npn \str_mdfive_hash:e #1 { \tex_mdfivesum:D {#1} }
+\str_const:Ne \c_ampersand_str { \cs_to_str:N \& }
+\str_const:Ne \c_atsign_str { \cs_to_str:N \@ }
+\str_const:Ne \c_backslash_str { \cs_to_str:N \\ }
+\str_const:Ne \c_left_brace_str { \cs_to_str:N \{ }
+\str_const:Ne \c_right_brace_str { \cs_to_str:N \} }
+\str_const:Ne \c_circumflex_str { \cs_to_str:N \^ }
+\str_const:Ne \c_colon_str { \cs_to_str:N \: }
+\str_const:Ne \c_dollar_str { \cs_to_str:N \$ }
+\str_const:Ne \c_hash_str { \cs_to_str:N \# }
+\str_const:Ne \c_percent_str { \cs_to_str:N \% }
+\str_const:Ne \c_tilde_str { \cs_to_str:N \~ }
+\str_const:Ne \c_underscore_str { \cs_to_str:N \_ }
+\str_const:Ne \c_zero_str { 0 }
+\cs_new_eq:NN \c_empty_str \c_empty_tl
+\str_new:N \l_tmpa_str
+\str_new:N \l_tmpb_str
+\str_new:N \g_tmpa_str
+\str_new:N \g_tmpb_str
+\cs_new_eq:NN \str_show:n \tl_show:n
+\cs_new_protected:Npn \str_show:N #1
+ {
+ \__kernel_chk_tl_type:NnnT #1 { str } { \tl_to_str:N #1 }
+ { \tl_show:N #1 }
+ }
+\cs_generate_variant:Nn \str_show:N { c }
+\cs_new_eq:NN \str_log:n \tl_log:n
+\cs_new_protected:Npn \str_log:N #1
+ {
+ \__kernel_chk_tl_type:NnnT #1 { str } { \tl_to_str:N #1 }
+ { \tl_log:N #1 }
+ }
+\cs_generate_variant:Nn \str_log:N { c }
+%% File: l3seq.dtx
+\scan_new:N \s__seq
+\scan_new:N \s__seq_mark
+\scan_new:N \s__seq_stop
+\cs_new:Npn \__seq_item:n
+ {
+ \msg_expandable_error:nn { seq } { misused }
+ \use_none:n
+ }
+\tl_new:N \l__seq_internal_a_tl
+\tl_new:N \l__seq_internal_b_tl
+\cs_new_eq:NN \__seq_tmp:w ?
+\tl_const:Nn \c_empty_seq { \s__seq }
+\cs_new_protected:Npn \seq_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_eq:NN #1 \c_empty_seq
+ }
+\cs_generate_variant:Nn \seq_new:N { c }
+\cs_new_protected:Npn \seq_clear:N #1
+ { \seq_set_eq:NN #1 \c_empty_seq }
+\cs_generate_variant:Nn \seq_clear:N { c }
+\cs_new_protected:Npn \seq_gclear:N #1
+ { \seq_gset_eq:NN #1 \c_empty_seq }
+\cs_generate_variant:Nn \seq_gclear:N { c }
+\cs_new_protected:Npn \seq_clear_new:N #1
+ { \seq_if_exist:NTF #1 { \seq_clear:N #1 } { \seq_new:N #1 } }
+\cs_generate_variant:Nn \seq_clear_new:N { c }
+\cs_new_protected:Npn \seq_gclear_new:N #1
+ { \seq_if_exist:NTF #1 { \seq_gclear:N #1 } { \seq_new:N #1 } }
+\cs_generate_variant:Nn \seq_gclear_new:N { c }
+\cs_new_eq:NN \seq_set_eq:NN \tl_set_eq:NN
+\cs_new_eq:NN \seq_set_eq:Nc \tl_set_eq:Nc
+\cs_new_eq:NN \seq_set_eq:cN \tl_set_eq:cN
+\cs_new_eq:NN \seq_set_eq:cc \tl_set_eq:cc
+\cs_new_eq:NN \seq_gset_eq:NN \tl_gset_eq:NN
+\cs_new_eq:NN \seq_gset_eq:Nc \tl_gset_eq:Nc
+\cs_new_eq:NN \seq_gset_eq:cN \tl_gset_eq:cN
+\cs_new_eq:NN \seq_gset_eq:cc \tl_gset_eq:cc
+\cs_new_protected:Npn \seq_set_from_clist:NN #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \s__seq \clist_map_function:NN #2 \__seq_wrap_item:n }
+ }
+\cs_new_protected:Npn \seq_set_from_clist:Nn #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ { \s__seq \clist_map_function:nN {#2} \__seq_wrap_item:n }
+ }
+\cs_new_protected:Npn \seq_gset_from_clist:NN #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \s__seq \clist_map_function:NN #2 \__seq_wrap_item:n }
+ }
+\cs_new_protected:Npn \seq_gset_from_clist:Nn #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ { \s__seq \clist_map_function:nN {#2} \__seq_wrap_item:n }
+ }
+\cs_generate_variant:Nn \seq_set_from_clist:NN { Nc }
+\cs_generate_variant:Nn \seq_set_from_clist:NN { c , cc }
+\cs_generate_variant:Nn \seq_set_from_clist:Nn { c }
+\cs_generate_variant:Nn \seq_gset_from_clist:NN { Nc }
+\cs_generate_variant:Nn \seq_gset_from_clist:NN { c , cc }
+\cs_generate_variant:Nn \seq_gset_from_clist:Nn { c }
+\cs_new_protected:Npn \seq_const_from_clist:Nn #1#2
+ {
+ \tl_const:Ne #1
+ { \s__seq \clist_map_function:nN {#2} \__seq_wrap_item:n }
+ }
+\cs_generate_variant:Nn \seq_const_from_clist:Nn { c }
+\cs_new_protected:Npn \seq_set_split:Nnn
+ { \__seq_set_split:NNNnn \__kernel_tl_set:Nx \tl_trim_spaces:n }
+\cs_new_protected:Npn \seq_gset_split:Nnn
+ { \__seq_set_split:NNNnn \__kernel_tl_gset:Nx \tl_trim_spaces:n }
+\cs_new_protected:Npn \seq_set_split_keep_spaces:Nnn
+ { \__seq_set_split:NNNnn \__kernel_tl_set:Nx \exp_not:n }
+\cs_new_protected:Npn \seq_gset_split_keep_spaces:Nnn
+ { \__seq_set_split:NNNnn \__kernel_tl_gset:Nx \exp_not:n }
+\cs_new_protected:Npn \__seq_set_split:NNNnn #1#2#3#4#5
+ {
+ \tl_if_empty:nTF {#4}
+ {
+ \tl_set:Nn \l__seq_internal_a_tl
+ { \tl_map_function:nN {#5} \__seq_wrap_item:n }
+ }
+ {
+ \tl_set:Nn \l__seq_internal_a_tl
+ {
+ \__seq_set_split:Nw #2 \prg_do_nothing:
+ #5
+ \__seq_set_split_end:
+ }
+ \tl_replace_all:Nnn \l__seq_internal_a_tl {#4}
+ {
+ \__seq_set_split_end:
+ \__seq_set_split:Nw #2 \prg_do_nothing:
+ }
+ \__kernel_tl_set:Nx \l__seq_internal_a_tl { \l__seq_internal_a_tl }
+ }
+ #1 #3 { \s__seq \l__seq_internal_a_tl }
+ }
+\cs_new:Npn \__seq_set_split:Nw #1#2 \__seq_set_split_end:
+ {
+ \exp_not:N \__seq_set_split:w
+ \exp_args:No #1 {#2}
+ \exp_not:N \__seq_set_split_end:
+ }
+\cs_new:Npn \__seq_set_split:w #1 \__seq_set_split_end:
+ { \__seq_wrap_item:n {#1} }
+\cs_generate_variant:Nn \seq_set_split:Nnn { NV , NnV , NVV , Nne , Nee }
+\cs_generate_variant:Nn \seq_set_split:Nnn { Nnx , Nxx }
+\cs_generate_variant:Nn \seq_gset_split:Nnn { NV , NnV , NVV , Nne , Nee }
+\cs_generate_variant:Nn \seq_gset_split:Nnn { Nnx , Nxx }
+\cs_generate_variant:Nn \seq_set_split_keep_spaces:Nnn { NnV }
+\cs_generate_variant:Nn \seq_gset_split_keep_spaces:Nnn { NnV }
+\cs_new_protected:Npn \seq_set_filter:NNn
+ { \__seq_set_filter:NNNn \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \seq_gset_filter:NNn
+ { \__seq_set_filter:NNNn \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \__seq_set_filter:NNNn #1#2#3#4
+ {
+ \__seq_push_item_def:n { \bool_if:nT {#4} { \__seq_wrap_item:n {##1} } }
+ #1 #2 { #3 }
+ \__seq_pop_item_def:
+ }
+\cs_new_protected:Npn \seq_concat:NNN #1#2#3
+ { \tl_set:Nf #1 { \exp_after:wN \use_i:nn \exp_after:wN #2 #3 } }
+\cs_new_protected:Npn \seq_gconcat:NNN #1#2#3
+ { \tl_gset:Nf #1 { \exp_after:wN \use_i:nn \exp_after:wN #2 #3 } }
+\cs_generate_variant:Nn \seq_concat:NNN { ccc }
+\cs_generate_variant:Nn \seq_gconcat:NNN { ccc }
+\prg_new_eq_conditional:NNn \seq_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \seq_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+\cs_new_protected:Npn \seq_put_left:Nn #1#2
+ {
+ \__kernel_tl_set:Nx #1
+ {
+ \exp_not:n { \s__seq \__seq_item:n {#2} }
+ \exp_not:f { \exp_after:wN \__seq_put_left_aux:w #1 }
+ }
+ }
+\cs_new_protected:Npn \seq_gput_left:Nn #1#2
+ {
+ \__kernel_tl_gset:Nx #1
+ {
+ \exp_not:n { \s__seq \__seq_item:n {#2} }
+ \exp_not:f { \exp_after:wN \__seq_put_left_aux:w #1 }
+ }
+ }
+\cs_new:Npn \__seq_put_left_aux:w \s__seq { \exp_stop_f: }
+\cs_generate_variant:Nn \seq_put_left:Nn { NV , Nv , Ne , No , Nx }
+\cs_generate_variant:Nn \seq_put_left:Nn { c , cV , cv , ce , co ,cx }
+\cs_generate_variant:Nn \seq_gput_left:Nn { NV , Nv , Ne , No , Nx }
+\cs_generate_variant:Nn \seq_gput_left:Nn { c , cV , cv , ce , co , cx }
+\cs_new_protected:Npn \seq_put_right:Nn #1#2
+ { \tl_put_right:Nn #1 { \__seq_item:n {#2} } }
+\cs_new_protected:Npn \seq_gput_right:Nn #1#2
+ { \tl_gput_right:Nn #1 { \__seq_item:n {#2} } }
+\cs_generate_variant:Nn \seq_put_right:Nn { NV , Nv , Ne , No , Nx }
+\cs_generate_variant:Nn \seq_put_right:Nn { c , cV , cv , ce , co , cx }
+\cs_generate_variant:Nn \seq_gput_right:Nn { NV , Nv , Ne , No , Nx }
+\cs_generate_variant:Nn \seq_gput_right:Nn { c , cV , cv , ce , co , cx }
+\cs_new:Npn \__seq_wrap_item:n #1 { \exp_not:n { \__seq_item:n {#1} } }
+\seq_new:N \l__seq_remove_seq
+\cs_new_protected:Npn \seq_remove_duplicates:N
+ { \__seq_remove_duplicates:NN \seq_set_eq:NN }
+\cs_new_protected:Npn \seq_gremove_duplicates:N
+ { \__seq_remove_duplicates:NN \seq_gset_eq:NN }
+\cs_new_protected:Npn \__seq_remove_duplicates:NN #1#2
+ {
+ \seq_clear:N \l__seq_remove_seq
+ \seq_map_inline:Nn #2
+ {
+ \seq_if_in:NnF \l__seq_remove_seq {##1}
+ { \seq_put_right:Nn \l__seq_remove_seq {##1} }
+ }
+ #1 #2 \l__seq_remove_seq
+ }
+\cs_generate_variant:Nn \seq_remove_duplicates:N { c }
+\cs_generate_variant:Nn \seq_gremove_duplicates:N { c }
+\cs_new_protected:Npn \seq_remove_all:Nn
+ { \__seq_remove_all_aux:NNn \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \seq_gremove_all:Nn
+ { \__seq_remove_all_aux:NNn \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \__seq_remove_all_aux:NNn #1#2#3
+ {
+ \__seq_push_item_def:n
+ {
+ \str_if_eq:nnT {##1} {#3}
+ {
+ \if_false: { \fi: }
+ \tl_set:Nn \l__seq_internal_b_tl {##1}
+ #1 #2
+ { \if_false: } \fi:
+ \exp_not:o {#2}
+ \tl_if_eq:NNT \l__seq_internal_a_tl \l__seq_internal_b_tl
+ { \use_none:nn }
+ }
+ \__seq_wrap_item:n {##1}
+ }
+ \tl_set:Nn \l__seq_internal_a_tl {#3}
+ #1 #2 {#2}
+ \__seq_pop_item_def:
+ }
+\cs_generate_variant:Nn \seq_remove_all:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \seq_remove_all:Nn { Nx , cx }
+\cs_generate_variant:Nn \seq_gremove_all:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \seq_gremove_all:Nn { Nx , cx }
+\cs_new_eq:NN \__seq_int_eval:w \tex_numexpr:D
+\cs_new_protected:Npn \seq_set_item:Nnn #1#2#3
+ { \__seq_set_item:NnnNN #1 {#2} {#3} \__kernel_tl_set:Nx \use_i:nn }
+\cs_new_protected:Npn \seq_gset_item:Nnn #1#2#3
+ { \__seq_set_item:NnnNN #1 {#2} {#3} \__kernel_tl_gset:Nx \use_i:nn }
+\cs_generate_variant:Nn \seq_set_item:Nnn { c }
+\cs_generate_variant:Nn \seq_gset_item:Nnn { c }
+\prg_new_protected_conditional:Npnn \seq_set_item:Nnn #1#2#3 { TF , T , F }
+ { \__seq_set_item:NnnNN #1 {#2} {#3} \__kernel_tl_set:Nx \use_ii:nn }
+\prg_new_protected_conditional:Npnn \seq_gset_item:Nnn #1#2#3 { TF , T , F }
+ { \__seq_set_item:NnnNN #1 {#2} {#3} \__kernel_tl_gset:Nx \use_ii:nn }
+\prg_generate_conditional_variant:Nnn \seq_set_item:Nnn { c } { TF , T , F }
+\prg_generate_conditional_variant:Nnn \seq_gset_item:Nnn { c } { TF , T , F }
+\cs_new_protected:Npn \__seq_set_item:NnnNN #1#2#3
+ {
+ \tl_set:Nn \l__seq_internal_a_tl { \__seq_item:n {#3} }
+ \exp_args:Nff \__seq_set_item:nnNNNN
+ { \int_eval:n {#2} } { \seq_count:N #1 } #1 \use_none:nn
+ }
+\cs_new_protected:Npn \__seq_set_item:nnNNNN #1#2
+ {
+ \int_compare:nNnTF {#1} > 0
+ { \int_compare:nNnF {#1} > {#2} { \__seq_set_item:nNnnNNNN { #1 - 1 } } }
+ {
+ \int_compare:nNnF {#1} < {-#2}
+ {
+ \int_compare:nNnF {#1} = 0
+ { \__seq_set_item:nNnnNNNN { #2 + #1 } }
+ }
+ }
+ \__seq_set_item_false:nnNNNN {#1} {#2}
+ }
+\cs_new_protected:Npn \__seq_set_item_false:nnNNNN #1#2#3#4#5#6
+ {
+ #6
+ {
+ \msg_error:nneee { seq } { item-too-large }
+ { \token_to_str:N #3 } {#2} {#1}
+ }
+ { \prg_return_false: }
+ }
+\cs_new_protected:Npn \__seq_set_item:nNnnNNNN #1#2#3#4#5#6#7#8
+ {
+ #7 #5
+ {
+ \s__seq
+ \exp_after:wN \__seq_set_item:wn
+ \int_value:w \__seq_int_eval:w #1
+ #5 \s__seq_stop #6
+ }
+ #8 { } { \prg_return_true: }
+ }
+\cs_new:Npn \__seq_set_item:wn #1 \__seq_item:n #2
+ {
+ \if_meaning:w 0 #1 \__seq_set_item_end:w \fi:
+ \exp_not:n { \__seq_item:n {#2} }
+ \exp_after:wN \__seq_set_item:wn
+ \int_value:w \__seq_int_eval:w #1 - 1 \s__seq
+ }
+\cs_new:Npn \__seq_set_item_end:w #1 \exp_not:n #2 #3 \s__seq #4 \s__seq_stop #5
+ {
+ #1
+ \exp_not:o \l__seq_internal_a_tl
+ \exp_not:n {#4}
+ #5 #2
+ }
+\cs_new_protected:Npn \seq_reverse:N
+ { \__seq_reverse:NN \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \seq_greverse:N
+ { \__seq_reverse:NN \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \__seq_reverse:NN #1 #2
+ {
+ \cs_set_eq:NN \__seq_tmp:w \__seq_item:n
+ \cs_set_eq:NN \__seq_item:n \__seq_reverse_item:nwn
+ #1 #2 { #2 \exp_not:n { } }
+ \cs_set_eq:NN \__seq_item:n \__seq_tmp:w
+ }
+\cs_new:Npn \__seq_reverse_item:nwn #1 #2 \exp_not:n #3
+ {
+ #2
+ \exp_not:n { \__seq_item:n {#1} #3 }
+ }
+\cs_generate_variant:Nn \seq_reverse:N { c }
+\cs_generate_variant:Nn \seq_greverse:N { c }
+\prg_new_conditional:Npnn \seq_if_empty:N #1 { p , T , F , TF }
+ {
+ \if_meaning:w #1 \c_empty_seq
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \seq_if_empty:N
+ { c } { p , T , F , TF }
+\seq_new:N \g__seq_internal_seq
+\cs_new_protected:Npn \seq_shuffle:N { \__seq_shuffle:NN \seq_set_eq:NN }
+\cs_new_protected:Npn \seq_gshuffle:N { \__seq_shuffle:NN \seq_gset_eq:NN }
+\cs_new_protected:Npn \__seq_shuffle:NN #1#2
+ {
+ \int_compare:nNnTF { \seq_count:N #2 } > \c_max_register_int
+ {
+ \msg_error:nne { seq } { shuffle-too-large }
+ { \token_to_str:N #2 }
+ }
+ {
+ \group_begin:
+ \int_zero:N \l__seq_internal_a_int
+ \__seq_push_item_def:
+ \cs_gset_eq:NN \__seq_item:n \__seq_shuffle_item:n
+ #2
+ \__seq_pop_item_def:
+ \seq_gclear:N \g__seq_internal_seq
+ \int_step_inline:nn \l__seq_internal_a_int
+ {
+ \seq_gput_right:Ne \g__seq_internal_seq
+ { \tex_the:D \tex_toks:D ##1 }
+ }
+ \group_end:
+ #1 #2 \g__seq_internal_seq
+ \seq_gclear:N \g__seq_internal_seq
+ }
+ }
+\cs_new_protected:Npn \__seq_shuffle_item:n
+ {
+ \int_incr:N \l__seq_internal_a_int
+ \int_set:Nn \l__seq_internal_b_int
+ { 1 + \tex_uniformdeviate:D \l__seq_internal_a_int }
+ \tex_toks:D \l__seq_internal_a_int
+ = \tex_toks:D \l__seq_internal_b_int
+ \tex_toks:D \l__seq_internal_b_int
+ }
+\cs_generate_variant:Nn \seq_shuffle:N { c }
+\cs_generate_variant:Nn \seq_gshuffle:N { c }
+\prg_new_protected_conditional:Npnn \seq_if_in:Nn #1#2
+ { T , F , TF }
+ {
+ \group_begin:
+ \tl_set:Nn \l__seq_internal_a_tl {#2}
+ \cs_set_protected:Npn \__seq_item:n ##1
+ {
+ \tl_set:Nn \l__seq_internal_b_tl {##1}
+ \if_meaning:w \l__seq_internal_a_tl \l__seq_internal_b_tl
+ \exp_after:wN \__seq_if_in:
+ \fi:
+ }
+ #1
+ \group_end:
+ \prg_return_false:
+ \prg_break_point:
+ }
+\cs_new:Npn \__seq_if_in:
+ { \prg_break:n { \group_end: \prg_return_true: } }
+\prg_generate_conditional_variant:Nnn \seq_if_in:Nn
+ { NV , Nv , Ne , No , Nx , c , cV , cv , ce , co , cx } { T , F , TF }
+\cs_new_protected:Npn \__seq_pop:NNNN #1#2#3#4
+ {
+ \if_meaning:w #3 \c_empty_seq
+ \tl_set:Nn #4 { \q_no_value }
+ \else:
+ #1#2#3#4
+ \fi:
+ }
+\cs_new_protected:Npn \__seq_pop_TF:NNNN #1#2#3#4
+ {
+ \if_meaning:w #3 \c_empty_seq
+ % \tl_set:Nn #4 { \q_no_value }
+ \prg_return_false:
+ \else:
+ #1#2#3#4
+ \prg_return_true:
+ \fi:
+ }
+\cs_new_protected:Npn \seq_get_left:NN #1#2
+ {
+ \__kernel_tl_set:Nx #2
+ {
+ \exp_after:wN \__seq_get_left:wnw
+ #1 \__seq_item:n { \q_no_value } \s__seq_stop
+ }
+ }
+\cs_new:Npn \__seq_get_left:wnw #1 \__seq_item:n #2#3 \s__seq_stop
+ { \exp_not:n {#2} }
+\cs_generate_variant:Nn \seq_get_left:NN { c }
+\cs_new_protected:Npn \seq_pop_left:NN
+ { \__seq_pop:NNNN \__seq_pop_left:NNN \tl_set:Nn }
+\cs_new_protected:Npn \seq_gpop_left:NN
+ { \__seq_pop:NNNN \__seq_pop_left:NNN \tl_gset:Nn }
+\cs_new_protected:Npn \__seq_pop_left:NNN #1#2#3
+ { \exp_after:wN \__seq_pop_left:wnwNNN #2 \s__seq_stop #1#2#3 }
+\cs_new_protected:Npn \__seq_pop_left:wnwNNN
+ #1 \__seq_item:n #2#3 \s__seq_stop #4#5#6
+ {
+ #4 #5 { #1 #3 }
+ \tl_set:Nn #6 {#2}
+ }
+\cs_generate_variant:Nn \seq_pop_left:NN { c }
+\cs_generate_variant:Nn \seq_gpop_left:NN { c }
+\cs_new_protected:Npn \seq_get_right:NN #1#2
+ {
+ \__kernel_tl_set:Nx #2
+ {
+ \exp_after:wN \use_i_ii:nnn
+ \exp_after:wN \__seq_get_right_loop:nw
+ \exp_after:wN \q_no_value
+ #1
+ \__seq_get_right_end:NnN \__seq_item:n
+ }
+ }
+\cs_new:Npn \__seq_get_right_loop:nw #1#2 \__seq_item:n
+ {
+ #2 \use_none:n {#1}
+ \__seq_get_right_loop:nw
+ }
+\cs_new:Npn \__seq_get_right_end:NnN #1#2#3 { \exp_not:n {#2} }
+\cs_generate_variant:Nn \seq_get_right:NN { c }
+\cs_new_protected:Npn \seq_pop_right:NN
+ { \__seq_pop:NNNN \__seq_pop_right:NNN \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \seq_gpop_right:NN
+ { \__seq_pop:NNNN \__seq_pop_right:NNN \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \__seq_pop_right:NNN #1#2#3
+ {
+ \cs_set_eq:NN \__seq_tmp:w \__seq_item:n
+ \cs_set_eq:NN \__seq_item:n \scan_stop:
+ #1 #2
+ { \if_false: } \fi: \s__seq
+ \exp_after:wN \use_i:nnn
+ \exp_after:wN \__seq_pop_right_loop:nn
+ #2
+ {
+ \if_false: { \fi: }
+ \__kernel_tl_set:Nx #3
+ }
+ { } \use_none:nn
+ \cs_set_eq:NN \__seq_item:n \__seq_tmp:w
+ }
+\cs_new:Npn \__seq_pop_right_loop:nn #1#2
+ {
+ #2 { \exp_not:n {#1} }
+ \__seq_pop_right_loop:nn
+ }
+\cs_generate_variant:Nn \seq_pop_right:NN { c }
+\cs_generate_variant:Nn \seq_gpop_right:NN { c }
+\prg_new_protected_conditional:Npnn \seq_get_left:NN #1#2 { T , F , TF }
+ { \__seq_pop_TF:NNNN \prg_do_nothing: \seq_get_left:NN #1#2 }
+\prg_new_protected_conditional:Npnn \seq_get_right:NN #1#2 { T , F , TF }
+ { \__seq_pop_TF:NNNN \prg_do_nothing: \seq_get_right:NN #1#2 }
+\prg_generate_conditional_variant:Nnn \seq_get_left:NN
+ { c } { T , F , TF }
+\prg_generate_conditional_variant:Nnn \seq_get_right:NN
+ { c } { T , F , TF }
+\prg_new_protected_conditional:Npnn \seq_pop_left:NN #1#2
+ { T , F , TF }
+ { \__seq_pop_TF:NNNN \__seq_pop_left:NNN \tl_set:Nn #1 #2 }
+\prg_new_protected_conditional:Npnn \seq_gpop_left:NN #1#2
+ { T , F , TF }
+ { \__seq_pop_TF:NNNN \__seq_pop_left:NNN \tl_gset:Nn #1 #2 }
+\prg_new_protected_conditional:Npnn \seq_pop_right:NN #1#2
+ { T , F , TF }
+ { \__seq_pop_TF:NNNN \__seq_pop_right:NNN \__kernel_tl_set:Nx #1 #2 }
+\prg_new_protected_conditional:Npnn \seq_gpop_right:NN #1#2
+ { T , F , TF }
+ { \__seq_pop_TF:NNNN \__seq_pop_right:NNN \__kernel_tl_gset:Nx #1 #2 }
+\prg_generate_conditional_variant:Nnn \seq_pop_left:NN { c }
+ { T , F , TF }
+\prg_generate_conditional_variant:Nnn \seq_gpop_left:NN { c }
+ { T , F , TF }
+\prg_generate_conditional_variant:Nnn \seq_pop_right:NN { c }
+ { T , F , TF }
+\prg_generate_conditional_variant:Nnn \seq_gpop_right:NN { c }
+ { T , F , TF }
+\cs_new:Npn \seq_item:Nn #1
+ { \exp_after:wN \__seq_item:wNn #1 \s__seq_stop #1 }
+\cs_new:Npn \__seq_item:wNn \s__seq #1 \s__seq_stop #2#3
+ {
+ \exp_args:Nf \__seq_item:nwn
+ { \exp_args:Nf \__seq_item:nN { \int_eval:n {#3} } #2 }
+ #1
+ \prg_break: \__seq_item:n { }
+ \prg_break_point:
+ }
+\cs_new:Npn \__seq_item:nN #1#2
+ {
+ \int_compare:nNnTF {#1} < 0
+ { \int_eval:n { \seq_count:N #2 + 1 + #1 } }
+ {#1}
+ }
+\cs_new:Npn \__seq_item:nwn #1#2 \__seq_item:n #3
+ {
+ #2
+ \int_compare:nNnTF {#1} = 1
+ { \prg_break:n { \exp_not:n {#3} } }
+ { \exp_args:Nf \__seq_item:nwn { \int_eval:n { #1 - 1 } } }
+ }
+\cs_generate_variant:Nn \seq_item:Nn { NV , Ne , c , cV , ce }
+\cs_new:Npn \seq_rand_item:N #1
+ {
+ \seq_if_empty:NF #1
+ { \seq_item:Nn #1 { \int_rand:nn { 1 } { \seq_count:N #1 } } }
+ }
+\cs_generate_variant:Nn \seq_rand_item:N { c }
+\cs_new:Npn \seq_map_break:
+ { \prg_map_break:Nn \seq_map_break: { } }
+\cs_new:Npn \seq_map_break:n
+ { \prg_map_break:Nn \seq_map_break: }
+\cs_new:Npn \seq_map_function:NN #1#2
+ {
+ \exp_after:wN \use_i_ii:nnn
+ \exp_after:wN \__seq_map_function:Nw
+ \exp_after:wN #2
+ #1
+ \prg_break:
+ \__seq_item:n { } \__seq_item:n { } \__seq_item:n { } \__seq_item:n { }
+ \prg_break_point:
+ \prg_break_point:Nn \seq_map_break: { }
+ }
+\cs_new:Npn \__seq_map_function:Nw #1
+ #2 \__seq_item:n #3
+ #4 \__seq_item:n #5
+ #6 \__seq_item:n #7
+ #8 \__seq_item:n #9
+ {
+ #2 #1 {#3}
+ #4 #1 {#5}
+ #6 #1 {#7}
+ #8 #1 {#9}
+ \__seq_map_function:Nw #1
+ }
+\cs_generate_variant:Nn \seq_map_function:NN { c }
+\cs_new_protected:Npn \__seq_push_item_def:n
+ {
+ \__seq_push_item_def:
+ \cs_gset:Npn \__seq_item:n ##1
+ }
+\cs_new_protected:Npn \__seq_push_item_def:e
+ {
+ \__seq_push_item_def:
+ \cs_gset:Npe \__seq_item:n ##1
+ }
+\cs_new_protected:Npn \__seq_push_item_def:
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \cs_gset_eq:cN { __seq_map_ \int_use:N \g__kernel_prg_map_int :w }
+ \__seq_item:n
+ }
+\cs_new_protected:Npn \__seq_pop_item_def:
+ {
+ \cs_gset_eq:Nc \__seq_item:n
+ { __seq_map_ \int_use:N \g__kernel_prg_map_int :w }
+ \int_gdecr:N \g__kernel_prg_map_int
+ }
+\cs_new_protected:Npn \seq_map_inline:Nn #1#2
+ {
+ \__seq_push_item_def:n {#2}
+ #1
+ \prg_break_point:Nn \seq_map_break: { \__seq_pop_item_def: }
+ }
+\cs_generate_variant:Nn \seq_map_inline:Nn { c }
+\cs_new:Npn \seq_map_tokens:Nn #1#2
+ {
+ \exp_last_unbraced:Nno
+ \use_i:nn { \__seq_map_tokens:nw {#2} } #1
+ \prg_break:
+ \__seq_item:n { } \__seq_item:n { } \__seq_item:n { } \__seq_item:n { }
+ \prg_break_point:
+ \prg_break_point:Nn \seq_map_break: { }
+ }
+\cs_generate_variant:Nn \seq_map_tokens:Nn { c }
+\cs_new:Npn \__seq_map_tokens:nw #1
+ #2 \__seq_item:n #3
+ #4 \__seq_item:n #5
+ #6 \__seq_item:n #7
+ #8 \__seq_item:n #9
+ {
+ #2 \use:n {#1} {#3}
+ #4 \use:n {#1} {#5}
+ #6 \use:n {#1} {#7}
+ #8 \use:n {#1} {#9}
+ \__seq_map_tokens:nw {#1}
+ }
+\cs_new_protected:Npn \seq_map_variable:NNn #1#2#3
+ {
+ \__seq_push_item_def:e
+ {
+ \tl_set:Nn \exp_not:N #2 {##1}
+ \exp_not:n {#3}
+ }
+ #1
+ \prg_break_point:Nn \seq_map_break: { \__seq_pop_item_def: }
+ }
+\cs_generate_variant:Nn \seq_map_variable:NNn { Nc }
+\cs_generate_variant:Nn \seq_map_variable:NNn { c , cc }
+\cs_new:Npn \seq_map_indexed_function:NN #1#2
+ {
+ \__seq_map_indexed:NN #1#2
+ \prg_break_point:Nn \seq_map_break: { }
+ }
+\cs_new_protected:Npn \seq_map_indexed_inline:Nn #1#2
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \cs_gset_protected:cpn
+ { __seq_map_ \int_use:N \g__kernel_prg_map_int :w } ##1##2 {#2}
+ \exp_args:NNc \__seq_map_indexed:NN #1
+ { __seq_map_ \int_use:N \g__kernel_prg_map_int :w }
+ \prg_break_point:Nn \seq_map_break:
+ { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+\cs_new:Npn \__seq_map_indexed:NN #1#2
+ {
+ \exp_after:wN \__seq_map_indexed:Nw
+ \exp_after:wN #2
+ \int_value:w 1
+ \exp_after:wN \use_i:nn
+ \exp_after:wN ;
+ #1
+ \prg_break: \__seq_item:n { } \prg_break_point:
+ }
+\cs_new:Npn \__seq_map_indexed:Nw #1#2 ; #3 \__seq_item:n #4
+ {
+ #3
+ #1 {#2} {#4}
+ \exp_after:wN \__seq_map_indexed:Nw
+ \exp_after:wN #1
+ \int_value:w \int_eval:w 1 + #2 ;
+ }
+\cs_new:Npn \seq_map_pairwise_function:NNN #1#2#3
+ { \exp_after:wN \__seq_map_pairwise_function:wNN #2 \s__seq_stop #1 #3 }
+\cs_new:Npn \__seq_map_pairwise_function:wNN \s__seq #1 \s__seq_stop #2#3
+ {
+ \exp_after:wN \__seq_map_pairwise_function:wNw #2 \s__seq_stop #3
+ #1 { ? \prg_break: } { }
+ \prg_break_point:
+ }
+\cs_new:Npn \__seq_map_pairwise_function:wNw \s__seq #1 \s__seq_stop #2
+ {
+ \__seq_map_pairwise_function:Nnnwnn #2
+ #1 { ? \prg_break: } { }
+ \s__seq_stop
+ }
+\cs_new:Npn \__seq_map_pairwise_function:Nnnwnn #1#2#3#4 \s__seq_stop #5#6
+ {
+ \use_none:n #2
+ \use_none:n #5
+ #1 {#3} {#6}
+ \__seq_map_pairwise_function:Nnnwnn #1 #4 \s__seq_stop
+ }
+\cs_generate_variant:Nn \seq_map_pairwise_function:NNN { Nc , c , cc }
+\cs_new_protected:Npn \seq_set_map_e:NNn
+ { \__seq_set_map_e:NNNn \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \seq_gset_map_e:NNn
+ { \__seq_set_map_e:NNNn \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \__seq_set_map_e:NNNn #1#2#3#4
+ {
+ \__seq_push_item_def:n { \exp_not:N \__seq_item:n {#4} }
+ #1 #2 { #3 }
+ \__seq_pop_item_def:
+ }
+\cs_new_protected:Npn \seq_set_map:NNn
+ { \__seq_set_map:NNNn \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \seq_gset_map:NNn
+ { \__seq_set_map:NNNn \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \__seq_set_map:NNNn #1#2#3#4
+ {
+ \__seq_push_item_def:n { \exp_not:n { \__seq_item:n {#4} } }
+ #1 #2 { #3 }
+ \__seq_pop_item_def:
+ }
+\cs_new:Npn \seq_count:N #1
+ {
+ \int_eval:n
+ {
+ \exp_after:wN \use_i:nn
+ \exp_after:wN \__seq_count:w
+ #1
+ \__seq_count_end:w \__seq_item:n 7
+ \__seq_count_end:w \__seq_item:n 6
+ \__seq_count_end:w \__seq_item:n 5
+ \__seq_count_end:w \__seq_item:n 4
+ \__seq_count_end:w \__seq_item:n 3
+ \__seq_count_end:w \__seq_item:n 2
+ \__seq_count_end:w \__seq_item:n 1
+ \__seq_count_end:w \__seq_item:n 0
+ \prg_break_point:
+ }
+ }
+\cs_new:Npn \__seq_count:w
+ #1 \__seq_item:n #2 \__seq_item:n #3 \__seq_item:n #4 \__seq_item:n
+ #5 \__seq_item:n #6 \__seq_item:n #7 \__seq_item:n #8 #9 \__seq_item:n
+ { #9 8 + \__seq_count:w }
+\cs_new:Npn \__seq_count_end:w 8 + \__seq_count:w #1#2 \prg_break_point: {#1}
+\cs_generate_variant:Nn \seq_count:N { c }
+\cs_new:Npn \seq_use:Nnnn #1#2#3#4
+ {
+ \seq_if_exist:NTF #1
+ {
+ \int_case:nnF { \seq_count:N #1 }
+ {
+ { 0 } { }
+ { 1 } { \exp_after:wN \__seq_use:NNnNnn #1 ? { } { } }
+ { 2 } { \exp_after:wN \__seq_use:NNnNnn #1 {#2} }
+ }
+ {
+ \exp_after:wN \__seq_use_setup:w #1 \__seq_item:n
+ \s__seq_mark { \__seq_use:nwwwwnwn {#3} }
+ \s__seq_mark { \__seq_use:nwwn {#4} }
+ \s__seq_stop { }
+ }
+ }
+ {
+ \msg_expandable_error:nnn
+ { kernel } { bad-variable } {#1}
+ }
+ }
+\cs_generate_variant:Nn \seq_use:Nnnn { c }
+\cs_new:Npn \__seq_use:NNnNnn #1#2#3#4#5#6 { \exp_not:n { #3 #6 #5 } }
+\cs_new:Npn \__seq_use_setup:w \s__seq { \__seq_use:nwwwwnwn { } }
+\cs_new:Npn \__seq_use:nwwwwnwn
+ #1 \__seq_item:n #2 \__seq_item:n #3 \__seq_item:n #4#5
+ \s__seq_mark #6#7 \s__seq_stop #8
+ {
+ #6 \__seq_item:n {#3} \__seq_item:n {#4} #5
+ \s__seq_mark {#6} #7 \s__seq_stop { #8 #1 #2 }
+ }
+\cs_new:Npn \__seq_use:nwwn #1 \__seq_item:n #2 #3 \s__seq_stop #4
+ { \exp_not:n { #4 #1 #2 } }
+\cs_new:Npn \seq_use:Nn #1#2
+ { \seq_use:Nnnn #1 {#2} {#2} {#2} }
+\cs_generate_variant:Nn \seq_use:Nn { c }
+\cs_new_eq:NN \seq_push:Nn \seq_put_left:Nn
+\cs_generate_variant:Nn \seq_push:Nn { NV , Nv , Ne , c , cV , cv , ce }
+\cs_generate_variant:Nn \seq_push:Nn { No , Nx , co , cx }
+\cs_new_eq:NN \seq_gpush:Nn \seq_gput_left:Nn
+\cs_generate_variant:Nn \seq_gpush:Nn { NV , Nv , Ne , c , cV , cv , ce }
+\cs_generate_variant:Nn \seq_gpush:Nn { No , Nx , co , cx }
+\cs_new_eq:NN \seq_get:NN \seq_get_left:NN
+\cs_new_eq:NN \seq_get:cN \seq_get_left:cN
+\cs_new_eq:NN \seq_pop:NN \seq_pop_left:NN
+\cs_new_eq:NN \seq_pop:cN \seq_pop_left:cN
+\cs_new_eq:NN \seq_gpop:NN \seq_gpop_left:NN
+\cs_new_eq:NN \seq_gpop:cN \seq_gpop_left:cN
+\prg_new_eq_conditional:NNn \seq_get:NN \seq_get_left:NN { T , F , TF }
+\prg_new_eq_conditional:NNn \seq_get:cN \seq_get_left:cN { T , F , TF }
+\prg_new_eq_conditional:NNn \seq_pop:NN \seq_pop_left:NN { T , F , TF }
+\prg_new_eq_conditional:NNn \seq_pop:cN \seq_pop_left:cN { T , F , TF }
+\prg_new_eq_conditional:NNn \seq_gpop:NN \seq_gpop_left:NN { T , F , TF }
+\prg_new_eq_conditional:NNn \seq_gpop:cN \seq_gpop_left:cN { T , F , TF }
+\cs_new_protected:Npn \seq_show:N { \__seq_show:NN \msg_show:nneeee }
+\cs_generate_variant:Nn \seq_show:N { c }
+\cs_new_protected:Npn \seq_log:N { \__seq_show:NN \msg_log:nneeee }
+\cs_generate_variant:Nn \seq_log:N { c }
+\cs_new_protected:Npn \__seq_show:NN #1#2
+ {
+ \__kernel_chk_tl_type:NnnT #2 { seq }
+ {
+ \s__seq
+ \exp_after:wN \use_i:nn \exp_after:wN \__seq_show_validate:nn #2
+ \q_recursion_tail \q_recursion_tail \q_recursion_stop
+ }
+ {
+ #1 { seq } { show }
+ { \token_to_str:N #2 }
+ { \seq_map_function:NN #2 \msg_show_item:n }
+ { } { }
+ }
+ }
+\cs_new:Npn \__seq_show_validate:nn #1#2
+ {
+ \quark_if_recursion_tail_stop:n {#2}
+ \__seq_wrap_item:n {#2}
+ \__seq_show_validate:nn
+ }
+\seq_new:N \l_tmpa_seq
+\seq_new:N \l_tmpb_seq
+\seq_new:N \g_tmpa_seq
+\seq_new:N \g_tmpb_seq
+%% File: l3int.dtx
+\cs_new_eq:NN \int_value:w \tex_number:D
+\cs_new_eq:NN \__int_eval:w \tex_numexpr:D
+\cs_new_eq:NN \__int_eval_end: \tex_relax:D
+\cs_new_eq:NN \if_int_odd:w \tex_ifodd:D
+\cs_new_eq:NN \if_case:w \tex_ifcase:D
+\scan_new:N \s__int_mark
+\scan_new:N \s__int_stop
+\cs_new:Npn \__int_use_none_delimit_by_s_stop:w #1 \s__int_stop { }
+\quark_new:N \q__int_recursion_tail
+\quark_new:N \q__int_recursion_stop
+\__kernel_quark_new_test:N \__int_if_recursion_tail_stop_do:Nn
+\__kernel_quark_new_test:N \__int_if_recursion_tail_stop:N
+\cs_new:Npn \int_eval:n #1
+ { \tex_the:D \__int_eval:w #1 \__int_eval_end: }
+\cs_new:Npn \int_eval:w { \tex_the:D \__int_eval:w }
+\cs_new:Npn \int_sign:n #1
+ {
+ \int_value:w \exp_after:wN \__int_sign:Nw
+ \int_value:w \__int_eval:w #1 \__int_eval_end: ;
+ \exp_stop_f:
+ }
+\cs_new:Npn \__int_sign:Nw #1#2 ;
+ {
+ \if_meaning:w 0 #1
+ 0
+ \else:
+ \if_meaning:w - #1 - \fi: 1
+ \fi:
+ }
+\cs_new:Npn \int_abs:n #1
+ {
+ \int_value:w \exp_after:wN \__int_abs:N
+ \int_value:w \__int_eval:w #1 \__int_eval_end:
+ \exp_stop_f:
+ }
+\cs_new:Npn \__int_abs:N #1
+ { \if_meaning:w - #1 \else: \exp_after:wN #1 \fi: }
+\cs_new:Npn \int_max:nn #1#2
+ {
+ \int_value:w \exp_after:wN \__int_maxmin:wwN
+ \int_value:w \__int_eval:w #1 \exp_after:wN ;
+ \int_value:w \__int_eval:w #2 ;
+ >
+ \exp_stop_f:
+ }
+\cs_new:Npn \int_min:nn #1#2
+ {
+ \int_value:w \exp_after:wN \__int_maxmin:wwN
+ \int_value:w \__int_eval:w #1 \exp_after:wN ;
+ \int_value:w \__int_eval:w #2 ;
+ <
+ \exp_stop_f:
+ }
+\cs_new:Npn \__int_maxmin:wwN #1 ; #2 ; #3
+ {
+ \if_int_compare:w #1 #3 #2 ~
+ #1
+ \else:
+ #2
+ \fi:
+ }
+\cs_new:Npn \int_div_truncate:nn #1#2
+ {
+ \int_value:w \__int_eval:w
+ \exp_after:wN \__int_div_truncate:NwNw
+ \int_value:w \__int_eval:w #1 \exp_after:wN ;
+ \int_value:w \__int_eval:w #2 ;
+ \__int_eval_end:
+ }
+\cs_new:Npn \__int_div_truncate:NwNw #1#2; #3#4;
+ {
+ \if_meaning:w 0 #1
+ 0
+ \else:
+ (
+ #1#2
+ \if_meaning:w - #1 + \else: - \fi:
+ ( \if_meaning:w - #3 - \fi: #3#4 - 1 ) / 2
+ )
+ \fi:
+ / #3#4
+ }
+\cs_new:Npn \int_div_round:nn #1#2
+ { \int_value:w \__int_eval:w ( #1 ) / ( #2 ) \__int_eval_end: }
+\cs_new:Npn \int_mod:nn #1#2
+ {
+ \int_value:w \__int_eval:w \exp_after:wN \__int_mod:ww
+ \int_value:w \__int_eval:w #1 \exp_after:wN ;
+ \int_value:w \__int_eval:w #2 ;
+ \__int_eval_end:
+ }
+\cs_new:Npn \__int_mod:ww #1; #2;
+ { #1 - ( \__int_div_truncate:NwNw #1 ; #2 ; ) * #2 }
+\cs_new:Npn \__kernel_int_add:nnn #1#2#3
+ {
+ \int_value:w \__int_eval:w #1
+ \if_int_compare:w #2 < \c_zero_int \exp_after:wN \reverse_if:N \fi:
+ \if_int_compare:w #1 < \c_zero_int + #2 + #3 \else: + #3 + #2 \fi:
+ \__int_eval_end:
+ }
+\cs_new_protected:Npn \int_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs:w newcount \cs_end: #1
+ }
+\cs_generate_variant:Nn \int_new:N { c }
+\cs_new_protected:Npn \int_const:Nn #1#2
+ { \__int_const:eN { \int_eval:n {#2} } #1 }
+\cs_generate_variant:Nn \int_const:Nn { c }
+\cs_new_protected:Npn \__int_const:nN #1#2
+ {
+ \int_compare:nNnTF {#1} < \c_zero_int
+ {
+ \int_new:N #2
+ \tex_global:D
+ }
+ {
+ \int_compare:nNnTF {#1} > \c__int_max_constdef_int
+ {
+ \int_new:N #2
+ \tex_global:D
+ }
+ {
+ \__kernel_chk_if_free_cs:N #2
+ \tex_global:D \__int_constdef:Nw
+ }
+ }
+ #2 = \__int_eval:w #1 \__int_eval_end:
+ }
+\cs_generate_variant:Nn \__int_const:nN { e }
+\if_int_odd:w 0
+ \cs_if_exist:NT \tex_luatexversion:D { 1 }
+ \cs_if_exist:NT \tex_omathchardef:D { 1 }
+ \cs_if_exist:NT \tex_XeTeXversion:D { 1 } ~
+ \cs_if_exist:NTF \tex_omathchardef:D
+ { \cs_new_eq:NN \__int_constdef:Nw \tex_omathchardef:D }
+ { \cs_new_eq:NN \__int_constdef:Nw \tex_chardef:D }
+ \tex_global:D \__int_constdef:Nw \c__int_max_constdef_int 1114111 ~
+\else:
+ \cs_new_eq:NN \__int_constdef:Nw \tex_mathchardef:D
+ \tex_global:D \__int_constdef:Nw \c__int_max_constdef_int 32767 ~
+\fi:
+\cs_new_protected:Npn \int_zero:N #1 { #1 = \c_zero_int }
+\cs_new_protected:Npn \int_gzero:N #1 { \tex_global:D #1 = \c_zero_int }
+\cs_generate_variant:Nn \int_zero:N { c }
+\cs_generate_variant:Nn \int_gzero:N { c }
+\cs_new_protected:Npn \int_zero_new:N #1
+ { \int_if_exist:NTF #1 { \int_zero:N #1 } { \int_new:N #1 } }
+\cs_new_protected:Npn \int_gzero_new:N #1
+ { \int_if_exist:NTF #1 { \int_gzero:N #1 } { \int_new:N #1 } }
+\cs_generate_variant:Nn \int_zero_new:N { c }
+\cs_generate_variant:Nn \int_gzero_new:N { c }
+\cs_new_protected:Npn \int_set_eq:NN #1#2 { #1 = #2 }
+\cs_generate_variant:Nn \int_set_eq:NN { c , Nc , cc }
+\cs_new_protected:Npn \int_gset_eq:NN #1#2 { \tex_global:D #1 = #2 }
+\cs_generate_variant:Nn \int_gset_eq:NN { c , Nc , cc }
+\prg_new_eq_conditional:NNn \int_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \int_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+\cs_new_protected:Npn \int_add:Nn #1#2
+ { \tex_advance:D #1 \__int_eval:w #2 \__int_eval_end: }
+\cs_new_protected:Npn \int_sub:Nn #1#2
+ { \tex_advance:D #1 - \__int_eval:w #2 \__int_eval_end: }
+\cs_new_protected:Npn \int_gadd:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 \__int_eval:w #2 \__int_eval_end: }
+\cs_new_protected:Npn \int_gsub:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 - \__int_eval:w #2 \__int_eval_end: }
+\cs_generate_variant:Nn \int_add:Nn { c }
+\cs_generate_variant:Nn \int_gadd:Nn { c }
+\cs_generate_variant:Nn \int_sub:Nn { c }
+\cs_generate_variant:Nn \int_gsub:Nn { c }
+\cs_new_protected:Npn \int_incr:N #1
+ { \tex_advance:D #1 \c_one_int }
+\cs_new_protected:Npn \int_decr:N #1
+ { \tex_advance:D #1 - \c_one_int }
+\cs_new_protected:Npn \int_gincr:N #1
+ { \tex_global:D \tex_advance:D #1 \c_one_int }
+\cs_new_protected:Npn \int_gdecr:N #1
+ { \tex_global:D \tex_advance:D #1 - \c_one_int }
+\cs_generate_variant:Nn \int_incr:N { c }
+\cs_generate_variant:Nn \int_decr:N { c }
+\cs_generate_variant:Nn \int_gincr:N { c }
+\cs_generate_variant:Nn \int_gdecr:N { c }
+\cs_new_protected:Npn \int_set:Nn #1#2
+ { #1 = \__int_eval:w #2 \__int_eval_end: }
+\cs_new_protected:Npn \int_gset:Nn #1#2
+ { \tex_global:D #1 = \__int_eval:w #2 \__int_eval_end: }
+\cs_generate_variant:Nn \int_set:Nn { c }
+\cs_generate_variant:Nn \int_gset:Nn { c }
+\cs_new_eq:NN \int_use:N \tex_the:D
+\cs_new:Npn \int_use:c #1 { \tex_the:D \cs:w #1 \cs_end: }
+\cs_new_protected:Npn \__int_compare_error:
+ {
+ \if_int_compare:w \c_zero_int \c_zero_int \fi:
+ =
+ \__int_compare_error:
+ }
+\cs_new:Npn \__int_compare_error:Nw
+ #1#2 \s__int_stop
+ {
+ { }
+ \c_zero_int \fi:
+ \msg_expandable_error:nnn
+ { kernel } { unknown-comparison } {#1}
+ \prg_return_false:
+ }
+\prg_new_conditional:Npnn \int_compare:n #1 { p , T , F , TF }
+ {
+ \exp_after:wN \__int_compare:w
+ \int_value:w \__int_eval:w #1 \__int_compare_error:
+ }
+\cs_new:Npn \__int_compare:w #1 \__int_compare_error:
+ {
+ \exp_after:wN \if_false: \int_value:w
+ \__int_compare:Nw #1 e { = nd_ } \s__int_stop
+ }
+\cs_new:Npn \__int_compare:Nw #1#2 \s__int_stop
+ {
+ \exp_after:wN \__int_compare:NNw
+ \__int_to_roman:w - 0 #2 \s__int_mark
+ #1#2 \s__int_stop
+ }
+\cs_new:Npn \__int_compare:NNw #1#2#3 \s__int_mark
+ {
+ \__kernel_exp_not:w
+ \use:c
+ {
+ __int_compare_ \token_to_str:N #1
+ \if_meaning:w = #2 = \fi:
+ :NNw
+ }
+ \__int_compare_error:Nw #1
+ }
+\cs_new:cpn { __int_compare_end_=:NNw } #1#2#3 e #4 \s__int_stop
+ {
+ {#3} \exp_stop_f:
+ \prg_return_false: \else: \prg_return_true: \fi:
+ }
+\cs_new:Npn \__int_compare:nnN #1#2#3
+ {
+ {#2} \exp_stop_f:
+ \prg_return_false: \exp_after:wN \__int_use_none_delimit_by_s_stop:w
+ \fi:
+ #1 #2 #3 \exp_after:wN \__int_compare:Nw \int_value:w \__int_eval:w
+ }
+\cs_new:cpn { __int_compare_=:NNw } #1#2#3 =
+ { \__int_compare:nnN { \reverse_if:N \if_int_compare:w } {#3} = }
+\cs_new:cpn { __int_compare_<:NNw } #1#2#3 <
+ { \__int_compare:nnN { \reverse_if:N \if_int_compare:w } {#3} < }
+\cs_new:cpn { __int_compare_>:NNw } #1#2#3 >
+ { \__int_compare:nnN { \reverse_if:N \if_int_compare:w } {#3} > }
+\cs_new:cpn { __int_compare_==:NNw } #1#2#3 ==
+ { \__int_compare:nnN { \reverse_if:N \if_int_compare:w } {#3} = }
+\cs_new:cpn { __int_compare_!=:NNw } #1#2#3 !=
+ { \__int_compare:nnN { \if_int_compare:w } {#3} = }
+\cs_new:cpn { __int_compare_<=:NNw } #1#2#3 <=
+ { \__int_compare:nnN { \if_int_compare:w } {#3} > }
+\cs_new:cpn { __int_compare_>=:NNw } #1#2#3 >=
+ { \__int_compare:nnN { \if_int_compare:w } {#3} < }
+\prg_new_conditional:Npnn \int_compare:nNn #1#2#3 { p , T , F , TF }
+ {
+ \if_int_compare:w \__int_eval:w #1 #2 \__int_eval:w #3 \__int_eval_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_new_conditional:Npnn \int_if_zero:n #1 { p , T , F , TF }
+ {
+ \if_int_compare:w \__int_eval:w #1 = \c_zero_int
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \int_case:nnTF #1
+ {
+ \exp:w
+ \exp_args:Nf \__int_case:nnTF { \int_eval:n {#1} }
+ }
+\cs_new:Npn \int_case:nnT #1#2#3
+ {
+ \exp:w
+ \exp_args:Nf \__int_case:nnTF { \int_eval:n {#1} } {#2} {#3} { }
+ }
+\cs_new:Npn \int_case:nnF #1#2
+ {
+ \exp:w
+ \exp_args:Nf \__int_case:nnTF { \int_eval:n {#1} } {#2} { }
+ }
+\cs_new:Npn \int_case:nn #1#2
+ {
+ \exp:w
+ \exp_args:Nf \__int_case:nnTF { \int_eval:n {#1} } {#2} { } { }
+ }
+\cs_new:Npn \__int_case:nnTF #1#2#3#4
+ { \__int_case:nw {#1} #2 {#1} { } \s__int_mark {#3} \s__int_mark {#4} \s__int_stop }
+\cs_new:Npn \__int_case:nw #1#2#3
+ {
+ \int_compare:nNnTF {#1} = {#2}
+ { \__int_case_end:nw {#3} }
+ { \__int_case:nw {#1} }
+ }
+\cs_new:Npn \__int_case_end:nw #1#2#3 \s__int_mark #4#5 \s__int_stop
+ { \exp_end: #1 #4 }
+\prg_new_conditional:Npnn \int_if_odd:n #1 { p , T , F , TF}
+ {
+ \if_int_odd:w \__int_eval:w #1 \__int_eval_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_new_conditional:Npnn \int_if_even:n #1 { p , T , F , TF}
+ {
+ \reverse_if:N \if_int_odd:w \__int_eval:w #1 \__int_eval_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \int_while_do:nn #1#2
+ {
+ \int_compare:nT {#1}
+ {
+ #2
+ \int_while_do:nn {#1} {#2}
+ }
+ }
+\cs_new:Npn \int_until_do:nn #1#2
+ {
+ \int_compare:nF {#1}
+ {
+ #2
+ \int_until_do:nn {#1} {#2}
+ }
+ }
+\cs_new:Npn \int_do_while:nn #1#2
+ {
+ #2
+ \int_compare:nT {#1}
+ { \int_do_while:nn {#1} {#2} }
+ }
+\cs_new:Npn \int_do_until:nn #1#2
+ {
+ #2
+ \int_compare:nF {#1}
+ { \int_do_until:nn {#1} {#2} }
+ }
+\cs_new:Npn \int_while_do:nNnn #1#2#3#4
+ {
+ \int_compare:nNnT {#1} #2 {#3}
+ {
+ #4
+ \int_while_do:nNnn {#1} #2 {#3} {#4}
+ }
+ }
+\cs_new:Npn \int_until_do:nNnn #1#2#3#4
+ {
+ \int_compare:nNnF {#1} #2 {#3}
+ {
+ #4
+ \int_until_do:nNnn {#1} #2 {#3} {#4}
+ }
+ }
+\cs_new:Npn \int_do_while:nNnn #1#2#3#4
+ {
+ #4
+ \int_compare:nNnT {#1} #2 {#3}
+ { \int_do_while:nNnn {#1} #2 {#3} {#4} }
+ }
+\cs_new:Npn \int_do_until:nNnn #1#2#3#4
+ {
+ #4
+ \int_compare:nNnF {#1} #2 {#3}
+ { \int_do_until:nNnn {#1} #2 {#3} {#4} }
+ }
+\cs_new:Npn \int_step_function:nnnN #1#2#3
+ {
+ \exp_after:wN \__int_step:wwwN
+ \int_value:w \__int_eval:w #1 \exp_after:wN ;
+ \int_value:w \__int_eval:w #2 \exp_after:wN ;
+ \int_value:w \__int_eval:w #3 ;
+ }
+\cs_new:Npn \__int_step:wwwN #1; #2; #3; #4
+ {
+ \int_compare:nNnTF {#2} > \c_zero_int
+ { \__int_step:NwnnN > }
+ {
+ \int_compare:nNnTF {#2} = \c_zero_int
+ {
+ \msg_expandable_error:nnn
+ { kernel } { zero-step } {#4}
+ \prg_break:
+ }
+ { \__int_step:NwnnN < }
+ }
+ #1 ; {#2} {#3} #4
+ \prg_break_point:
+ }
+\cs_new:Npn \__int_step:NwnnN #1#2 ; #3#4#5
+ {
+ \if_int_compare:w #2 #1 #4 \exp_stop_f:
+ \prg_break:n
+ \fi:
+ #5 {#2}
+ \exp_after:wN \__int_step:NwnnN
+ \exp_after:wN #1
+ \int_value:w \__int_eval:w #2 + #3 ; {#3} {#4} #5
+ }
+\cs_new:Npn \int_step_function:nN
+ { \int_step_function:nnnN { 1 } { 1 } }
+\cs_new:Npn \int_step_function:nnN #1
+ { \int_step_function:nnnN {#1} { 1 } }
+\cs_new_protected:Npn \int_step_inline:nn
+ { \int_step_inline:nnnn { 1 } { 1 } }
+\cs_new_protected:Npn \int_step_inline:nnn #1
+ { \int_step_inline:nnnn {#1} { 1 } }
+\cs_new_protected:Npn \int_step_inline:nnnn
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:NNc \__int_step:NNnnnn
+ \cs_gset_protected:Npn
+ { __int_map_ \int_use:N \g__kernel_prg_map_int :w }
+ }
+\cs_new_protected:Npn \int_step_variable:nNn
+ { \int_step_variable:nnnNn { 1 } { 1 } }
+\cs_new_protected:Npn \int_step_variable:nnNn #1
+ { \int_step_variable:nnnNn {#1} { 1 } }
+\cs_new_protected:Npn \int_step_variable:nnnNn #1#2#3#4#5
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:NNc \__int_step:NNnnnn
+ \cs_gset_protected:Npe
+ { __int_map_ \int_use:N \g__kernel_prg_map_int :w }
+ {#1}{#2}{#3}
+ {
+ \tl_set:Nn \exp_not:N #4 {##1}
+ \exp_not:n {#5}
+ }
+ }
+\cs_new_protected:Npn \__int_step:NNnnnn #1#2#3#4#5#6
+ {
+ #1 #2 ##1 {#6}
+ \int_step_function:nnnN {#3} {#4} {#5} #2
+ \prg_break_point:Nn \scan_stop: { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+\cs_new_eq:NN \int_to_arabic:n \int_eval:n
+\cs_generate_variant:Nn \int_to_arabic:n { v }
+\cs_new:Npn \int_to_symbols:nnn #1#2#3
+ {
+ \int_compare:nNnTF {#1} > {#2}
+ {
+ \__int_to_symbols:ennn
+ {
+ \int_case:nn
+ { 1 + \int_mod:nn { #1 - 1 } {#2} }
+ {#3}
+ }
+ {#1} {#2} {#3}
+ }
+ { \int_case:nn {#1} {#3} }
+ }
+\cs_new:Npn \__int_to_symbols:nnnn #1#2#3#4
+ {
+ \exp_args:Nf \int_to_symbols:nnn
+ { \int_div_truncate:nn { #2 - 1 } {#3} } {#3} {#4}
+ #1
+ }
+\cs_generate_variant:Nn \__int_to_symbols:nnnn { e }
+\cs_new:Npn \int_to_alph:n #1
+ {
+ \int_to_symbols:nnn {#1} { 26 }
+ {
+ { 1 } { a }
+ { 2 } { b }
+ { 3 } { c }
+ { 4 } { d }
+ { 5 } { e }
+ { 6 } { f }
+ { 7 } { g }
+ { 8 } { h }
+ { 9 } { i }
+ { 10 } { j }
+ { 11 } { k }
+ { 12 } { l }
+ { 13 } { m }
+ { 14 } { n }
+ { 15 } { o }
+ { 16 } { p }
+ { 17 } { q }
+ { 18 } { r }
+ { 19 } { s }
+ { 20 } { t }
+ { 21 } { u }
+ { 22 } { v }
+ { 23 } { w }
+ { 24 } { x }
+ { 25 } { y }
+ { 26 } { z }
+ }
+ }
+\cs_new:Npn \int_to_Alph:n #1
+ {
+ \int_to_symbols:nnn {#1} { 26 }
+ {
+ { 1 } { A }
+ { 2 } { B }
+ { 3 } { C }
+ { 4 } { D }
+ { 5 } { E }
+ { 6 } { F }
+ { 7 } { G }
+ { 8 } { H }
+ { 9 } { I }
+ { 10 } { J }
+ { 11 } { K }
+ { 12 } { L }
+ { 13 } { M }
+ { 14 } { N }
+ { 15 } { O }
+ { 16 } { P }
+ { 17 } { Q }
+ { 18 } { R }
+ { 19 } { S }
+ { 20 } { T }
+ { 21 } { U }
+ { 22 } { V }
+ { 23 } { W }
+ { 24 } { X }
+ { 25 } { Y }
+ { 26 } { Z }
+ }
+ }
+\cs_new:Npn \int_to_base:nn #1
+ { \exp_args:Nf \__int_to_base:nn { \int_eval:n {#1} } }
+\cs_new:Npn \int_to_Base:nn #1
+ { \exp_args:Nf \__int_to_Base:nn { \int_eval:n {#1} } }
+\cs_new:Npn \__int_to_base:nn #1#2
+ {
+ \int_compare:nNnTF {#1} < 0
+ { \exp_args:No \__int_to_base:nnN { \use_none:n #1 } {#2} - }
+ { \__int_to_base:nnN {#1} {#2} \c_empty_tl }
+ }
+\cs_new:Npn \__int_to_Base:nn #1#2
+ {
+ \int_compare:nNnTF {#1} < 0
+ { \exp_args:No \__int_to_Base:nnN { \use_none:n #1 } {#2} - }
+ { \__int_to_Base:nnN {#1} {#2} \c_empty_tl }
+ }
+\cs_new:Npn \__int_to_base:nnN #1#2#3
+ {
+ \int_compare:nNnTF {#1} < {#2}
+ { \exp_last_unbraced:Nf #3 { \__int_to_letter:n {#1} } }
+ {
+ \exp_args:Nf \__int_to_base:nnnN
+ { \__int_to_letter:n { \int_mod:nn {#1} {#2} } }
+ {#1}
+ {#2}
+ #3
+ }
+ }
+\cs_new:Npn \__int_to_base:nnnN #1#2#3#4
+ {
+ \exp_args:Nf \__int_to_base:nnN
+ { \int_div_truncate:nn {#2} {#3} }
+ {#3}
+ #4
+ #1
+ }
+\cs_new:Npn \__int_to_Base:nnN #1#2#3
+ {
+ \int_compare:nNnTF {#1} < {#2}
+ { \exp_last_unbraced:Nf #3 { \__int_to_Letter:n {#1} } }
+ {
+ \exp_args:Nf \__int_to_Base:nnnN
+ { \__int_to_Letter:n { \int_mod:nn {#1} {#2} } }
+ {#1}
+ {#2}
+ #3
+ }
+ }
+\cs_new:Npn \__int_to_Base:nnnN #1#2#3#4
+ {
+ \exp_args:Nf \__int_to_Base:nnN
+ { \int_div_truncate:nn {#2} {#3} }
+ {#3}
+ #4
+ #1
+ }
+\cs_new:Npn \__int_to_letter:n #1
+ {
+ \exp_after:wN \exp_after:wN
+ \if_case:w \__int_eval:w #1 - 10 \__int_eval_end:
+ a
+ \or: b
+ \or: c
+ \or: d
+ \or: e
+ \or: f
+ \or: g
+ \or: h
+ \or: i
+ \or: j
+ \or: k
+ \or: l
+ \or: m
+ \or: n
+ \or: o
+ \or: p
+ \or: q
+ \or: r
+ \or: s
+ \or: t
+ \or: u
+ \or: v
+ \or: w
+ \or: x
+ \or: y
+ \or: z
+ \else: \int_value:w \__int_eval:w #1 \exp_after:wN \__int_eval_end:
+ \fi:
+ }
+\cs_new:Npn \__int_to_Letter:n #1
+ {
+ \exp_after:wN \exp_after:wN
+ \if_case:w \__int_eval:w #1 - 10 \__int_eval_end:
+ A
+ \or: B
+ \or: C
+ \or: D
+ \or: E
+ \or: F
+ \or: G
+ \or: H
+ \or: I
+ \or: J
+ \or: K
+ \or: L
+ \or: M
+ \or: N
+ \or: O
+ \or: P
+ \or: Q
+ \or: R
+ \or: S
+ \or: T
+ \or: U
+ \or: V
+ \or: W
+ \or: X
+ \or: Y
+ \or: Z
+ \else: \int_value:w \__int_eval:w #1 \exp_after:wN \__int_eval_end:
+ \fi:
+ }
+\cs_new:Npn \int_to_bin:n #1
+ { \int_to_base:nn {#1} { 2 } }
+\cs_new:Npn \int_to_hex:n #1
+ { \int_to_base:nn {#1} { 16 } }
+\cs_new:Npn \int_to_Hex:n #1
+ { \int_to_Base:nn {#1} { 16 } }
+\cs_new:Npn \int_to_oct:n #1
+ { \int_to_base:nn {#1} { 8 } }
+\cs_new:Npn \int_to_roman:n #1
+ {
+ \exp_after:wN \__int_to_roman:N
+ \__int_to_roman:w \int_eval:n {#1} Q
+ }
+\cs_new:Npn \__int_to_roman:N #1
+ {
+ \use:c { __int_to_roman_ #1 :w }
+ \__int_to_roman:N
+ }
+\cs_new:Npn \int_to_Roman:n #1
+ {
+ \exp_after:wN \__int_to_Roman_aux:N
+ \__int_to_roman:w \int_eval:n {#1} Q
+ }
+\cs_new:Npn \__int_to_Roman_aux:N #1
+ {
+ \use:c { __int_to_Roman_ #1 :w }
+ \__int_to_Roman_aux:N
+ }
+\cs_new:Npn \__int_to_roman_i:w { i }
+\cs_new:Npn \__int_to_roman_v:w { v }
+\cs_new:Npn \__int_to_roman_x:w { x }
+\cs_new:Npn \__int_to_roman_l:w { l }
+\cs_new:Npn \__int_to_roman_c:w { c }
+\cs_new:Npn \__int_to_roman_d:w { d }
+\cs_new:Npn \__int_to_roman_m:w { m }
+\cs_new:Npn \__int_to_roman_Q:w #1 { }
+\cs_new:Npn \__int_to_Roman_i:w { I }
+\cs_new:Npn \__int_to_Roman_v:w { V }
+\cs_new:Npn \__int_to_Roman_x:w { X }
+\cs_new:Npn \__int_to_Roman_l:w { L }
+\cs_new:Npn \__int_to_Roman_c:w { C }
+\cs_new:Npn \__int_to_Roman_d:w { D }
+\cs_new:Npn \__int_to_Roman_m:w { M }
+\cs_new:Npn \__int_to_Roman_Q:w #1 { }
+\cs_new:Npn \__int_pass_signs:wn #1
+ {
+ \if:w + \if:w - \exp_not:N #1 + \fi: \exp_not:N #1
+ \exp_after:wN \__int_pass_signs:wn
+ \else:
+ \exp_after:wN \__int_pass_signs_end:wn
+ \exp_after:wN #1
+ \fi:
+ }
+\cs_new:Npn \__int_pass_signs_end:wn #1 \s__int_stop #2 { #2 #1 }
+\cs_new:Npn \int_from_alph:n #1
+ {
+ \int_eval:n
+ {
+ \exp_after:wN \__int_pass_signs:wn \tl_to_str:n {#1}
+ \s__int_stop { \__int_from_alph:nN { 0 } }
+ \q__int_recursion_tail \q__int_recursion_stop
+ }
+ }
+\cs_new:Npn \__int_from_alph:nN #1#2
+ {
+ \__int_if_recursion_tail_stop_do:Nn #2 {#1}
+ \exp_args:Nf \__int_from_alph:nN
+ { \int_eval:n { #1 * 26 + \__int_from_alph:N #2 } }
+ }
+\cs_new:Npn \__int_from_alph:N #1
+ { `#1 - \int_compare:nNnTF { `#1 } < { 91 } { 64 } { 96 } }
+\cs_new:Npn \int_from_base:nn #1#2
+ {
+ \int_eval:n
+ {
+ \exp_after:wN \__int_pass_signs:wn \tl_to_str:n {#1}
+ \s__int_stop { \__int_from_base:nnN { 0 } {#2} }
+ \q__int_recursion_tail \q__int_recursion_stop
+ }
+ }
+\cs_new:Npn \__int_from_base:nnN #1#2#3
+ {
+ \__int_if_recursion_tail_stop_do:Nn #3 {#1}
+ \exp_args:Nf \__int_from_base:nnN
+ { \int_eval:n { #1 * #2 + \__int_from_base:N #3 } }
+ {#2}
+ }
+\cs_new:Npn \__int_from_base:N #1
+ {
+ \int_compare:nNnTF { `#1 } < { 58 }
+ {#1}
+ { `#1 - \int_compare:nNnTF { `#1 } < { 91 } { 55 } { 87 } }
+ }
+\cs_new:Npn \int_from_bin:n #1
+ { \int_from_base:nn {#1} { 2 } }
+\cs_new:Npn \int_from_hex:n #1
+ { \int_from_base:nn {#1} { 16 } }
+\cs_new:Npn \int_from_oct:n #1
+ { \int_from_base:nn {#1} { 8 } }
+\int_const:cn { c__int_from_roman_i_int } { 1 }
+\int_const:cn { c__int_from_roman_v_int } { 5 }
+\int_const:cn { c__int_from_roman_x_int } { 10 }
+\int_const:cn { c__int_from_roman_l_int } { 50 }
+\int_const:cn { c__int_from_roman_c_int } { 100 }
+\int_const:cn { c__int_from_roman_d_int } { 500 }
+\int_const:cn { c__int_from_roman_m_int } { 1000 }
+\int_const:cn { c__int_from_roman_I_int } { 1 }
+\int_const:cn { c__int_from_roman_V_int } { 5 }
+\int_const:cn { c__int_from_roman_X_int } { 10 }
+\int_const:cn { c__int_from_roman_L_int } { 50 }
+\int_const:cn { c__int_from_roman_C_int } { 100 }
+\int_const:cn { c__int_from_roman_D_int } { 500 }
+\int_const:cn { c__int_from_roman_M_int } { 1000 }
+\cs_new:Npn \int_from_roman:n #1
+ {
+ \int_eval:n
+ {
+ (
+ 0
+ \exp_after:wN \__int_from_roman:NN \tl_to_str:n {#1}
+ \q__int_recursion_tail \q__int_recursion_tail \q__int_recursion_stop
+ )
+ }
+ }
+\cs_new:Npn \__int_from_roman:NN #1#2
+ {
+ \__int_if_recursion_tail_stop:N #1
+ \int_if_exist:cF { c__int_from_roman_ #1 _int }
+ { \__int_from_roman_error:w }
+ \__int_if_recursion_tail_stop_do:Nn #2
+ { + \use:c { c__int_from_roman_ #1 _int } }
+ \int_if_exist:cF { c__int_from_roman_ #2 _int }
+ { \__int_from_roman_error:w }
+ \int_compare:nNnTF
+ { \use:c { c__int_from_roman_ #1 _int } }
+ <
+ { \use:c { c__int_from_roman_ #2 _int } }
+ {
+ + \use:c { c__int_from_roman_ #2 _int }
+ - \use:c { c__int_from_roman_ #1 _int }
+ \__int_from_roman:NN
+ }
+ {
+ + \use:c { c__int_from_roman_ #1 _int }
+ \__int_from_roman:NN #2
+ }
+ }
+\cs_new:Npn \__int_from_roman_error:w #1 \q__int_recursion_stop #2
+ { #2 * 0 - 1 }
+\cs_new_eq:NN \int_show:N \__kernel_register_show:N
+\cs_generate_variant:Nn \int_show:N { c }
+\cs_new_protected:Npn \int_show:n
+ { \__kernel_msg_show_eval:Nn \int_eval:n }
+\cs_new_eq:NN \int_log:N \__kernel_register_log:N
+\cs_generate_variant:Nn \int_log:N { c }
+\cs_new_protected:Npn \int_log:n
+ { \__kernel_msg_log_eval:Nn \int_eval:n }
+\int_const:Nn \c_one_int { 1 }
+\int_const:Nn \c_max_int { 2 147 483 647 }
+\int_const:Nn \c_max_char_int
+ {
+ \if_int_odd:w 0
+ \cs_if_exist:NT \tex_luatexversion:D { 1 }
+ \cs_if_exist:NT \tex_XeTeXversion:D { 1 } ~
+ "10FFFF
+ \else:
+ "FF
+ \fi:
+ }
+\int_new:N \l_tmpa_int
+\int_new:N \l_tmpb_int
+\int_new:N \g_tmpa_int
+\int_new:N \g_tmpb_int
+\int_new:N \l__seq_internal_a_int
+\int_new:N \l__seq_internal_b_int
+%% File: l3flag.dtx
+\cs_new_protected:Npn \flag_new:N #1
+ { \cs_new_protected:Npe #1 { \cs_to_str:N #1 } }
+\cs_generate_variant:Nn \flag_new:N { c }
+\flag_new:N \l_tmpa_flag
+\flag_new:N \l_tmpb_flag
+\cs_new_protected:Npn \flag_clear:N #1
+ {
+ \__flag_clear:wN 0 ; #1
+ \prg_break_point:
+ }
+\cs_generate_variant:Nn \flag_clear:N { c }
+\cs_new_protected:Npn \__flag_clear:wN #1 ; #2
+ {
+ \if_cs_exist:w #2 #1 \cs_end: \else:
+ \prg_break:n
+ \fi:
+ \cs_set_eq:cN { #2 #1 } \tex_undefined:D
+ \exp_after:wN \__flag_clear:wN
+ \int_value:w \int_eval:w \c_one_int + #1 ; #2
+ }
+\cs_new_protected:Npn \flag_clear_new:N #1
+ { \flag_if_exist:NTF #1 { \flag_clear:N } { \flag_new:N } #1 }
+\cs_generate_variant:Nn \flag_clear_new:N { c }
+\cs_new_protected:Npn \flag_show:N { \__flag_show:NN \tl_show:n }
+\cs_generate_variant:Nn \flag_show:N { c }
+\cs_new_protected:Npn \flag_log:N { \__flag_show:NN \tl_log:n }
+\cs_generate_variant:Nn \flag_log:N { c }
+\cs_new_protected:Npn \__flag_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ { \exp_args:Ne #1 { \tl_to_str:n { #2 height } = \flag_height:N #2 } }
+ }
+\prg_new_eq_conditional:NNn \flag_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \flag_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+\prg_new_conditional:Npnn \flag_if_raised:N #1 { p , T , F , TF }
+ {
+ \if_cs_exist:w #1 0 \cs_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \flag_if_raised:N
+ { c } { p , T , F , TF }
+\cs_new:Npn \flag_height:N #1 { \__flag_height_loop:wN 0; #1 }
+\cs_new:Npn \__flag_height_loop:wN #1 ; #2
+ {
+ \if_cs_exist:w #2 #1 \cs_end: \else:
+ \exp_after:wN \__flag_height_end:wN
+ \fi:
+ \exp_after:wN \__flag_height_loop:wN
+ \int_value:w \int_eval:w \c_one_int + #1 ; #2
+ }
+\cs_new:Npn \__flag_height_end:wN #1 + #2 ; #3 {#2}
+\cs_generate_variant:Nn \flag_height:N { c }
+\cs_new:Npn \flag_raise:N #1
+ { \exp_after:wN \use_none:n \cs:w #1 \flag_height:N #1 \cs_end: }
+\cs_generate_variant:Nn \flag_raise:N { c }
+\cs_new:Npn \flag_ensure_raised:N #1
+ { \exp_after:wN \use_none:n \cs:w #1 0 \cs_end: }
+\cs_generate_variant:Nn \flag_ensure_raised:N { c }
+\cs_new_protected:Npn \flag_new:n #1 { \flag_new:c { l_#1_flag } }
+\cs_new_protected:Npn \flag_clear:n #1 { \flag_clear:c { l_#1_flag } }
+\cs_new_protected:Npn \flag_clear_new:n #1 { \flag_clear_new:c { l_#1_flag } }
+\cs_new:Npn \flag_if_exist_p:n #1 { \flag_if_exist_p:c { l_#1_flag } }
+\cs_new:Npn \flag_if_exist:nT #1 { \flag_if_exist:cT { l_#1_flag } }
+\cs_new:Npn \flag_if_exist:nF #1 { \flag_if_exist:cF { l_#1_flag } }
+\cs_new:Npn \flag_if_exist:nTF #1 { \flag_if_exist:cTF { l_#1_flag } }
+\cs_new:Npn \flag_if_raised_p:n #1 { \flag_if_raised_p:c { l_#1_flag } }
+\cs_new:Npn \flag_if_raised:nT #1 { \flag_if_raised:cT { l_#1_flag } }
+\cs_new:Npn \flag_if_raised:nF #1 { \flag_if_raised:cF { l_#1_flag } }
+\cs_new:Npn \flag_if_raised:nTF #1 { \flag_if_raised:cTF { l_#1_flag } }
+\cs_new:Npn \flag_height:n #1 { \flag_height:c { l_#1_flag } }
+\cs_new:Npn \flag_raise:n #1 { \flag_raise:c { l_#1_flag } }
+\cs_new:Npn \flag_ensure_raised:n #1 { \flag_ensure_raised:c { l_#1_flag } }
+\cs_new_protected:Npn \flag_show:n { \__flag_show:Nn \tl_show:n }
+\cs_new_protected:Npn \flag_log:n { \__flag_show:Nn \tl_log:n }
+\cs_new_protected:Npn \__flag_show:Nn #1#2
+ {
+ \exp_args:Nc \__kernel_chk_defined:NT { l_#2_flag }
+ {
+ \exp_args:Ne #1
+ { \tl_to_str:n { flag~#2~height } = \flag_height:n {#2} }
+ }
+ }
+%% File: l3prg.dtx
+\cs_new_eq:NN \if_predicate:w \tex_ifodd:D
+\cs_new_protected:Npn \bool_new:N #1 { \cs_new_eq:NN #1 \c_false_bool }
+\cs_generate_variant:Nn \bool_new:N { c }
+\cs_new_protected:Npn \bool_const:Nn #1#2
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \tex_global:D \tex_chardef:D #1 = \bool_if_p:n {#2}
+ }
+\cs_generate_variant:Nn \bool_const:Nn { c }
+\cs_new_protected:Npn \bool_set_true:N #1
+ { \cs_set_eq:NN #1 \c_true_bool }
+\cs_new_protected:Npn \bool_set_false:N #1
+ { \cs_set_eq:NN #1 \c_false_bool }
+\cs_new_protected:Npn \bool_gset_true:N #1
+ { \cs_gset_eq:NN #1 \c_true_bool }
+\cs_new_protected:Npn \bool_gset_false:N #1
+ { \cs_gset_eq:NN #1 \c_false_bool }
+\cs_generate_variant:Nn \bool_set_true:N { c }
+\cs_generate_variant:Nn \bool_set_false:N { c }
+\cs_generate_variant:Nn \bool_gset_true:N { c }
+\cs_generate_variant:Nn \bool_gset_false:N { c }
+\cs_new_eq:NN \bool_set_eq:NN \tl_set_eq:NN
+\cs_new_eq:NN \bool_gset_eq:NN \tl_gset_eq:NN
+\cs_generate_variant:Nn \bool_set_eq:NN { Nc, cN, cc }
+\cs_generate_variant:Nn \bool_gset_eq:NN { Nc, cN, cc }
+\cs_new_protected:Npn \bool_set:Nn #1#2
+ {
+ \exp_last_unbraced:NNNf
+ \tex_chardef:D #1 = { \bool_if_p:n {#2} }
+ }
+\cs_new_protected:Npn \bool_gset:Nn #1#2
+ {
+ \exp_last_unbraced:NNNNf
+ \tex_global:D \tex_chardef:D #1 = { \bool_if_p:n {#2} }
+ }
+\cs_generate_variant:Nn \bool_set:Nn { c }
+\cs_generate_variant:Nn \bool_gset:Nn { c }
+\cs_new_protected:Npn \bool_set_inverse:N #1
+ { \bool_if:NTF #1 { \bool_set_false:N } { \bool_set_true:N } #1 }
+\cs_generate_variant:Nn \bool_set_inverse:N { c }
+\cs_new_protected:Npn \bool_gset_inverse:N #1
+ { \bool_if:NTF #1 { \bool_gset_false:N } { \bool_gset_true:N } #1 }
+\cs_generate_variant:Nn \bool_gset_inverse:N { c }
+\quark_new:N \q__bool_recursion_tail
+\quark_new:N \q__bool_recursion_stop
+\cs_new:Npn \__bool_use_i_delimit_by_q_recursion_stop:nw
+ #1 #2 \q__bool_recursion_stop {#1}
+\__kernel_quark_new_test:N \__bool_if_recursion_tail_stop_do:nn
+\prg_new_conditional:Npnn \bool_if:N #1 { p , T , F , TF }
+ {
+ \if_bool:N #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \bool_if:N { c } { p , T , F , TF }
+\cs_new:Npe \bool_to_str:N #1
+ {
+ \exp_not:N \bool_if:NTF #1
+ { \tl_to_str:n { true } } { \tl_to_str:n { false } }
+ }
+\cs_generate_variant:Nn \bool_to_str:N { c }
+\cs_new:Npe \bool_to_str:n #1
+ {
+ \exp_not:N \bool_if:nTF {#1}
+ { \tl_to_str:n { true } } { \tl_to_str:n { false } }
+ }
+\cs_new_protected:Npn \bool_show:n
+ { \__kernel_msg_show_eval:Nn \bool_to_str:n }
+\cs_new_protected:Npn \bool_log:n
+ { \__kernel_msg_log_eval:Nn \bool_to_str:n }
+\cs_new_protected:Npn \bool_show:N { \__bool_show:NN \tl_show:n }
+\cs_generate_variant:Nn \bool_show:N { c }
+\cs_new_protected:Npn \bool_log:N { \__bool_show:NN \tl_log:n }
+\cs_generate_variant:Nn \bool_log:N { c }
+\cs_new_protected:Npn \__bool_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ \token_case_meaning:NnF #2
+ {
+ \c_true_bool { \exp_args:Ne #1 { \token_to_str:N #2 = true } }
+ \c_false_bool { \exp_args:Ne #1 { \token_to_str:N #2 = false } }
+ }
+ {
+ \msg_error:nneee { kernel } { bad-type }
+ { \token_to_str:N #2 } { \token_to_meaning:N #2 } { bool }
+ }
+ }
+ }
+\bool_new:N \l_tmpa_bool
+\bool_new:N \l_tmpb_bool
+\bool_new:N \g_tmpa_bool
+\bool_new:N \g_tmpb_bool
+\prg_new_eq_conditional:NNn \bool_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \bool_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+\prg_new_conditional:Npnn \bool_if:n #1 { T , F , TF }
+ {
+ \if_predicate:w \bool_if_p:n {#1}
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \bool_if_p:n { \exp_args:Nf \__bool_if_p:n }
+\cs_new:Npn \__bool_if_p:n #1
+ {
+ \tl_if_empty:oT { \use_none:nn #1 . } { \__bool_if_p_aux:w }
+ \group_align_safe_begin:
+ \exp_after:wN
+ \group_align_safe_end:
+ \exp:w \exp_end_continue_f:w % (
+ \__bool_get_next:NN \use_i:nnnn #1 )
+ }
+\cs_new:Npn \__bool_if_p_aux:w #1 \use_i:nnnn #2#3
+ { \bool_if:NTF #2 \c_true_bool \c_false_bool }
+\cs_new:Npn \__bool_get_next:NN #1#2
+ {
+ \use:c
+ {
+ __bool_
+ \if_meaning:w !#2 ! \else: \if_meaning:w (#2 ( \else: p \fi: \fi:
+ :Nw
+ }
+ #1 #2
+ }
+\cs_new:cpn { __bool_!:Nw } #1#2
+ {
+ \exp_after:wN \__bool_get_next:NN
+ #1 \use_ii:nnnn \use_i:nnnn \use_iii:nnnn \use_iv:nnnn
+ }
+\cs_new:cpn { __bool_(:Nw } #1#2
+ {
+ \exp_after:wN \__bool_choose:NNN \exp_after:wN #1
+ \int_value:w \__bool_get_next:NN \use_i:nnnn
+ }
+\cs_new:cpn { __bool_p:Nw } #1
+ { \exp_after:wN \__bool_choose:NNN \exp_after:wN #1 \int_value:w }
+\cs_new:Npn \__bool_choose:NNN #1#2#3
+ {
+ \use:c
+ {
+ __bool_ \token_to_str:N #3 _
+ #1 #2 { \if_meaning:w 0 #2 1 \else: 0 \fi: } 2 0 :
+ }
+ }
+\cs_new:cpn { __bool_)_0: } { \c_false_bool }
+\cs_new:cpn { __bool_)_1: } { \c_true_bool }
+\cs_new:cpn { __bool_)_2: } { \c_true_bool }
+\cs_new:cpn { __bool_&_0: } & { \__bool_get_next:NN \use_iv:nnnn }
+\cs_new:cpn { __bool_&_1: } & { \__bool_get_next:NN \use_i:nnnn }
+\cs_new:cpn { __bool_&_2: } & { \__bool_get_next:NN \use_iii:nnnn }
+\cs_new:cpn { __bool_|_0: } | { \__bool_get_next:NN \use_i:nnnn }
+\cs_new:cpn { __bool_|_1: } | { \__bool_get_next:NN \use_iii:nnnn }
+\cs_new:cpn { __bool_|_2: } | { \__bool_get_next:NN \use_iii:nnnn }
+\cs_new:Npn \bool_lazy_all_p:n #1
+ { \__bool_lazy_all:n #1 \q__bool_recursion_tail \q__bool_recursion_stop }
+\prg_new_conditional:Npnn \bool_lazy_all:n #1 { T , F , TF }
+ {
+ \if_predicate:w \bool_lazy_all_p:n {#1}
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \__bool_lazy_all:n #1
+ {
+ \__bool_if_recursion_tail_stop_do:nn {#1} { \c_true_bool }
+ \bool_if:nF {#1}
+ { \__bool_use_i_delimit_by_q_recursion_stop:nw { \c_false_bool } }
+ \__bool_lazy_all:n
+ }
+\prg_new_conditional:Npnn \bool_lazy_and:nn #1#2 { p , T , F , TF }
+ {
+ \if_predicate:w
+ \bool_if:nTF {#1} { \bool_if_p:n {#2} } { \c_false_bool }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \bool_lazy_any_p:n #1
+ { \__bool_lazy_any:n #1 \q__bool_recursion_tail \q__bool_recursion_stop }
+\prg_new_conditional:Npnn \bool_lazy_any:n #1 { T , F , TF }
+ {
+ \if_predicate:w \bool_lazy_any_p:n {#1}
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \__bool_lazy_any:n #1
+ {
+ \__bool_if_recursion_tail_stop_do:nn {#1} { \c_false_bool }
+ \bool_if:nT {#1}
+ { \__bool_use_i_delimit_by_q_recursion_stop:nw { \c_true_bool } }
+ \__bool_lazy_any:n
+ }
+\prg_new_conditional:Npnn \bool_lazy_or:nn #1#2 { p , T , F , TF }
+ {
+ \if_predicate:w
+ \bool_if:nTF {#1} { \c_true_bool } { \bool_if_p:n {#2} }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \bool_not_p:n #1 { \bool_if_p:n { ! ( #1 ) } }
+\prg_new_conditional:Npnn \bool_xor:nn #1#2 { p , T , F , TF }
+ {
+ \bool_if:nT {#1} \reverse_if:N
+ \if_predicate:w \bool_if_p:n {#2}
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \bool_while_do:Nn #1#2
+ { \bool_if:NT #1 { #2 \bool_while_do:Nn #1 {#2} } }
+\cs_new:Npn \bool_until_do:Nn #1#2
+ { \bool_if:NF #1 { #2 \bool_until_do:Nn #1 {#2} } }
+\cs_generate_variant:Nn \bool_while_do:Nn { c }
+\cs_generate_variant:Nn \bool_until_do:Nn { c }
+\cs_new:Npn \bool_do_while:Nn #1#2
+ { #2 \bool_if:NT #1 { \bool_do_while:Nn #1 {#2} } }
+\cs_new:Npn \bool_do_until:Nn #1#2
+ { #2 \bool_if:NF #1 { \bool_do_until:Nn #1 {#2} } }
+\cs_generate_variant:Nn \bool_do_while:Nn { c }
+\cs_generate_variant:Nn \bool_do_until:Nn { c }
+\cs_new:Npn \bool_while_do:nn #1#2
+ {
+ \bool_if:nT {#1}
+ {
+ #2
+ \bool_while_do:nn {#1} {#2}
+ }
+ }
+\cs_new:Npn \bool_do_while:nn #1#2
+ {
+ #2
+ \bool_if:nT {#1} { \bool_do_while:nn {#1} {#2} }
+ }
+\cs_new:Npn \bool_until_do:nn #1#2
+ {
+ \bool_if:nF {#1}
+ {
+ #2
+ \bool_until_do:nn {#1} {#2}
+ }
+ }
+\cs_new:Npn \bool_do_until:nn #1#2
+ {
+ #2
+ \bool_if:nF {#1} { \bool_do_until:nn {#1} {#2} }
+ }
+\scan_new:N \s__bool_mark
+\scan_new:N \s__bool_stop
+\cs_new:Npn \bool_case:nTF
+ { \exp:w \__bool_case:nTF }
+\cs_new:Npn \bool_case:nT #1#2
+ { \exp:w \__bool_case:nTF {#1} {#2} { } }
+\cs_new:Npn \bool_case:nF #1
+ { \exp:w \__bool_case:nTF {#1} { } }
+\cs_new:Npn \bool_case:n #1
+ { \exp:w \__bool_case:nTF {#1} { } { } }
+\cs_new:Npn \__bool_case:nTF #1#2#3
+ {
+ \__bool_case:w
+ #1 \c_true_bool { } \s__bool_mark {#2} \s__bool_mark {#3} \s__bool_stop
+ }
+\cs_new:Npn \__bool_case:w #1#2
+ {
+ \bool_if:nTF {#1}
+ { \__bool_case_end:nw {#2} }
+ { \__bool_case:w }
+ }
+\cs_new:Npn \__bool_case_end:nw #1#2#3 \s__bool_mark #4#5 \s__bool_stop
+ { \exp_end: #1 #4 }
+\cs_new:Npn \prg_replicate:nn #1
+ {
+ \exp:w
+ \exp_after:wN \__prg_replicate_first:N
+ \int_value:w \int_eval:n {#1}
+ \cs_end:
+ }
+\cs_new:Npn \__prg_replicate:N #1
+ { \cs:w __prg_replicate_#1 :n \__prg_replicate:N }
+\cs_new:Npn \__prg_replicate_first:N #1
+ { \cs:w __prg_replicate_first_ #1 :n \__prg_replicate:N }
+\cs_new:Npn \__prg_replicate_ :n #1 { \cs_end: }
+\cs_new:cpn { __prg_replicate_0:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} }
+\cs_new:cpn { __prg_replicate_1:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1 }
+\cs_new:cpn { __prg_replicate_2:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1 }
+\cs_new:cpn { __prg_replicate_3:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1 }
+\cs_new:cpn { __prg_replicate_4:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1 }
+\cs_new:cpn { __prg_replicate_5:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1#1 }
+\cs_new:cpn { __prg_replicate_6:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1#1#1 }
+\cs_new:cpn { __prg_replicate_7:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1#1#1#1 }
+\cs_new:cpn { __prg_replicate_8:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1#1#1#1#1 }
+\cs_new:cpn { __prg_replicate_9:n } #1
+ { \cs_end: {#1#1#1#1#1#1#1#1#1#1} #1#1#1#1#1#1#1#1#1 }
+\cs_new:cpn { __prg_replicate_first_-:n } #1
+ {
+ \exp_end:
+ \msg_expandable_error:nn { prg } { negative-replication }
+ }
+\cs_new:cpn { __prg_replicate_first_0:n } #1 { \exp_end: }
+\cs_new:cpn { __prg_replicate_first_1:n } #1 { \exp_end: #1 }
+\cs_new:cpn { __prg_replicate_first_2:n } #1 { \exp_end: #1#1 }
+\cs_new:cpn { __prg_replicate_first_3:n } #1 { \exp_end: #1#1#1 }
+\cs_new:cpn { __prg_replicate_first_4:n } #1 { \exp_end: #1#1#1#1 }
+\cs_new:cpn { __prg_replicate_first_5:n } #1 { \exp_end: #1#1#1#1#1 }
+\cs_new:cpn { __prg_replicate_first_6:n } #1 { \exp_end: #1#1#1#1#1#1 }
+\cs_new:cpn { __prg_replicate_first_7:n } #1 { \exp_end: #1#1#1#1#1#1#1 }
+\cs_new:cpn { __prg_replicate_first_8:n } #1 { \exp_end: #1#1#1#1#1#1#1#1 }
+\cs_new:cpn { __prg_replicate_first_9:n } #1
+ { \exp_end: #1#1#1#1#1#1#1#1#1 }
+\prg_new_conditional:Npnn \mode_if_vertical: { p , T , F , TF }
+ { \if_mode_vertical: \prg_return_true: \else: \prg_return_false: \fi: }
+\prg_new_conditional:Npnn \mode_if_horizontal: { p , T , F , TF }
+ { \if_mode_horizontal: \prg_return_true: \else: \prg_return_false: \fi: }
+\prg_new_conditional:Npnn \mode_if_inner: { p , T , F , TF }
+ { \if_mode_inner: \prg_return_true: \else: \prg_return_false: \fi: }
+\prg_new_conditional:Npnn \mode_if_math: { p , T , F , TF }
+ { \if_mode_math: \prg_return_true: \else: \prg_return_false: \fi: }
+\group_begin:
+\tex_catcode:D `\^^@ = 2 \exp_stop_f:
+\cs_new:Npn \group_align_safe_begin:
+ { \exp:w \if_false: { \fi: `^^@ \exp_stop_f: }
+\tex_catcode:D `\^^@ = 1 \exp_stop_f:
+\cs_new:Npn \group_align_safe_end:
+ { \exp:w `^^@ \if_false: } \fi: \exp_stop_f: }
+\group_end:
+\int_new:N \g__kernel_prg_map_int
+%% File: l3sys.dtx
+\tl_new:N \l__sys_tmp_tl
+\cs_new_protected:Npn \__sys_const:nn #1#2
+ {
+ \bool_if:nTF {#2}
+ {
+ \cs_new_eq:cN { #1 :T } \use:n
+ \cs_new_eq:cN { #1 :F } \use_none:n
+ \cs_new_eq:cN { #1 :TF } \use_i:nn
+ \cs_new_eq:cN { #1 _p: } \c_true_bool
+ }
+ {
+ \cs_new_eq:cN { #1 :T } \use_none:n
+ \cs_new_eq:cN { #1 :F } \use:n
+ \cs_new_eq:cN { #1 :TF } \use_ii:nn
+ \cs_new_eq:cN { #1 _p: } \c_false_bool
+ }
+ }
+\str_const:Ne \c_sys_engine_str
+ {
+ \cs_if_exist:NT \tex_luatexversion:D { luatex }
+ \cs_if_exist:NT \tex_pdftexversion:D { pdftex }
+ \cs_if_exist:NT \tex_kanjiskip:D
+ {
+ \cs_if_exist:NTF \tex_enablecjktoken:D
+ { uptex }
+ { ptex }
+ }
+ \cs_if_exist:NT \tex_XeTeXversion:D { xetex }
+ }
+\tl_map_inline:nn { { luatex } { pdftex } { ptex } { uptex } { xetex } }
+ {
+ \__sys_const:nn { sys_if_engine_ #1 }
+ { \str_if_eq_p:Vn \c_sys_engine_str {#1} }
+ }
+\group_begin:
+ \cs_set_eq:NN \lua_now:e \tex_directlua:D
+ \str_const:Ne \c_sys_engine_exec_str
+ {
+ \sys_if_engine_pdftex:T { pdf }
+ \sys_if_engine_xetex:T { xe }
+ \sys_if_engine_ptex:T { ep }
+ \sys_if_engine_uptex:T { eup }
+ \sys_if_engine_luatex:T
+ {
+ lua \lua_now:e
+ {
+ if (pcall(require, 'luaharfbuzz')) then ~
+ tex.print("hb") ~
+ end
+ }
+ }
+ tex
+ }
+\group_end:
+\str_const:Ne \c_sys_engine_format_str
+ {
+ \cs_if_exist:NTF \fmtname
+ {
+ \bool_lazy_or:nnTF
+ { \str_if_eq_p:Vn \fmtname { plain } }
+ { \str_if_eq_p:Vn \fmtname { LaTeX2e } }
+ {
+ \sys_if_engine_pdftex:T
+ { \int_compare:nNnT { \tex_pdfoutput:D } = { 1 } { pdf } }
+ \sys_if_engine_xetex:T { xe }
+ \sys_if_engine_ptex:T { p }
+ \sys_if_engine_uptex:T { up }
+ \sys_if_engine_luatex:T
+ {
+ \int_compare:nNnT { \tex_pdfoutput:D } = { 0 } { dvi }
+ lua
+ }
+ \str_if_eq:VnTF \fmtname { LaTeX2e }
+ { latex }
+ {
+ \bool_lazy_and:nnT
+ { \sys_if_engine_pdftex_p: }
+ { \int_compare_p:nNn { \tex_pdfoutput:D } = { 0 } }
+ { e }
+ tex
+ }
+ }
+ { \fmtname }
+ }
+ { unknown }
+ }
+\str_const:Ne \c_sys_engine_version_str
+ {
+ \str_case:on \c_sys_engine_str
+ {
+ { pdftex }
+ {
+ \int_div_truncate:nn { \tex_pdftexversion:D } { 100 }
+ .
+ \int_mod:nn { \tex_pdftexversion:D } { 100 }
+ .
+ \tex_pdftexrevision:D
+ }
+ { ptex }
+ {
+ \cs_if_exist:NT \tex_ptexversion:D
+ {
+ p
+ \int_use:N \tex_ptexversion:D
+ .
+ \int_use:N \tex_ptexminorversion:D
+ \tex_ptexrevision:D
+ -
+ \int_use:N \tex_epTeXversion:D
+ }
+ }
+ { luatex }
+ {
+ \int_div_truncate:nn { \tex_luatexversion:D } { 100 }
+ .
+ \int_mod:nn { \tex_luatexversion:D } { 100 }
+ .
+ \tex_luatexrevision:D
+ }
+ { uptex }
+ {
+ \cs_if_exist:NT \tex_ptexversion:D
+ {
+ p
+ \int_use:N \tex_ptexversion:D
+ .
+ \int_use:N \tex_ptexminorversion:D
+ \tex_ptexrevision:D
+ -
+ u
+ \int_use:N \tex_uptexversion:D
+ \tex_uptexrevision:D
+ -
+ \int_use:N \tex_epTeXversion:D
+ }
+ }
+ { xetex }
+ {
+ \int_use:N \tex_XeTeXversion:D
+ \tex_XeTeXrevision:D
+ }
+ }
+ }
+\cs_new_protected:Npn \sys_load_backend:n #1
+ {
+ \sys_finalise:
+ \str_if_exist:NTF \c_sys_backend_str
+ {
+ \str_if_eq:VnF \c_sys_backend_str {#1}
+ { \msg_error:nn { sys } { backend-set } }
+ }
+ {
+ \tl_if_blank:nF {#1}
+ { \tl_gset:Nn \g__sys_backend_tl {#1} }
+ \__sys_load_backend_check:N \g__sys_backend_tl
+ \str_const:Ne \c_sys_backend_str { \g__sys_backend_tl }
+ \__kernel_sys_configuration_load:n
+ { l3backend- \c_sys_backend_str }
+ }
+ }
+\cs_new_protected:Npn \__sys_load_backend_check:N #1
+ {
+ \sys_if_engine_xetex:TF
+ {
+ \str_case:VnF #1
+ {
+ { dvisvgm } { }
+ { xdvipdfmx } { \tl_gset:Nn #1 { xetex } }
+ { xetex } { }
+ }
+ {
+ \msg_error:nnee { sys } { wrong-backend }
+ #1 { xetex }
+ \tl_gset:Nn #1 { xetex }
+ }
+ }
+ {
+ \sys_if_output_pdf:TF
+ {
+ \str_if_eq:VnTF #1 { pdfmode }
+ {
+ \sys_if_engine_luatex:TF
+ { \tl_gset:Nn #1 { luatex } }
+ { \tl_gset:Nn #1 { pdftex } }
+ }
+ {
+ \bool_lazy_or:nnF
+ { \str_if_eq_p:Vn #1 { luatex } }
+ { \str_if_eq_p:Vn #1 { pdftex } }
+ {
+ \msg_error:nnee { sys } { wrong-backend }
+ #1 { \sys_if_engine_luatex:TF { luatex } { pdftex } }
+ \sys_if_engine_luatex:TF
+ { \tl_gset:Nn #1 { luatex } }
+ { \tl_gset:Nn #1 { pdftex } }
+ }
+ }
+ }
+ {
+ \str_case:VnF #1
+ {
+ { dvipdfmx } { }
+ { dvips } { }
+ { dvisvgm } { }
+ }
+ {
+ \msg_error:nnee { sys } { wrong-backend }
+ #1 { dvips }
+ \tl_gset:Nn #1 { dvips }
+ }
+ }
+ }
+ }
+\cs_new_protected:Npn \sys_ensure_backend:
+ {
+ \str_if_exist:NF \c_sys_backend_str
+ { \sys_load_backend:n { } }
+ }
+\bool_new:N \g__sys_debug_bool
+\cs_new_protected:Npn \sys_load_debug:
+ {
+ \bool_if:NF \g__sys_debug_bool
+ { \__kernel_sys_configuration_load:n { l3debug } }
+ \bool_gset_true:N \g__sys_debug_bool
+ }
+\cs_if_exist:NT \@expl@finalise@setup@@
+ {
+ \tl_gput_right:Nn \@expl@finalise@setup@@
+ {
+ \tl_gput_right:Nn \@kernel@after@begindocument
+ {
+ \cs_gset_protected:Npn \sys_load_debug:
+ { \msg_error:nn { sys } { load-debug-in-preamble } }
+ }
+ }
+ }
+\tl_new:N \l__sys_internal_tl
+\tl_const:Ne \c__sys_marker_tl { : \token_to_str:N : }
+\cs_new_protected:Npn \sys_get_shell:nnN #1#2#3
+ {
+ \sys_get_shell:nnNF {#1} {#2} #3
+ { \tl_set:Nn #3 { \q_no_value } }
+ }
+\prg_new_protected_conditional:Npnn \sys_get_shell:nnN #1#2#3 { T , F , TF }
+ {
+ \sys_if_shell:TF
+ { \exp_args:No \__sys_get:nnN { \tl_to_str:n {#1} } {#2} #3 }
+ { \prg_return_false: }
+ }
+\cs_new_protected:Npn \__sys_get:nnN #1#2#3
+ {
+ \tl_if_in:nnTF {#1} { " }
+ {
+ \msg_error:nne
+ { kernel } { quote-in-shell } {#1}
+ \prg_return_false:
+ }
+ {
+ \group_begin:
+ \if_false: { \fi:
+ \int_set_eq:NN \tex_tracingnesting:D \c_zero_int
+ \exp_args:No \tex_everyeof:D { \c__sys_marker_tl }
+ #2 \scan_stop:
+ \exp_after:wN \__sys_get_do:Nw
+ \exp_after:wN #3
+ \exp_after:wN \prg_do_nothing:
+ \tex_input:D | "#1" \scan_stop:
+ \if_false: } \fi:
+ \prg_return_true:
+ }
+ }
+\exp_args:Nno \use:nn
+ { \cs_new_protected:Npn \__sys_get_do:Nw #1#2 }
+ { \c__sys_marker_tl }
+ {
+ \group_end:
+ \tl_set:No #1 {#2}
+ }
+\sys_if_engine_luatex:F
+ { \int_const:Nn \c__sys_shell_stream_int { 18 } }
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \sys_shell_now:n #1
+ { \__sys_shell_now:e { \exp_not:n {#1} } }
+ }
+ {
+ \cs_new_protected:Npn \sys_shell_now:n #1
+ { \iow_now:Nn \c__sys_shell_stream_int {#1} }
+ }
+\cs_generate_variant:Nn \sys_shell_now:n { e, x }
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \sys_shell_shipout:n #1
+ { \__sys_shell_shipout:e { \exp_not:n {#1} } }
+ }
+ {
+ \cs_new_protected:Npn \sys_shell_shipout:n #1
+ { \iow_shipout:Nn \c__sys_shell_stream_int {#1} }
+ }
+\cs_generate_variant:Nn \sys_shell_shipout:n { e , x }
+\cs_new_protected:Npn \__kernel_sys_everyjob:
+ {
+ \tl_use:N \g__sys_everyjob_tl
+ \tl_gclear:N \g__sys_everyjob_tl
+ }
+\cs_new_protected:Npn \__sys_everyjob:n #1
+ { \tl_gput_right:Nn \g__sys_everyjob_tl {#1} }
+\tl_new:N \g__sys_everyjob_tl
+\__sys_everyjob:n
+ { \cs_new_eq:NN \c_sys_jobname_str \tex_jobname:D }
+\__sys_everyjob:n
+ {
+ \group_begin:
+ \cs_set:Npn \__sys_tmp:w #1
+ {
+ \str_if_eq:eeTF { \cs_meaning:N #1 } { \token_to_str:N #1 }
+ { #1 }
+ {
+ \cs_if_exist:NTF \tex_primitive:D
+ {
+ \bool_lazy_and:nnTF
+ { \sys_if_engine_xetex_p: }
+ {
+ \int_compare_p:nNn
+ { \exp_after:wN \use_none:n \tex_XeTeXrevision:D }
+ < { 99999 }
+ }
+ { 0 }
+ { \tex_primitive:D #1 }
+ }
+ { 0 }
+ }
+ }
+ \int_const:Nn \c_sys_minute_int
+ { \int_mod:nn { \__sys_tmp:w \time } { 60 } }
+ \int_const:Nn \c_sys_hour_int
+ { \int_div_truncate:nn { \__sys_tmp:w \time } { 60 } }
+ \int_const:Nn \c_sys_day_int { \__sys_tmp:w \day }
+ \int_const:Nn \c_sys_month_int { \__sys_tmp:w \month }
+ \int_const:Nn \c_sys_year_int { \__sys_tmp:w \year }
+ \group_end:
+ }
+\__sys_everyjob:n
+ {
+ \str_const:Ne \c_sys_timestamp_str
+ {
+ \cs_if_exist:NTF \tex_directlua:D
+ { \tex_directlua:D { tex.print(pdf.getcreationdate()) } }
+ { \tex_creationdate:D }
+ }
+ }
+\__sys_everyjob:n
+ {
+ \cs_new:Npn \sys_rand_seed: { \tex_the:D \tex_randomseed:D }
+ }
+\__sys_everyjob:n
+ {
+ \cs_new_protected:Npn \sys_gset_rand_seed:n #1
+ { \tex_setrandomseed:D \int_eval:n {#1} \exp_stop_f: }
+ }
+\sys_if_engine_luatex:TF
+ {
+ \cs_new:Npn \sys_timer:
+ { \__sys_elapsedtime: }
+ }
+ {
+ \cs_if_exist:NTF \tex_elapsedtime:D
+ {
+ \cs_new:Npn \sys_timer:
+ { \int_value:w \tex_elapsedtime:D }
+ }
+ {
+ \cs_new:Npn \sys_timer:
+ {
+ \int_value:w
+ \msg_expandable_error:nnn { kernel } { no-elapsed-time }
+ { \sys_timer: }
+ \c_zero_int
+ }
+ }
+ }
+\__sys_const:nn { sys_if_timer_exist }
+ { \cs_if_exist_p:N \tex_elapsedtime:D || \cs_if_exist_p:N \__sys_elapsedtime: }
+\__sys_everyjob:n
+ {
+ \int_const:Nn \c_sys_shell_escape_int
+ {
+ \sys_if_engine_luatex:TF
+ {
+ \tex_directlua:D
+ { tex.sprint(status.shell_escape~or~os.execute()) }
+ }
+ { \tex_shellescape:D }
+ }
+ }
+\__sys_everyjob:n
+ {
+ \__sys_const:nn { sys_if_shell }
+ { \int_compare_p:nNn \c_sys_shell_escape_int > 0 }
+ \__sys_const:nn { sys_if_shell_unrestricted }
+ { \int_compare_p:nNn \c_sys_shell_escape_int = 1 }
+ \__sys_const:nn { sys_if_shell_restricted }
+ { \int_compare_p:nNn \c_sys_shell_escape_int = 2 }
+ }
+\cs_new_protected:Npn \sys_get_query:nN #1#2
+ { \sys_get_query:nnnN {#1} { } { } #2 }
+\cs_new_protected:Npn \sys_get_query:nnN #1#2#3
+ { \sys_get_query:nnnN {#1} { } {#2} #3 }
+\cs_new_protected:Npn \sys_get_query:nnnN #1#2#3#4
+ {
+ \tl_clear:N #4
+ \__sys_get_query_auxi:neeN {#1} {#2} {#3} #4
+ }
+\cs_new:Npn \__sys_get_query_auxi:nnnN #1#2#3#4
+ {
+ \__sys_get_query_auxii:neeN {#1}
+ { \tl_if_blank:nF {#2} { \tl_to_str:n { ~ #2 } } }
+ {
+ \tl_if_blank:nF {#3}
+ {
+ \c_space_tl
+ \sys_if_shell_restricted:F '
+ \tl_to_str:n {#3}
+ \sys_if_shell_restricted:F '
+ }
+ }
+ #4
+ }
+\cs_generate_variant:Nn \__sys_get_query_auxi:nnnN { nee }
+\cs_new_protected:Npn \__sys_get_query_auxii:nnnN #1#2#3#4
+ {
+ \sys_if_shell:T
+ {
+ \sys_get_shell:nnN
+ { l3sys-query~#1 #2 #3 }
+ {
+ \int_step_inline:nnn { 0 } { `A - 1 }
+ { \char_set_catcode_other:n {##1} }
+ \int_step_inline:nnn { `Z + 1 } { `a - 1 }
+ { \char_set_catcode_other:n {##1} }
+ \int_step_inline:nnn { `z + 1 } { 127 }
+ { \char_set_catcode_other:n {##1} }
+ \char_set_catcode_active:n { `\ }
+ \tex_endlinechar:D 13 \scan_stop:
+ }
+ \l__sys_tmp_tl
+ \tl_if_empty:NF \l__sys_tmp_tl
+ {
+ \exp_after:wN \__sys_get_query:Nw \exp_after:wN #4
+ \l__sys_tmp_tl \q_stop
+ }
+ }
+ }
+\cs_generate_variant:Nn \__sys_get_query_auxii:nnnN { nee }
+\group_begin:
+ \tex_lccode:D `\* = 13 \scan_stop:
+ \tex_lowercase:D
+ {
+ \group_end:
+ \cs_new_protected:Npn \__sys_get_query:Nw #1#2 * \q_stop
+ }
+ { \tl_set:Nn #1 {#2} }
+\cs_new_protected:Npn \sys_split_query:nN #1#2
+ { \sys_split_query:nnnN {#1} { } { } #2 }
+\cs_new_protected:Npn \sys_split_query:nnN #1#2#3
+ { \sys_split_query:nnnN {#1} { } {#2} #3 }
+\group_begin:
+ \tex_lccode:D `\* = 13 \scan_stop:
+ \tex_lowercase:D
+ {
+ \group_end:
+ \cs_new_protected:Npn \sys_split_query:nnnN #1#2#3#4
+ {
+ \seq_clear:N #4
+ \sys_get_query:nnnN {#1} {#2} {#3} \l__sys_tmp_tl
+ \tl_if_empty:NF \l__sys_tmp_tl
+ { \seq_set_split:NnV #4 * \l__sys_tmp_tl }
+ }
+ }
+\__sys_everyjob:n
+ { \cs_gset_eq:NN \g_file_curr_name_str \tex_jobname:D }
+\cs_new_protected:Npn \sys_finalise:
+ {
+ \__kernel_sys_everyjob:
+ \tl_use:N \g__sys_finalise_tl
+ \tl_gclear:N \g__sys_finalise_tl
+ }
+\cs_new_protected:Npn \__sys_finalise:n #1
+ { \tl_gput_right:Nn \g__sys_finalise_tl {#1} }
+\tl_new:N \g__sys_finalise_tl
+\__sys_finalise:n
+ {
+ \str_const:Ne \c_sys_output_str
+ {
+ \int_compare:nNnTF
+ { \cs_if_exist_use:NF \tex_pdfoutput:D { 0 } } > { 0 }
+ { pdf }
+ { dvi }
+ }
+ \__sys_const:nn { sys_if_output_dvi }
+ { \str_if_eq_p:Vn \c_sys_output_str { dvi } }
+ \__sys_const:nn { sys_if_output_pdf }
+ { \str_if_eq_p:Vn \c_sys_output_str { pdf } }
+ }
+\tl_new:N \g__sys_backend_tl
+\__sys_finalise:n
+ {
+ \__kernel_tl_gset:Nx \g__sys_backend_tl
+ {
+ \sys_if_engine_xetex:TF
+ { xetex }
+ {
+ \sys_if_output_pdf:TF
+ {
+ \sys_if_engine_pdftex:TF
+ { pdftex }
+ { luatex }
+ }
+ { dvips }
+ }
+ }
+ }
+\__sys_finalise:n
+ {
+ \cs_if_exist:NT \@classoptionslist
+ {
+ \cs_if_eq:NNF \@classoptionslist \scan_stop:
+ {
+ \clist_map_inline:Nn \@classoptionslist
+ {
+ \str_case:nnT {#1}
+ {
+ { dvipdfmx }
+ { \tl_gset:Nn \g__sys_backend_tl { dvipdfmx } }
+ { dvips }
+ { \tl_gset:Nn \g__sys_backend_tl { dvips } }
+ { dvisvgm }
+ { \tl_gset:Nn \g__sys_backend_tl { dvisvgm } }
+ { pdftex }
+ { \tl_gset:Nn \g__sys_backend_tl { pdfmode } }
+ { xetex }
+ { \tl_gset:Nn \g__sys_backend_tl { xdvipdfmx } }
+ }
+ { \clist_remove_all:Nn \@unusedoptionlist {#1} }
+ }
+ }
+ }
+ }
+%% File: l3clist.dtx
+\cs_new_eq:NN \c_empty_clist \c_empty_tl
+\tl_new:N \l__clist_internal_clist
+\scan_new:N \s__clist_mark
+\scan_new:N \s__clist_stop
+\cs_new:Npn \__clist_use_none_delimit_by_s_mark:w #1 \s__clist_mark { }
+\cs_new:Npn \__clist_use_none_delimit_by_s_stop:w #1 \s__clist_stop { }
+\cs_new:Npn \__clist_use_i_delimit_by_s_stop:nw #1 #2 \s__clist_stop {#1}
+\cs_new_protected:Npn \__clist_tmp:w { }
+\cs_new:Npn \__clist_trim_next:w #1 ,
+ {
+ \tl_if_empty:oTF { \use_none:nn #1 ? }
+ { \__clist_trim_next:w \prg_do_nothing: }
+ { \tl_trim_spaces_apply:oN {#1} \exp_end: }
+ }
+\cs_new:Npn \__clist_sanitize:n #1
+ {
+ \exp_after:wN \__clist_sanitize:Nn \exp_after:wN \c_empty_tl
+ \exp:w \__clist_trim_next:w \prg_do_nothing:
+ #1 , \s__clist_stop \prg_break: , \prg_break_point:
+ }
+\cs_new:Npn \__clist_sanitize:Nn #1#2
+ {
+ \__clist_use_none_delimit_by_s_stop:w #2 \s__clist_stop
+ #1 \__clist_wrap_item:w #2 ,
+ \exp_after:wN \__clist_sanitize:Nn \exp_after:wN ,
+ \exp:w \__clist_trim_next:w \prg_do_nothing:
+ }
+\prg_new_conditional:Npnn \__clist_if_wrap:n #1 { TF }
+ {
+ \tl_if_empty:oTF
+ {
+ \__clist_if_wrap:w
+ \s__clist_mark ? #1 ~ \s__clist_mark ? ~ #1
+ \s__clist_mark , ~ \s__clist_mark #1 ,
+ }
+ {
+ \tl_if_head_is_group:nTF { #1 { } }
+ {
+ \tl_if_empty:nTF {#1}
+ { \prg_return_true: }
+ {
+ \tl_if_empty:oTF { \use_none:n #1}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ }
+ { \prg_return_false: }
+ }
+ { \prg_return_true: }
+ }
+\cs_new:Npn \__clist_if_wrap:w #1 \s__clist_mark ? ~ #2 ~ \s__clist_mark #3 , { }
+\cs_new:Npn \__clist_wrap_item:w #1 ,
+ { \__clist_if_wrap:nTF {#1} { \exp_not:n { {#1} } } { \exp_not:n {#1} } }
+\cs_new_eq:NN \clist_new:N \tl_new:N
+\cs_new_eq:NN \clist_new:c \tl_new:c
+\cs_new_protected:Npn \clist_const:Nn #1#2
+ { \tl_const:Ne #1 { \__clist_sanitize:n {#2} } }
+\cs_generate_variant:Nn \clist_const:Nn { Ne , c , ce }
+\cs_generate_variant:Nn \clist_const:Nn { Nx , cx }
+\cs_new_eq:NN \clist_clear:N \tl_clear:N
+\cs_new_eq:NN \clist_clear:c \tl_clear:c
+\cs_new_eq:NN \clist_gclear:N \tl_gclear:N
+\cs_new_eq:NN \clist_gclear:c \tl_gclear:c
+\cs_new_eq:NN \clist_clear_new:N \tl_clear_new:N
+\cs_new_eq:NN \clist_clear_new:c \tl_clear_new:c
+\cs_new_eq:NN \clist_gclear_new:N \tl_gclear_new:N
+\cs_new_eq:NN \clist_gclear_new:c \tl_gclear_new:c
+\cs_new_eq:NN \clist_set_eq:NN \tl_set_eq:NN
+\cs_new_eq:NN \clist_set_eq:Nc \tl_set_eq:Nc
+\cs_new_eq:NN \clist_set_eq:cN \tl_set_eq:cN
+\cs_new_eq:NN \clist_set_eq:cc \tl_set_eq:cc
+\cs_new_eq:NN \clist_gset_eq:NN \tl_gset_eq:NN
+\cs_new_eq:NN \clist_gset_eq:Nc \tl_gset_eq:Nc
+\cs_new_eq:NN \clist_gset_eq:cN \tl_gset_eq:cN
+\cs_new_eq:NN \clist_gset_eq:cc \tl_gset_eq:cc
+\cs_new_protected:Npn \clist_set_from_seq:NN
+ { \__clist_set_from_seq:NNNN \clist_clear:N \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \clist_gset_from_seq:NN
+ { \__clist_set_from_seq:NNNN \clist_gclear:N \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \__clist_set_from_seq:NNNN #1#2#3#4
+ {
+ \seq_if_empty:NTF #4
+ { #1 #3 }
+ {
+ #2 #3
+ {
+ \exp_after:wN \use_none:n \exp:w \exp_end_continue_f:w
+ \seq_map_function:NN #4 \__clist_set_from_seq:n
+ }
+ }
+ }
+\cs_new:Npn \__clist_set_from_seq:n #1
+ {
+ ,
+ \__clist_if_wrap:nTF {#1}
+ { \exp_not:n { {#1} } }
+ { \exp_not:n {#1} }
+ }
+\cs_generate_variant:Nn \clist_set_from_seq:NN { Nc }
+\cs_generate_variant:Nn \clist_set_from_seq:NN { c , cc }
+\cs_generate_variant:Nn \clist_gset_from_seq:NN { Nc }
+\cs_generate_variant:Nn \clist_gset_from_seq:NN { c , cc }
+\cs_new_protected:Npn \clist_concat:NNN
+ { \__clist_concat:NNNN \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \clist_gconcat:NNN
+ { \__clist_concat:NNNN \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \__clist_concat:NNNN #1#2#3#4
+ {
+ #1 #2
+ {
+ \exp_not:o #3
+ \clist_if_empty:NF #3 { \clist_if_empty:NF #4 { , } }
+ \exp_not:o #4
+ }
+ }
+\cs_generate_variant:Nn \clist_concat:NNN { ccc }
+\cs_generate_variant:Nn \clist_gconcat:NNN { ccc }
+\prg_new_eq_conditional:NNn \clist_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \clist_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+\cs_new_protected:Npn \clist_set:Nn #1#2
+ { \__kernel_tl_set:Nx #1 { \__clist_sanitize:n {#2} } }
+\cs_new_protected:Npn \clist_gset:Nn #1#2
+ { \__kernel_tl_gset:Nx #1 { \__clist_sanitize:n {#2} } }
+\cs_generate_variant:Nn \clist_set:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \clist_set:Nn { No , Nx , co , cx }
+\cs_generate_variant:Nn \clist_gset:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \clist_gset:Nn { No , Nx , co , cx }
+\cs_new_protected:Npn \clist_put_left:Nn
+ { \__clist_put_left:NNNn \clist_concat:NNN \clist_set:Nn }
+\cs_new_protected:Npn \clist_gput_left:Nn
+ { \__clist_put_left:NNNn \clist_gconcat:NNN \clist_set:Nn }
+\cs_new_protected:Npn \__clist_put_left:NNNn #1#2#3#4
+ {
+ #2 \l__clist_internal_clist {#4}
+ #1 #3 \l__clist_internal_clist #3
+ }
+\cs_generate_variant:Nn \clist_put_left:Nn { NV , Nv , Ne , c , cV , cv , ce }
+\cs_generate_variant:Nn \clist_put_left:Nn { No , Nx , co , cx }
+\cs_generate_variant:Nn \clist_gput_left:Nn { NV , Nv , Ne , c , cV , cv , ce }
+\cs_generate_variant:Nn \clist_gput_left:Nn { No , Nx , co , cx }
+\cs_new_protected:Npn \clist_put_right:Nn
+ { \__clist_put_right:NNNn \clist_concat:NNN \clist_set:Nn }
+\cs_new_protected:Npn \clist_gput_right:Nn
+ { \__clist_put_right:NNNn \clist_gconcat:NNN \clist_set:Nn }
+\cs_new_protected:Npn \__clist_put_right:NNNn #1#2#3#4
+ {
+ #2 \l__clist_internal_clist {#4}
+ #1 #3 #3 \l__clist_internal_clist
+ }
+\cs_generate_variant:Nn \clist_put_right:Nn
+ { NV , Nv , Ne , c , cV , cv , ce }
+\cs_generate_variant:Nn \clist_put_right:Nn
+ { No , Nx , co , cx }
+\cs_generate_variant:Nn \clist_gput_right:Nn
+ { NV , Nv , Ne , c , cV , cv , ce }
+\cs_generate_variant:Nn \clist_gput_right:Nn
+ { No , Nx , co , cx }
+\cs_new_protected:Npn \clist_get:NN #1#2
+ {
+ \if_meaning:w #1 \c_empty_clist
+ \tl_set:Nn #2 { \q_no_value }
+ \else:
+ \exp_after:wN \__clist_get:wN #1 , \s__clist_stop #2
+ \fi:
+ }
+\cs_new_protected:Npn \__clist_get:wN #1 , #2 \s__clist_stop #3
+ { \tl_set:Nn #3 {#1} }
+\cs_generate_variant:Nn \clist_get:NN { c }
+\cs_new_protected:Npn \clist_pop:NN
+ { \__clist_pop:NNN \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \clist_gpop:NN
+ { \__clist_pop:NNN \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \__clist_pop:NNN #1#2#3
+ {
+ \if_meaning:w #2 \c_empty_clist
+ \tl_set:Nn #3 { \q_no_value }
+ \else:
+ \exp_after:wN \__clist_pop:wwNNN #2 , \s__clist_mark \s__clist_stop #1#2#3
+ \fi:
+ }
+\cs_new_protected:Npn \__clist_pop:wwNNN #1 , #2 \s__clist_stop #3#4#5
+ {
+ \tl_set:Nn #5 {#1}
+ #3 #4
+ {
+ \__clist_pop:wN \prg_do_nothing:
+ #2 \exp_not:o
+ , \s__clist_mark \use_none:n
+ \s__clist_stop
+ }
+ }
+\cs_new:Npn \__clist_pop:wN #1 , \s__clist_mark #2 #3 \s__clist_stop { #2 {#1} }
+\cs_generate_variant:Nn \clist_pop:NN { c }
+\cs_generate_variant:Nn \clist_gpop:NN { c }
+\prg_new_protected_conditional:Npnn \clist_get:NN #1#2 { T , F , TF }
+ {
+ \if_meaning:w #1 \c_empty_clist
+ \prg_return_false:
+ \else:
+ \exp_after:wN \__clist_get:wN #1 , \s__clist_stop #2
+ \prg_return_true:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \clist_get:NN { c } { T , F , TF }
+\prg_new_protected_conditional:Npnn \clist_pop:NN #1#2 { T , F , TF }
+ { \__clist_pop_TF:NNN \__kernel_tl_set:Nx #1 #2 }
+\prg_new_protected_conditional:Npnn \clist_gpop:NN #1#2 { T , F , TF }
+ { \__clist_pop_TF:NNN \__kernel_tl_gset:Nx #1 #2 }
+\cs_new_protected:Npn \__clist_pop_TF:NNN #1#2#3
+ {
+ \if_meaning:w #2 \c_empty_clist
+ \prg_return_false:
+ \else:
+ \exp_after:wN \__clist_pop:wwNNN #2 , \s__clist_mark \s__clist_stop #1#2#3
+ \prg_return_true:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \clist_pop:NN { c } { T , F , TF }
+\prg_generate_conditional_variant:Nnn \clist_gpop:NN { c } { T , F , TF }
+\cs_new_eq:NN \clist_push:Nn \clist_put_left:Nn
+\cs_generate_variant:Nn \clist_push:Nn { NV , No , Nx , c , cV , co , cx }
+\cs_new_eq:NN \clist_gpush:Nn \clist_gput_left:Nn
+\cs_generate_variant:Nn \clist_gpush:Nn { NV , No , Nx , c , cV , co , cx }
+\clist_new:N \l__clist_internal_remove_clist
+\seq_new:N \l__clist_internal_remove_seq
+\cs_new_protected:Npn \clist_remove_duplicates:N
+ { \__clist_remove_duplicates:NN \clist_set_eq:NN }
+\cs_new_protected:Npn \clist_gremove_duplicates:N
+ { \__clist_remove_duplicates:NN \clist_gset_eq:NN }
+\cs_new_protected:Npn \__clist_remove_duplicates:NN #1#2
+ {
+ \clist_clear:N \l__clist_internal_remove_clist
+ \clist_map_inline:Nn #2
+ {
+ \clist_if_in:NnF \l__clist_internal_remove_clist {##1}
+ {
+ \tl_put_right:Ne \l__clist_internal_remove_clist
+ {
+ \clist_if_empty:NF \l__clist_internal_remove_clist { , }
+ \__clist_if_wrap:nTF {##1} { \exp_not:n { {##1} } } { \exp_not:n {##1} }
+ }
+ }
+ }
+ #1 #2 \l__clist_internal_remove_clist
+ }
+\cs_generate_variant:Nn \clist_remove_duplicates:N { c }
+\cs_generate_variant:Nn \clist_gremove_duplicates:N { c }
+\cs_new_protected:Npn \clist_remove_all:Nn
+ { \__clist_remove_all:NNNn \clist_set_from_seq:NN \__kernel_tl_set:Nx }
+\cs_new_protected:Npn \clist_gremove_all:Nn
+ { \__clist_remove_all:NNNn \clist_gset_from_seq:NN \__kernel_tl_gset:Nx }
+\cs_new_protected:Npn \__clist_remove_all:NNNn #1#2#3#4
+ {
+ \__clist_if_wrap:nTF {#4}
+ {
+ \seq_set_from_clist:NN \l__clist_internal_remove_seq #3
+ \seq_remove_all:Nn \l__clist_internal_remove_seq {#4}
+ #1 #3 \l__clist_internal_remove_seq
+ }
+ {
+ \cs_set:Npn \__clist_tmp:w ##1 , #4 ,
+ {
+ ##1
+ , \s__clist_mark , \__clist_use_none_delimit_by_s_stop:w ,
+ \__clist_remove_all:
+ }
+ #2 #3
+ {
+ \exp_after:wN \__clist_remove_all:
+ #3 , \s__clist_mark , #4 , \s__clist_stop
+ }
+ \clist_if_empty:NF #3
+ {
+ #2 #3
+ {
+ \exp_args:No \exp_not:o
+ { \exp_after:wN \use_none:n #3 }
+ }
+ }
+ }
+ }
+\cs_new:Npn \__clist_remove_all:
+ { \exp_after:wN \__clist_remove_all:w \__clist_tmp:w , }
+\cs_new:Npn \__clist_remove_all:w #1 , \s__clist_mark , #2 , { \exp_not:n {#1} }
+\cs_generate_variant:Nn \clist_remove_all:Nn { c , NV , cV }
+\cs_generate_variant:Nn \clist_gremove_all:Nn { c , NV , cV }
+\cs_new_protected:Npn \clist_reverse:N #1
+ { \__kernel_tl_set:Nx #1 { \exp_args:No \clist_reverse:n {#1} } }
+\cs_new_protected:Npn \clist_greverse:N #1
+ { \__kernel_tl_gset:Nx #1 { \exp_args:No \clist_reverse:n {#1} } }
+\cs_generate_variant:Nn \clist_reverse:N { c }
+\cs_generate_variant:Nn \clist_greverse:N { c }
+\cs_new:Npn \clist_reverse:n #1
+ {
+ \__clist_reverse:wwNww ? #1 ,
+ \s__clist_mark \__clist_reverse:wwNww ! ,
+ \s__clist_mark \__clist_reverse_end:ww
+ \s__clist_stop ? \s__clist_mark
+ }
+\cs_new:Npn \__clist_reverse:wwNww
+ #1 , #2 \s__clist_mark #3 #4 \s__clist_stop ? #5 \s__clist_mark
+ { #3 ? #2 \s__clist_mark #3 #4 \s__clist_stop #1 , #5 \s__clist_mark }
+\cs_new:Npn \__clist_reverse_end:ww #1 ! #2 , \s__clist_mark
+ { \exp_not:o { \use_none:n #2 } }
+\prg_new_eq_conditional:NNn \clist_if_empty:N \tl_if_empty:N
+ { p , T , F , TF }
+\prg_new_eq_conditional:NNn \clist_if_empty:c \tl_if_empty:c
+ { p , T , F , TF }
+\prg_new_conditional:Npnn \clist_if_empty:n #1 { p , T , F , TF }
+ {
+ \__clist_if_empty_n:w ? #1
+ , \s__clist_mark \prg_return_false:
+ , \s__clist_mark \prg_return_true:
+ \s__clist_stop
+ }
+\cs_new:Npn \__clist_if_empty_n:w #1 ,
+ {
+ \tl_if_empty:oTF { \use_none:nn #1 ? }
+ { \__clist_if_empty_n:w ? }
+ { \__clist_if_empty_n:wNw }
+ }
+\cs_new:Npn \__clist_if_empty_n:wNw #1 \s__clist_mark #2#3 \s__clist_stop {#2}
+\prg_new_protected_conditional:Npnn \clist_if_in:Nn #1#2 { T , F , TF }
+ {
+ \exp_args:No \__clist_if_in_return:nnN #1 {#2} #1
+ }
+\prg_new_protected_conditional:Npnn \clist_if_in:nn #1#2 { T , F , TF }
+ {
+ \clist_set:Nn \l__clist_internal_clist {#1}
+ \exp_args:No \__clist_if_in_return:nnN \l__clist_internal_clist {#2}
+ \l__clist_internal_clist
+ }
+\cs_new_protected:Npn \__clist_if_in_return:nnN #1#2#3
+ {
+ \__clist_if_wrap:nTF {#2}
+ {
+ \cs_set:Npe \__clist_tmp:w ##1
+ {
+ \exp_not:N \tl_if_eq:nnT {##1}
+ \exp_not:n
+ {
+ {#2}
+ { \clist_map_break:n { \prg_return_true: \use_none:n } }
+ }
+ }
+ \clist_map_function:NN #3 \__clist_tmp:w
+ \prg_return_false:
+ }
+ {
+ \cs_set:Npn \__clist_tmp:w ##1 ,#2, { }
+ \tl_if_empty:oTF
+ { \__clist_tmp:w ,#1, {} {} ,#2, }
+ { \prg_return_false: } { \prg_return_true: }
+ }
+ }
+\prg_generate_conditional_variant:Nnn \clist_if_in:Nn
+ { NV , No , c , cV , co } { T , F , TF }
+\prg_generate_conditional_variant:Nnn \clist_if_in:nn
+ { nV , no } { T , F , TF }
+\cs_new:Npn \clist_map_function:NN #1#2
+ {
+ \clist_if_empty:NF #1
+ {
+ \exp_after:wN \__clist_map_function:Nw \exp_after:wN #2 #1 ,
+ \s__clist_stop , \s__clist_stop , \s__clist_stop , \s__clist_stop ,
+ \s__clist_stop , \s__clist_stop , \s__clist_stop , \s__clist_stop ,
+ \prg_break_point:Nn \clist_map_break: { }
+ }
+ }
+\cs_new:Npn \__clist_map_function:Nw #1 #2, #3, #4, #5, #6, #7, #8, #9,
+ {
+ \__clist_use_none_delimit_by_s_stop:w
+ #9 \__clist_map_function_end:w \s__clist_stop
+ #1 {#2} #1 {#3} #1 {#4} #1 {#5} #1 {#6} #1 {#7} #1 {#8} #1 {#9}
+ \__clist_map_function:Nw #1
+ }
+\cs_new:Npn \__clist_map_function_end:w \s__clist_stop #1#2
+ {
+ \__clist_use_none_delimit_by_s_stop:w #2 \clist_map_break: \s__clist_stop
+ #1 {#2}
+ \__clist_map_function_end:w \s__clist_stop
+ }
+\cs_generate_variant:Nn \clist_map_function:NN { c }
+\cs_new:Npn \clist_map_function:nN #1#2
+ {
+ \exp_after:wN \__clist_map_function_n:Nn \exp_after:wN #2
+ \exp:w \__clist_trim_next:w \prg_do_nothing: #1 ,
+ \s__clist_stop \clist_map_break: ,
+ \prg_break_point:Nn \clist_map_break: { }
+ }
+\cs_generate_variant:Nn \clist_map_function:nN { e }
+\cs_new:Npn \__clist_map_function_n:Nn #1 #2
+ {
+ \__clist_use_none_delimit_by_s_stop:w #2 \s__clist_stop
+ \__clist_map_unbrace:wn #2 , #1
+ \exp_after:wN \__clist_map_function_n:Nn \exp_after:wN #1
+ \exp:w \__clist_trim_next:w \prg_do_nothing:
+ }
+\cs_new:Npn \__clist_map_unbrace:wn #1, #2 { #2 {#1} }
+\cs_new_protected:Npn \clist_map_inline:Nn #1#2
+ {
+ \clist_if_empty:NF #1
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \cs_gset_protected:cpn
+ { __clist_map_ \int_use:N \g__kernel_prg_map_int :w } ##1 {#2}
+ \exp_last_unbraced:Nco \__clist_map_function:Nw
+ { __clist_map_ \int_use:N \g__kernel_prg_map_int :w }
+ #1 ,
+ \s__clist_stop , \s__clist_stop , \s__clist_stop , \s__clist_stop ,
+ \s__clist_stop , \s__clist_stop , \s__clist_stop , \s__clist_stop ,
+ \prg_break_point:Nn \clist_map_break:
+ { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+ }
+\cs_new_protected:Npn \clist_map_inline:nn #1
+ {
+ \clist_set:Nn \l__clist_internal_clist {#1}
+ \clist_map_inline:Nn \l__clist_internal_clist
+ }
+\cs_generate_variant:Nn \clist_map_inline:Nn { c }
+\cs_new_protected:Npn \clist_map_variable:NNn #1#2#3
+ { \clist_map_tokens:Nn #1 { \__clist_map_variable:Nnn #2 {#3} } }
+\cs_generate_variant:Nn \clist_map_variable:NNn { c }
+\cs_new_protected:Npn \__clist_map_variable:Nnn #1#2#3
+ { \tl_set:Nn #1 {#3} #2 }
+\cs_new_protected:Npn \clist_map_variable:nNn #1
+ {
+ \clist_set:Nn \l__clist_internal_clist {#1}
+ \clist_map_variable:NNn \l__clist_internal_clist
+ }
+\cs_new:Npn \clist_map_tokens:Nn #1#2
+ {
+ \clist_if_empty:NF #1
+ {
+ \exp_last_unbraced:Nno \__clist_map_tokens:nw {#2} #1 ,
+ \s__clist_stop , \s__clist_stop , \s__clist_stop , \s__clist_stop ,
+ \s__clist_stop , \s__clist_stop , \s__clist_stop , \s__clist_stop ,
+ \prg_break_point:Nn \clist_map_break: { }
+ }
+ }
+\cs_new:Npn \__clist_map_tokens:nw #1 #2, #3, #4, #5, #6, #7, #8, #9,
+ {
+ \__clist_use_none_delimit_by_s_stop:w
+ #9 \__clist_map_tokens_end:w \s__clist_stop
+ \use:n {#1} {#2} \use:n {#1} {#3} \use:n {#1} {#4} \use:n {#1} {#5}
+ \use:n {#1} {#6} \use:n {#1} {#7} \use:n {#1} {#8} \use:n {#1} {#9}
+ \__clist_map_tokens:nw {#1}
+ }
+\cs_new:Npn \__clist_map_tokens_end:w \s__clist_stop \use:n #1#2
+ {
+ \__clist_use_none_delimit_by_s_stop:w #2 \clist_map_break: \s__clist_stop
+ #1 {#2}
+ \__clist_map_tokens_end:w \s__clist_stop
+ }
+\cs_generate_variant:Nn \clist_map_tokens:Nn { c }
+\cs_new:Npn \clist_map_tokens:nn #1#2
+ {
+ \__clist_map_tokens_n:nw {#2}
+ \prg_do_nothing: #1 , \s__clist_stop \clist_map_break: ,
+ \prg_break_point:Nn \clist_map_break: { }
+ }
+\cs_new:Npn \__clist_map_tokens_n:nw #1#2 ,
+ {
+ \tl_if_empty:oF { \use_none:nn #2 ? }
+ {
+ \__clist_use_none_delimit_by_s_stop:w #2 \s__clist_stop
+ \tl_trim_spaces_apply:oN {#2} \use_ii_i:nn
+ \__clist_map_unbrace:wn , {#1}
+ }
+ \__clist_map_tokens_n:nw {#1} \prg_do_nothing:
+ }
+\cs_new:Npn \clist_map_break:
+ { \prg_map_break:Nn \clist_map_break: { } }
+\cs_new:Npn \clist_map_break:n
+ { \prg_map_break:Nn \clist_map_break: }
+\cs_new:Npn \clist_count:N #1
+ {
+ \int_eval:n
+ {
+ 0
+ \clist_map_function:NN #1 \__clist_count:n
+ }
+ }
+\cs_generate_variant:Nn \clist_count:N { c }
+\cs_new:Npn \__clist_count:n #1 { + 1 }
+\cs_set_protected:Npn \__clist_tmp:w #1
+ {
+ \cs_new:Npn \clist_count:n ##1
+ {
+ \int_eval:n
+ {
+ 0
+ \__clist_count:w #1
+ ##1 , \s__clist_stop \prg_break: , \prg_break_point:
+ }
+ }
+ \cs_new:Npn \__clist_count:w ##1 ,
+ {
+ \__clist_use_none_delimit_by_s_stop:w ##1 \s__clist_stop
+ \tl_if_blank:nF {##1} { + 1 }
+ \__clist_count:w #1
+ }
+ }
+\exp_args:No \__clist_tmp:w \c_space_tl
+\cs_generate_variant:Nn \clist_count:n { e }
+\cs_new:Npn \clist_use:Nnnn #1#2#3#4
+ {
+ \clist_if_exist:NTF #1
+ {
+ \int_case:nnF { \clist_count:N #1 }
+ {
+ { 0 } { }
+ { 1 } { \exp_after:wN \__clist_use:wwn #1 , , { } }
+ { 2 } { \exp_after:wN \__clist_use:wwn #1 , {#2} }
+ }
+ {
+ \exp_after:wN \__clist_use:nwwwwnwn
+ \exp_after:wN { \exp_after:wN } #1 ,
+ \s__clist_mark , { \__clist_use:nwwwwnwn {#3} }
+ \s__clist_mark , { \__clist_use:nwwn {#4} }
+ \s__clist_stop { }
+ }
+ }
+ {
+ \msg_expandable_error:nnn
+ { kernel } { bad-variable } {#1}
+ }
+ }
+\cs_generate_variant:Nn \clist_use:Nnnn { c }
+\cs_new:Npn \__clist_use:wwn #1 , #2 , #3 { \exp_not:n { #1 #3 #2 } }
+\cs_new:Npn \__clist_use:nwwwwnwn
+ #1#2 , #3 , #4 , #5 \s__clist_mark , #6#7 \s__clist_stop #8
+ { #6 {#3} , {#4} , #5 \s__clist_mark , {#6} #7 \s__clist_stop { #8 #1 #2 } }
+\cs_new:Npn \__clist_use:nwwn #1#2 , #3 \s__clist_stop #4
+ { \exp_not:n { #4 #1 #2 } }
+\cs_new:Npn \clist_use:Nn #1#2
+ { \clist_use:Nnnn #1 {#2} {#2} {#2} }
+\cs_generate_variant:Nn \clist_use:Nn { c }
+\cs_new:Npn \clist_use:nnnn #1#2#3#4
+ {
+ \__clist_use:Nw \__clist_use_none_delimit_by_s_stop:w
+ \__clist_use:Nw \__clist_use_one:w
+ \__clist_use:Nw \__clist_use_end:w
+ \__clist_use_more:w ;
+ {#2} {#3} {#4} ;
+ \prg_do_nothing: #1 , \s__clist_mark ,
+ \s__clist_stop
+ }
+\cs_new:Npn \__clist_use:Nw #1#2 ; #3 ; #4 ,
+ {
+ \tl_if_empty:oTF { \use_none:nn #4 ? }
+ { \__clist_use:Nw #1#2 ; }
+ {
+ \__clist_use_none_delimit_by_s_mark:w #4 #1 \s__clist_mark
+ \tl_trim_spaces_apply:oN {#4} \use_ii_i:nn
+ \__clist_map_unbrace:wn , { #2 ; }
+ }
+ #3 ; \prg_do_nothing:
+ }
+\cs_new:Npn \__clist_use_one:w \s__clist_mark #1 , #2#3#4 \s__clist_stop
+ { \exp_not:n {#3} }
+\cs_new:Npn \__clist_use_end:w
+ \s__clist_mark #1 , #2#3#4#5#6 \s__clist_stop
+ { \exp_not:n { #4 #5 #3 } }
+\cs_new:Npn \__clist_use_more:w ; #1#2#3#4#5#6 ;
+ {
+ \exp_not:n { #3 #5 }
+ \__clist_use:Nw \__clist_use_end:w \__clist_use_more:w ;
+ {#1} {#2} {#6} {#5} {#6} ;
+ }
+\cs_new:Npn \clist_use:nn #1#2 { \clist_use:nnnn {#1} {#2} {#2} {#2} }
+\cs_new:Npn \clist_item:Nn #1#2
+ {
+ \__clist_item:ffoN
+ { \clist_count:N #1 }
+ { \int_eval:n {#2} }
+ #1
+ \__clist_item_N_loop:nw
+ }
+\cs_new:Npn \__clist_item:nnnN #1#2#3#4
+ {
+ \int_compare:nNnTF {#2} < 0
+ {
+ \int_compare:nNnTF {#2} < { - #1 }
+ { \__clist_use_none_delimit_by_s_stop:w }
+ { \exp_args:Nf #4 { \int_eval:n { #2 + 1 + #1 } } }
+ }
+ {
+ \int_compare:nNnTF {#2} > {#1}
+ { \__clist_use_none_delimit_by_s_stop:w }
+ { #4 {#2} }
+ }
+ { } , #3 , \s__clist_stop
+ }
+\cs_generate_variant:Nn \__clist_item:nnnN { ffo, ff }
+\cs_new:Npn \__clist_item_N_loop:nw #1 #2,
+ {
+ \int_compare:nNnTF {#1} = 0
+ { \__clist_use_i_delimit_by_s_stop:nw { \exp_not:n {#2} } }
+ { \exp_args:Nf \__clist_item_N_loop:nw { \int_eval:n { #1 - 1 } } }
+ }
+\cs_generate_variant:Nn \clist_item:Nn { c }
+\cs_new:Npn \clist_item:nn #1#2
+ {
+ \__clist_item:ffnN
+ { \clist_count:n {#1} }
+ { \int_eval:n {#2} }
+ {#1}
+ \__clist_item_n:nw
+ }
+\cs_generate_variant:Nn \clist_item:nn { e }
+\cs_new:Npn \__clist_item_n:nw #1
+ { \__clist_item_n_loop:nw {#1} \prg_do_nothing: }
+\cs_new:Npn \__clist_item_n_loop:nw #1 #2,
+ {
+ \exp_args:No \tl_if_blank:nTF {#2}
+ { \__clist_item_n_loop:nw {#1} \prg_do_nothing: }
+ {
+ \int_compare:nNnTF {#1} = 0
+ { \exp_args:No \__clist_item_n_end:n {#2} }
+ {
+ \exp_args:Nf \__clist_item_n_loop:nw
+ { \int_eval:n { #1 - 1 } }
+ \prg_do_nothing:
+ }
+ }
+ }
+\cs_new:Npn \__clist_item_n_end:n #1 #2 \s__clist_stop
+ { \tl_trim_spaces_apply:nN {#1} \__clist_item_n_strip:n }
+\cs_new:Npn \__clist_item_n_strip:n #1 { \__clist_item_n_strip:w #1 , }
+\cs_new:Npn \__clist_item_n_strip:w #1 , { \exp_not:n {#1} }
+\cs_new:Npn \clist_rand_item:n #1
+ { \exp_args:Nf \__clist_rand_item:nn { \clist_count:n {#1} } {#1} }
+\cs_new:Npn \__clist_rand_item:nn #1#2
+ {
+ \int_compare:nNnF {#1} = 0
+ { \clist_item:nn {#2} { \int_rand:nn { 1 } {#1} } }
+ }
+\cs_new:Npn \clist_rand_item:N #1
+ {
+ \clist_if_empty:NF #1
+ { \clist_item:Nn #1 { \int_rand:nn { 1 } { \clist_count:N #1 } } }
+ }
+\cs_generate_variant:Nn \clist_rand_item:N { c }
+\cs_new_protected:Npn \clist_show:N { \__clist_show:NN \msg_show:nneeee }
+\cs_generate_variant:Nn \clist_show:N { c }
+\cs_new_protected:Npn \clist_log:N { \__clist_show:NN \msg_log:nneeee }
+\cs_generate_variant:Nn \clist_log:N { c }
+\cs_new_protected:Npn \__clist_show:NN #1#2
+ {
+ \__kernel_chk_tl_type:NnnT #2 { clist } { \exp_not:o #2 }
+ {
+ \int_compare:nNnTF { \clist_count:N #2 }
+ = { \exp_args:No \clist_count:n #2 }
+ {
+ #1 { clist } { show }
+ { \token_to_str:N #2 }
+ { \clist_map_function:NN #2 \msg_show_item:n }
+ { } { }
+ }
+ {
+ \msg_error:nnee { clist } { non-clist }
+ { \token_to_str:N #2 } { \tl_to_str:N #2 }
+ }
+ }
+ }
+\cs_new_protected:Npn \clist_show:n { \__clist_show:Nn \msg_show:nneeee }
+\cs_new_protected:Npn \clist_log:n { \__clist_show:Nn \msg_log:nneeee }
+\cs_new_protected:Npn \__clist_show:Nn #1#2
+ {
+ #1 { clist } { show }
+ { } { \clist_map_function:nN {#2} \msg_show_item:n } { } { }
+ }
+\clist_new:N \l_tmpa_clist
+\clist_new:N \l_tmpb_clist
+\clist_new:N \g_tmpa_clist
+\clist_new:N \g_tmpb_clist
+%% File: l3token.dtx
+\scan_new:N \s__char_stop
+\quark_new:N \q__char_no_value
+\__kernel_quark_new_conditional:Nn \__char_quark_if_no_value:N { TF }
+\cs_new_protected:Npn \char_set_catcode:nn #1#2
+ { \tex_catcode:D \int_eval:n {#1} = \int_eval:n {#2} \exp_stop_f: }
+\cs_new:Npn \char_value_catcode:n #1
+ { \tex_the:D \tex_catcode:D \int_eval:n {#1} \exp_stop_f: }
+\cs_new_protected:Npn \char_show_value_catcode:n #1
+ { \exp_args:Nf \tl_show:n { \char_value_catcode:n {#1} } }
+\cs_new_protected:Npn \char_set_catcode_escape:N #1
+ { \char_set_catcode:nn { `#1 } { 0 } }
+\cs_new_protected:Npn \char_set_catcode_group_begin:N #1
+ { \char_set_catcode:nn { `#1 } { 1 } }
+\cs_new_protected:Npn \char_set_catcode_group_end:N #1
+ { \char_set_catcode:nn { `#1 } { 2 } }
+\cs_new_protected:Npn \char_set_catcode_math_toggle:N #1
+ { \char_set_catcode:nn { `#1 } { 3 } }
+\cs_new_protected:Npn \char_set_catcode_alignment:N #1
+ { \char_set_catcode:nn { `#1 } { 4 } }
+\cs_new_protected:Npn \char_set_catcode_end_line:N #1
+ { \char_set_catcode:nn { `#1 } { 5 } }
+\cs_new_protected:Npn \char_set_catcode_parameter:N #1
+ { \char_set_catcode:nn { `#1 } { 6 } }
+\cs_new_protected:Npn \char_set_catcode_math_superscript:N #1
+ { \char_set_catcode:nn { `#1 } { 7 } }
+\cs_new_protected:Npn \char_set_catcode_math_subscript:N #1
+ { \char_set_catcode:nn { `#1 } { 8 } }
+\cs_new_protected:Npn \char_set_catcode_ignore:N #1
+ { \char_set_catcode:nn { `#1 } { 9 } }
+\cs_new_protected:Npn \char_set_catcode_space:N #1
+ { \char_set_catcode:nn { `#1 } { 10 } }
+\cs_new_protected:Npn \char_set_catcode_letter:N #1
+ { \char_set_catcode:nn { `#1 } { 11 } }
+\cs_new_protected:Npn \char_set_catcode_other:N #1
+ { \char_set_catcode:nn { `#1 } { 12 } }
+\cs_new_protected:Npn \char_set_catcode_active:N #1
+ { \char_set_catcode:nn { `#1 } { 13 } }
+\cs_new_protected:Npn \char_set_catcode_comment:N #1
+ { \char_set_catcode:nn { `#1 } { 14 } }
+\cs_new_protected:Npn \char_set_catcode_invalid:N #1
+ { \char_set_catcode:nn { `#1 } { 15 } }
+\cs_new_protected:Npn \char_set_catcode_escape:n #1
+ { \char_set_catcode:nn {#1} { 0 } }
+\cs_new_protected:Npn \char_set_catcode_group_begin:n #1
+ { \char_set_catcode:nn {#1} { 1 } }
+\cs_new_protected:Npn \char_set_catcode_group_end:n #1
+ { \char_set_catcode:nn {#1} { 2 } }
+\cs_new_protected:Npn \char_set_catcode_math_toggle:n #1
+ { \char_set_catcode:nn {#1} { 3 } }
+\cs_new_protected:Npn \char_set_catcode_alignment:n #1
+ { \char_set_catcode:nn {#1} { 4 } }
+\cs_new_protected:Npn \char_set_catcode_end_line:n #1
+ { \char_set_catcode:nn {#1} { 5 } }
+\cs_new_protected:Npn \char_set_catcode_parameter:n #1
+ { \char_set_catcode:nn {#1} { 6 } }
+\cs_new_protected:Npn \char_set_catcode_math_superscript:n #1
+ { \char_set_catcode:nn {#1} { 7 } }
+\cs_new_protected:Npn \char_set_catcode_math_subscript:n #1
+ { \char_set_catcode:nn {#1} { 8 } }
+\cs_new_protected:Npn \char_set_catcode_ignore:n #1
+ { \char_set_catcode:nn {#1} { 9 } }
+\cs_new_protected:Npn \char_set_catcode_space:n #1
+ { \char_set_catcode:nn {#1} { 10 } }
+\cs_new_protected:Npn \char_set_catcode_letter:n #1
+ { \char_set_catcode:nn {#1} { 11 } }
+\cs_new_protected:Npn \char_set_catcode_other:n #1
+ { \char_set_catcode:nn {#1} { 12 } }
+\cs_new_protected:Npn \char_set_catcode_active:n #1
+ { \char_set_catcode:nn {#1} { 13 } }
+\cs_new_protected:Npn \char_set_catcode_comment:n #1
+ { \char_set_catcode:nn {#1} { 14 } }
+\cs_new_protected:Npn \char_set_catcode_invalid:n #1
+ { \char_set_catcode:nn {#1} { 15 } }
+\cs_new_protected:Npn \char_set_mathcode:nn #1#2
+ { \tex_mathcode:D \int_eval:n {#1} = \int_eval:n {#2} \exp_stop_f: }
+\cs_new:Npn \char_value_mathcode:n #1
+ { \tex_the:D \tex_mathcode:D \int_eval:n {#1} \exp_stop_f: }
+\cs_new_protected:Npn \char_show_value_mathcode:n #1
+ { \exp_args:Nf \tl_show:n { \char_value_mathcode:n {#1} } }
+\cs_new_protected:Npn \char_set_lccode:nn #1#2
+ { \tex_lccode:D \int_eval:n {#1} = \int_eval:n {#2} \exp_stop_f: }
+\cs_new:Npn \char_value_lccode:n #1
+ { \tex_the:D \tex_lccode:D \int_eval:n {#1} \exp_stop_f: }
+\cs_new_protected:Npn \char_show_value_lccode:n #1
+ { \exp_args:Nf \tl_show:n { \char_value_lccode:n {#1} } }
+\cs_new_protected:Npn \char_set_uccode:nn #1#2
+ { \tex_uccode:D \int_eval:n {#1} = \int_eval:n {#2} \exp_stop_f: }
+\cs_new:Npn \char_value_uccode:n #1
+ { \tex_the:D \tex_uccode:D \int_eval:n {#1} \exp_stop_f: }
+\cs_new_protected:Npn \char_show_value_uccode:n #1
+ { \exp_args:Nf \tl_show:n { \char_value_uccode:n {#1} } }
+\cs_new_protected:Npn \char_set_sfcode:nn #1#2
+ { \tex_sfcode:D \int_eval:n {#1} = \int_eval:n {#2} \exp_stop_f: }
+\cs_new:Npn \char_value_sfcode:n #1
+ { \tex_the:D \tex_sfcode:D \int_eval:n {#1} \exp_stop_f: }
+\cs_new_protected:Npn \char_show_value_sfcode:n #1
+ { \exp_args:Nf \tl_show:n { \char_value_sfcode:n {#1} } }
+\seq_new:N \l_char_special_seq
+\seq_set_split:Nnn \l_char_special_seq { }
+ { \ \" \# \$ \% \& \\ \^ \_ \{ \} \~ }
+\seq_new:N \l_char_active_seq
+\seq_set_split:Nnn \l_char_active_seq { }
+ { \" \$ \& \^ \_ \~ }
+\group_begin:
+ \char_set_catcode_active:N \^^@
+ \cs_set_protected:Npn \__char_tmp:nN #1#2
+ {
+ \cs_new_protected:cpn { #1 :nN } ##1
+ {
+ \group_begin:
+ \char_set_lccode:nn { `\^^@ } { ##1 }
+ \tex_lowercase:D { \group_end: #2 ^^@ }
+ }
+ \cs_new_protected:cpe { #1 :NN } ##1
+ { \exp_not:c { #1 : nN } { `##1 } }
+ }
+ \__char_tmp:nN { char_set_active_eq } \cs_set_eq:NN
+ \__char_tmp:nN { char_gset_active_eq } \cs_gset_eq:NN
+\group_end:
+\cs_generate_variant:Nn \char_set_active_eq:NN { Nc }
+\cs_generate_variant:Nn \char_gset_active_eq:NN { Nc }
+\cs_generate_variant:Nn \char_set_active_eq:nN { nc }
+\cs_generate_variant:Nn \char_gset_active_eq:nN { nc }
+\cs_new_eq:NN \__char_int_to_roman:w \tex_romannumeral:D
+\cs_new:Npn \char_generate:nn #1#2
+ {
+ \exp:w \exp_after:wN \__char_generate_aux:w
+ \int_value:w \int_eval:n {#1} \exp_after:wN ;
+ \int_value:w \int_eval:n {#2} ;
+ }
+\cs_new:Npn \__char_generate_aux:w #1 ; #2 ;
+ {
+ \if_int_odd:w 0
+ \if_int_compare:w #2 < 1 \exp_stop_f: 1 \fi:
+ \if_int_compare:w #2 = 5 \exp_stop_f: 1 \fi:
+ \if_int_compare:w #2 = 9 \exp_stop_f: 1 \fi:
+ \if_int_compare:w #2 > 13 \exp_stop_f: 1 \fi: \exp_stop_f:
+ \msg_expandable_error:nn { char }
+ { invalid-catcode }
+ \else:
+ \if_int_odd:w 0
+ \if_int_compare:w #1 < \c_zero_int 1 \fi:
+ \if_int_compare:w #1 > \c_max_char_int 1 \fi: \exp_stop_f:
+ \msg_expandable_error:nn { char }
+ { out-of-range }
+ \else:
+ \if_int_compare:w #2#1 = 100 \exp_stop_f:
+ \msg_expandable_error:nn { char } { null-space }
+ \else:
+ \__char_generate_aux:nnw {#1} {#2}
+ \fi:
+ \fi:
+ \fi:
+ \exp_end:
+ }
+\tl_new:N \l__char_tmp_tl
+\group_begin:
+ \char_set_catcode_active:N \^^L
+ \cs_set:Npn ^^L { }
+ \if_cs_exist:N \tex_Ucharcat:D
+ \cs_new:Npn \__char_generate_aux:nnw #1#2#3 \exp_end:
+ {
+ #3
+ \exp_after:wN \exp_end:
+ \tex_Ucharcat:D #1 \exp_stop_f: #2 \exp_stop_f:
+ }
+ \else:
+ \char_set_catcode_active:n { 0 }
+ \tl_set:Nn \l__char_tmp_tl { \exp_not:N ^^@ \exp_not:N \or: }
+ \char_set_catcode_other:n { 0 }
+ \tl_put_right:Nn \l__char_tmp_tl { ^^@ \exp_not:N \or: }
+ \char_set_catcode_letter:n { 0 }
+ \tl_put_right:Nn \l__char_tmp_tl { ^^@ \exp_not:N \or: }
+ \tl_put_right:Nn \l__char_tmp_tl { \use:n { ~ } \exp_not:N \or: }
+ \tl_put_right:Nn \l__char_tmp_tl { \exp_not:N \or: }
+ \char_set_catcode_math_subscript:n { 0 }
+ \tl_put_right:Nn \l__char_tmp_tl { ^^@ \exp_not:N \or: }
+ \char_set_catcode_math_superscript:n { 0 }
+ \tl_put_right:Nn \l__char_tmp_tl { ^^@ \exp_not:N \or: }
+ \char_set_catcode_parameter:n { 0 }
+ \tl_put_right:Nn \l__char_tmp_tl { ^^@ \exp_not:N \or: }
+ \tl_put_right:Nn \l__char_tmp_tl { { \if_false: } \fi: \exp_not:N \or: }
+ \char_set_catcode_alignment:n { 0 }
+ \tl_put_right:Nn \l__char_tmp_tl { ^^@ \exp_not:N \or: }
+ \char_set_catcode_math_toggle:n { 0 }
+ \tl_put_right:Nn \l__char_tmp_tl { ^^@ \exp_not:N \or: }
+ \char_set_catcode_group_end:n { 0 }
+ \tl_put_right:Nn \l__char_tmp_tl { \if_false: { \fi: ^^@ \exp_not:N \or: } % }
+ \char_set_catcode_group_begin:n { 0 } % {
+ \tl_put_right:Nn \l__char_tmp_tl { ^^@ \exp_not:N \or: } }
+ \cs_set_protected:Npn \__char_tmp:n #1
+ {
+ \char_set_lccode:nn { 0 } {#1}
+ \char_set_lccode:nn { 32 } {#1}
+ \exp_args:Ne \tex_lowercase:D
+ {
+ \tl_const:Ne
+ \exp_not:c { c__char_ \__char_int_to_roman:w #1 _tl }
+ { \exp_not:o \l__char_tmp_tl }
+ }
+ }
+ \int_step_function:nnN { 0 } { 255 } \__char_tmp:n
+ \cs_new:Npn \__char_generate_aux:nnw #1#2#3 \exp_end:
+ {
+ #3
+ \if_false: { \fi:
+ \exp_after:wN \exp_after:wN \exp_after:wN \exp_end:
+ \exp_after:wN \exp_after:wN
+ \if_case:w \tex_numexpr:D 13 - #2
+ \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_after:wN \exp_after:wN \exp_after:wN \scan_stop:
+ \exp_after:wN \exp_after:wN \exp_after:wN \exp_not:N
+ \cs:w c__char_ \__char_int_to_roman:w #1 _tl \cs_end:
+ }
+ \fi:
+ }
+ \fi:
+\group_end:
+\group_begin:
+ \char_set_catcode_active:N *
+ \char_set_lccode:nn { `* } { `\ }
+ \tex_lowercase:D { \tl_const:Nn \c_catcode_active_space_tl { * } }
+\group_end:
+\tl_const:Ne \c_catcode_other_space_tl { \char_generate:nn { `\ } { 12 } }
+\scan_new:N \s__token_mark
+\scan_new:N \s__token_stop
+\cs_new:Npn \token_to_catcode:N
+ { \int_value:w \group_align_safe_begin: \__token_to_catcode:N }
+\cs_new:Npn \__token_to_catcode:N #1
+ {
+ \if_catcode:w \exp_not:N #1 \c_catcode_letter_token
+ 11
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_catcode_other_token
+ 12
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_math_toggle_token
+ 3
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_alignment_token
+ 4
+ \else:
+ \if_catcode:w \exp_not:N #1 ##
+ 6
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_math_superscript_token
+ 7
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_math_subscript_token
+ 8
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_group_begin_token
+ 1
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_group_end_token
+ 2
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_space_token
+ 10
+ \else:
+ \token_if_cs:NTF #1 { 16 } { 13 }
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \group_align_safe_end:
+ \exp_stop_f:
+ }
+\group_begin:
+ \__kernel_chk_if_free_cs:N \c_group_begin_token
+ \tex_global:D \tex_let:D \c_group_begin_token {
+ \__kernel_chk_if_free_cs:N \c_group_end_token
+ \tex_global:D \tex_let:D \c_group_end_token }
+ \char_set_catcode_math_toggle:N \*
+ \cs_new_eq:NN \c_math_toggle_token *
+ \char_set_catcode_alignment:N \*
+ \cs_new_eq:NN \c_alignment_token *
+ \cs_new_eq:NN \c_parameter_token #
+ \cs_new_eq:NN \c_math_superscript_token ^
+ \char_set_catcode_math_subscript:N \*
+ \cs_new_eq:NN \c_math_subscript_token *
+ \__kernel_chk_if_free_cs:N \c_space_token
+ \use:n { \tex_global:D \tex_let:D \c_space_token = ~ } ~
+ \cs_new_eq:NN \c_catcode_letter_token a
+ \cs_new_eq:NN \c_catcode_other_token 1
+\group_end:
+\group_begin:
+ \char_set_catcode_active:N \*
+ \tl_const:Nn \c_catcode_active_tl { \exp_not:N * }
+\group_end:
+\prg_new_conditional:Npnn \token_if_group_begin:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_group_begin_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_new_conditional:Npnn \token_if_group_end:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_group_end_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_new_conditional:Npnn \token_if_math_toggle:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_math_toggle_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_new_conditional:Npnn \token_if_alignment:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_alignment_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\group_begin:
+\cs_set_eq:NN \c_parameter_token \scan_stop:
+\prg_new_conditional:Npnn \token_if_parameter:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_parameter_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\group_end:
+\prg_new_conditional:Npnn \token_if_math_superscript:N #1
+ { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_math_superscript_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_new_conditional:Npnn \token_if_math_subscript:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_math_subscript_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_new_conditional:Npnn \token_if_space:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_space_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_new_conditional:Npnn \token_if_letter:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_catcode_letter_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_new_conditional:Npnn \token_if_other:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_catcode_other_token
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_new_conditional:Npnn \token_if_active:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \c_catcode_active_tl
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_new_eq_conditional:NNn \token_if_eq_meaning:NN \cs_if_eq:NN
+ { p , T , F , TF }
+\prg_new_conditional:Npnn \token_if_eq_catcode:NN #1#2 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \exp_not:N #2
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_new_conditional:Npnn \token_if_eq_charcode:NN #1#2 { p , T , F , TF }
+ {
+ \if_charcode:w \exp_not:N #1 \exp_not:N #2
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\use:e
+ {
+ \prg_new_conditional:Npnn \exp_not:N \token_if_macro:N #1
+ { p , T , F , TF }
+ {
+ \exp_not:N \exp_after:wN \exp_not:N \__token_if_macro_p:w
+ \exp_not:N \token_to_meaning:N #1 \tl_to_str:n { ma : }
+ \s__token_stop
+ }
+ \cs_new:Npn \exp_not:N \__token_if_macro_p:w
+ #1 \tl_to_str:n { ma } #2 \c_colon_str #3 \s__token_stop
+ }
+ {
+ \str_if_eq:nnTF { #2 } { cro }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\prg_new_conditional:Npnn \token_if_cs:N #1 { p , T , F , TF }
+ {
+ \if_catcode:w \exp_not:N #1 \scan_stop:
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_new_conditional:Npnn \token_if_expandable:N #1 { p , T , F , TF }
+ {
+ \exp_after:wN \if_meaning:w \exp_not:N #1 #1
+ \prg_return_false:
+ \else:
+ \if_cs_exist:N #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ }
+\group_begin:
+\cs_set_protected:Npn \__token_tmp:w #1
+ {
+ \use:e
+ {
+ \cs_new:Npn \exp_not:c { __token_delimit_by_ #1 :w }
+ ##1 \tl_to_str:n {#1} ##2 \s__token_stop
+ { ##1 \tl_to_str:n {#1} }
+ }
+ }
+\__token_tmp:w { char" }
+\__token_tmp:w { count }
+\__token_tmp:w { dimen }
+\__token_tmp:w { ~ font }
+\__token_tmp:w { macro }
+\__token_tmp:w { muskip }
+\__token_tmp:w { skip }
+\__token_tmp:w { toks }
+\group_end:
+\group_begin:
+\cs_set_protected:Npn \__token_tmp:w #1#2#3
+ {
+ \use:e
+ {
+ \prg_new_conditional:Npnn \exp_not:c { token_if_ #1 :N } ##1
+ { p , T , F , TF }
+ {
+ \cs_if_exist:cT { tex_ #2 :D }
+ {
+ \exp_not:N \if_meaning:w ##1 \exp_not:c { tex_ #2 :D }
+ \exp_not:N \prg_return_false:
+ \exp_not:N \else:
+ \exp_not:N \if_meaning:w ##1 \exp_not:c { tex_ #2 def:D }
+ \exp_not:N \prg_return_false:
+ \exp_not:N \else:
+ }
+ \exp_not:N \str_if_eq:eeTF
+ {
+ \exp_not:N \exp_after:wN
+ \exp_not:c { __token_delimit_by_ #2 :w }
+ \exp_not:N \token_to_meaning:N ##1
+ ? \tl_to_str:n {#2} \s__token_stop
+ }
+ { \exp_not:n {#3} }
+ { \exp_not:N \prg_return_true: }
+ { \exp_not:N \prg_return_false: }
+ \cs_if_exist:cT { tex_ #2 :D }
+ {
+ \exp_not:N \fi:
+ \exp_not:N \fi:
+ }
+ }
+ }
+ }
+\__token_tmp:w { chardef } { char" } { \token_to_str:N \char" }
+\__token_tmp:w { mathchardef } { char" } { \token_to_str:N \mathchar" }
+\__token_tmp:w { long_macro } { macro } { \tl_to_str:n { \long } macro }
+\__token_tmp:w { protected_macro } { macro }
+ { \tl_to_str:n { \protected } macro }
+\__token_tmp:w { protected_long_macro } { macro }
+ { \token_to_str:N \protected \tl_to_str:n { \long } macro }
+\__token_tmp:w { font_selection } { ~ font } { select ~ font }
+\__token_tmp:w { dim_register } { dimen } { \token_to_str:N \dimen }
+\__token_tmp:w { int_register } { count } { \token_to_str:N \count }
+\__token_tmp:w { muskip_register } { muskip } { \token_to_str:N \muskip }
+\__token_tmp:w { skip_register } { skip } { \token_to_str:N \skip }
+\__token_tmp:w { toks_register } { toks } { \token_to_str:N \toks }
+\group_end:
+\sys_if_engine_luatex:TF
+ {
+ \prg_new_conditional:Npnn \token_if_primitive:N #1 { p , T , F , TF }
+ {
+ \__token_if_primitive_lua:N #1
+ }
+ }
+ {
+ \tex_global:D \tex_chardef:D \c__token_A_int = `A ~ %
+ \use:e
+ {
+ \prg_new_conditional:Npnn \exp_not:N \token_if_primitive:N #1
+ { p , T , F , TF }
+ {
+ \exp_not:N \token_if_macro:NTF #1
+ \exp_not:N \prg_return_false:
+ {
+ \exp_not:N \exp_after:wN \exp_not:N \__token_if_primitive:NNw
+ \exp_not:N \token_to_meaning:N #1
+ \tl_to_str:n { : : : } \s__token_stop #1
+ }
+ }
+ \cs_new:Npn \exp_not:N \__token_if_primitive:NNw
+ #1#2 #3 \c_colon_str #4 \s__token_stop
+ {
+ \exp_not:N \tl_if_empty:oTF
+ { \exp_not:N \__token_if_primitive_space:w #3 ~ }
+ {
+ \exp_not:N \__token_if_primitive_loop:N #3
+ \c_colon_str \s__token_stop
+ }
+ { \exp_not:N \__token_if_primitive_nullfont:N }
+ }
+ }
+ \cs_new:Npn \__token_if_primitive_space:w #1 ~ { }
+ \cs_new:Npn \__token_if_primitive_nullfont:N #1
+ {
+ \if_meaning:w \tex_nullfont:D #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ \cs_new:Npn \__token_if_primitive_loop:N #1
+ {
+ \if_int_compare:w `#1 < \c__token_A_int %
+ \exp_after:wN \__token_if_primitive:Nw
+ \exp_after:wN #1
+ \else:
+ \exp_after:wN \__token_if_primitive_loop:N
+ \fi:
+ }
+ \cs_new:Npn \__token_if_primitive:Nw #1 #2 \s__token_stop
+ {
+ \if:w : #1
+ \exp_after:wN \__token_if_primitive_undefined:N
+ \else:
+ \prg_return_false:
+ \exp_after:wN \use_none:n
+ \fi:
+ }
+ \cs_new:Npn \__token_if_primitive_undefined:N #1
+ {
+ \if_cs_exist:N #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ }
+\cs_new:Npn \token_case_catcode:Nn #1#2
+ { \exp:w \__token_case:NNnTF \token_if_eq_catcode:NNTF #1 {#2} { } { } }
+\cs_new:Npn \token_case_catcode:NnT #1#2#3
+ { \exp:w \__token_case:NNnTF \token_if_eq_catcode:NNTF #1 {#2} {#3} { } }
+\cs_new:Npn \token_case_catcode:NnF #1#2
+ { \exp:w \__token_case:NNnTF \token_if_eq_catcode:NNTF #1 {#2} { } }
+\cs_new:Npn \token_case_catcode:NnTF
+ { \exp:w \__token_case:NNnTF \token_if_eq_catcode:NNTF }
+\cs_new:Npn \token_case_charcode:Nn #1#2
+ { \exp:w \__token_case:NNnTF \token_if_eq_charcode:NNTF #1 {#2} { } { } }
+\cs_new:Npn \token_case_charcode:NnT #1#2#3
+ { \exp:w \__token_case:NNnTF \token_if_eq_charcode:NNTF #1 {#2} {#3} { } }
+\cs_new:Npn \token_case_charcode:NnF #1#2
+ { \exp:w \__token_case:NNnTF \token_if_eq_charcode:NNTF #1 {#2} { } }
+\cs_new:Npn \token_case_charcode:NnTF
+ { \exp:w \__token_case:NNnTF \token_if_eq_charcode:NNTF }
+\cs_new:Npn \token_case_meaning:Nn #1#2
+ { \exp:w \__token_case:NNnTF \token_if_eq_meaning:NNTF #1 {#2} { } { } }
+\cs_new:Npn \token_case_meaning:NnT #1#2#3
+ { \exp:w \__token_case:NNnTF \token_if_eq_meaning:NNTF #1 {#2} {#3} { } }
+\cs_new:Npn \token_case_meaning:NnF #1#2
+ { \exp:w \__token_case:NNnTF \token_if_eq_meaning:NNTF #1 {#2} { } }
+\cs_new:Npn \token_case_meaning:NnTF
+ { \exp:w \__token_case:NNnTF \token_if_eq_meaning:NNTF }
+\cs_new:Npn \__token_case:NNnTF #1#2#3#4#5
+ {
+ \__token_case:NNw #1 #2 #3 #2 { }
+ \s__token_mark {#4}
+ \s__token_mark {#5}
+ \s__token_stop
+ }
+\cs_new:Npn \__token_case:NNw #1#2#3#4
+ {
+ #1 #2 #3
+ { \__token_case_end:nw {#4} }
+ { \__token_case:NNw #1 #2 }
+ }
+\cs_new:Npn \__token_case_end:nw #1#2#3 \s__token_mark #4#5 \s__token_stop
+ { \exp_end: #1 #4 }
+\cs_new_eq:NN \l_peek_token ?
+\cs_new_eq:NN \g_peek_token ?
+\cs_new_eq:NN \l__peek_search_token ?
+\tl_new:N \l__peek_search_tl
+\cs_new:Npn \__peek_true:w { }
+\cs_new:Npn \__peek_true_aux:w { }
+\cs_new:Npn \__peek_false:w { }
+\cs_new:Npn \__peek_tmp:w { }
+\scan_new:N \s__peek_mark
+\scan_new:N \s__peek_stop
+\cs_new:Npn \__peek_use_none_delimit_by_s_stop:w #1 \s__peek_stop { }
+\cs_new_protected:Npn \peek_after:Nw
+ { \tex_futurelet:D \l_peek_token }
+\cs_new_protected:Npn \peek_gafter:Nw
+ { \tex_global:D \tex_futurelet:D \g_peek_token }
+\cs_new_protected:Npn \__peek_true_remove:w
+ {
+ \tex_afterassignment:D \__peek_true_aux:w
+ \cs_set_eq:NN \__peek_tmp:w
+ }
+\cs_new_protected:Npn \peek_remove_spaces:n #1
+ {
+ \cs_set:Npe \__peek_false:w { \exp_not:n {#1} }
+ \group_align_safe_begin:
+ \cs_set:Npn \__peek_true_aux:w { \peek_after:Nw \__peek_remove_spaces: }
+ \__peek_true_aux:w
+ }
+\cs_new_protected:Npn \__peek_remove_spaces:
+ {
+ \if_meaning:w \l_peek_token \c_space_token
+ \exp_after:wN \__peek_true_remove:w
+ \else:
+ \group_align_safe_end:
+ \exp_after:wN \__peek_false:w
+ \fi:
+ }
+\cs_new_protected:Npn \peek_remove_filler:n #1
+ {
+ \cs_set:Npn \__peek_true_aux:w { \__peek_remove_filler:w }
+ \cs_set:Npe \__peek_false:w
+ {
+ \exp_not:N \group_align_safe_end:
+ \exp_not:n {#1}
+ }
+ \group_align_safe_begin:
+ \__peek_remove_filler:w
+ }
+\cs_new_protected:Npn \__peek_remove_filler:w
+ {
+ \exp_after:wN \peek_after:Nw \exp_after:wN \__peek_remove_filler:
+ \exp:w \exp_end_continue_f:w
+ }
+\cs_new_protected:Npn \__peek_remove_filler:
+ {
+ \if_catcode:w \exp_not:N \l_peek_token \c_space_token
+ \exp_after:wN \__peek_true_remove:w
+ \else:
+ \if_meaning:w \l_peek_token \scan_stop:
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \__peek_true_remove:w
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \__peek_remove_filler_expand:w
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \__peek_remove_filler_expand:w
+ {
+ \exp_after:wN \if_meaning:w \exp_not:N \l_peek_token \l_peek_token
+ \exp_after:wN \__peek_false:w
+ \else:
+ \exp_after:wN \__peek_remove_filler:w
+ \fi:
+ }
+\cs_new_protected:Npn \__peek_token_generic_aux:NNNTF #1#2#3#4#5
+ {
+ \group_align_safe_begin:
+ \cs_set_eq:NN \l__peek_search_token #3
+ \tl_set:Nn \l__peek_search_tl {#3}
+ \cs_set:Npe \__peek_true_aux:w
+ {
+ \exp_not:N \group_align_safe_end:
+ \exp_not:n {#4}
+ }
+ \cs_set_eq:NN \__peek_true:w #1
+ \cs_set:Npe \__peek_false:w
+ {
+ \exp_not:N \group_align_safe_end:
+ \exp_not:n {#5}
+ }
+ \peek_after:Nw #2
+ }
+\cs_new_protected:Npn \__peek_token_generic:NNTF
+ { \__peek_token_generic_aux:NNNTF \__peek_true_aux:w }
+\cs_new_protected:Npn \__peek_token_generic:NNT #1#2#3
+ { \__peek_token_generic:NNTF #1 #2 {#3} { } }
+\cs_new_protected:Npn \__peek_token_generic:NNF #1#2#3
+ { \__peek_token_generic:NNTF #1 #2 { } {#3} }
+\cs_new_protected:Npn \__peek_token_remove_generic:NNTF
+ { \__peek_token_generic_aux:NNNTF \__peek_true_remove:w }
+\cs_new_protected:Npn \__peek_token_remove_generic:NNT #1#2#3
+ { \__peek_token_remove_generic:NNTF #1 #2 {#3} { } }
+\cs_new_protected:Npn \__peek_token_remove_generic:NNF #1#2#3
+ { \__peek_token_remove_generic:NNTF #1 #2 { } {#3} }
+\cs_new:Npn \__peek_execute_branches_meaning:
+ {
+ \if_meaning:w \l_peek_token \l__peek_search_token
+ \exp_after:wN \__peek_true:w
+ \else:
+ \exp_after:wN \__peek_false:w
+ \fi:
+ }
+\cs_new:Npn \__peek_execute_branches_catcode:
+ { \if_catcode:w \__peek_execute_branches_catcode_aux: }
+\cs_new:Npn \__peek_execute_branches_charcode:
+ { \if_charcode:w \__peek_execute_branches_catcode_aux: }
+\cs_new:Npn \__peek_execute_branches_catcode_aux:
+ {
+ \if_catcode:w \exp_not:N \l_peek_token \scan_stop:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__peek_execute_branches_catcode_auxii:N
+ \exp_after:wN \exp_not:N
+ \else:
+ \exp_after:wN \__peek_execute_branches_catcode_auxiii:
+ \fi:
+ }
+\cs_new:Npn \__peek_execute_branches_catcode_auxii:N #1
+ {
+ \exp_not:N #1
+ \exp_after:wN \exp_not:N \l__peek_search_tl
+ \exp_after:wN \__peek_true:w
+ \else:
+ \exp_after:wN \__peek_false:w
+ \fi:
+ #1
+ }
+\cs_new:Npn \__peek_execute_branches_catcode_auxiii:
+ {
+ \exp_not:N \l_peek_token
+ \exp_after:wN \exp_not:N \l__peek_search_tl
+ \exp_after:wN \__peek_true:w
+ \else:
+ \exp_after:wN \__peek_false:w
+ \fi:
+ }
+\tl_map_inline:nn { { catcode } { charcode } { meaning } }
+ {
+ \tl_map_inline:nn { { } { _remove } }
+ {
+ \tl_map_inline:nn { { TF } { T } { F } }
+ {
+ \cs_new_protected:cpe { peek_ #1 ##1 :N ####1 }
+ {
+ \exp_not:c { __peek_token ##1 _generic:NN ####1 }
+ \exp_not:c { __peek_execute_branches_ #1 : }
+ }
+ }
+ }
+ }
+\group_begin:
+ \cs_set_protected:Npn \__peek_tmp:w #1 \s__peek_stop
+ {
+ \cs_new_protected:Npn \__peek_execute_branches_N_type:
+ {
+ \if_int_odd:w
+ \if_catcode:w \exp_not:N \l_peek_token { \c_zero_int \fi:
+ \if_catcode:w \exp_not:N \l_peek_token } \c_zero_int \fi:
+ \if_meaning:w \l_peek_token \c_space_token \c_zero_int \fi:
+ \c_one_int
+ \exp_after:wN \__peek_N_type:w
+ \token_to_meaning:N \l_peek_token
+ \s__peek_mark \__peek_N_type_aux:nnw
+ #1 \s__peek_mark \__peek_use_none_delimit_by_s_stop:w
+ \s__peek_stop
+ \exp_after:wN \__peek_true:w
+ \else:
+ \exp_after:wN \__peek_false:w
+ \fi:
+ }
+ \cs_new_protected:Npn \__peek_N_type:w ##1 #1 ##2 \s__peek_mark ##3
+ { ##3 {##1} {##2} }
+ }
+ \exp_after:wN \__peek_tmp:w \tl_to_str:n { outer } \s__peek_stop
+\group_end:
+\cs_new_protected:Npn \__peek_N_type_aux:nnw #1 #2 #3 \fi:
+ {
+ \fi:
+ \tl_if_in:noTF {#1} { \tl_to_str:n {ma} }
+ { \__peek_true:w }
+ { \tl_if_empty:nTF {#2} { \__peek_true:w } { \__peek_false:w } }
+ }
+\cs_new_protected:Npn \peek_N_type:TF
+ {
+ \__peek_token_generic:NNTF
+ \__peek_execute_branches_N_type: \scan_stop:
+ }
+\cs_new_protected:Npn \peek_N_type:T
+ { \__peek_token_generic:NNT \__peek_execute_branches_N_type: \scan_stop: }
+\cs_new_protected:Npn \peek_N_type:F
+ { \__peek_token_generic:NNF \__peek_execute_branches_N_type: \scan_stop: }
+%% File: l3prop.dtx
+\cs_new_eq:NN \__prop_tmp:w ?
+\tl_new:N \l__prop_internal_tl
+\scan_new:N \s__prop_mark
+\scan_new:N \s__prop_stop
+\quark_new:N \q__prop_recursion_tail
+\quark_new:N \q__prop_recursion_stop
+\__kernel_quark_new_test:N \__prop_if_recursion_tail_stop:n
+\cs_generate_variant:Nn \__prop_if_recursion_tail_stop:n { o }
+\scan_new:N \s__prop
+\cs_new_protected:Npn \__prop_chk:w { \__prop_chk_loop:nw { } }
+\cs_new_protected:Npn \__prop_chk_loop:nw #1
+ {
+ \peek_meaning:NTF \__prop_pair:wn
+ { \__prop_chk_get:nw {#1} }
+ { \msg_error:nne { prop } { misused } {#1} }
+ }
+\cs_new_protected:Npn \__prop_chk_get:nw #1 \__prop_pair:wn #2 \s__prop #3
+ { \__prop_chk_loop:nw { #1 , ~ {#2} = { \tl_to_str:n {#3} } } }
+\cs_new:Npn \__prop_pair:wn #1 \s__prop #2 { }
+\cs_new_protected:Npn \__prop_flatten:w #1 \s__prop #2#3
+ { \use:e { \__prop_flatten_aux:N #3 } }
+\cs_new:Npn \__prop_flatten:N #1
+ { \exp_after:wN \__prop_flatten_aux:w #1 }
+\cs_new:Npn \__prop_flatten_aux:w #1 \s__prop #2 { \__prop_flatten_aux:N }
+\cs_new:Npn \__prop_flatten_aux:N #1
+ {
+ \s__prop \__prop_chk:w
+ \exp_after:wN \__prop_flatten_loop:w #1
+ \use_none:nnnn \__prop_pair:wn \s__prop { }
+ }
+\cs_new:Npn \__prop_flatten_loop:w #1#2#3 \__prop_pair:wn #4 \s__prop #5
+ {
+ #3
+ \exp_not:n { \__prop_pair:wn #4 \s__prop {#5} }
+ \exp_after:wN \__prop_flatten_loop:w
+ }
+\int_new:N \g__prop_prefix_int
+\int_const:Nn \c__prop_basis_int { \c_max_char_int - `\! }
+\cs_new_protected:Npn \__prop_next_prefix:
+ {
+ \tl_set:Ne \l__prop_internal_tl
+ { \__prop_to_prefix:n { \g__prop_prefix_int } }
+ \int_gincr:N \g__prop_prefix_int
+ }
+\cs_new:Npn \__prop_to_prefix:n #1
+ {
+ \int_compare:nNnTF {#1} > \c__prop_basis_int
+ {
+ \exp_args:Nf \__prop_to_prefix:n
+ { \int_div_truncate:nn {#1} \c__prop_basis_int }
+ \exp_args:Nf \__prop_to_prefix:n
+ { \int_mod:nn {#1} \c__prop_basis_int }
+ }
+ { \char_generate:nn { `\! + #1 } { 12 } }
+ }
+\cs_new:Npn \__prop_if_flat:NTF #1
+ {
+ \prop_if_exist:NT #1
+ \exp_after:wN \__prop_if_flat_aux:w #1
+ \s__prop_mark \use_ii:nn
+ \__prop_flatten:w \s__prop_mark \use_i:nn \s__prop_stop
+ }
+\cs_new:Npn \__prop_if_flat_aux:w
+ #1 \__prop_flatten:w #2 \s__prop_mark #3 #4 \s__prop_stop {#3}
+\tl_const:Nn \c_empty_prop { \s__prop \__prop_chk:w }
+\cs_new_protected:Npn \prop_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_eq:NN #1 \c_empty_prop
+ }
+\cs_generate_variant:Nn \prop_new:N { c }
+\cs_new_protected:Npn \prop_new_linked:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \__prop_new_linked:N #1
+ }
+\cs_new_protected:Npn \__prop_new_linked:N #1
+ {
+ \__prop_next_prefix:
+ \cs_gset_nopar:Npe #1
+ {
+ \__prop_flatten:w
+ \exp_not:c { __prop ~ \l__prop_internal_tl }
+ \s__prop { \l__prop_internal_tl }
+ \exp_not:c { __prop ~ \l__prop_internal_tl }
+ }
+ \cs_gset_nopar:cpe { __prop ~ \l__prop_internal_tl }
+ {
+ \exp_not:N \use_none:n
+ \exp_not:N #1
+ }
+ }
+\cs_generate_variant:Nn \prop_new_linked:N { c }
+\cs_new_protected:Npn \prop_clear:N
+ { \__prop_clear:NNN \cs_set_eq:NN \cs_set_nopar:Npe }
+\cs_generate_variant:Nn \prop_clear:N { c }
+\cs_new_protected:Npn \prop_gclear:N
+ { \__prop_clear:NNN \cs_gset_eq:NN \cs_gset_nopar:Npe }
+\cs_generate_variant:Nn \prop_gclear:N { c }
+\cs_new_protected:Npn \__prop_clear:NNN #1#2#3
+ {
+ \__prop_if_flat:NTF #3
+ { #1 #3 \c_empty_prop }
+ { \exp_after:wN \__prop_clear:wNNN #3 #1 #2 #3 }
+ }
+\cs_new_protected:Npn \__prop_clear:wNNN
+ \__prop_flatten:w #1 \s__prop #2#3#4#5#6
+ {
+ \__prop_clear_entries:NN #4 #3
+ #5 #6 { \exp_not:n { \__prop_flatten:w #1 \s__prop {#2} #1 } }
+ #5 #1 { \exp_not:n { \use_none:n #6 } }
+ }
+\cs_new_protected:Npn \__prop_clear_entries:NN #1#2
+ {
+ \exp_after:wN \__prop_clear_loop:Nw \exp_after:wN #1 #2
+ \use_none:nnnn \__prop_pair:wn \s__prop { }
+ }
+\cs_new_protected:Npn \__prop_clear_loop:Nw
+ #1#2#3#4 \__prop_pair:wn #5 \s__prop #6
+ {
+ #1 #3 \tex_undefined:D
+ #4
+ \exp_after:wN \__prop_clear_loop:Nw
+ \exp_after:wN #1
+ }
+\cs_new_protected:Npn \prop_clear_new:N #1
+ { \prop_if_exist:NTF #1 { \prop_clear:N #1 } { \prop_new:N #1 } }
+\cs_generate_variant:Nn \prop_clear_new:N { c }
+\cs_new_protected:Npn \prop_gclear_new:N #1
+ { \prop_if_exist:NTF #1 { \prop_gclear:N #1 } { \prop_new:N #1 } }
+\cs_generate_variant:Nn \prop_gclear_new:N { c }
+\cs_new_protected:Npn \prop_clear_new_linked:N #1
+ { \prop_if_exist:NTF #1 { \prop_clear:N #1 } { \prop_new_linked:N #1 } }
+\cs_generate_variant:Nn \prop_clear_new_linked:N { c }
+\cs_new_protected:Npn \prop_gclear_new_linked:N #1
+ { \prop_if_exist:NTF #1 { \prop_gclear:N #1 } { \prop_new_linked:N #1 } }
+\cs_generate_variant:Nn \prop_gclear_new_linked:N { c }
+\cs_new_protected:Npn \prop_set_eq:NN
+ { \__prop_set_eq:NNNN \cs_set_eq:NN \cs_set_nopar:Npe }
+\cs_generate_variant:Nn \prop_set_eq:NN { Nc , cN , cc }
+\cs_new_protected:Npn \prop_gset_eq:NN
+ { \__prop_set_eq:NNNN \cs_gset_eq:NN \cs_gset_nopar:Npe }
+\cs_generate_variant:Nn \prop_gset_eq:NN { Nc , cN , cc }
+\cs_new_protected:Npn \__prop_set_eq:NNNN #1#2#3#4
+ {
+ \cs_if_eq:NNF #3#4
+ {
+ \__prop_if_flat:NTF #3
+ {
+ \__prop_if_flat:NTF #4
+ { #1 #3 #4 }
+ { #2 #3 { \__prop_flatten:N #4 } }
+ }
+ { \exp_after:wN \__prop_set_eq:wNNNN #3 #1#2#3#4 }
+ }
+ }
+\cs_new_protected:Npn \__prop_set_eq:wNNNN
+ \__prop_flatten:w #1 \s__prop #2#3#4#5#6#7
+ {
+ \__prop_clear_entries:NN #4 #3
+ \exp_args:Nf \__prop_set_eq:nNnNN {#7} #1 {#2} #5 #6
+ }
+\cs_new_protected:Npn \__prop_set_eq:nNnNN #1#2#3#4#5
+ {
+ \use_i:nnn
+ {
+ \__prop_set_eq_loop:NNnw #5 #4 {#3}
+ \__prop_flatten:w #2 \s__prop {#3}
+ }
+ #1
+ \use_none:n \__prop_pair:wn ? \s__prop
+ }
+\cs_new_protected:Npn \__prop_set_eq_loop:NNnw
+ #1#2#3#4 \s__prop #5#6 \__prop_pair:wn #7 \s__prop
+ {
+ \tl_set:Ne \l__prop_internal_tl { \exp_not:c { __prop ~ #3 #6 ~ #7 } }
+ #2 #1 { \exp_not:n { #4 \s__prop {#5} } \exp_not:o \l__prop_internal_tl }
+ \use_none:n #6 \__prop_set_eq_end:w
+ \exp_after:wN \__prop_set_eq_loop:NNnw \l__prop_internal_tl #2 {#3}
+ \use_none:n #1 \__prop_pair:wn #7 \s__prop
+ }
+\cs_new_protected:Npn \__prop_set_eq_end:w
+ \exp_after:wN \__prop_set_eq_loop:NNnw #1#2#3
+ \use_none:n #4#5 \s__prop
+ {
+ \exp_after:wN #2 \l__prop_internal_tl { \exp_not:n { \use_none:n #4 } }
+ }
+\cs_new_protected:Npn \prop_make_flat:N #1
+ {
+ \int_compare:nNnTF { \tex_currentgrouplevel:D } = 0
+ {
+ \__prop_if_flat:NTF #1 { }
+ { \exp_args:NNf \__prop_make_flat:Nn #1 {#1} }
+ }
+ {
+ \msg_error:nnee { prop } { inner-make }
+ { \token_to_str:N \prop_make_flat:N } { \token_to_str:N #1 }
+ }
+ }
+\cs_generate_variant:Nn \prop_make_flat:N { c }
+\cs_new_protected:Npn \__prop_make_flat:Nn #1#2
+ {
+ \exp_after:wN \__prop_clear:wNNN #1 \cs_set_eq:NN \cs_set_nopar:Npe #1
+ \cs_set_nopar:Npe #1 { \exp_not:n {#2} }
+ }
+\cs_new_protected:Npn \prop_make_linked:N #1
+ {
+ \int_compare:nNnTF { \tex_currentgrouplevel:D } = 0
+ {
+ \__prop_if_flat:NTF #1
+ { \exp_args:NNo \__prop_make_linked:Nn #1 {#1} } { }
+ }
+ {
+ \msg_error:nnee { prop } { inner-make }
+ { \token_to_str:N \prop_make_linked:N } { \token_to_str:N #1 }
+ }
+ }
+\cs_generate_variant:Nn \prop_make_linked:N { c }
+\cs_new_protected:Npn \__prop_make_linked:Nn #1#2
+ {
+ \__prop_new_linked:N #1
+ \tl_set:Nn \l__prop_internal_tl {#2}
+ \exp_after:wN \__prop_set_eq:wNNNN #1
+ \cs_set_eq:NN \cs_set_nopar:Npe #1 \l__prop_internal_tl
+ }
+\prop_new:N \l_tmpa_prop
+\prop_new:N \l_tmpb_prop
+\prop_new:N \g_tmpa_prop
+\prop_new:N \g_tmpb_prop
+\cs_new_protected:Npn \prop_concat:NNN
+ { \__prop_concat:NNNNN \cs_set_eq:NN \cs_set_nopar:Npe }
+\cs_generate_variant:Nn \prop_concat:NNN { ccc }
+\cs_new_protected:Npn \prop_gconcat:NNN
+ { \__prop_concat:NNNNN \cs_gset_eq:NN \cs_gset_nopar:Npe }
+\cs_generate_variant:Nn \prop_gconcat:NNN { ccc }
+\cs_new_protected:Npn \__prop_concat:NNNNN #1#2#3#4#5
+ {
+ \cs_if_eq:NNTF #3 #5
+ { \__prop_concat:nNNN \use_none:nnn #2 #3 #4 }
+ {
+ \__prop_set_eq:NNNN #1 #2 #3 #4
+ \__prop_concat:nNNN { } #2 #3 #5
+ }
+ }
+\cs_new_protected:Npn \__prop_concat:nNNN #1#2#3#4
+ {
+ \cs_gset_eq:NN \__prop_tmp:w \__prop_pair:wn
+ \cs_gset_protected:Npn \__prop_pair:wn ##1 \s__prop
+ { \__prop_put:nNNnn {#1} #2 #3 {##1} }
+ \exp_last_unbraced:Nf \use_none:nn #4
+ \cs_gset_eq:NN \__prop_pair:wn \__prop_tmp:w
+ }
+\cs_new_protected:Npn \prop_put_from_keyval:Nn #1
+ { \__prop_from_keyval:nn { \__prop_put:nNNnn { } \cs_set_nopar:Npe #1 } }
+\cs_generate_variant:Nn \prop_put_from_keyval:Nn { c }
+\cs_new_protected:Npn \prop_gput_from_keyval:Nn #1
+ { \__prop_from_keyval:nn { \__prop_put:nNNnn { } \cs_gset_nopar:Npe #1 } }
+\cs_generate_variant:Nn \prop_gput_from_keyval:Nn { c }
+\cs_new_protected:Npn \__prop_from_keyval:nn
+ {
+ \bool_if:NTF \l__kernel_keyval_allow_blank_keys_bool
+ { \__prop_from_keyval:Nnn \c_true_bool }
+ { \__prop_from_keyval:Nnn \c_false_bool }
+ }
+\cs_new_protected:Npn \__prop_from_keyval:Nnn #1#2#3
+ {
+ \bool_set_eq:NN \l__kernel_keyval_allow_blank_keys_bool \c_true_bool
+ \keyval_parse:nnn \__prop_missing_eq:n {#2} {#3}
+ \bool_set_eq:NN \l__kernel_keyval_allow_blank_keys_bool #1
+ }
+\cs_new_protected:Npn \__prop_missing_eq:n
+ { \msg_error:nnn { prop } { prop-keyval } }
+\cs_new_protected:Npn \prop_set_from_keyval:Nn #1
+ {
+ \__prop_clear:NNN \cs_set_eq:NN \cs_set_nopar:Npe #1
+ \prop_put_from_keyval:Nn #1
+ }
+\cs_generate_variant:Nn \prop_set_from_keyval:Nn { c }
+\cs_new_protected:Npn \prop_gset_from_keyval:Nn #1
+ {
+ \__prop_clear:NNN \cs_gset_eq:NN \cs_gset_nopar:Npe #1
+ \prop_gput_from_keyval:Nn #1
+ }
+\cs_generate_variant:Nn \prop_gset_from_keyval:Nn { c }
+\cs_new_protected:Npn \prop_const_from_keyval:Nn #1
+ {
+ \prop_new:N #1
+ \__prop_from_keyval:nn { \__prop_put:nNNnn { } \cs_gset_nopar:Npe #1 }
+ }
+\cs_generate_variant:Nn \prop_const_from_keyval:Nn { c }
+\cs_new_protected:Npn \prop_const_linked_from_keyval:Nn #1
+ {
+ \prop_new_linked:N #1
+ \__prop_from_keyval:nn { \__prop_put:nNNnn { } \cs_gset_nopar:Npe #1 }
+ }
+\cs_generate_variant:Nn \prop_const_linked_from_keyval:Nn { c }
+\cs_new_protected:Npn \__prop_split:NnTFn #1#2
+ {
+ \exp_after:wN \__prop_split_aux:nNTFn
+ \exp_after:wN { \tl_to_str:n {#2} } #1
+ }
+\cs_new_protected:Npn \__prop_split_aux:nNTFn #1#2#3
+ {
+ \cs_set:Npn \__prop_split_flat:w \__prop_split_linked:w ##1
+ \__prop_pair:wn #1 \s__prop ##2 ##3 \s__prop_mark ##4 ##5 \s__prop_stop
+ { ##4 {#3} }
+ \exp_after:wN \__prop_split_test:wn #2 \s__prop_mark \use_i:nnn
+ \__prop_pair:wn #1 \s__prop { ? \fi: \__prop_split_wrong:Nw #2 }
+ \s__prop_mark \use_ii:nnn
+ \s__prop_stop
+ }
+\cs_new:Npn \__prop_split_flat:w { }
+\cs_new_protected:Npn \__prop_split_test:wn #1 \s__prop #2
+ {
+ \if_meaning:w \__prop_chk:w #2 \exp_after:wN \__prop_split_flat:w \fi:
+ \__prop_split_linked:w
+ }
+\cs_new_protected:Npn \__prop_split_linked:w #1 \s__prop_stop #2#3 {#3}
+\cs_new_protected:Npn \__prop_split_wrong:Nw #1#2 \s__prop_stop #3#4
+ {
+ \prop_show:N #1
+ \cs_gset_eq:NN #1 \c_empty_prop
+ #3
+ }
+\cs_new_protected:Npn \prop_get:NnN #1#2#3
+ {
+ \__prop_get:NnnTF #1 {#2}
+ { \tl_set:Nn #3 } { } { \tl_set:Nn #3 { \q_no_value } }
+ }
+\cs_generate_variant:Nn \prop_get:NnN { NV , Nv , Ne , c , cV , cv , ce }
+\cs_generate_variant:Nn \prop_get:NnN { No , Nx , co , cx }
+\cs_generate_variant:Nn \prop_get:NnN { cnc }
+\prg_new_protected_conditional:Npnn \prop_get:NnN #1#2#3 { T , F , TF }
+ {
+ \__prop_get:NnnTF #1 {#2}
+ { \tl_set:Nn #3 } \prg_return_true: \prg_return_false:
+ }
+\prg_generate_conditional_variant:Nnn \prop_get:NnN
+ { NV , Nv , Ne , c , cV , cv , ce } { T , F , TF }
+\prg_generate_conditional_variant:Nnn \prop_get:NnN
+ { No , Nx , co , cx } { T , F , TF }
+\prg_generate_conditional_variant:Nnn \prop_get:NnN
+ { cnc } { T , F , TF }
+\cs_new_protected:Npn \__prop_get:NnnTF #1#2#3#4#5
+ {
+ \__prop_split:NnTFn #1 {#2}
+ { #3 {##2} #4 }
+ {#5}
+ { \exp_after:wN \__prop_get_linked:w #1 {#2} {#3} {#4} {#5} }
+ }
+\cs_new:Npn \__prop_get_linked:w
+ \__prop_flatten:w #1 \s__prop #2#3#4#5#6#7
+ {
+ \if_cs_exist:w __prop ~ #2 ~ \tl_to_str:n {#4} \cs_end:
+ \exp_after:wN \exp_after:wN \exp_after:wN \__prop_get_linked_aux:w
+ \cs:w __prop ~ #2 ~ \tl_to_str:n {#4} \exp_after:wN \cs_end:
+ \else:
+ \exp_after:wN \__prop_get_linked_aux:w
+ \fi:
+ \s__prop_mark {#5} {#6}
+ \s__prop { } \s__prop_mark \use_none:n {#7}
+ \s__prop_stop
+ }
+\cs_new:Npn \__prop_get_linked_aux:w
+ #1 \s__prop #2 #3 \s__prop_mark #4 #5 #6 \s__prop_stop { #4 {#2} #5 }
+\cs_new:Npn \prop_item:Nn #1#2
+ {
+ \__prop_if_flat:NTF #1
+ {
+ \exp_args:NNo \prop_map_tokens:Nn #1
+ {
+ \exp_after:wN \__prop_item:nnn
+ \exp_after:wN { \tl_to_str:n {#2} }
+ }
+ }
+ { \exp_after:wN \__prop_get_linked:w #1 {#2} \use:n { } { } }
+ }
+\cs_new:Npn \__prop_item:nnn #1#2#3
+ {
+ \str_if_eq:eeT {#1} {#2}
+ { \prop_map_break:n { \exp_not:n {#3} } }
+ }
+\cs_generate_variant:Nn \prop_item:Nn { NV , No , Ne , c , cV , co , ce }
+\cs_new_protected:Npn \__prop_pop:NnNNnTF #1#2#3#4#5#6#7
+ {
+ \__prop_split:NnTFn #1 {#2}
+ {
+ #4 #1 { \exp_not:n { \s__prop \__prop_chk:w ##1 ##3 } }
+ #5 {##2}
+ #6
+ }
+ {#7}
+ {
+ \exp_after:wN \__prop_pop_linked:wnNNnTF #1 {#2}
+ #3 #4 {#5} {#6} {#7}
+ }
+ }
+\cs_new_protected:Npn \__prop_pop_linked:wnNNnTF
+ \__prop_flatten:w #1 \s__prop #2#3#4#5#6#7
+ {
+ \if_cs_exist:w __prop ~ #2 ~ \tl_to_str:n {#4} \cs_end:
+ \exp_after:wN \__prop_pop_linked:NNNn
+ \cs:w __prop ~ #2 ~ \tl_to_str:n {#4} \cs_end:
+ #5 #6 {#7}
+ \else:
+ \exp_after:wN \use_iii:nnn
+ \fi:
+ \use_i:nn
+ }
+\cs_new_protected:Npn \__prop_pop_linked:NNNn #1#2#3#4
+ {
+ \if_meaning:w \scan_stop: #1
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_iii:nnn
+ \else:
+ \exp_after:wN \__prop_pop_linked:w #1 #1 #2 #3 {#4}
+ \fi:
+ }
+\cs_new_protected:Npn \__prop_pop_linked:w
+ \use_none:n #1#2 \s__prop #3#4#5#6#7#8
+ {
+ #6 #5 \tex_undefined:D
+ #7 #1
+ {
+ \exp_after:wN \__prop_pop_linked_prev:w #1
+ \exp_not:N #4
+ }
+ #7 #4
+ {
+ \exp_not:n { \use_none:n #1 }
+ \exp_not:f { \exp_after:wN \__prop_pop_linked_next:w #4 }
+ }
+ #8 {#3}
+ }
+\cs_new:Npn \__prop_pop_linked_prev:w #1 \s__prop #2#3
+ { \exp_not:n { #1 \s__prop {#2} } }
+\cs_new:Npn \__prop_pop_linked_next:w \use_none:n #1 { \exp_stop_f: }
+\cs_new_protected:Npn \prop_remove:Nn #1#2
+ {
+ \__prop_pop:NnNNnTF #1 {#2}
+ \cs_set_eq:NN \cs_set_nopar:Npe
+ \use_none:n { } { }
+ }
+\cs_new_protected:Npn \prop_gremove:Nn #1#2
+ {
+ \__prop_pop:NnNNnTF #1 {#2}
+ \cs_gset_eq:NN \cs_gset_nopar:Npe
+ \use_none:n { } { }
+ }
+\cs_generate_variant:Nn \prop_remove:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \prop_gremove:Nn { NV , Ne , c , cV , ce }
+\cs_new_protected:Npn \prop_pop:NnN #1#2#3
+ {
+ \__prop_pop:NnNNnTF #1 {#2}
+ \cs_set_eq:NN \cs_set_nopar:Npe
+ { \tl_set:Nn #3 } { } { \tl_set:Nn #3 { \q_no_value } }
+ }
+\cs_new_protected:Npn \prop_gpop:NnN #1#2#3
+ {
+ \__prop_pop:NnNNnTF #1 {#2}
+ \cs_gset_eq:NN \cs_gset_nopar:Npe
+ { \tl_set:Nn #3 } { } { \tl_set:Nn #3 { \q_no_value } }
+ }
+\cs_generate_variant:Nn \prop_pop:NnN { NV , No }
+\cs_generate_variant:Nn \prop_pop:NnN { c , cV , co }
+\cs_generate_variant:Nn \prop_gpop:NnN { NV , No }
+\cs_generate_variant:Nn \prop_gpop:NnN { c , cV , co }
+\prg_new_protected_conditional:Npnn \prop_pop:NnN #1#2#3 { T , F , TF }
+ {
+ \__prop_pop:NnNNnTF #1 {#2}
+ \cs_set_eq:NN \cs_set_nopar:Npe
+ { \tl_set:Nn #3 } \prg_return_true: \prg_return_false:
+ }
+\prg_new_protected_conditional:Npnn \prop_gpop:NnN #1#2#3 { T , F , TF }
+ {
+ \__prop_pop:NnNNnTF #1 {#2}
+ \cs_gset_eq:NN \cs_gset_nopar:Npe
+ { \tl_set:Nn #3 } \prg_return_true: \prg_return_false:
+ }
+\prg_generate_conditional_variant:Nnn \prop_pop:NnN
+ { NV , No , c , cV , co } { T , F , TF }
+\prg_generate_conditional_variant:Nnn \prop_gpop:NnN
+ { NV , No , c , cV , co } { T , F , TF }
+\cs_new_protected:Npn \prop_put:Nnn
+ { \__prop_put:nNNnn { } \cs_set_nopar:Npe }
+\cs_new_protected:Npn \prop_gput:Nnn
+ { \__prop_put:nNNnn { } \cs_gset_nopar:Npe }
+\cs_new_protected:Npn \prop_put_if_not_in:Nnn
+ { \__prop_put:nNNnn \use_none:nnn \cs_set_nopar:Npe }
+\cs_new_protected:Npn \prop_gput_if_not_in:Nnn
+ { \__prop_put:nNNnn \use_none:nnn \cs_gset_nopar:Npe }
+\cs_generate_variant:Nn \prop_put:Nnn
+ {
+ NnV , Nnv , Nne , NV , NVV , NVv , NVe ,
+ Nv , NvV , Nvv , Nve , Ne , NeV , Nev , Nee
+ }
+\cs_generate_variant:Nn \prop_put:Nnn
+ { Nno , No , Noo , Nnx , NVx , NxV , Nxx }
+\cs_generate_variant:Nn \prop_put:Nnn
+ {
+ c , cnV , cnv , cne , cV , cVV , cVv , cVe ,
+ cv , cvV , cvv , cve , ce , ceV , cev , cee
+ }
+\cs_generate_variant:Nn \prop_put:Nnn
+ { cno , co , coo , cnx , cVx , cxV , cxx }
+\cs_generate_variant:Nn \prop_gput:Nnn
+ {
+ NnV , Nnv , Nne , NV , NVV , NVv , NVe ,
+ Nv , NvV , Nvv , Nve , Ne , NeV , Nev , Nee
+ }
+\cs_generate_variant:Nn \prop_gput:Nnn
+ { Nno , No , Noo , Nnx , NVx , NxV , Nxx }
+\cs_generate_variant:Nn \prop_gput:Nnn
+ {
+ c , cnV , cnv , cne , cV , cVV , cVv , cVe ,
+ cv , cvV , cvv , cve , ce , ceV , cev , cee
+ }
+\cs_generate_variant:Nn \prop_gput:Nnn
+ { cno , co , coo , cnx , cVx , cxV , cxx }
+\cs_generate_variant:Nn \prop_put_if_not_in:Nnn
+ { NnV , NV , c , cnV , cV }
+\cs_generate_variant:Nn \prop_gput_if_not_in:Nnn
+ { NnV , NV , c , cnV , cV }
+\cs_new_protected:Npn \__prop_put:nNNnn #1#2#3#4#5
+ {
+ \tl_set:Nn \l__prop_internal_tl
+ {
+ \exp_not:N \__prop_pair:wn \tl_to_str:n {#4}
+ \s__prop { \exp_not:n {#5} }
+ }
+ \__prop_split:NnTFn #3 {#4}
+ {
+ #1 #2 #3
+ {
+ \s__prop \__prop_chk:w \exp_not:n {##1}
+ \l__prop_internal_tl \exp_not:n {##3}
+ }
+ }
+ { #2 #3 { \exp_not:o {#3} \l__prop_internal_tl } }
+ { \exp_after:wN \__prop_put_linked:wnnN #3 {#4} {#1} #2 }
+ }
+\cs_new_protected:Npn \__prop_put_linked:wnnN
+ \__prop_flatten:w #1 \s__prop #2#3#4
+ {
+ \exp_after:wN \__prop_put_linked:NNnN
+ \cs:w __prop ~ #2 ~ \tl_to_str:n {#4} \cs_end:
+ #1
+ }
+\cs_new_protected:Npn \__prop_put_linked:NNnN #1#2#3#4
+ {
+ \if_meaning:w \scan_stop: #1
+ \exp_after:wN \__prop_put_linked_new:w #2 #1 #2 #4
+ \else:
+ \exp_after:wN \__prop_put_linked_old:w #1 { #3 #4 #1 }
+ \fi:
+ }
+\cs_new_protected:Npn \__prop_put_linked_new:w
+ \use_none:n #1#2#3#4
+ {
+ #4 #1
+ {
+ \exp_after:wN \__prop_pop_linked_prev:w #1
+ \exp_not:N #2
+ }
+ #4 #2
+ {
+ \exp_not:n { \use_none:n #1 }
+ \l__prop_internal_tl
+ \exp_not:N #3
+ }
+ #4 #3 { \exp_not:n { \use_none:n #2 } }
+ }
+\cs_new_protected:Npn \__prop_put_linked_old:w
+ \use_none:n #1#2 \s__prop #3#4#5
+ {
+ #5
+ {
+ \exp_not:n { \use_none:n #1 }
+ \l__prop_internal_tl
+ \exp_not:N #4
+ }
+ }
+\prg_new_eq_conditional:NNn \prop_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \prop_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+\prg_new_conditional:Npnn \prop_if_empty:N #1 { p , T , F , TF }
+ {
+ \if_meaning:w #1 \c_empty_prop
+ \prg_return_true:
+ \else:
+ \exp_after:wN \__prop_if_empty_return:w #1
+ \__prop_flatten:w 2 \s__prop 34 \s__prop_stop
+ \fi:
+ }
+\cs_new:Npn \__prop_if_empty_return:w
+ #1 \__prop_flatten:w #2 \s__prop #3#4#5 \s__prop_stop
+ {
+ \if_meaning:w #2 #4
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \prop_if_empty:N
+ { c } { p , T , F , TF }
+\prg_new_conditional:Npnn \prop_if_in:Nn #1#2 { p , T , F , TF }
+ {
+ \__prop_if_flat:NTF #1
+ {
+ \exp_after:wN \prop_map_tokens:Nn \exp_after:wN #1
+ {
+ \exp_after:wN \__prop_if_in_flat:nnn
+ \exp_after:wN { \tl_to_str:n {#2} }
+ }
+ \prg_return_false:
+ }
+ {
+ \exp_after:wN \__prop_get_linked:w #1 {#2}
+ \use_none:n \prg_return_true: \prg_return_false:
+ }
+ }
+\cs_new:Npn \__prop_if_in_flat:nnn #1#2#3
+ {
+ \str_if_eq:eeT {#1} {#2}
+ { \prop_map_break:n { \use_i:nn \prg_return_true: } }
+ }
+\prg_generate_conditional_variant:Nnn \prop_if_in:Nn
+ { NV , Ne , No , c , cV , ce , co } { p , T , F , TF }
+\cs_new:Npn \prop_map_function:NN #1#2
+ {
+ \exp_last_unbraced:Nnf
+ \use_i:nnn { \__prop_map_function:Nw #2 } #1
+ \__prop_pair:wn \fi: \prop_map_break: \s__prop { }
+ \__prop_pair:wn \fi: \prop_map_break: \s__prop { }
+ \__prop_pair:wn \fi: \prop_map_break: \s__prop { }
+ \__prop_pair:wn \fi: \prop_map_break: \s__prop { }
+ \prg_break_point:Nn \prop_map_break: { }
+ }
+\cs_new:Npn \__prop_map_function:Nw #1
+ \__prop_pair:wn #2 \s__prop #3
+ \__prop_pair:wn #4 \s__prop #5
+ \__prop_pair:wn #6 \s__prop #7
+ \__prop_pair:wn #8 \s__prop #9
+ {
+ \if_false: #2 \fi: #1 {#2} {#3}
+ \if_false: #4 \fi: #1 {#4} {#5}
+ \if_false: #6 \fi: #1 {#6} {#7}
+ \if_false: #8 \fi: #1 {#8} {#9}
+ \__prop_map_function:Nw #1
+ }
+\cs_generate_variant:Nn \prop_map_function:NN { Nc , c , cc }
+\cs_new_protected:Npn \prop_map_inline:Nn #1#2
+ {
+ \cs_gset_eq:cN
+ { __prop_map_ \int_use:N \g__kernel_prg_map_int :wn } \__prop_pair:wn
+ \int_gincr:N \g__kernel_prg_map_int
+ \cs_gset_protected:Npn \__prop_pair:wn ##1 \s__prop ##2 {#2}
+ \exp_last_unbraced:Nf \use_none:nn #1
+ \prg_break_point:Nn \prop_map_break:
+ {
+ \int_gdecr:N \g__kernel_prg_map_int
+ \cs_gset_eq:Nc \__prop_pair:wn
+ { __prop_map_ \int_use:N \g__kernel_prg_map_int :wn }
+ }
+ }
+\cs_generate_variant:Nn \prop_map_inline:Nn { c }
+\cs_new:Npn \prop_map_tokens:Nn #1#2
+ {
+ \exp_last_unbraced:Nnf
+ \use_i:nnn { \__prop_map_tokens:nw {#2} } #1
+ \__prop_pair:wn \fi: \prop_map_break: \s__prop { }
+ \__prop_pair:wn \fi: \prop_map_break: \s__prop { }
+ \__prop_pair:wn \fi: \prop_map_break: \s__prop { }
+ \__prop_pair:wn \fi: \prop_map_break: \s__prop { }
+ \prg_break_point:Nn \prop_map_break: { }
+ }
+\cs_new:Npn \__prop_map_tokens:nw #1
+ \__prop_pair:wn #2 \s__prop #3
+ \__prop_pair:wn #4 \s__prop #5
+ \__prop_pair:wn #6 \s__prop #7
+ \__prop_pair:wn #8 \s__prop #9
+ {
+ \if_false: #2 \fi: \use:n {#1} {#2} {#3}
+ \if_false: #4 \fi: \use:n {#1} {#4} {#5}
+ \if_false: #6 \fi: \use:n {#1} {#6} {#7}
+ \if_false: #8 \fi: \use:n {#1} {#8} {#9}
+ \__prop_map_tokens:nw {#1}
+ }
+\cs_generate_variant:Nn \prop_map_tokens:Nn { c }
+\cs_new:Npn \prop_map_break:
+ { \prg_map_break:Nn \prop_map_break: { } }
+\cs_new:Npn \prop_map_break:n
+ { \prg_map_break:Nn \prop_map_break: }
+\cs_new:Npn \prop_count:N #1
+ {
+ \int_eval:n
+ {
+ 0
+ \prop_map_function:NN #1 \__prop_count:nn
+ }
+ }
+\cs_new:Npn \__prop_count:nn #1#2 { + 1 }
+\cs_generate_variant:Nn \prop_count:N { c }
+\cs_new:Npn \prop_to_keyval:N #1
+ {
+ \__kernel_exp_not:w
+ \prop_if_empty:NTF #1
+ { {} }
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ {
+ \tex_expanded:D
+ {
+ \exp_not:N \use_none:n
+ \prop_map_function:NN #1 \__prop_to_keyval:nn
+ }
+ }
+ }
+ }
+\cs_new:Npn \__prop_to_keyval:nn #1#2
+ { , ~ {#1} =~ { \__kernel_exp_not:w {#2} } }
+\cs_new_protected:Npn \prop_show:N { \__prop_show:NN \msg_show:nneeee }
+\cs_generate_variant:Nn \prop_show:N { c }
+\cs_new_protected:Npn \prop_log:N { \__prop_show:NN \msg_log:nneeee }
+\cs_generate_variant:Nn \prop_log:N { c }
+\cs_new_protected:Npn \__prop_show:NN #1#2
+ {
+ \__kernel_chk_tl_type:NnnT #2 { prop }
+ {
+ \__prop_if_flat:NTF #2
+ {
+ \s__prop \__prop_chk:w
+ \exp_after:wN \__prop_show_flat:w #2
+ \s__prop { }
+ \__prop_pair:wn \q__prop_recursion_tail \s__prop { }
+ \q__prop_recursion_stop
+ }
+ { \exp_after:wN \__prop_show_linked:w #2 \s__prop ! ? \s__prop_stop }
+ }
+ {
+ \__prop_if_flat:NTF #2
+ { \__prop_show_finally:NNn #1 #2 { flat } }
+ {
+ \tl_set:Nn \l__prop_internal_tl { #1 #2 }
+ \exp_after:wN \__prop_show_prepare:w #2 #2
+ }
+ }
+ }
+\cs_new:Npn \__prop_show_flat:w #1 \__prop_pair:wn #2 \s__prop #3
+ {
+ \__prop_if_recursion_tail_stop:n {#2}
+ \exp_not:N \__prop_pair:wn \tl_to_str:n {#2} \s__prop \exp_not:n { {#3} }
+ \__prop_show_flat:w
+ }
+\cs_new:Npn \__prop_show_linked:w #1 \s__prop #2#3#4 \s__prop_stop
+ {
+ \exp_not:N \__prop_flatten:w
+ \exp_not:c { __prop ~ \tl_to_str:n {#2} }
+ \s__prop { \tl_to_str:n {#2} }
+ \exp_not:n {#3}
+ }
+\cs_new_protected:Npn \__prop_show_finally:NNn #1#2#3
+ {
+ #1 { prop } { show }
+ { \token_to_str:N #2 }
+ { \prop_map_function:NN #2 \msg_show_item:nn }
+ {#3} { }
+ }
+\cs_new_protected:Npn \__prop_show_prepare:w
+ \__prop_flatten:w #1 \s__prop #2#3#4
+ {
+ \use:e
+ {
+ \cs_set_nopar:Npn \exp_not:N \__prop_tmp:w
+ ##1 \token_to_str:N #1 ##2 \s__prop_mark ##3 \s__prop_stop
+ {
+ \exp_not:N \tl_if_empty:nTF {##1}
+ {
+ \exp_not:N \tl_if_head_is_space:nTF {##2}
+ { \exp_not:N \exp_args:Nf \__prop_show_loop_key:wNNN }
+ { \exp_not:N \tl_if_empty:nTF }
+ {##2}
+ }
+ { \exp_not:N \use_ii:nn }
+ \__prop_show_end:NNN
+ \__prop_show_bad_name:NNN
+ }
+ }
+ \exp_last_unbraced:NNNo \__prop_show_loop:NNw #1 #4 #4
+ }
+\cs_new_protected:Npn \__prop_show_loop:NNw #1#2 #3 \s__prop #4#5
+ {
+ \exp_last_two_unbraced:Noo \__prop_tmp:w
+ { \token_to_str:N #5 \s__prop_mark }
+ { \token_to_str:N #1 \s__prop_mark \s__prop_stop }
+ #1 #2 #5
+ }
+\cs_new_protected:Npn \__prop_show_bad_name:NNN #1#2#3
+ {
+ \msg_error:nneeee { prop } { bad-link }
+ { \tl_tail:N \l__prop_internal_tl }
+ { \token_to_str:N #2 }
+ { \token_to_str:N #3 }
+ { \token_to_str:N #1 }
+ }
+\cs_new_protected:Npn \__prop_show_end:NNN #1#2#3
+ {
+ \__kernel_chk_tl_type:NnnT #3
+ { \tl_tail:N \l__prop_internal_tl prop~entry }
+ { \exp_not:n { \use_none:n #2 } }
+ {
+ \exp_after:wN \__prop_show_finally:NNn
+ \l__prop_internal_tl { linked }
+ }
+ }
+\cs_new_protected:Npn \__prop_show_loop_key:wNNN #1#2#3#4#5#6
+ {
+ \__kernel_chk_tl_type:NnnT #6
+ { \tl_tail:N \l__prop_internal_tl prop~entry }
+ {
+ \exp_not:n { \use_none:n #5 }
+ \exp_after:wN \__prop_show_flat:w #6 \s__prop { }
+ \__prop_pair:wn \q__prop_recursion_tail \s__prop { }
+ \q__prop_recursion_stop
+ \tl_item:Nn #6 { -1 }
+ }
+ { \exp_last_unbraced:NNNo \__prop_show_loop:NNw #4 #6 #6 }
+ }
+%% File: l3msg.dtx
+\tl_new:N \l__msg_internal_tl
+\str_new:N \l__msg_name_str
+\str_new:N \l__msg_text_str
+\scan_new:N \s__msg_mark
+\scan_new:N \s__msg_stop
+\cs_new:Npn \__msg_use_none_delimit_by_s_stop:w #1 \s__msg_stop { }
+\tl_const:Nn \c__msg_text_prefix_tl { msg~text~>~ }
+\tl_const:Nn \c__msg_more_text_prefix_tl { msg~extra~text~>~ }
+\prg_new_conditional:Npnn \msg_if_exist:nn #1#2 { p , T , F , TF }
+ {
+ \cs_if_exist:cTF { \c__msg_text_prefix_tl #1 / #2 }
+ { \prg_return_true: } { \prg_return_false: }
+ }
+\cs_new_protected:Npn \__msg_chk_free:nn #1#2
+ {
+ \msg_if_exist:nnT {#1} {#2}
+ {
+ \msg_error:nnnn { msg } { already-defined }
+ {#1} {#2}
+ }
+ }
+\cs_new_protected:Npn \msg_new:nnnn #1#2#3#4
+ {
+ \__msg_chk_free:nn {#1} {#2}
+ \cs_gset:cpn { \c__msg_text_prefix_tl #1 / #2 }
+ ##1##2##3##4 {#3}
+ \cs_gset:cpn { \c__msg_more_text_prefix_tl #1 / #2 }
+ ##1##2##3##4 {#4}
+ }
+\cs_generate_variant:Nn \msg_new:nnnn { nnee , nnxx }
+\cs_new_protected:Npn \msg_new:nnn #1#2#3
+ { \msg_new:nnnn {#1} {#2} {#3} { } }
+\cs_generate_variant:Nn \msg_new:nnn { nne , nnx }
+\cs_new_protected:Npn \msg_set:nnnn #1#2#3#4
+ {
+ \cs_set:cpn { \c__msg_text_prefix_tl #1 / #2 }
+ ##1##2##3##4 {#3}
+ \cs_set:cpn { \c__msg_more_text_prefix_tl #1 / #2 }
+ ##1##2##3##4 {#4}
+ }
+\cs_new_protected:Npn \msg_set:nnn #1#2#3
+ { \msg_set:nnnn {#1} {#2} {#3} { } }
+\tl_const:Nn \c__msg_coding_error_text_tl
+ {
+ This~is~a~coding~error.
+ \\ \\
+ }
+\tl_const:Nn \c__msg_continue_text_tl
+ { Type~<return>~to~continue }
+\tl_const:Nn \c__msg_critical_text_tl
+ { Reading~the~current~file~'\g_file_curr_name_str'~will~stop. }
+\tl_const:Nn \c__msg_fatal_text_tl
+ { This~is~a~fatal~error:~LaTeX~will~abort. }
+\tl_const:Nn \c__msg_help_text_tl
+ { For~immediate~help~type~H~<return> }
+\tl_const:Nn \c__msg_no_info_text_tl
+ {
+ LaTeX~does~not~know~anything~more~about~this~error,~sorry.
+ \c__msg_return_text_tl
+ }
+\tl_const:Nn \c__msg_on_line_text_tl { on~line }
+\tl_const:Nn \c__msg_return_text_tl
+ {
+ \\ \\
+ Try~typing~<return>~to~proceed.
+ \\
+ If~that~doesn't~work,~type~X~<return>~to~quit.
+ }
+\tl_const:Nn \c__msg_trouble_text_tl
+ {
+ \\ \\
+ More~errors~will~almost~certainly~follow: \\
+ the~LaTeX~run~should~be~aborted.
+ }
+\cs_new:Npn \msg_line_number: { \int_use:N \tex_inputlineno:D }
+\cs_gset:Npn \msg_line_context:
+ {
+ \c__msg_on_line_text_tl
+ \c_space_tl
+ \msg_line_number:
+ }
+\cs_new_protected:Npn \__msg_interrupt:NnnnN #1#2#3#4#5
+ {
+ \str_set:Ne \l__msg_text_str { #1 {#2} }
+ \str_set:Ne \l__msg_name_str { \msg_module_name:n {#2} }
+ \cs_if_eq:cNTF
+ { \c__msg_more_text_prefix_tl #2 / #3 }
+ \__msg_no_more_text:nnnn
+ {
+ \__msg_interrupt_wrap:nnn
+ { \use:c { \c__msg_text_prefix_tl #2 / #3 } #4 }
+ { \c__msg_continue_text_tl }
+ {
+ \c__msg_no_info_text_tl
+ \tl_if_empty:NF #5
+ { \\ \\ #5 }
+ }
+ }
+ {
+ \__msg_interrupt_wrap:nnn
+ { \use:c { \c__msg_text_prefix_tl #2 / #3 } #4 }
+ { \c__msg_help_text_tl }
+ {
+ \use:c { \c__msg_more_text_prefix_tl #2 / #3 } #4
+ \tl_if_empty:NF #5
+ { \\ \\ #5 }
+ }
+ }
+ }
+\cs_new:Npn \__msg_no_more_text:nnnn #1#2#3#4 { }
+\cs_new_protected:Npn \__msg_interrupt_wrap:nnn #1#2#3
+ {
+ \iow_wrap:nnnN { \\ #3 } { } { } \__msg_interrupt_more_text:n
+ \group_begin:
+ \int_sub:Nn \l_iow_line_count_int { 2 }
+ \iow_wrap:nenN { \l__msg_text_str : ~ #1 }
+ {
+ ( \l__msg_name_str )
+ \prg_replicate:nn
+ {
+ \str_count:N \l__msg_text_str
+ - \str_count:N \l__msg_name_str
+ + 2
+ }
+ { ~ }
+ }
+ { } \__msg_interrupt_text:n
+ \iow_wrap:nnnN { \l__msg_internal_tl \\ \\ #2 } { } { }
+ \__msg_interrupt:n
+ }
+\cs_new_protected:Npn \__msg_interrupt_text:n #1
+ {
+ \group_end:
+ \tl_set:Nn \l__msg_internal_tl {#1}
+ }
+\cs_new_protected:Npn \__msg_interrupt_more_text:n #1
+ { \exp_args:Ne \tex_errhelp:D { #1 \iow_newline: } }
+\group_begin:
+ \char_set_lccode:nn { 38 } { 32 } % &
+ \char_set_lccode:nn { 46 } { 32 } % .
+ \char_set_lccode:nn { 123 } { 32 } % {
+ \char_set_lccode:nn { 125 } { 32 } % }
+ \char_set_catcode_active:N \&
+\tex_lowercase:D
+ {
+ \group_end:
+ \cs_new_protected:Npn \__msg_interrupt:n #1
+ {
+ \iow_term:n { }
+ \__kernel_iow_with:Nnn \tex_newlinechar:D { `\^^J }
+ {
+ \__kernel_iow_with:Nnn \tex_errorcontextlines:D { -1 }
+ {
+ \group_begin:
+ \cs_set_protected:Npn &
+ {
+ \tex_errmessage:D
+ {
+ #1
+ \use_none:n
+ { ............................................ }
+ }
+ }
+ \exp_after:wN
+ \group_end:
+ &
+ }
+ }
+ }
+ }
+\int_gset:Nn \tex_errorcontextlines:D { -1 }
+\cs_new:Npn \msg_fatal_text:n #1
+ {
+ Fatal ~
+ \msg_error_text:n {#1}
+ }
+\cs_new:Npn \msg_critical_text:n #1
+ {
+ Critical ~
+ \msg_error_text:n {#1}
+ }
+\cs_new:Npn \msg_error_text:n #1
+ { \__msg_text:nn {#1} { Error } }
+\cs_new:Npn \msg_warning_text:n #1
+ { \__msg_text:nn {#1} { Warning } }
+\cs_new:Npn \msg_info_text:n #1
+ { \__msg_text:nn {#1} { Info } }
+\cs_new:Npn \__msg_text:nn #1#2
+ {
+ \exp_args:Nf \__msg_text:n { \msg_module_type:n {#1} }
+ \exp_args:Nf \__msg_text:n { \msg_module_name:n {#1} }
+ #2
+ }
+\cs_new:Npn \__msg_text:n #1
+ {
+ \tl_if_blank:nF {#1}
+ { #1 ~ }
+ }
+\prop_new:N \g_msg_module_name_prop
+\prop_new:N \g_msg_module_type_prop
+\prop_gput:Nnn \g_msg_module_type_prop { LaTeX } { }
+\cs_new:Npn \msg_module_type:n #1
+ {
+ \prop_if_in:NnTF \g_msg_module_type_prop {#1}
+ { \prop_item:Nn \g_msg_module_type_prop {#1} }
+ { Package }
+ }
+\cs_new:Npn \msg_module_name:n #1
+ {
+ \prop_if_in:NnTF \g_msg_module_name_prop {#1}
+ { \prop_item:Nn \g_msg_module_name_prop {#1} }
+ {#1}
+ }
+\cs_new:Npn \msg_see_documentation_text:n #1
+ {
+ See~the~ \msg_module_name:n {#1} ~
+ documentation~for~further~information.
+ }
+\group_begin:
+ \cs_set_protected:Npn \__msg_class_new:nn #1#2
+ {
+ \prop_new:c { l__msg_redirect_ #1 _prop }
+ \cs_new_protected:cpn { __msg_ #1 _code:nnnnnn }
+ ##1##2##3##4##5##6 {#2}
+ \cs_new_protected:cpn { msg_ #1 :nnnnnn } ##1##2##3##4##5##6
+ {
+ \use:e
+ {
+ \exp_not:n { \__msg_use:nnnnnnn {#1} {##1} {##2} }
+ { \tl_to_str:n {##3} } { \tl_to_str:n {##4} }
+ { \tl_to_str:n {##5} } { \tl_to_str:n {##6} }
+ }
+ }
+ \cs_new_protected:cpe { msg_ #1 :nnnnn } ##1##2##3##4##5
+ { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} {##3} {##4} {##5} { } }
+ \cs_new_protected:cpe { msg_ #1 :nnnn } ##1##2##3##4
+ { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} {##3} {##4} { } { } }
+ \cs_new_protected:cpe { msg_ #1 :nnn } ##1##2##3
+ { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} {##3} { } { } { } }
+ \cs_new_protected:cpe { msg_ #1 :nn } ##1##2
+ { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} { } { } { } { } }
+ \cs_generate_variant:cn { msg_ #1 :nnn }
+ { nnV , nne , nnx }
+ \cs_generate_variant:cn { msg_ #1 :nnnn }
+ { nnVV , nnVn , nnnV , nnne , nnnx , nnee , nnxx }
+ \cs_generate_variant:cn { msg_ #1 :nnnnn }
+ { nnnee , nnnxx , nneee , nnxxx }
+ \cs_generate_variant:cn { msg_ #1 :nnnnnn } { nneeee , nnxxxx }
+ }
+ \__msg_class_new:nn { fatal }
+ {
+ \__msg_interrupt:NnnnN
+ \msg_fatal_text:n {#1} {#2}
+ { {#3} {#4} {#5} {#6} }
+ \c__msg_fatal_text_tl
+ \__msg_fatal_exit:
+ }
+ \cs_new_protected:Npn \__msg_fatal_exit:
+ {
+ \tex_batchmode:D
+ \tex_read:D -1 to \l__msg_internal_tl
+ }
+ \__msg_class_new:nn { critical }
+ {
+ \__msg_interrupt:NnnnN
+ \msg_critical_text:n {#1} {#2}
+ { {#3} {#4} {#5} {#6} }
+ \c__msg_critical_text_tl
+ \tex_endinput:D
+ }
+ \cs_undefine:N \msg_error:nnee
+ \cs_undefine:N \msg_error:nne
+ \cs_undefine:N \msg_error:nn
+ \__msg_class_new:nn { error }
+ {
+ \__msg_interrupt:NnnnN
+ \msg_error_text:n {#1} {#2}
+ { {#3} {#4} {#5} {#6} }
+ \c_empty_tl
+ }
+ \cs_new_protected:Npn \__msg_info_aux:NNnnnnnn #1#2#3#4#5#6#7#8
+ {
+ \str_set:Ne \l__msg_text_str { #2 {#3} }
+ \str_set:Ne \l__msg_name_str { \msg_module_name:n {#3} }
+ #1 { }
+ \iow_wrap:nenN
+ {
+ \l__msg_text_str : ~
+ \use:c { \c__msg_text_prefix_tl #3 / #4 } {#5} {#6} {#7} {#8}
+ }
+ {
+ ( \l__msg_name_str )
+ \prg_replicate:nn
+ {
+ \str_count:N \l__msg_text_str
+ - \str_count:N \l__msg_name_str
+ }
+ { ~ }
+ }
+ { } #1
+ #1 { }
+ }
+ \__msg_class_new:nn { warning }
+ {
+ \__msg_info_aux:NNnnnnnn \iow_term:n \msg_warning_text:n
+ {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+ \__msg_class_new:nn { note }
+ {
+ \__msg_info_aux:NNnnnnnn \iow_term:n \msg_info_text:n
+ {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+ \__msg_class_new:nn { info }
+ {
+ \__msg_info_aux:NNnnnnnn \iow_log:n \msg_info_text:n
+ {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+ \__msg_class_new:nn { log }
+ {
+ \iow_wrap:nnnN
+ { \use:c { \c__msg_text_prefix_tl #1 / #2 } {#3} {#4} {#5} {#6} }
+ { } { } \iow_log:n
+ }
+ \__msg_class_new:nn { term }
+ {
+ \iow_wrap:nnnN
+ { \use:c { \c__msg_text_prefix_tl #1 / #2 } {#3} {#4} {#5} {#6} }
+ { } { } \iow_term:n
+ }
+ \__msg_class_new:nn { none } { }
+ \__msg_class_new:nn { show }
+ {
+ \iow_wrap:nnnN
+ { \use:c { \c__msg_text_prefix_tl #1 / #2 } {#3} {#4} {#5} {#6} }
+ { } { } \__msg_show:n
+ }
+ \cs_new_protected:Npn \__msg_show:n #1
+ {
+ \tl_if_in:nnTF { ^^J #1 } { ^^J > ~ }
+ {
+ \tl_if_in:nnTF { #1 \s__msg_mark } { . \s__msg_mark }
+ { \__msg_show_dot:w } { \__msg_show:w }
+ ^^J #1 \s__msg_stop
+ }
+ { \__msg_show:nn { ? #1 } { } }
+ }
+ \cs_new:Npn \__msg_show_dot:w #1 ^^J > ~ #2 . \s__msg_stop
+ { \__msg_show:nn {#1} {#2} }
+ \cs_new:Npn \__msg_show:w #1 ^^J > ~ #2 \s__msg_stop
+ { \__msg_show:nn {#1} {#2} }
+ \cs_new_protected:Npn \__msg_show:nn #1#2
+ {
+ \tl_if_empty:nF {#1}
+ { \exp_args:No \iow_term:n { \use_none:n #1 } }
+ \tl_set:Nn \l__msg_internal_tl {#2}
+ \__kernel_iow_with:Nnn \tex_newlinechar:D { 10 }
+ {
+ \__kernel_iow_with:Nnn \tex_errorcontextlines:D { -1 }
+ {
+ \tex_showtokens:D \exp_after:wN \exp_after:wN \exp_after:wN
+ { \exp_after:wN \l__msg_internal_tl }
+ }
+ }
+ }
+\group_end:
+\cs_new:Npe \msg_show_item:n #1
+ { ^^J > ~ \c_space_tl \exp_not:N \tl_to_str:n { {#1} } }
+\cs_new:Npe \msg_show_item_unbraced:n #1
+ { ^^J > ~ \c_space_tl \exp_not:N \tl_to_str:n {#1} }
+\cs_new:Npe \msg_show_item:nn #1#2
+ {
+ ^^J > \use:nn { ~ } { ~ }
+ \exp_not:N \tl_to_str:n { {#1} }
+ \use:nn { ~ } { ~ } => \use:nn { ~ } { ~ }
+ \exp_not:N \tl_to_str:n { {#2} }
+ }
+\cs_new:Npe \msg_show_item_unbraced:nn #1#2
+ {
+ ^^J > \use:nn { ~ } { ~ }
+ \exp_not:N \tl_to_str:n {#1}
+ \use:nn { ~ } { ~ } => \use:nn { ~ } { ~ }
+ \exp_not:N \tl_to_str:n {#2}
+ }
+\cs_new:Npn \__msg_class_chk_exist:nT #1
+ {
+ \cs_if_free:cTF { __msg_ #1 _code:nnnnnn }
+ { \msg_error:nnn { msg } { class-unknown } {#1} }
+ }
+\tl_new:N \l__msg_class_tl
+\tl_new:N \l__msg_current_class_tl
+\prop_new:N \l__msg_redirect_prop
+\seq_new:N \l__msg_hierarchy_seq
+\seq_new:N \l__msg_class_loop_seq
+\cs_new_protected:Npn \__msg_use:nnnnnnn #1#2#3#4#5#6#7
+ {
+ \cs_if_exist_use:N \conditionally@traceoff
+ \msg_if_exist:nnTF {#2} {#3}
+ {
+ \__msg_class_chk_exist:nT {#1}
+ {
+ \tl_set:Nn \l__msg_current_class_tl {#1}
+ \cs_set_protected:Npe \__msg_use_code:
+ {
+ \exp_not:n
+ {
+ \use:c { __msg_ \l__msg_class_tl _code:nnnnnn }
+ {#2} {#3} {#4} {#5} {#6} {#7}
+ }
+ }
+ \__msg_use_redirect_name:n { #2 / #3 }
+ }
+ }
+ { \msg_error:nnnn { msg } { unknown } {#2} {#3} }
+ \cs_if_exist_use:N \conditionally@traceon
+ }
+\cs_new_protected:Npn \__msg_use_code: { }
+\cs_new_protected:Npn \__msg_use_redirect_name:n #1
+ {
+ \prop_get:NnNTF \l__msg_redirect_prop { / #1 } \l__msg_class_tl
+ { \__msg_use_code: }
+ {
+ \seq_clear:N \l__msg_hierarchy_seq
+ \__msg_use_hierarchy:nwwN { }
+ #1 \s__msg_mark \__msg_use_hierarchy:nwwN
+ / \s__msg_mark \__msg_use_none_delimit_by_s_stop:w
+ \s__msg_stop
+ \__msg_use_redirect_module:n { }
+ }
+ }
+\cs_new_protected:Npn \__msg_use_hierarchy:nwwN #1#2 / #3 \s__msg_mark #4
+ {
+ \seq_put_left:Nn \l__msg_hierarchy_seq {#1}
+ #4 { #1 / #2 } #3 \s__msg_mark #4
+ }
+\cs_new_protected:Npn \__msg_use_redirect_module:n #1
+ {
+ \seq_map_inline:Nn \l__msg_hierarchy_seq
+ {
+ \prop_get:cnNTF { l__msg_redirect_ \l__msg_current_class_tl _prop }
+ {##1} \l__msg_class_tl
+ {
+ \seq_map_break:n
+ {
+ \tl_if_eq:NNTF \l__msg_current_class_tl \l__msg_class_tl
+ { \__msg_use_code: }
+ {
+ \tl_set_eq:NN \l__msg_current_class_tl \l__msg_class_tl
+ \__msg_use_redirect_module:n {##1}
+ }
+ }
+ }
+ {
+ \str_if_eq:nnT {##1} {#1}
+ {
+ \tl_set_eq:NN \l__msg_class_tl \l__msg_current_class_tl
+ \seq_map_break:n { \__msg_use_code: }
+ }
+ }
+ }
+ }
+\cs_new_protected:Npn \msg_redirect_name:nnn #1#2#3
+ {
+ \tl_if_empty:nTF {#3}
+ { \prop_remove:Nn \l__msg_redirect_prop { / #1 / #2 } }
+ {
+ \__msg_class_chk_exist:nT {#3}
+ { \prop_put:Nnn \l__msg_redirect_prop { / #1 / #2 } {#3} }
+ }
+ }
+\cs_new_protected:Npn \msg_redirect_class:nn
+ { \__msg_redirect:nnn { } }
+\cs_new_protected:Npn \msg_redirect_module:nnn #1
+ { \__msg_redirect:nnn { / #1 } }
+\cs_new_protected:Npn \__msg_redirect:nnn #1#2#3
+ {
+ \__msg_class_chk_exist:nT {#2}
+ {
+ \tl_if_empty:nTF {#3}
+ { \prop_remove:cn { l__msg_redirect_ #2 _prop } {#1} }
+ {
+ \__msg_class_chk_exist:nT {#3}
+ {
+ \prop_put:cnn { l__msg_redirect_ #2 _prop } {#1} {#3}
+ \tl_set:Nn \l__msg_current_class_tl {#2}
+ \seq_clear:N \l__msg_class_loop_seq
+ \__msg_redirect_loop_chk:nnn {#2} {#3} {#1}
+ }
+ }
+ }
+ }
+\cs_new_protected:Npn \__msg_redirect_loop_chk:nnn #1#2#3
+ {
+ \seq_put_right:Nn \l__msg_class_loop_seq {#1}
+ \prop_get:cnNT { l__msg_redirect_ #1 _prop } {#3} \l__msg_class_tl
+ {
+ \str_if_eq:VnF \l__msg_class_tl {#1}
+ {
+ \tl_if_eq:NNTF \l__msg_class_tl \l__msg_current_class_tl
+ {
+ \prop_put:cnn { l__msg_redirect_ #2 _prop } {#3} {#2}
+ \msg_warning:nneeee
+ { msg } { redirect-loop }
+ { \seq_item:Nn \l__msg_class_loop_seq { 1 } }
+ { \seq_item:Nn \l__msg_class_loop_seq { 2 } }
+ {#3}
+ {
+ \seq_map_function:NN \l__msg_class_loop_seq
+ \__msg_redirect_loop_list:n
+ { \seq_item:Nn \l__msg_class_loop_seq { 1 } }
+ }
+ }
+ { \__msg_redirect_loop_chk:onn \l__msg_class_tl {#2} {#3} }
+ }
+ }
+ }
+\cs_generate_variant:Nn \__msg_redirect_loop_chk:nnn { o }
+\cs_new:Npn \__msg_redirect_loop_list:n #1 { {#1} ~ => ~ }
+\cs_new_protected:Npn \__kernel_msg_show_eval:Nn #1#2
+ { \exp_args:Nf \__msg_show_eval:nnN { #1 {#2} } {#2} \tl_show:n }
+\cs_new_protected:Npn \__kernel_msg_log_eval:Nn #1#2
+ { \exp_args:Nf \__msg_show_eval:nnN { #1 {#2} } {#2} \tl_log:n }
+\cs_new_protected:Npn \__msg_show_eval:nnN #1#2#3 { #3 { #2 = #1 } }
+\cs_new_protected:Npn \__kernel_msg_new:nnnn #1
+ { \msg_new:nnnn { LaTeX / #1 } }
+\cs_new_protected:Npn \__kernel_msg_new:nnn #1
+ { \msg_new:nnn { LaTeX / #1 } }
+\cs_new_protected:Npn \__kernel_msg_info:nnxx #1
+ { \msg_info:nnee { LaTeX / #1 } }
+\cs_new_protected:Npn \__kernel_msg_warning:nnx #1
+ { \msg_warning:nne { LaTeX / #1 } }
+\cs_new_protected:Npn \__kernel_msg_warning:nnxx #1
+ { \msg_warning:nnee { LaTeX / #1 } }
+\cs_new_protected:Npn \__kernel_msg_error:nnx #1
+ { \msg_error:nne { LaTeX / #1 } }
+\cs_new_protected:Npn \__kernel_msg_error:nnxx #1
+ { \msg_error:nnee { LaTeX / #1 } }
+\cs_new_protected:Npn \__kernel_msg_error:nnxxx #1
+ { \msg_error:nneee { LaTeX / #1 } }
+\cs_new:Npn \__kernel_msg_expandable_error:nnn #1
+ { \msg_expandable_error:nnn { LaTeX / #1 } }
+\cs_new:Npn \__kernel_msg_expandable_error:nnf #1
+ { \msg_expandable_error:nnf { LaTeX / #1 } }
+\cs_new:Npn \__kernel_msg_expandable_error:nnff #1
+ { \msg_expandable_error:nnff { LaTeX / #1 } }
+\msg_new:nnnn { msg } { already-defined }
+ { Message~'#2'~for~module~'#1'~already~defined. }
+ {
+ \c__msg_coding_error_text_tl
+ LaTeX~was~asked~to~define~a~new~message~called~'#2'\\
+ by~the~module~'#1':~this~message~already~exists.
+ \c__msg_return_text_tl
+ }
+\msg_new:nnnn { msg } { unknown }
+ { Unknown~message~'#2'~for~module~'#1'. }
+ {
+ \c__msg_coding_error_text_tl
+ LaTeX~was~asked~to~display~a~message~called~'#2'\\
+ by~the~module~'#1':~this~message~does~not~exist.
+ \c__msg_return_text_tl
+ }
+\msg_new:nnnn { msg } { class-unknown }
+ { Unknown~message~class~'#1'. }
+ {
+ LaTeX~has~been~asked~to~redirect~messages~to~a~class~'#1':\\
+ this~was~never~defined.
+ \c__msg_return_text_tl
+ }
+\msg_new:nnnn { msg } { redirect-loop }
+ {
+ Message~redirection~loop~caused~by~ {#1} ~=>~ {#2}
+ \tl_if_empty:nF {#3} { ~for~module~' \use_none:n #3 ' } .
+ }
+ {
+ Adding~the~message~redirection~ {#1} ~=>~ {#2}
+ \tl_if_empty:nF {#3} { ~for~the~module~' \use_none:n #3 ' } ~
+ created~an~infinite~loop\\\\
+ \iow_indent:n { #4 \\\\ }
+ }
+\msg_new:nnnn { kernel } { bad-number-of-arguments }
+ { Function~'#1'~cannot~be~defined~with~#2~arguments. }
+ {
+ \c__msg_coding_error_text_tl
+ LaTeX~has~been~asked~to~define~a~function~'#1'~with~
+ #2~arguments.~
+ TeX~allows~between~0~and~9~arguments~for~a~single~function.
+ }
+\msg_new:nnnn { kernel } { command-already-defined }
+ { Control~sequence~#1~already~defined. }
+ {
+ \c__msg_coding_error_text_tl
+ LaTeX~has~been~asked~to~create~a~new~control~sequence~'#1'~
+ but~this~name~has~already~been~used~elsewhere. \\ \\
+ The~current~meaning~is:\\
+ \ \ #2
+ }
+\msg_new:nnnn { kernel } { command-not-defined }
+ { Control~sequence~#1~undefined. }
+ {
+ \c__msg_coding_error_text_tl
+ LaTeX~has~been~asked~to~use~a~control~sequence~'#1':\\
+ this~has~not~been~defined~yet.
+ }
+\msg_new:nnnn { kernel } { empty-search-pattern }
+ { Empty~search~pattern. }
+ {
+ \c__msg_coding_error_text_tl
+ LaTeX~has~been~asked~to~replace~an~empty~pattern~by~'#1':~that~
+ would~lead~to~an~infinite~loop!
+ }
+\cs_if_exist:NF \tex_elapsedtime:D
+ {
+ \msg_new:nnnn { kernel } { no-elapsed-time }
+ { No~clock~detected~for~#1. }
+ { The~current~engine~provides~no~way~to~access~the~system~time. }
+ }
+\msg_new:nnnn { kernel } { non-base-function }
+ { Function~'#1'~is~not~a~base~function }
+ {
+ \c__msg_coding_error_text_tl
+ Functions~defined~through~\iow_char:N\\cs_new:Nn~must~have~
+ a~signature~consisting~of~only~normal~arguments~'N'~and~'n'.~
+ The~signature~'#2'~of~'#1'~contains~other~arguments~'#3'.~
+ To~define~variants~use~\iow_char:N\\cs_generate_variant:Nn~
+ and~to~define~other~functions~use~\iow_char:N\\cs_new:Npn.
+ }
+\msg_new:nnnn { kernel } { missing-colon }
+ { Function~'#1'~contains~no~':'. }
+ {
+ \c__msg_coding_error_text_tl
+ Code-level~functions~must~contain~':'~to~separate~the~
+ argument~specification~from~the~function~name.~This~is~
+ needed~when~defining~conditionals~or~variants,~or~when~building~a~
+ parameter~text~from~the~number~of~arguments~of~the~function.
+ }
+\msg_new:nnnn { kernel } { overflow }
+ { Integers~larger~than~2^{30}-1~cannot~be~stored~in~arrays. }
+ {
+ An~attempt~was~made~to~store~#3~
+ \tl_if_empty:nF {#2} { at~position~#2~ } in~the~array~'#1'.~
+ The~largest~allowed~value~#4~will~be~used~instead.
+ }
+\msg_new:nnnn { kernel } { out-of-bounds }
+ { Access~to~an~entry~beyond~an~array's~bounds. }
+ {
+ An~attempt~was~made~to~access~or~store~data~at~position~#2~of~the~
+ array~'#1',~but~this~array~has~entries~at~positions~from~1~to~#3.
+ }
+\msg_new:nnnn { kernel } { protected-predicate }
+ { Predicate~'#1'~must~be~expandable. }
+ {
+ \c__msg_coding_error_text_tl
+ LaTeX~has~been~asked~to~define~'#1'~as~a~protected~predicate.~
+ Only~expandable~tests~can~have~a~predicate~version.
+ }
+\msg_new:nnn { kernel } { randint-backward-range }
+ { Wrong~order~of~bounds~in~\iow_char:N\\int_rand:nn{#1}{#2}. }
+\msg_new:nnnn { kernel } { conditional-form-unknown }
+ { Conditional~form~'#1'~for~function~'#2'~unknown. }
+ {
+ \c__msg_coding_error_text_tl
+ LaTeX~has~been~asked~to~define~the~conditional~form~'#1'~of~
+ the~function~'#2',~but~only~'TF',~'T',~'F',~and~'p'~forms~exist.
+ }
+\msg_new:nnnn { kernel } { variant-too-long }
+ { Variant~form~'#1'~longer~than~base~signature~of~'#2'. }
+ {
+ \c__msg_coding_error_text_tl
+ LaTeX~has~been~asked~to~create~a~variant~of~the~function~'#2'~
+ with~a~signature~starting~with~'#1',~but~that~is~longer~than~
+ the~signature~(part~after~the~colon)~of~'#2'.
+ }
+\msg_new:nnnn { kernel } { invalid-variant }
+ { Variant~form~'#1'~invalid~for~base~form~'#2'. }
+ {
+ \c__msg_coding_error_text_tl
+ LaTeX~has~been~asked~to~create~a~variant~of~the~function~'#2'~
+ with~a~signature~starting~with~'#1',~but~cannot~change~an~argument~
+ from~type~'#3'~to~type~'#4'.
+ }
+\msg_new:nnnn { kernel } { invalid-exp-args }
+ { Invalid~variant~specifier~'#1'~in~'#2'. }
+ {
+ \c__msg_coding_error_text_tl
+ LaTeX~has~been~asked~to~create~an~\iow_char:N\\exp_args:N...~
+ function~with~signature~'N#2'~but~'#1'~is~not~a~valid~argument~
+ specifier.
+ }
+\msg_new:nnn { kernel } { deprecated-variant }
+ {
+ Variant~form~'#1'~deprecated~for~base~form~'#2'.~
+ One~should~not~change~an~argument~from~type~'#3'~to~type~'#4'
+ \str_case:nnF {#3}
+ {
+ { n } { :~use~a~'\token_if_eq_charcode:NNTF #4 c v V'~variant? }
+ { N } { :~base~form~only~accepts~a~single~token~argument. }
+ {#4} { :~base~form~is~already~a~variant. }
+ } { . }
+ }
+\msg_new:nnn { char } { active }
+ { Cannot~generate~active~chars. }
+\msg_new:nnn { char } { invalid-catcode }
+ { Invalid~catcode~for~char~generation. }
+\msg_new:nnn { char } { null-space }
+ { Cannot~generate~null~char~as~a~space. }
+\msg_new:nnn { char } { out-of-range }
+ { Charcode~requested~out~of~engine~range. }
+\msg_new:nnn { dim } { zero-unit }
+ { Zero~unit~in~conversion. }
+\msg_new:nnnn { kernel } { quote-in-shell }
+ { Quotes~in~shell~command~'#1'. }
+ { Shell~commands~cannot~contain~quotes~("). }
+\msg_new:nnnn { keys } { no-property }
+ { No~property~given~in~definition~of~key~'#1'. }
+ {
+ \c__msg_coding_error_text_tl
+ Inside~\keys_define:nn each~key~name~
+ needs~a~property: \\ \\
+ \iow_indent:n { #1 .<property> } \\ \\
+ LaTeX~did~not~find~a~'.'~to~indicate~the~start~of~a~property.
+ }
+\msg_new:nnnn { keys } { property-boolean-values-only }
+ { The~property~'#1'~accepts~boolean~values~only. }
+ {
+ \c__msg_coding_error_text_tl
+ The~property~'#1'~only~accepts~the~values~'true'~and~'false'.
+ }
+\msg_new:nnnn { keys } { property-requires-value }
+ { The~property~'#1'~requires~a~value. }
+ {
+ \c__msg_coding_error_text_tl
+ LaTeX~was~asked~to~set~property~'#1'~for~key~'#2'.\\
+ No~value~was~given~for~the~property,~and~one~is~required.
+ }
+\msg_new:nnnn { keys } { property-unknown }
+ { The~key~property~'#1'~is~unknown. }
+ {
+ \c__msg_coding_error_text_tl
+ LaTeX~has~been~asked~to~set~the~property~'#1'~for~key~'#2':~
+ this~property~is~not~defined.
+ }
+\msg_new:nnnn { quark } { invalid-function }
+ { Quark~test~function~'#1'~is~invalid. }
+ {
+ \c__msg_coding_error_text_tl
+ LaTeX~has~been~asked~to~create~quark~test~function~'#1'~
+ \tl_if_empty:nTF {#2}
+ { but~that~name~ }
+ { with~signature~'#2',~but~that~signature~ }
+ is~not~valid.
+ }
+\__kernel_msg_new:nnn { quark } { invalid }
+ { Invalid~quark~variable~'#1'. }
+\msg_new:nnnn { scanmark } { already-defined }
+ { Scan~mark~#1~already~defined. }
+ {
+ \c__msg_coding_error_text_tl
+ LaTeX~has~been~asked~to~create~a~new~scan~mark~'#1'~
+ but~this~name~has~already~been~used~for~a~scan~mark.
+ }
+\msg_new:nnnn { seq } { item-too-large }
+ { Sequence~'#1'~does~not~have~an~item~#3 }
+ {
+ An~attempt~was~made~to~push~or~pop~the~item~at~position~#3~
+ of~'#1',~but~this~
+ \int_compare:nTF { #3 = 0 }
+ { position~does~not~exist. }
+ { sequence~only~has~#2~item \int_compare:nF { #2 = 1 } {s}. }
+ }
+\msg_new:nnnn { seq } { shuffle-too-large }
+ { The~sequence~#1~is~too~long~to~be~shuffled~by~TeX. }
+ {
+ TeX~has~ \int_eval:n { \c_max_register_int + 1 } ~
+ toks~registers:~this~only~allows~to~shuffle~up~to~
+ \int_use:N \c_max_register_int \ items.~
+ The~list~will~not~be~shuffled.
+ }
+\msg_new:nnnn { kernel } { variable-not-defined }
+ { Variable~#1~undefined. }
+ {
+ \c__msg_coding_error_text_tl
+ LaTeX~has~been~asked~to~show~a~variable~#1,~but~this~has~not~
+ been~defined~yet.
+ }
+\msg_new:nnnn { kernel } { bad-type }
+ { Variable~'#1'~is~not~a~valid~#3. }
+ {
+ \c__msg_coding_error_text_tl
+ The~variable~'#1'~with~\tl_if_empty:nTF {#4} {meaning} {value}\\\\
+ \iow_indent:n {#2}\\\\
+ should~be~a~#3~variable,~but~
+ \tl_if_empty:nTF {#4}
+ { it~is~not \str_if_eq:nnF {#3} { bool } { ~a~short~macro } . }
+ {
+ it~does~not~have~the~correct~
+ \str_if_eq:nnTF {#2} {#4}
+ { category~codes. }
+ { internal~structure:\\\\\iow_indent:n {#4} }
+ }
+ }
+\msg_new:nnnn { prop } { bad-link }
+ { Variable~'#1'~is~not~a~valid~(linked)~prop. }
+ {
+ \c__msg_coding_error_text_tl
+ The~variable~'#1'~has~an~incorrect~internal~structure.~
+ Its~internal~entry~'#2'~points~to~'#3',~whose~name~is~not~of~the~
+ form~'#4~<key>'.
+ }
+\msg_new:nnnn { clist } { non-clist }
+ { Variable~'#1'~is~not~a~valid~clist. }
+ {
+ \c__msg_coding_error_text_tl
+ The~variable~'#1'~with~value\\\\
+ \iow_indent:n {#2}\\\\
+ should~be~a~clist~variable,~but~it~includes~empty~or~blank~items~
+ without~braces.
+ }
+\msg_new:nnnn { prop } { misused }
+ { A~property~list~was~misused. }
+ {
+ \c__msg_coding_error_text_tl
+ A~property~list~variable~was~used~without~an~accessor~function.~
+ It~
+ \tl_if_empty:nTF {#1}
+ { is~empty. }
+ { contains~the~key-value~pairs \use_none:n #1 . }
+ }
+\msg_new:nnnn { prop } { inner-make }
+ { '#1'~ cannot~ be~ used~ in~ a~ group. }
+ {
+ \c__msg_coding_error_text_tl
+ The~ command~ '#1'~ was~ applied~ to~ the~ property~ list~
+ variable~ '#2', but~ the~ storage~ type~ can~ only~ be~ changed~
+ at~ the~ outermost~ group~ level.
+ }
+\msg_new:nnn { kernel } { bad-exp-end-f }
+ { Misused~\exp_end_continue_f:w or~:nw }
+\msg_new:nnn { kernel } { bad-variable }
+ { Erroneous~variable~#1 used! }
+\msg_new:nnn { seq } { misused }
+ { A~sequence~was~misused. }
+\msg_new:nnn { prg } { negative-replication }
+ { Negative~argument~for~\iow_char:N\\prg_replicate:nn. }
+\msg_new:nnn { prop } { prop-keyval }
+ { Missing~'='~in~'#1'~(in~'..._keyval:Nn') }
+\msg_new:nnn { kernel } { unknown-comparison }
+ { Relation~'#1'~not~among~=,<,>,==,!=,<=,>=. }
+\msg_new:nnn { kernel } { zero-step }
+ { Zero~step~size~for~function~#1. }
+\msg_new:nnn { clist } { show }
+ {
+ The~comma~list~ \tl_if_empty:nF {#1} { #1 ~ }
+ \tl_if_empty:nTF {#2}
+ { is~empty \\>~ . }
+ { contains~the~items~(without~outer~braces): #2 . }
+ }
+\msg_new:nnn { intarray } { show }
+ { The~integer~array~#1~contains~#2~items: \\ #3 . }
+\msg_new:nnn { prop } { show }
+ {
+ The~ \str_if_eq:nnF {#3} { flat } { #3~ }
+ property~list~#1~
+ \tl_if_empty:nTF {#2}
+ { is~empty \\>~ . }
+ { contains~the~pairs~(without~outer~braces): #2 . }
+ }
+\msg_new:nnn { seq } { show }
+ {
+ The~sequence~#1~
+ \tl_if_empty:nTF {#2}
+ { is~empty \\>~ . }
+ { contains~the~items~(without~outer~braces): #2 . }
+ }
+\msg_new:nnn { kernel } { show-streams }
+ {
+ \tl_if_empty:nTF {#2} { No~ } { The~following~ }
+ \str_case:nn {#1}
+ {
+ { ior } { input ~ }
+ { iow } { output ~ }
+ }
+ streams~are~
+ \tl_if_empty:nTF {#2} { open } { in~use: #2 . }
+ }
+\msg_new:nnnn { sys } { backend-set }
+ { Backend~configuration~already~set. }
+ {
+ Run-time~backend~selection~may~only~be~carried~out~once~during~a~run.~
+ This~second~attempt~to~set~them~will~be~ignored.
+ }
+\msg_new:nnnn { sys } { load-debug-in-preamble }
+ { Load~debug~support~in~the~preamble. }
+ {
+ Debugging~requires~support~loaded~in~the~preamble: \\
+ Use~\sys_load_debug:~before~\begin{document}.
+ }
+\msg_new:nnnn { sys } { wrong-backend }
+ { Backend~request~inconsistent~with~engine:~using~'#2'~backend. }
+ {
+ You~have~requested~backend~'#1',~but~this~is~not~suitable~for~use~with~the~
+ active~engine.~LaTeX~will~use~the~'#2'~backend~instead.
+ }
+\cs_set_protected:Npn \__msg_tmp:w #1
+ {
+ \cs_new:Npn #1 ? { }
+ \cs_new:Npn \__msg_expandable_error:nn ##1##2
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__msg_use_none_delimit_by_s_stop:w
+ \use:n { #1 ~ ! ~ ##2 : ~ ##1 } \s__msg_stop
+ }
+ }
+\exp_args:Nc \__msg_tmp:w { ??? }
+\exp_args_generate:n { oooo }
+\cs_new:Npn \msg_expandable_error:nnnnnn #1#2#3#4#5#6
+ {
+ \exp_args:Nee \__msg_expandable_error:nn
+ {
+ \exp_args:Nc \exp_args:Noooo
+ { \c__msg_text_prefix_tl #1 / #2 }
+ { \tl_to_str:n {#3} }
+ { \tl_to_str:n {#4} }
+ { \tl_to_str:n {#5} }
+ { \tl_to_str:n {#6} }
+ }
+ { \msg_error_text:n {#1} }
+ }
+\cs_new:Npn \msg_expandable_error:nnnnn #1#2#3#4#5
+ { \msg_expandable_error:nnnnnn {#1} {#2} {#3} {#4} {#5} { } }
+\cs_new:Npn \msg_expandable_error:nnnn #1#2#3#4
+ { \msg_expandable_error:nnnnnn {#1} {#2} {#3} {#4} { } { } }
+\cs_new:Npn \msg_expandable_error:nnn #1#2#3
+ { \msg_expandable_error:nnnnnn {#1} {#2} {#3} { } { } { } }
+\cs_new:Npn \msg_expandable_error:nn #1#2
+ { \msg_expandable_error:nnnnnn {#1} {#2} { } { } { } { } }
+\cs_generate_variant:Nn \msg_expandable_error:nnnnnn { nnffff }
+\cs_generate_variant:Nn \msg_expandable_error:nnnnn { nnfff }
+\cs_generate_variant:Nn \msg_expandable_error:nnnn { nnff }
+\cs_generate_variant:Nn \msg_expandable_error:nnn { nnf }
+\prop_gput:Nnn \g_msg_module_name_prop { kernel } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { kernel } { }
+\clist_map_inline:nn
+ {
+ char , clist , coffin , debug , deprecation , dim, msg ,
+ quark , prg , prop , scanmark , seq , sys
+ }
+ {
+ \prop_gput:Nnn \g_msg_module_name_prop {#1} { LaTeX }
+ \prop_gput:Nnn \g_msg_module_type_prop {#1} { }
+ }
+\prop_gput:Nnn \g_msg_module_name_prop { LaTeX / cmd } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { LaTeX / cmd } { }
+\prop_gput:Nnn \g_msg_module_name_prop { LaTeX / ltcmd } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { LaTeX / ltcmd } { }
+%% File: l3file.dtx
+\tl_new:N \l__ior_internal_tl
+\int_const:Nn \c__ior_term_ior { 16 }
+\seq_new:N \g__ior_streams_seq
+\tl_new:N \l__ior_stream_tl
+\prop_new:N \g__ior_streams_prop
+\int_step_inline:nnn
+ { 0 }
+ {
+ \cs_if_exist:NTF \contextversion
+ { \tex_count:D 38 ~ }
+ {
+ \tex_count:D 16 ~ %
+ \cs_if_exist:NT \loccount { - 1 }
+ }
+ }
+ {
+ \prop_gput:Nnn \g__ior_streams_prop {#1} { Reserved~by~format }
+ }
+\cs_new_protected:Npn \ior_new:N #1 { \cs_new_eq:NN #1 \c__ior_term_ior }
+\cs_generate_variant:Nn \ior_new:N { c }
+\ior_new:N \g_tmpa_ior
+\ior_new:N \g_tmpb_ior
+\cs_new_protected:Npn \ior_open:Nn #1#2
+ { \ior_open:NnF #1 {#2} { \__kernel_file_missing:n {#2} } }
+\cs_generate_variant:Nn \ior_open:Nn { c }
+\tl_new:N \l__ior_file_name_tl
+\prg_new_protected_conditional:Npnn \ior_open:Nn #1#2 { T , F , TF }
+ {
+ \file_get_full_name:nNTF {#2} \l__ior_file_name_tl
+ {
+ \__kernel_ior_open:No #1 \l__ior_file_name_tl
+ \prg_return_true:
+ }
+ { \prg_return_false: }
+ }
+\prg_generate_conditional_variant:Nnn \ior_open:Nn { c } { T , F , TF }
+\exp_args:NNf \cs_new_protected:Npn \__ior_new:N
+ { \exp_args:NNc \exp_after:wN \exp_stop_f: { newread } }
+\cs_if_exist:NT \contextversion
+ {
+ \cs_new_eq:NN \__ior_new_aux:N \__ior_new:N
+ \cs_gset_protected:Npn \__ior_new:N #1
+ {
+ \cs_undefine:N #1
+ \__ior_new_aux:N #1
+ }
+ }
+\cs_new_protected:Npn \__kernel_ior_open:Nn #1#2
+ {
+ \ior_close:N #1
+ \seq_gpop:NNTF \g__ior_streams_seq \l__ior_stream_tl
+ { \__ior_open_stream:Nn #1 {#2} }
+ {
+ \__ior_new:N #1
+ \__kernel_tl_set:Nx \l__ior_stream_tl { \int_eval:n {#1} }
+ \__ior_open_stream:Nn #1 {#2}
+ }
+ }
+\cs_generate_variant:Nn \__kernel_ior_open:Nn { No }
+\cs_new_protected:Npe \__ior_open_stream:Nn #1#2
+ {
+ \tex_global:D \tex_chardef:D #1 = \exp_not:N \l__ior_stream_tl \scan_stop:
+ \prop_gput:NVn \exp_not:N \g__ior_streams_prop #1 {#2}
+ \tex_openin:D #1
+ \sys_if_engine_luatex:TF
+ { {#2} }
+ { \exp_not:N \__kernel_file_name_quote:n {#2} \scan_stop: }
+ }
+\cs_new_protected:Npn \ior_shell_open:Nn #1#2
+ {
+ \sys_if_shell:TF
+ { \__ior_shell_open:oN { \tl_to_str:n {#2} } #1 }
+ { \msg_error:nn { kernel } { pipe-failed } }
+ }
+\cs_new_protected:Npn \__ior_shell_open:nN #1#2
+ {
+ \tl_if_in:nnTF {#1} { " }
+ {
+ \msg_error:nne
+ { kernel } { quote-in-shell } {#1}
+ }
+ { \__kernel_ior_open:Nn #2 { |#1 } }
+ }
+\cs_generate_variant:Nn \__ior_shell_open:nN { o }
+\msg_new:nnnn { kernel } { pipe-failed }
+ { Cannot~run~piped~system~commands. }
+ {
+ LaTeX~tried~to~call~a~system~process~but~this~was~not~possible.\\
+ Try~the~"--shell-escape"~(or~"--enable-pipes")~option.
+ }
+\cs_new_protected:Npn \ior_close:N #1
+ {
+ \int_compare:nT { -1 < #1 < \c__ior_term_ior }
+ {
+ \tex_closein:D #1
+ \prop_gremove:NV \g__ior_streams_prop #1
+ \seq_if_in:NVF \g__ior_streams_seq #1
+ { \seq_gpush:NV \g__ior_streams_seq #1 }
+ \cs_gset_eq:NN #1 \c__ior_term_ior
+ }
+ }
+\cs_generate_variant:Nn \ior_close:N { c }
+\cs_new_protected:Npn \ior_show:N { \__ior_show:NN \tl_show:n }
+\cs_generate_variant:Nn \ior_show:N { c }
+\cs_new_protected:Npn \ior_log:N { \__ior_show:NN \tl_log:n }
+\cs_generate_variant:Nn \ior_log:N { c }
+\cs_new_protected:Npn \__ior_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ \prop_get:NVNTF \g__ior_streams_prop #2 \l__ior_internal_tl
+ {
+ \exp_args:Ne #1
+ { \token_to_str:N #2 ~ open: ~ \l__ior_internal_tl }
+ }
+ { \exp_args:Ne #1 { \token_to_str:N #2 ~ closed } }
+ }
+ }
+\cs_new_protected:Npn \ior_show_list: { \__ior_list:N \msg_show:nneeee }
+\cs_new_protected:Npn \ior_log_list: { \__ior_list:N \msg_log:nneeee }
+\cs_new_protected:Npn \__ior_list:N #1
+ {
+ #1 { kernel } { show-streams }
+ { ior }
+ {
+ \prop_map_function:NN \g__ior_streams_prop
+ \msg_show_item_unbraced:nn
+ }
+ { } { }
+ }
+\cs_new_eq:NN \if_eof:w \tex_ifeof:D
+\prg_new_conditional:Npnn \ior_if_eof:N #1 { p , T , F , TF }
+ {
+ \if_int_compare:w -1 < #1
+ \if_int_compare:w #1 < \c__ior_term_ior
+ \if_eof:w #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \else:
+ \prg_return_true:
+ \fi:
+ \else:
+ \prg_return_true:
+ \fi:
+ }
+\cs_new_protected:Npn \ior_get:NN #1#2
+ { \ior_get:NNF #1 #2 { \tl_set:Nn #2 { \q_no_value } } }
+\cs_new_protected:Npn \__ior_get:NN #1#2
+ { \tex_read:D #1 to #2 }
+\prg_new_protected_conditional:Npnn \ior_get:NN #1#2 { T , F , TF }
+ {
+ \ior_if_eof:NTF #1
+ { \prg_return_false: }
+ {
+ \__ior_get:NN #1 #2
+ \prg_return_true:
+ }
+ }
+\cs_new_protected:Npn \ior_str_get:NN #1#2
+ { \ior_str_get:NNF #1 #2 { \tl_set:Nn #2 { \q_no_value } } }
+\cs_new_protected:Npn \__ior_str_get:NN #1#2
+ {
+ \exp_args:Nno \use:n
+ {
+ \int_set:Nn \tex_endlinechar:D { -1 }
+ \tex_readline:D #1 to #2
+ \int_set:Nn \tex_endlinechar:D
+ } { \int_use:N \tex_endlinechar:D }
+ }
+\prg_new_protected_conditional:Npnn \ior_str_get:NN #1#2 { T , F , TF }
+ {
+ \ior_if_eof:NTF #1
+ { \prg_return_false: }
+ {
+ \__ior_str_get:NN #1 #2
+ \prg_return_true:
+ }
+ }
+\int_const:Nn \c__ior_term_noprompt_ior { -1 }
+\cs_new_protected:Npn \ior_get_term:nN #1#2
+ { \__ior_get_term:NnN \__ior_get:NN {#1} #2 }
+\cs_new_protected:Npn \ior_str_get_term:nN #1#2
+ { \__ior_get_term:NnN \__ior_str_get:NN {#1} #2 }
+\cs_new_protected:Npn \__ior_get_term:NnN #1#2#3
+ {
+ \group_begin:
+ \tex_escapechar:D = -1 \scan_stop:
+ \tl_if_blank:nTF {#2}
+ { \exp_args:NNc #1 \c__ior_term_noprompt_ior }
+ { \exp_args:NNc #1 \c__ior_term_ior }
+ {#2}
+ \exp_args:NNNv \group_end:
+ \tl_set:Nn #3 {#2}
+ }
+\cs_new:Npn \ior_map_break:
+ { \prg_map_break:Nn \ior_map_break: { } }
+\cs_new:Npn \ior_map_break:n
+ { \prg_map_break:Nn \ior_map_break: }
+\cs_new_protected:Npn \ior_map_inline:Nn
+ { \__ior_map_inline:NNn \__ior_get:NN }
+\cs_new_protected:Npn \ior_str_map_inline:Nn
+ { \__ior_map_inline:NNn \__ior_str_get:NN }
+\cs_new_protected:Npn \__ior_map_inline:NNn
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:Nc \__ior_map_inline:NNNn
+ { __ior_map_ \int_use:N \g__kernel_prg_map_int :n }
+ }
+\cs_new_protected:Npn \__ior_map_inline:NNNn #1#2#3#4
+ {
+ \cs_gset_protected:Npn #1 ##1 {#4}
+ \ior_if_eof:NF #3 { \__ior_map_inline_loop:NNN #1#2#3 }
+ \prg_break_point:Nn \ior_map_break:
+ { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+\cs_new_protected:Npn \__ior_map_inline_loop:NNN #1#2#3
+ {
+ #2 #3 \l__ior_internal_tl
+ \if_eof:w #3
+ \exp_after:wN \ior_map_break:
+ \fi:
+ \exp_args:No #1 \l__ior_internal_tl
+ \__ior_map_inline_loop:NNN #1#2#3
+ }
+\cs_new_protected:Npn \ior_map_variable:NNn
+ { \__ior_map_variable:NNNn \ior_get:NN }
+\cs_new_protected:Npn \ior_str_map_variable:NNn
+ { \__ior_map_variable:NNNn \ior_str_get:NN }
+\cs_new_protected:Npn \__ior_map_variable:NNNn #1#2#3#4
+ {
+ \ior_if_eof:NF #2 { \__ior_map_variable_loop:NNNn #1#2#3 {#4} }
+ \prg_break_point:Nn \ior_map_break: { }
+ }
+\cs_new_protected:Npn \__ior_map_variable_loop:NNNn #1#2#3#4
+ {
+ #1 #2 #3
+ \if_eof:w #2
+ \exp_after:wN \ior_map_break:
+ \fi:
+ #4
+ \__ior_map_variable_loop:NNNn #1#2#3 {#4}
+ }
+\tl_new:N \l__iow_internal_tl
+\int_const:Nn \c_log_iow { -1 }
+\int_const:Nn \c_term_iow
+ {
+ \bool_lazy_and:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \int_compare_p:nNn \tex_luatexversion:D > { 80 } }
+ { 128 }
+ { 16 }
+ }
+\seq_new:N \g__iow_streams_seq
+\tl_new:N \l__iow_stream_tl
+\prop_new:N \g__iow_streams_prop
+\int_step_inline:nnn
+ { 0 }
+ {
+ \cs_if_exist:NTF \contextversion
+ { \tex_count:D 39 ~ }
+ {
+ \tex_count:D 17 ~
+ \cs_if_exist:NT \loccount { - 1 }
+ }
+ }
+ {
+ \prop_gput:Nnn \g__iow_streams_prop {#1} { Reserved~by~format }
+ }
+\scan_new:N \s__iow_mark
+\scan_new:N \s__iow_stop
+\cs_new:Npn \__iow_use_i_delimit_by_s_stop:nw #1 #2 \s__iow_stop {#1}
+\quark_new:N \q__iow_nil
+\cs_new_protected:Npn \iow_new:N #1 { \cs_new_eq:NN #1 \c_term_iow }
+\cs_generate_variant:Nn \iow_new:N { c }
+\iow_new:N \g_tmpa_iow
+\iow_new:N \g_tmpb_iow
+\exp_args:NNf \cs_new_protected:Npn \__iow_new:N
+ { \exp_args:NNc \exp_after:wN \exp_stop_f: { newwrite } }
+\cs_if_exist:NT \contextversion
+ {
+ \cs_new_eq:NN \__iow_new_aux:N \__iow_new:N
+ \cs_gset_protected:Npn \__iow_new:N #1
+ {
+ \cs_undefine:N #1
+ \__iow_new_aux:N #1
+ }
+ }
+\tl_new:N \l__iow_file_name_tl
+\cs_new_protected:Npn \iow_open:Nn #1#2
+ {
+ \__kernel_tl_set:Nx \l__iow_file_name_tl
+ { \__kernel_file_name_sanitize:n {#2} }
+ \__kernel_iow_open:No #1 \l__iow_file_name_tl
+ }
+\cs_generate_variant:Nn \iow_open:Nn { NV , c , cV }
+\cs_new_protected:Npn \__kernel_iow_open:Nn #1#2
+ {
+ \iow_close:N #1
+ \seq_gpop:NNTF \g__iow_streams_seq \l__iow_stream_tl
+ { \__iow_open_stream:Nn #1 {#2} }
+ {
+ \__iow_new:N #1
+ \__kernel_tl_set:Nx \l__iow_stream_tl { \int_eval:n {#1} }
+ \__iow_open_stream:Nn #1 {#2}
+ }
+ }
+\cs_generate_variant:Nn \__kernel_iow_open:Nn { No }
+\cs_new_protected:Npn \__iow_open_stream:Nn #1#2
+ {
+ \tex_global:D \tex_chardef:D #1 = \l__iow_stream_tl \scan_stop:
+ \prop_gput:NVn \g__iow_streams_prop #1 {#2}
+ \tex_immediate:D \tex_openout:D
+ #1 \__kernel_file_name_quote:n {#2} \scan_stop:
+ }
+\cs_generate_variant:Nn \__iow_open_stream:Nn { NV }
+\cs_new_protected:Npn \iow_shell_open:Nn #1#2
+ {
+ \sys_if_shell:TF
+ { \__iow_shell_open:oN { \tl_to_str:n {#2} } #1 }
+ { \msg_error:nn { kernel } { pipe-failed } }
+ }
+\cs_new_protected:Npn \__iow_shell_open:nN #1#2
+ {
+ \tl_if_in:nnTF {#1} { " }
+ {
+ \msg_error:nne
+ { kernel } { quote-in-shell } {#1}
+ }
+ { \__kernel_iow_open:Nn #2 { |#1 } }
+ }
+\cs_generate_variant:Nn \__iow_shell_open:nN { o }
+\cs_new_protected:Npn \iow_close:N #1
+ {
+ \int_compare:nT { \c_log_iow < #1 < \c_term_iow }
+ {
+ \tex_immediate:D \tex_closeout:D #1
+ \prop_gremove:NV \g__iow_streams_prop #1
+ \seq_if_in:NVF \g__iow_streams_seq #1
+ { \seq_gpush:NV \g__iow_streams_seq #1 }
+ \cs_gset_eq:NN #1 \c_term_iow
+ }
+ }
+\cs_generate_variant:Nn \iow_close:N { c }
+\cs_new_protected:Npn \iow_show:N { \__iow_show:NN \tl_show:n }
+\cs_generate_variant:Nn \iow_show:N { c }
+\cs_new_protected:Npn \iow_log:N { \__iow_show:NN \tl_log:n }
+\cs_generate_variant:Nn \iow_log:N { c }
+\cs_new_protected:Npn \__iow_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ \prop_get:NVNTF \g__iow_streams_prop #2 \l__iow_internal_tl
+ {
+ \exp_args:Ne #1
+ { \token_to_str:N #2 ~ open: ~ \l__iow_internal_tl }
+ }
+ { \exp_args:Ne #1 { \token_to_str:N #2 ~ closed } }
+ }
+ }
+\cs_new_protected:Npn \iow_show_list: { \__iow_list:N \msg_show:nneeee }
+\cs_new_protected:Npn \iow_log_list: { \__iow_list:N \msg_log:nneeee }
+\cs_new_protected:Npn \__iow_list:N #1
+ {
+ #1 { kernel } { show-streams }
+ { iow }
+ {
+ \prop_map_function:NN \g__iow_streams_prop
+ \msg_show_item_unbraced:nn
+ }
+ { } { }
+ }
+\cs_new_protected:Npn \iow_shipout_e:Nn #1#2
+ { \tex_write:D #1 {#2} }
+\cs_generate_variant:Nn \iow_shipout_e:Nn { Ne , c, ce }
+\cs_new_protected:Npn \iow_shipout:Nn #1#2
+ { \tex_write:D #1 { \exp_not:n {#2} } }
+\cs_generate_variant:Nn \iow_shipout:Nn { Ne , c, ce }
+\cs_generate_variant:Nn \iow_shipout:Nn { Nx , cx }
+\cs_new_protected:Npn \__kernel_iow_with:Nnn #1#2
+ {
+ \int_compare:nNnTF {#1} = {#2}
+ { \use:n }
+ { \__iow_with:oNnn { \int_use:N #1 } #1 {#2} }
+ }
+\cs_new_protected:Npn \__iow_with:nNnn #1#2#3#4
+ {
+ \int_set:Nn #2 {#3}
+ #4
+ \int_set:Nn #2 {#1}
+ }
+\cs_generate_variant:Nn \__iow_with:nNnn { o }
+\cs_new_protected:Npn \iow_now:Nn #1#2
+ {
+ \__kernel_iow_with:Nnn \tex_newlinechar:D { `\^^J }
+ { \tex_immediate:D \tex_write:D #1 { \exp_not:n {#2} } }
+ }
+\cs_generate_variant:Nn \iow_now:Nn { NV , Ne , c , cV , ce }
+\cs_generate_variant:Nn \iow_now:Nn { Nx , cx }
+\cs_new_protected:Npn \iow_log:n { \iow_now:Nn \c_log_iow }
+\cs_set_protected:Npn \iow_log:e { \iow_now:Ne \c_log_iow }
+\cs_generate_variant:Nn \iow_log:n { x }
+\cs_new_protected:Npn \iow_term:n { \iow_now:Nn \c_term_iow }
+\cs_set_protected:Npn \iow_term:e { \iow_now:Ne \c_term_iow }
+\cs_generate_variant:Nn \iow_term:n { x }
+\cs_new:Npn \iow_newline: { ^^J }
+\cs_new_eq:NN \iow_char:N \cs_to_str:N
+\int_new:N \l_iow_line_count_int
+\int_set:Nn \l_iow_line_count_int { 78 }
+\tl_new:N \l__iow_newline_tl
+\int_new:N \l__iow_line_target_int
+\tl_new:N \l__iow_one_indent_tl
+\int_new:N \l__iow_one_indent_int
+\cs_new:Npn \__iow_unindent:w { }
+\cs_new_protected:Npn \__iow_set_indent:n #1
+ {
+ \__kernel_tl_set:Nx \l__iow_one_indent_tl
+ { \exp_args:No \__kernel_str_to_other_fast:n { \tl_to_str:n {#1} } }
+ \int_set:Nn \l__iow_one_indent_int
+ { \str_count:N \l__iow_one_indent_tl }
+ \exp_last_unbraced:NNo
+ \cs_set:Npn \__iow_unindent:w \l__iow_one_indent_tl { }
+ }
+\exp_args:Ne \__iow_set_indent:n { \prg_replicate:nn { 4 } { ~ } }
+\tl_new:N \l__iow_indent_tl
+\int_new:N \l__iow_indent_int
+\tl_new:N \l__iow_line_tl
+\tl_new:N \l__iow_line_part_tl
+\bool_new:N \l__iow_line_break_bool
+\tl_new:N \l__iow_wrap_tl
+\group_begin:
+ \int_set:Nn \tex_escapechar:D { -1 }
+ \tl_const:Ne \c__iow_wrap_marker_tl
+ { \tl_to_str:n { \^^I \^^O \^^W \^^_ \^^W \^^R \^^A \^^P } }
+\group_end:
+\tl_map_inline:nn
+ { { end } { newline } { allow_break } { indent } { unindent } }
+ {
+ \tl_const:ce { c__iow_wrap_ #1 _marker_tl }
+ {
+ \c__iow_wrap_marker_tl
+ #1
+ \c_catcode_other_space_tl
+ }
+ }
+\cs_new_protected:Npn \iow_wrap_allow_break:
+ {
+ \msg_error:nnnn { kernel } { iow-indent }
+ { \iow_wrap:nnnN } { \iow_wrap_allow_break: }
+ }
+\cs_new:Npe \__iow_wrap_allow_break: { \c__iow_wrap_allow_break_marker_tl }
+\cs_new:Npn \__iow_wrap_allow_break_error:
+ {
+ \msg_expandable_error:nnnn { kernel } { iow-indent }
+ { \iow_wrap:nnnN } { \iow_wrap_allow_break: }
+ }
+\cs_new_protected:Npn \iow_indent:n #1
+ {
+ \msg_error:nnnnn { kernel } { iow-indent }
+ { \iow_wrap:nnnN } { \iow_indent:n } {#1}
+ #1
+ }
+\cs_new:Npe \__iow_indent:n #1
+ {
+ \c__iow_wrap_indent_marker_tl
+ #1
+ \c__iow_wrap_unindent_marker_tl
+ }
+\cs_new:Npn \__iow_indent_error:n #1
+ {
+ \msg_expandable_error:nnnnn { kernel } { iow-indent }
+ { \iow_wrap:nnnN } { \iow_indent:n } {#1}
+ #1
+ }
+\cs_new_protected:Npn \iow_wrap:nnnN #1#2#3#4
+ {
+ \group_begin:
+ \cs_if_exist_use:N \conditionally@traceoff
+ \int_set:Nn \tex_escapechar:D { -1 }
+ \cs_set:Npe \{ { \token_to_str:N \{ }
+ \cs_set:Npe \# { \token_to_str:N \# }
+ \cs_set:Npe \} { \token_to_str:N \} }
+ \cs_set:Npe \% { \token_to_str:N \% }
+ \cs_set:Npe \~ { \token_to_str:N \~ }
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \cs_set_eq:NN \\ \iow_newline:
+ \cs_set_eq:NN \ \c_catcode_other_space_tl
+ \cs_set_eq:NN \iow_wrap_allow_break: \__iow_wrap_allow_break:
+ \cs_set_eq:NN \iow_indent:n \__iow_indent:n
+ #3
+ \cs_set_eq:NN \protect \token_to_str:N
+ \__kernel_tl_set:Nx \l__iow_wrap_tl {#1}
+ \cs_set_eq:NN \iow_wrap_allow_break: \__iow_wrap_allow_break_error:
+ \cs_set_eq:NN \iow_indent:n \__iow_indent_error:n
+ \__kernel_tl_set:Nx \l__iow_newline_tl { \iow_newline: #2 }
+ \__kernel_tl_set:Nx \l__iow_newline_tl { \tl_to_str:N \l__iow_newline_tl }
+ \int_set:Nn \l__iow_line_target_int
+ { \l_iow_line_count_int - \str_count:N \l__iow_newline_tl + 1 }
+ \int_compare:nNnT { \l__iow_line_target_int } < 0
+ {
+ \tl_set:Nn \l__iow_newline_tl { \iow_newline: }
+ \int_set:Nn \l__iow_line_target_int
+ { \l_iow_line_count_int + 1 }
+ }
+ \__iow_wrap_do:
+ \exp_args:NNf \group_end:
+ #4 { \tl_to_str:N \l__iow_wrap_tl }
+ }
+\cs_generate_variant:Nn \iow_wrap:nnnN { ne }
+\cs_new_protected:Npn \__iow_wrap_do:
+ {
+ \__kernel_tl_set:Nx \l__iow_wrap_tl
+ {
+ \exp_args:No \__kernel_str_to_other_fast:n \l__iow_wrap_tl
+ \c__iow_wrap_end_marker_tl
+ }
+ \__kernel_tl_set:Nx \l__iow_wrap_tl
+ {
+ \exp_after:wN \__iow_wrap_fix_newline:w \l__iow_wrap_tl
+ ^^J \q__iow_nil ^^J \s__iow_stop
+ }
+ \exp_after:wN \__iow_wrap_start:w \l__iow_wrap_tl
+ }
+\cs_new:Npn \__iow_wrap_fix_newline:w #1 ^^J #2 ^^J
+ {
+ #1
+ \if_meaning:w \q__iow_nil #2
+ \__iow_use_i_delimit_by_s_stop:nw
+ \fi:
+ \c__iow_wrap_newline_marker_tl
+ \__iow_wrap_fix_newline:w #2 ^^J
+ }
+\cs_new_protected:Npn \__iow_wrap_start:w
+ {
+ \bool_set_false:N \l__iow_line_break_bool
+ \tl_clear:N \l__iow_line_tl
+ \tl_clear:N \l__iow_line_part_tl
+ \tl_set:Nn \l__iow_wrap_tl { ~ \use_none:n }
+ \int_zero:N \l__iow_indent_int
+ \tl_clear:N \l__iow_indent_tl
+ \__iow_wrap_chunk:nw { \l_iow_line_count_int }
+ }
+\cs_set_protected:Npn \__iow_tmp:w #1#2
+ {
+ \cs_new_protected:Npn \__iow_wrap_chunk:nw ##1##2 #2
+ {
+ \tl_if_empty:nTF {##2}
+ {
+ \tl_clear:N \l__iow_line_part_tl
+ \__iow_wrap_next:nw {##1}
+ }
+ {
+ \tl_if_empty:NTF \l__iow_line_tl
+ {
+ \__iow_wrap_line:nw
+ { \l__iow_indent_tl }
+ ##1 - \l__iow_indent_int ;
+ }
+ { \__iow_wrap_line:nw { } ##1 ; }
+ ##2 #1
+ \__iow_wrap_end_chunk:w 7 6 5 4 3 2 1 0 \s__iow_stop
+ }
+ }
+ \cs_new_protected:Npn \__iow_wrap_next:nw ##1##2 #1
+ { \use:c { __iow_wrap_##2:n } {##1} }
+ }
+\exp_args:NVV \__iow_tmp:w \c_catcode_other_space_tl \c__iow_wrap_marker_tl
+\cs_new_protected:Npn \__iow_wrap_line:nw #1
+ {
+ \tex_edef:D \l__iow_line_part_tl { \if_false: } \fi:
+ #1
+ \exp_after:wN \__iow_wrap_line_loop:w
+ \int_value:w \int_eval:w
+ }
+\cs_new:Npn \__iow_wrap_line_loop:w #1 ; #2#3#4#5#6#7#8#9
+ {
+ \if_int_compare:w #1 < 8 \exp_stop_f:
+ \__iow_wrap_line_aux:Nw #1
+ \fi:
+ #2 #3 #4 #5 #6 #7 #8 #9
+ \exp_after:wN \__iow_wrap_line_loop:w
+ \int_value:w \int_eval:w #1 - 8 ;
+ }
+\cs_new:Npn \__iow_wrap_line_aux:Nw #1#2#3 \exp_after:wN #4 ;
+ {
+ #2
+ \exp_after:wN \__iow_wrap_line_end:NnnnnnnnN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN \exp_after:wN
+ \if_case:w #1 \exp_stop_f:
+ \prg_do_nothing:
+ \or: \use_none:n
+ \or: \use_none:nn
+ \or: \use_none:nnn
+ \or: \use_none:nnnn
+ \or: \use_none:nnnnn
+ \or: \use_none:nnnnnn
+ \or: \__iow_wrap_line_seven:nnnnnnn
+ \fi:
+ { } { } { } { } { } { } { } #3
+ }
+\cs_new:Npn \__iow_wrap_line_seven:nnnnnnn #1#2#3#4#5#6#7 { \exp_stop_f: }
+\cs_new:Npn \__iow_wrap_line_end:NnnnnnnnN #1#2#3#4#5#6#7#8#9
+ {
+ #2 #3 #4 #5 #6 #7 #8
+ \use_none:nnnnn \int_eval:w 8 - ; #9
+ \token_if_eq_charcode:NNTF \c_space_token #9
+ { \__iow_wrap_line_end:nw { } }
+ { \if_false: { \fi: } \__iow_wrap_break:w #9 }
+ }
+\cs_new:Npn \__iow_wrap_line_end:nw #1
+ {
+ \if_false: { \fi: }
+ \__iow_wrap_store_do:n {#1}
+ \__iow_wrap_next_line:w
+ }
+\cs_new:Npn \__iow_wrap_end_chunk:w
+ #1 \int_eval:w #2 - #3 ; #4#5 \s__iow_stop
+ {
+ \if_false: { \fi: }
+ \exp_args:Nf \__iow_wrap_next:nw { \int_eval:n { #2 - #4 } }
+ }
+\cs_set_protected:Npn \__iow_tmp:w #1
+ {
+ \cs_new:Npn \__iow_wrap_break:w
+ {
+ \tex_edef:D \l__iow_line_part_tl
+ { \if_false: } \fi:
+ \exp_after:wN \__iow_wrap_break_first:w
+ \l__iow_line_part_tl
+ #1
+ { ? \__iow_wrap_break_end:w }
+ \s__iow_mark
+ }
+ \cs_new:Npn \__iow_wrap_break_first:w ##1 #1 ##2
+ {
+ \use_none:nn ##2 \__iow_wrap_break_none:w
+ \__iow_wrap_break_loop:w ##1 #1 ##2
+ }
+ \cs_new:Npn \__iow_wrap_break_none:w ##1##2 #1 ##3 \s__iow_mark ##4 #1
+ {
+ \tl_if_empty:NTF \l__iow_line_tl
+ { ##2 ##4 \__iow_wrap_line_end:nw { } }
+ { \__iow_wrap_line_end:nw { \__iow_wrap_trim:N } ##2 ##4 #1 }
+ }
+ \cs_new:Npn \__iow_wrap_break_loop:w ##1 #1 ##2 #1 ##3
+ {
+ \use_none:n ##3
+ ##1 #1
+ \__iow_wrap_break_loop:w ##2 #1 ##3
+ }
+ \cs_new:Npn \__iow_wrap_break_end:w ##1 #1 ##2 ##3 #1 ##4 \s__iow_mark
+ { ##1 \__iow_wrap_line_end:nw { } ##3 }
+ }
+\exp_args:NV \__iow_tmp:w \c_catcode_other_space_tl
+\cs_new_protected:Npn \__iow_wrap_next_line:w #1#2 \s__iow_stop
+ {
+ \tl_clear:N \l__iow_line_tl
+ \token_if_eq_meaning:NNTF #1 \__iow_wrap_end_chunk:w
+ {
+ \tl_clear:N \l__iow_line_part_tl
+ \bool_set_true:N \l__iow_line_break_bool
+ \__iow_wrap_next:nw { \l__iow_line_target_int }
+ }
+ {
+ \__iow_wrap_line:nw
+ { \l__iow_indent_tl }
+ \l__iow_line_target_int - \l__iow_indent_int ;
+ #1 #2 \s__iow_stop
+ }
+ }
+\cs_new_protected:Npn \__iow_wrap_allow_break:n #1
+ {
+ \__kernel_tl_set:Nx \l__iow_line_tl
+ { \l__iow_line_tl \__iow_wrap_trim:N \l__iow_line_part_tl }
+ \bool_set_false:N \l__iow_line_break_bool
+ \tl_if_empty:NTF \l__iow_line_part_tl
+ { \__iow_wrap_chunk:nw {#1} }
+ { \exp_args:Nf \__iow_wrap_chunk:nw { \int_eval:n { #1 + 1 } } }
+ }
+\cs_new_protected:Npn \__iow_wrap_indent:n #1
+ {
+ \tl_put_right:Ne \l__iow_line_tl { \l__iow_line_part_tl }
+ \bool_set_false:N \l__iow_line_break_bool
+ \int_add:Nn \l__iow_indent_int { \l__iow_one_indent_int }
+ \tl_put_right:No \l__iow_indent_tl { \l__iow_one_indent_tl }
+ \__iow_wrap_chunk:nw {#1}
+ }
+\cs_new_protected:Npn \__iow_wrap_unindent:n #1
+ {
+ \tl_put_right:Ne \l__iow_line_tl { \l__iow_line_part_tl }
+ \bool_set_false:N \l__iow_line_break_bool
+ \int_sub:Nn \l__iow_indent_int { \l__iow_one_indent_int }
+ \__kernel_tl_set:Nx \l__iow_indent_tl
+ { \exp_after:wN \__iow_unindent:w \l__iow_indent_tl }
+ \__iow_wrap_chunk:nw {#1}
+ }
+\cs_new_protected:Npn \__iow_wrap_newline:n #1
+ {
+ \bool_if:NF \l__iow_line_break_bool
+ { \__iow_wrap_store_do:n { \__iow_wrap_trim:N } }
+ \bool_set_false:N \l__iow_line_break_bool
+ \__iow_wrap_chunk:nw { \l__iow_line_target_int }
+ }
+\cs_new_protected:Npn \__iow_wrap_end:n #1
+ {
+ \bool_if:NF \l__iow_line_break_bool
+ { \__iow_wrap_store_do:n { \__iow_wrap_trim:N } }
+ \bool_set_false:N \l__iow_line_break_bool
+ }
+\cs_new_protected:Npn \__iow_wrap_store_do:n #1
+ {
+ \__kernel_tl_set:Nx \l__iow_line_tl
+ { \l__iow_line_tl \l__iow_line_part_tl }
+ \__kernel_tl_set:Nx \l__iow_wrap_tl
+ {
+ \l__iow_wrap_tl
+ \l__iow_newline_tl
+ #1 \l__iow_line_tl
+ }
+ \tl_clear:N \l__iow_line_tl
+ }
+\cs_set_protected:Npn \__iow_tmp:w #1
+ {
+ \cs_new:Npn \__iow_wrap_trim:N ##1
+ { \exp_after:wN \__iow_wrap_trim:w ##1 \s__iow_mark #1 \s__iow_mark \s__iow_stop }
+ \cs_new:Npn \__iow_wrap_trim:w ##1 #1 \s__iow_mark
+ { \__iow_wrap_trim_aux:w ##1 \s__iow_mark }
+ \cs_new:Npn \__iow_wrap_trim_aux:w ##1 \s__iow_mark ##2 \s__iow_stop {##1}
+ }
+\exp_args:NV \__iow_tmp:w \c_catcode_other_space_tl
+\tl_new:N \l__file_internal_tl
+\str_new:N \g_file_curr_dir_str
+\str_new:N \g_file_curr_ext_str
+\str_new:N \g_file_curr_name_str
+\seq_new:N \g__file_stack_seq
+\group_begin:
+ \cs_set_protected:Npn \__file_tmp:w #1#2#3
+ {
+ \tl_if_blank:nTF {#1}
+ {
+ \cs_set:Npn \__file_tmp:w ##1 " ##2 " ##3 \s__file_stop
+ { { } {##2} { } }
+ \seq_gput_right:Ne \g__file_stack_seq
+ {
+ \exp_after:wN \__file_tmp:w \tex_jobname:D
+ " \tex_jobname:D " \s__file_stop
+ }
+ }
+ {
+ \seq_gput_right:Nn \g__file_stack_seq { { } {#1} {#2} }
+ \__file_tmp:w
+ }
+ }
+ \cs_if_exist:NT \@currnamestack
+ {
+ \tl_if_empty:NF \@currnamestack
+ { \exp_after:wN \__file_tmp:w \@currnamestack }
+ }
+\group_end:
+\seq_new:N \g__file_record_seq
+\tl_new:N \l__file_base_name_tl
+\tl_new:N \l__file_full_name_tl
+\str_new:N \l__file_dir_str
+\str_new:N \l__file_ext_str
+\str_new:N \l__file_name_str
+\seq_new:N \l_file_search_path_seq
+\seq_new:N \l__file_tmp_seq
+\scan_new:N \s__file_stop
+\quark_new:N \q__file_nil
+\__kernel_quark_new_conditional:Nn \__file_quark_if_nil:n { TF }
+\quark_new:N \q__file_recursion_tail
+\quark_new:N \q__file_recursion_stop
+\__kernel_quark_new_test:N \__file_if_recursion_tail_stop:N
+\__kernel_quark_new_test:N \__file_if_recursion_tail_stop_do:nn
+\cs_new:Npn \__kernel_file_name_sanitize:n #1
+ {
+ \exp_args:Ne \__file_name_trim_spaces:n
+ {
+ \exp_args:Ne \__file_name_strip_quotes:n
+ { \__file_name_expand:n {#1} }
+ }
+ }
+\cs_new:Npn \__file_name_expand:n #1
+ {
+ \exp_after:wN \__file_name_expand_cleanup:Nw
+ \cs:w __file_name = #1 \cs_end:
+ \__file_name_expand_end:
+ }
+\cs_new:Npn \__file_name_expand_cleanup:Nw #1 #2 \__file_name_expand_end:
+ {
+ \tl_if_empty:nF {#2}
+ { \__file_name_expand_error:Nw #2 \__file_name_expand_end: }
+ \exp_after:wN \__file_name_expand_cleanup:w \token_to_str:N #1
+ }
+\exp_last_unbraced:NNNNo
+\cs_new:Npn \__file_name_expand_cleanup:w #1 \tl_to_str:n { __file_name = } { }
+\cs_new:Npn \__file_name_expand_end:
+ {
+ \msg_expandable_error:nn
+ { kernel } { filename-missing-endcsname }
+ \cs_end: \__file_name_expand_end:
+ }
+\cs_new:Npn \__file_name_expand_error:Nw #1 #2 \__file_name_expand_end:
+ { \__file_name_expand_error_aux:Nw #1 #2 \cs_end: \__file_name_expand_end: }
+\cs_new:Npn \__file_name_expand_error_aux:Nw #1 #2 \cs_end: #3
+ \__file_name_expand_end:
+ {
+ \msg_expandable_error:nnff
+ { kernel } { filename-chars-lost }
+ { \token_to_str:N #1 } { \exp_stop_f: #2 }
+ }
+\cs_new:Npn \__file_name_strip_quotes:n #1
+ {
+ \__file_name_strip_quotes:nw { 0 }
+ #1 " \q__file_recursion_tail " \q__file_recursion_stop {#1}
+ }
+\cs_new:Npn \__file_name_strip_quotes:nw #1#2 "
+ {
+ \if_meaning:w \q__file_recursion_tail #2
+ \__file_name_strip_quotes_end:wnwn
+ \fi:
+ #2
+ \__file_name_strip_quotes:nw { #1 + 1 }
+ }
+\cs_new:Npn \__file_name_strip_quotes_end:wnwn \fi: #1
+ \__file_name_strip_quotes:nw #2 \q__file_recursion_stop #3
+ {
+ \fi:
+ \int_if_odd:nT {#2}
+ {
+ \msg_expandable_error:nnn
+ { kernel } { unbalanced-quote-in-filename } {#3}
+ }
+ }
+\cs_new:Npn \__file_name_trim_spaces:n #1
+ { \__file_name_trim_spaces:nw {#1} #1 . \q__file_nil . \s__file_stop }
+\cs_new:Npn \__file_name_trim_spaces:nw #1#2 . #3 . #4 \s__file_stop
+ {
+ \__file_quark_if_nil:nTF {#3}
+ {
+ \tl_trim_spaces_apply:nN { #1 \s__file_stop }
+ \__file_name_trim_spaces_aux:n
+ }
+ { \tl_trim_spaces:n {#1} }
+ }
+\cs_new:Npn \__file_name_trim_spaces_aux:n #1
+ { \__file_name_trim_spaces_aux:w #1 }
+\cs_new:Npn \__file_name_trim_spaces_aux:w #1 \s__file_stop {#1}
+\cs_new:Npn \__kernel_file_name_quote:n #1
+ { \__file_name_quote:nw {#1} #1 ~ \q__file_nil \s__file_stop }
+\cs_new:Npn \__file_name_quote:nw #1 #2 ~ #3 \s__file_stop
+ {
+ \__file_quark_if_nil:nTF {#3}
+ { #1 }
+ { "#1" }
+ }
+\tl_const:Ne \c__file_marker_tl { : \token_to_str:N : }
+\cs_new_protected:Npn \file_get:nnN #1#2#3
+ {
+ \file_get:nnNF {#1} {#2} #3
+ { \tl_set:Nn #3 { \q_no_value } }
+ }
+\cs_generate_variant:Nn \file_get:nnN { V }
+\prg_new_protected_conditional:Npnn \file_get:nnN #1#2#3 { T , F , TF }
+ {
+ \file_get_full_name:nNTF {#1} \l__file_full_name_tl
+ {
+ \exp_args:NV \__file_get_aux:nnN
+ \l__file_full_name_tl
+ {#2} #3
+ \prg_return_true:
+ }
+ { \prg_return_false: }
+ }
+\prg_generate_conditional_variant:Nnn \file_get:nnN { V } { T , F , TF }
+\cs_new_protected:Npe \__file_get_aux:nnN #1#2#3
+ {
+ \exp_not:N \if_false: { \exp_not:N \fi:
+ \group_begin:
+ \int_set_eq:NN \tex_tracingnesting:D \c_zero_int
+ \exp_not:N \exp_args:No \tex_everyeof:D
+ { \exp_not:N \c__file_marker_tl }
+ #2 \scan_stop:
+ \exp_not:N \exp_after:wN \exp_not:N \__file_get_do:Nw
+ \exp_not:N \exp_after:wN #3
+ \exp_not:N \exp_after:wN \exp_not:N \prg_do_nothing:
+ \exp_not:N \tex_input:D
+ \sys_if_engine_luatex:TF
+ { {#1} }
+ { \exp_not:N \__kernel_file_name_quote:n {#1} \scan_stop: }
+ \exp_not:N \if_false: } \exp_not:N \fi:
+ }
+\exp_args:Nno \use:nn
+ { \cs_new_protected:Npn \__file_get_do:Nw #1#2 }
+ { \c__file_marker_tl }
+ {
+ \group_end:
+ \tl_set:No #1 {#2}
+ }
+\cs_new_eq:NN \__file_size:n \tex_filesize:D
+\cs_new:Npn \file_full_name:n #1
+ {
+ \exp_args:Ne \__file_full_name:n
+ { \__kernel_file_name_sanitize:n {#1} }
+ }
+\cs_generate_variant:Nn \file_full_name:n { V }
+\cs_new:Npn \__file_full_name:n #1
+ {
+ \tl_if_blank:nF {#1}
+ { \exp_args:Nne \__file_full_name_auxii:nn {#1} { \__file_full_name_aux:n {#1} } }
+ }
+\cs_new:Npn \__file_full_name_aux:n #1
+ {
+ \if_cs_exist:w __file_seen_ \tl_to_str:n {#1} : \cs_end:
+ -1
+ \else:
+ \exp_args:Ne \__file_full_name_auxi:nn { \__file_size:n {#1} } {#1}
+ \fi:
+ }
+\cs_new:Npn \__file_full_name_auxi:nn #1#2
+ {
+ \if:w \scan_stop: #1 \scan_stop:
+ \else:
+ \exp_after:wN \use_none:n
+ \cs:w __file_seen_ \tl_to_str:n {#2} : \cs_end:
+ #1
+ \fi:
+ }
+\cs_new:Npn \__file_full_name_auxii:nn #1 #2
+ {
+ \tl_if_blank:nTF {#2}
+ {
+ \seq_map_tokens:Nn \l_file_search_path_seq
+ { \__file_full_name_aux:Nnn \seq_map_break:n {#1} }
+ \cs_if_exist:NT \input@path
+ {
+ \tl_map_tokens:Nn \input@path
+ { \__file_full_name_aux:Nnn \tl_map_break:n {#1} }
+ }
+ \__file_name_end:
+ }
+ { \__file_ext_check:nn {#1} {#2} }
+ }
+\cs_new:Npn \__file_full_name_aux:Nnn #1#2#3
+ {
+ \exp_args:Ne \__file_full_name_aux:nN
+ { \__file_full_name_slash:n {#3} #2 }
+ #1
+ }
+\cs_new:Npn \__file_full_name_slash:n #1
+ {
+ \__file_full_name_slash:nw {#1} #1 \q_nil / \q_nil / \q_nil \q_stop
+ }
+\cs_new:Npn \__file_full_name_slash:nw #1#2 / \q_nil / #3 \q_stop
+ {
+ \quark_if_nil:nTF {#3}
+ { #1 / }
+ { #2 / }
+ }
+\cs_new:Npn \__file_full_name_aux:nN #1
+ { \exp_args:Nne \__file_full_name_aux:nnN {#1} { \__file_full_name_aux:n {#1} } }
+\cs_new:Npn \__file_full_name_aux:nnN #1 #2 #3
+ {
+ \tl_if_blank:nF {#2}
+ {
+ #3
+ {
+ \__file_ext_check:nn {#1} {#2}
+ \__file_name_cleanup:w
+ }
+ }
+ }
+\cs_new:Npn \__file_name_cleanup:w #1 \__file_name_end: { }
+\cs_new:Npn \__file_name_end: { }
+\cs_new:Npn \__file_ext_check:nn #1 #2
+{ \__file_ext_check:nnw {#2} { / } #1 / \q__file_nil / \s__file_stop }
+\cs_new:Npn \__file_ext_check:nnw #1 #2 #3 / #4 / #5 \s__file_stop
+ {
+ \__file_quark_if_nil:nTF {#4}
+ {
+ \exp_args:No \__file_ext_check:nnnw
+ { \use_none:n #2 } {#1} {#3} #3 . \q__file_nil . \s__file_stop
+ }
+ { \__file_ext_check:nnw {#1} { #2 #3 / } #4 / #5 \s__file_stop }
+ }
+\cs_new:Npe \__file_ext_check:nnnw #1#2#3#4 . #5 . #6 \s__file_stop
+ {
+ \exp_not:N \__file_quark_if_nil:nTF {#5}
+ {
+ \exp_not:N \__file_ext_check:nnn
+ { #1 #3 \tl_to_str:n { .tex } } { #1 #3 } {#2}
+ }
+ { #1 #3 }
+ }
+\cs_new:Npn \__file_ext_check:nnn #1
+ { \exp_args:Nne \__file_ext_check:nnnn {#1} { \__file_full_name_aux:n {#1} } }
+\cs_new:Npn \__file_ext_check:nnnn #1#2#3#4
+ {
+ \tl_if_blank:nTF {#2}
+ {#3}
+ {
+ \bool_lazy_or:nnTF
+ { \int_compare_p:nNn {#4} = {#2} }
+ { \int_compare_p:nNn {#2} = { -1 } }
+ {#1}
+ {#3}
+ }
+ }
+\cs_new_protected:Npn \file_get_full_name:nN #1#2
+ {
+ \file_get_full_name:nNF {#1} #2
+ { \tl_set:Nn #2 { \q_no_value } }
+ }
+\cs_generate_variant:Nn \file_get_full_name:nN { V }
+\prg_new_protected_conditional:Npnn \file_get_full_name:nN #1#2 { T , F , TF }
+ {
+ \__kernel_tl_set:Nx #2
+ { \file_full_name:n {#1} }
+ \tl_if_empty:NTF #2
+ { \prg_return_false: }
+ { \prg_return_true: }
+ }
+\prg_generate_conditional_variant:Nnn \file_get_full_name:nN
+ { V } { T , F , TF }
+\ior_new:N \g__file_internal_ior
+\cs_new:Npn \file_size:n #1
+ { \__file_details:nn {#1} { size } }
+\cs_generate_variant:Nn \file_size:n { V }
+\cs_new:Npn \file_timestamp:n #1
+ { \__file_details:nn {#1} { moddate } }
+\cs_generate_variant:Nn \file_timestamp:n { V }
+\cs_new:Npn \__file_details:nn #1#2
+ {
+ \exp_args:Ne \__file_details_aux:nn
+ { \file_full_name:n {#1} } {#2}
+ }
+\cs_new:Npn \__file_details_aux:nn #1#2
+ {
+ \tl_if_blank:nF {#1}
+ { \use:c { tex_file #2 :D } {#1} }
+ }
+\cs_new:Npn \file_mdfive_hash:n #1
+ { \exp_args:Ne \__file_mdfive_hash:n { \file_full_name:n {#1} } }
+\cs_generate_variant:Nn \file_mdfive_hash:n { V }
+\cs_new:Npn \__file_mdfive_hash:n #1
+ { \tex_mdfivesum:D file {#1} }
+\cs_new:Npn \file_hex_dump:nnn #1#2#3
+ {
+ \exp_args:Neee \__file_hex_dump_auxi:nnn
+ { \file_full_name:n {#1} }
+ { \int_eval:n {#2} }
+ { \int_eval:n {#3} }
+ }
+\cs_generate_variant:Nn \file_hex_dump:nnn { V }
+\cs_new:Npn \__file_hex_dump_auxi:nnn #1#2#3
+ {
+ \bool_lazy_any:nF
+ {
+ { \tl_if_blank_p:n {#1} }
+ { \int_compare_p:nNn {#2} = 0 }
+ { \int_compare_p:nNn {#3} = 0 }
+ }
+ {
+ \exp_args:Ne \__file_hex_dump_auxii:nnnn
+ { \__file_details_aux:nn {#1} { size } }
+ {#1} {#2} {#3}
+ }
+ }
+\cs_new:Npn \__file_hex_dump_auxii:nnnn #1#2#3#4
+ {
+ \int_compare:nNnTF {#3} > 0
+ { \__file_hex_dump_auxiii:nnnn {#3} }
+ {
+ \exp_args:Ne \__file_hex_dump_auxiii:nnnn
+ { \int_eval:n { #1 + #3 } }
+ }
+ {#1} {#2} {#4}
+ }
+\cs_new:Npn \__file_hex_dump_auxiii:nnnn #1#2#3#4
+ {
+ \int_compare:nNnTF {#4} > 0
+ { \__file_hex_dump_auxiv:nnn {#4} }
+ {
+ \exp_args:Ne \__file_hex_dump_auxiv:nnn
+ { \int_eval:n { #2 + #4 } }
+ }
+ {#1} {#3}
+ }
+\cs_new:Npn \__file_hex_dump_auxiv:nnn #1#2#3
+ {
+ \tex_filedump:D
+ offset ~ \int_eval:n { #2 - 1 } ~
+ length ~ \int_eval:n { #1 - #2 + 1 }
+ {#3}
+ }
+\cs_new:Npn \file_hex_dump:n #1
+ { \exp_args:Ne \__file_hex_dump:n { \file_full_name:n {#1} } }
+\cs_generate_variant:Nn \file_hex_dump:n { V }
+\sys_if_engine_luatex:TF
+ {
+ \cs_new:Npn \__file_hex_dump:n #1
+ {
+ \tl_if_blank:nF {#1}
+ { \tex_filedump:D whole {#1} {#1} }
+ }
+ }
+ {
+ \cs_new:Npn \__file_hex_dump:n #1
+ {
+ \tl_if_blank:nF {#1}
+ { \tex_filedump:D length \tex_filesize:D {#1} {#1} }
+ }
+ }
+\cs_new_protected:Npn \file_get_hex_dump:nN #1#2
+ { \file_get_hex_dump:nNF {#1} #2 { \tl_set:Nn #2 { \q_no_value } } }
+\cs_generate_variant:Nn \file_get_hex_dump:nN { V }
+\cs_new_protected:Npn \file_get_mdfive_hash:nN #1#2
+ { \file_get_mdfive_hash:nNF {#1} #2 { \tl_set:Nn #2 { \q_no_value } } }
+\cs_generate_variant:Nn \file_get_mdfive_hash:nN { V }
+\cs_new_protected:Npn \file_get_size:nN #1#2
+ { \file_get_size:nNF {#1} #2 { \tl_set:Nn #2 { \q_no_value } } }
+\cs_generate_variant:Nn \file_get_size:nN { V }
+\cs_new_protected:Npn \file_get_timestamp:nN #1#2
+ { \file_get_timestamp:nNF {#1} #2 { \tl_set:Nn #2 { \q_no_value } } }
+\cs_generate_variant:Nn \file_get_timestamp:nN { V }
+\prg_new_protected_conditional:Npnn \file_get_hex_dump:nN #1#2 { T , F , TF }
+ { \__file_get_details:nnN {#1} { hex_dump } #2 }
+\prg_generate_conditional_variant:Nnn \file_get_hex_dump:nN
+ { V } { T , F , TF }
+\prg_new_protected_conditional:Npnn \file_get_mdfive_hash:nN #1#2 { T , F , TF }
+ { \__file_get_details:nnN {#1} { mdfive_hash } #2 }
+\prg_generate_conditional_variant:Nnn \file_get_mdfive_hash:nN
+ { V } { T , F , TF }
+\prg_new_protected_conditional:Npnn \file_get_size:nN #1#2 { T , F , TF }
+ { \__file_get_details:nnN {#1} { size } #2 }
+\prg_generate_conditional_variant:Nnn \file_get_size:nN
+ { V } { T , F , TF }
+\prg_new_protected_conditional:Npnn \file_get_timestamp:nN #1#2 { T , F , TF }
+ { \__file_get_details:nnN {#1} { timestamp } #2 }
+\prg_generate_conditional_variant:Nnn \file_get_timestamp:nN
+ { V } { T , F , TF }
+\cs_new_protected:Npn \__file_get_details:nnN #1#2#3
+ {
+ \__kernel_tl_set:Nx #3
+ { \use:c { file_ #2 :n } {#1} }
+ \tl_if_empty:NTF #3
+ { \prg_return_false: }
+ { \prg_return_true: }
+ }
+\cs_new_protected:Npn \file_get_hex_dump:nnnN #1#2#3#4
+ {
+ \file_get_hex_dump:nnnNF {#1} {#2} {#3} #4
+ { \tl_set:Nn #4 { \q_no_value } }
+ }
+\cs_generate_variant:Nn \file_get_hex_dump:nnnN { V }
+\prg_new_protected_conditional:Npnn \file_get_hex_dump:nnnN #1#2#3#4
+ { T , F , TF }
+ {
+ \__kernel_tl_set:Nx #4
+ { \file_hex_dump:nnn {#1} {#2} {#3} }
+ \tl_if_empty:NTF #4
+ { \prg_return_false: }
+ { \prg_return_true: }
+ }
+\prg_generate_conditional_variant:Nnn \file_get_hex_dump:nnnN
+ { V } { T , F , TF }
+\cs_new_eq:NN \__file_str_cmp:nn \tex_strcmp:D
+\prg_new_conditional:Npnn \file_compare_timestamp:nNn #1#2#3
+ { p , T , F , TF }
+ {
+ \exp_args:Nee \__file_compare_timestamp:nnN
+ { \file_full_name:n {#1} }
+ { \file_full_name:n {#3} }
+ #2
+ }
+\prg_generate_conditional_variant:Nnn \file_compare_timestamp:nNn
+ { nNV , V , VNV } { p , T , F , TF }
+\cs_new:Npn \__file_compare_timestamp:nnN #1#2#3
+ {
+ \tl_if_blank:nTF {#1}
+ {
+ \if_charcode:w #3 <
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ {
+ \tl_if_blank:nTF {#2}
+ {
+ \if_charcode:w #3 >
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ {
+ \if_int_compare:w
+ \__file_str_cmp:nn
+ { \__file_timestamp:n {#1} }
+ { \__file_timestamp:n {#2} }
+ #3 \c_zero_int
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ }
+ }
+\cs_new_eq:NN \__file_timestamp:n \tex_filemoddate:D
+\prg_new_conditional:Npnn \file_if_exist:n #1 { p , T , F , TF }
+ {
+ \tl_if_blank:eTF { \file_full_name:n {#1} }
+ { \prg_return_false: }
+ { \prg_return_true: }
+ }
+\prg_generate_conditional_variant:Nnn \file_if_exist:n { V } { p , T , F , TF }
+\cs_new_protected:Npn \file_if_exist_input:n #1
+ {
+ \file_get_full_name:nNT {#1} \l__file_full_name_tl
+ { \__file_input:V \l__file_full_name_tl }
+ }
+\cs_generate_variant:Nn \file_if_exist_input:n { V }
+\cs_new_protected:Npn \file_if_exist_input:nF #1#2
+ {
+ \file_get_full_name:nNTF {#1} \l__file_full_name_tl
+ { \__file_input:V \l__file_full_name_tl }
+ {#2}
+ }
+\cs_generate_variant:Nn \file_if_exist_input:nF { V }
+\cs_new_protected:Npn \file_input_stop: { \tex_endinput:D }
+\cs_new_protected:Npn \__kernel_file_missing:n #1
+ {
+ \msg_error:nne { kernel } { file-not-found }
+ { \__kernel_file_name_sanitize:n {#1} }
+ }
+\cs_new_protected:Npn \file_input:n #1
+ {
+ \file_get_full_name:nNTF {#1} \l__file_full_name_tl
+ { \__file_input:V \l__file_full_name_tl }
+ { \__kernel_file_missing:n {#1} }
+ }
+\cs_generate_variant:Nn \file_input:n { V }
+\cs_new_protected:Npe \__file_input:n #1
+ {
+ \exp_not:N \clist_if_exist:NTF \exp_not:N \@filelist
+ { \exp_not:N \@addtofilelist {#1} }
+ { \seq_gput_right:Nn \exp_not:N \g__file_record_seq {#1} }
+ \exp_not:N \__file_input_push:n {#1}
+ \exp_not:N \tex_input:D
+ \sys_if_engine_luatex:TF
+ { {#1} }
+ { \exp_not:N \__kernel_file_name_quote:n {#1} \scan_stop: }
+ \exp_not:N \__file_input_pop:
+ }
+\cs_generate_variant:Nn \__file_input:n { V }
+\cs_new_protected:Npn \__file_input_push:n #1
+ {
+ \seq_gpush:Ne \g__file_stack_seq
+ {
+ { \g_file_curr_dir_str }
+ { \g_file_curr_name_str }
+ { \g_file_curr_ext_str }
+ }
+ \file_parse_full_name:nNNN {#1}
+ \l__file_dir_str \l__file_name_str \l__file_ext_str
+ \str_gset_eq:NN \g_file_curr_dir_str \l__file_dir_str
+ \str_gset_eq:NN \g_file_curr_name_str \l__file_name_str
+ \str_gset_eq:NN \g_file_curr_ext_str \l__file_ext_str
+ }
+\cs_new_eq:NN \__kernel_file_input_push:n \__file_input_push:n
+\cs_new_protected:Npn \__file_input_pop:
+ {
+ \seq_gpop:NN \g__file_stack_seq \l__file_internal_tl
+ \exp_after:wN \__file_input_pop:nnn \l__file_internal_tl
+ }
+\cs_new_eq:NN \__kernel_file_input_pop: \__file_input_pop:
+\cs_new_protected:Npn \__file_input_pop:nnn #1#2#3
+ {
+ \str_gset:Nn \g_file_curr_dir_str {#1}
+ \str_gset:Nn \g_file_curr_name_str {#2}
+ \str_gset:Nn \g_file_curr_ext_str {#3}
+ }
+\cs_new:Npn \file_input_raw:n #1
+ { \exp_args:Ne \__file_input_raw:nn { \file_full_name:n {#1} } {#1} }
+\cs_generate_variant:Nn \file_input_raw:n { V }
+\cs_new:Npe \__file_input_raw:nn #1#2
+ {
+ \exp_not:N \tl_if_blank:nTF {#1}
+ {
+ \exp_not:N \exp_args:Nnne \exp_not:N \msg_expandable_error:nnn
+ { kernel } { file-not-found }
+ { \exp_not:N \__kernel_file_name_sanitize:n {#2} }
+ }
+ {
+ \exp_not:N \tex_input:D
+ \sys_if_engine_luatex:TF
+ { {#1} }
+ { \exp_not:N \__kernel_file_name_quote:n {#1} \scan_stop: }
+ }
+ }
+\exp_args_generate:n { nne }
+\cs_new:Npn \file_parse_full_name:n #1
+ {
+ \file_parse_full_name_apply:nN {#1}
+ \prg_do_nothing:
+ }
+\cs_generate_variant:Nn \file_parse_full_name:n { V }
+\cs_new:Npn \file_parse_full_name_apply:nN #1
+ {
+ \exp_args:Ne \__file_parse_full_name_auxi:nN
+ { \__kernel_file_name_sanitize:n {#1} }
+ }
+\cs_generate_variant:Nn \file_parse_full_name_apply:nN { V }
+\cs_new:Npn \__file_parse_full_name_auxi:nN #1
+ {
+ \__file_parse_full_name_area:nw { } #1
+ / \s__file_stop
+ }
+\cs_new:Npn \__file_parse_full_name_area:nw #1 #2 / #3 \s__file_stop
+ {
+ \tl_if_empty:nTF {#3}
+ { \__file_parse_full_name_base:nw { } #2 . \s__file_stop {#1} }
+ { \__file_parse_full_name_area:nw { #1 / #2 } #3 \s__file_stop }
+ }
+\cs_new:Npn \__file_parse_full_name_base:nw #1 #2 . #3 \s__file_stop
+ {
+ \tl_if_empty:nTF {#3}
+ {
+ \tl_if_empty:nTF {#1}
+ {
+ \tl_if_empty:nTF {#2}
+ { \__file_parse_full_name_tidy:nnnN { } { } }
+ { \__file_parse_full_name_tidy:nnnN { .#2 } { } }
+ }
+ { \__file_parse_full_name_tidy:nnnN {#1} { .#2 } }
+ }
+ { \__file_parse_full_name_base:nw { #1 . #2 } #3 \s__file_stop }
+ }
+\cs_new:Npn \__file_parse_full_name_tidy:nnnN #1 #2 #3 #4
+ {
+ \exp_args:Nee #4
+ {
+ \str_if_eq:nnF {#3} { / } { \use_none:n }
+ #3 \prg_do_nothing:
+ }
+ { \use_none:n #1 \prg_do_nothing: }
+ {#2}
+ }
+\cs_new_protected:Npn \file_parse_full_name:nNNN #1 #2 #3 #4
+ {
+ \file_parse_full_name_apply:nN {#1}
+ \__file_full_name_assign:nnnNNN #2 #3 #4
+ }
+\cs_new_protected:Npn \__file_full_name_assign:nnnNNN #1 #2 #3 #4 #5 #6
+ {
+ \str_set:Nn #4 {#1}
+ \str_set:Nn #5 {#2}
+ \str_set:Nn #6 {#3}
+ }
+\cs_generate_variant:Nn \file_parse_full_name:nNNN { V }
+\cs_new_protected:Npn \file_show_list: { \__file_list:N \msg_show:nneeee }
+\cs_new_protected:Npn \file_log_list: { \__file_list:N \msg_log:nneeee }
+\cs_new_protected:Npn \__file_list:N #1
+ {
+ \seq_clear:N \l__file_tmp_seq
+ \clist_if_exist:NT \@filelist
+ {
+ \exp_args:NNe \seq_set_from_clist:Nn \l__file_tmp_seq
+ { \tl_to_str:N \@filelist }
+ }
+ \seq_concat:NNN \l__file_tmp_seq \l__file_tmp_seq \g__file_record_seq
+ \seq_remove_duplicates:N \l__file_tmp_seq
+ #1 { kernel } { file-list }
+ { \seq_map_function:NN \l__file_tmp_seq \__file_list_aux:n }
+ { } { } { }
+ }
+\cs_new:Npn \__file_list_aux:n #1 { \iow_newline: #1 }
+\cs_if_exist:NT \@filelist
+ {
+ \AtBeginDocument
+ {
+ \exp_args:NNe \seq_set_from_clist:Nn \l__file_tmp_seq
+ { \tl_to_str:N \@filelist }
+ \seq_gconcat:NNN
+ \g__file_record_seq
+ \g__file_record_seq
+ \l__file_tmp_seq
+ }
+ }
+\cs_new_protected:Npn \GetIdInfo
+ {
+ \tl_clear_new:N \ExplFileDescription
+ \tl_clear_new:N \ExplFileDate
+ \tl_clear_new:N \ExplFileName
+ \tl_clear_new:N \ExplFileExtension
+ \tl_clear_new:N \ExplFileVersion
+ \group_begin:
+ \char_set_catcode_space:n { 32 }
+ \exp_after:wN
+ \group_end:
+ \__file_id_info_auxi:w
+ }
+\cs_new_protected:Npn \__file_id_info_auxi:w $ #1 $ #2
+ {
+ \tl_set:Nn \ExplFileDescription {#2}
+ \str_if_eq:nnTF {#1} { Id }
+ {
+ \tl_set:Nn \ExplFileDate { 0000/00/00 }
+ \tl_set:Nn \ExplFileName { [unknown] }
+ \tl_set:Nn \ExplFileExtension { [unknown~extension] }
+ \tl_set:Nn \ExplFileVersion {-1}
+ }
+ { \__file_id_info_auxii:w #1 ~ \s__file_stop }
+ }
+\cs_new_protected:Npn \__file_id_info_auxii:w
+ #1 ~ #2.#3 ~ #4 ~ #5 ~ #6 \s__file_stop
+ {
+ \tl_set:Nn \ExplFileName {#2}
+ \tl_set:Nn \ExplFileExtension {#3}
+ \tl_set:Nn \ExplFileVersion {#4}
+ \str_if_eq:nnTF {#4} {-1}
+ { \tl_set:Nn \ExplFileDate { 0000/00/00 } }
+ { \__file_id_info_auxiii:w #5 - 0 - 0 - \s__file_stop }
+ }
+\cs_new_protected:Npn \__file_id_info_auxiii:w #1 - #2 - #3 - #4 \s__file_stop
+ { \tl_set:Nn \ExplFileDate { #1/#2/#3 } }
+\cs_new_protected:Npn \__kernel_dependency_version_check:Nn #1
+ { \exp_args:NV \__kernel_dependency_version_check:nn #1 }
+\cs_new_protected:Npn \__kernel_dependency_version_check:nn #1
+ {
+ \cs_if_exist:NTF \c__kernel_expl_date_tl
+ {
+ \exp_args:NV \__file_kernel_dependency_compare:nnn
+ \c__kernel_expl_date_tl {#1}
+ }
+ { \__file_kernel_dependency_compare:nnn { 0000-00-00 } {#1} }
+ }
+\cs_new_protected:Npn \__file_kernel_dependency_compare:nnn #1 #2 #3
+ {
+ \int_compare:nNnT
+ { \__file_parse_version:w #1 \s__file_stop } <
+ { \__file_parse_version:w #2 \s__file_stop }
+ { \__file_mismatched_dependency_error:nn {#2} {#3} }
+ }
+\cs_new:Npn \__file_parse_version:w #1 - #2 - #3 \s__file_stop {#1#2#3}
+\cs_new_protected:Npn \__file_mismatched_dependency_error:nn #1 #2
+ {
+ \exp_args:NNe \ior_shell_open:Nn \g__file_internal_ior
+ {
+ kpsewhich ~ --all ~
+ --engine = \c_sys_engine_exec_str
+ \c_space_tl \c_sys_engine_format_str
+ \bool_lazy_and:nnT
+ { \tl_if_exist_p:N \development@branch@name }
+ { ! \tl_if_empty_p:N \development@branch@name }
+ { -dev } .fmt
+ }
+ \seq_clear:N \l__file_tmp_seq
+ \ior_map_inline:Nn \g__file_internal_ior
+ { \seq_put_right:Nn \l__file_tmp_seq {##1} }
+ \ior_close:N \g__file_internal_ior
+ \msg_error:nnnn { kernel } { mismatched-support-file }
+ {#1} {#2}
+ \tex_endinput:D
+ }
+\msg_new:nnnn { kernel } { mismatched-support-file }
+ {
+ Mismatched~LaTeX~support~files~detected. \\
+ Loading~'#2'~aborted!
+ \tl_if_exist:NT \c__kernel_expl_date_tl
+ {
+ \\ \\
+ The~L3~programming~layer~in~the~LaTeX~format \\
+ is~dated~\c__kernel_expl_date_tl,~but~in~your~TeX~
+ tree~the~files~require \\ at~least~#1.
+ }
+ }
+ {
+ \int_compare:nNnTF { \seq_count:N \l__file_tmp_seq } > 1
+ {
+ The~cause~seems~to~be~an~old~format~file~in~the~user~tree. \\
+ LaTeX~found~these~files:
+ \seq_map_tokens:Nn \l__file_tmp_seq { \\~-~\use:n } \\
+ Try~deleting~the~file~in~the~user~tree~then~run~LaTeX~again.
+ }
+ {
+ The~most~likely~causes~are:
+ \\~-~A~recent~format~generation~failed;
+ \\~-~A~stray~format~file~in~the~user~tree~which~needs~
+ to~be~removed~or~rebuilt;
+ \\~-~You~are~running~a~manually~installed~version~of~#2 \\
+ \ \ \ which~is~incompatible~with~the~version~in~LaTeX. \\
+ }
+ \\
+ LaTeX~will~abort~loading~the~incompatible~support~files~
+ but~this~may~lead~to \\ later~errors.~Please~ensure~that~
+ your~LaTeX~format~is~correctly~regenerated.
+ }
+\msg_new:nnnn { kernel } { file-not-found }
+ { File~'#1'~not~found. }
+ {
+ The~requested~file~could~not~be~found~in~the~current~directory,~
+ in~the~TeX~search~path~or~in~the~LaTeX~search~path.
+ }
+\msg_new:nnn { kernel } { file-list }
+ {
+ >~File~List~<
+ #1 \\
+ .............
+ }
+\msg_new:nnnn { kernel } { filename-chars-lost }
+ { #1~invalid~in~file~name.~Lost:~#2. }
+ {
+ There~was~an~invalid~token~in~the~file~name~that~caused~
+ the~characters~following~it~to~be~lost.
+ }
+\msg_new:nnnn { kernel } { filename-missing-endcsname }
+ { Missing~\iow_char:N\\endcsname~inserted~in~filename. }
+ {
+ The~file~name~had~more~\iow_char:N\\csname~commands~than~
+ \iow_char:N\\endcsname~ones.~LaTeX~will~add~the~missing~
+ \iow_char:N\\endcsname~and~try~to~continue~as~best~as~it~can.
+ }
+\msg_new:nnnn { kernel } { unbalanced-quote-in-filename }
+ { Unbalanced~quotes~in~file~name~'#1'. }
+ {
+ File~names~must~contain~balanced~numbers~of~quotes~(").
+ }
+\msg_new:nnnn { kernel } { iow-indent }
+ { Only~#1 allows~#2 }
+ {
+ The~command~#2 can~only~be~used~in~messages~
+ which~will~be~wrapped~using~#1.
+ \tl_if_empty:nF {#3} { ~ It~was~called~with~argument~'#3'. }
+ }
+\sys_if_engine_luatex:TF
+ {
+ \str_const:Ne \c_sys_platform_str
+ { \tex_directlua:D { tex.print(os.type) } }
+ }
+ {
+ \file_if_exist:nTF { nul: }
+ {
+ \file_if_exist:nF { /dev/null }
+ { \str_const:Nn \c_sys_platform_str { windows } }
+ }
+ {
+ \file_if_exist:nT { /dev/null }
+ { \str_const:Nn \c_sys_platform_str { unix } }
+ }
+ }
+\cs_if_exist:NF \c_sys_platform_str
+ { \str_const:Nn \c_sys_platform_str { unknown } }
+\clist_map_inline:nn { unix , windows }
+ {
+ \__sys_const:nn { sys_if_platform_ #1 }
+ { \str_if_eq_p:Vn \c_sys_platform_str { #1 } }
+ }
+%% File: l3skip.dtx
+\cs_new_eq:NN \if_dim:w \tex_ifdim:D
+\cs_new_eq:NN \__dim_eval:w \tex_dimexpr:D
+\cs_new_eq:NN \__dim_eval_end: \tex_relax:D
+\scan_new:N \s__dim_mark
+\scan_new:N \s__dim_stop
+\cs_new:Npn \__dim_use_none_delimit_by_s_stop:w #1 \s__dim_stop { }
+\cs_new_protected:Npn \dim_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs:w newdimen \cs_end: #1
+ }
+\cs_generate_variant:Nn \dim_new:N { c }
+\cs_new_protected:Npn \dim_const:Nn #1#2
+ {
+ \dim_new:N #1
+ \tex_global:D #1 = \dim_eval:n {#2} \scan_stop:
+ }
+\cs_generate_variant:Nn \dim_const:Nn { c }
+\cs_new_protected:Npn \dim_zero:N #1 { #1 = \c_zero_skip }
+\cs_new_protected:Npn \dim_gzero:N #1
+ { \tex_global:D #1 = \c_zero_skip }
+\cs_generate_variant:Nn \dim_zero:N { c }
+\cs_generate_variant:Nn \dim_gzero:N { c }
+\cs_new_protected:Npn \dim_zero_new:N #1
+ { \dim_if_exist:NTF #1 { \dim_zero:N #1 } { \dim_new:N #1 } }
+\cs_new_protected:Npn \dim_gzero_new:N #1
+ { \dim_if_exist:NTF #1 { \dim_gzero:N #1 } { \dim_new:N #1 } }
+\cs_generate_variant:Nn \dim_zero_new:N { c }
+\cs_generate_variant:Nn \dim_gzero_new:N { c }
+\prg_new_eq_conditional:NNn \dim_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \dim_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+\cs_new_protected:Npn \dim_set:Nn #1#2
+ { #1 = \__dim_eval:w #2 \__dim_eval_end: \scan_stop: }
+\cs_new_protected:Npn \dim_gset:Nn #1#2
+ { \tex_global:D #1 = \__dim_eval:w #2 \__dim_eval_end: \scan_stop: }
+\cs_generate_variant:Nn \dim_set:Nn { c }
+\cs_generate_variant:Nn \dim_gset:Nn { c }
+\cs_new_protected:Npn \dim_set_eq:NN #1#2
+ { #1 = #2 \scan_stop: }
+\cs_generate_variant:Nn \dim_set_eq:NN { c , Nc , cc }
+\cs_new_protected:Npn \dim_gset_eq:NN #1#2
+ { \tex_global:D #1 = #2 \scan_stop: }
+\cs_generate_variant:Nn \dim_gset_eq:NN { c , Nc , cc }
+\cs_new_protected:Npn \dim_add:Nn #1#2
+ { \tex_advance:D #1 \__dim_eval:w #2 \__dim_eval_end: \scan_stop: }
+\cs_new_protected:Npn \dim_gadd:Nn #1#2
+ {
+ \tex_global:D \tex_advance:D #1
+ \__dim_eval:w #2 \__dim_eval_end: \scan_stop:
+ }
+\cs_generate_variant:Nn \dim_add:Nn { c }
+\cs_generate_variant:Nn \dim_gadd:Nn { c }
+\cs_new_protected:Npn \dim_sub:Nn #1#2
+ { \tex_advance:D #1 - \__dim_eval:w #2 \__dim_eval_end: \scan_stop: }
+\cs_new_protected:Npn \dim_gsub:Nn #1#2
+ {
+ \tex_global:D \tex_advance:D #1
+ -\__dim_eval:w #2 \__dim_eval_end: \scan_stop:
+ }
+\cs_generate_variant:Nn \dim_sub:Nn { c }
+\cs_generate_variant:Nn \dim_gsub:Nn { c }
+\cs_new:Npn \dim_abs:n #1
+ {
+ \exp_after:wN \__dim_abs:N
+ \dim_use:N \__dim_eval:w #1 \__dim_eval_end:
+ }
+\cs_new:Npn \__dim_abs:N #1
+ { \if_meaning:w - #1 \else: \exp_after:wN #1 \fi: }
+\cs_new:Npn \dim_max:nn #1#2
+ {
+ \dim_use:N \__dim_eval:w \exp_after:wN \__dim_maxmin:wwN
+ \dim_use:N \__dim_eval:w #1 \exp_after:wN ;
+ \dim_use:N \__dim_eval:w #2 ;
+ >
+ \__dim_eval_end:
+ }
+\cs_new:Npn \dim_min:nn #1#2
+ {
+ \dim_use:N \__dim_eval:w \exp_after:wN \__dim_maxmin:wwN
+ \dim_use:N \__dim_eval:w #1 \exp_after:wN ;
+ \dim_use:N \__dim_eval:w #2 ;
+ <
+ \__dim_eval_end:
+ }
+\cs_new:Npn \__dim_maxmin:wwN #1 ; #2 ; #3
+ {
+ \if_dim:w #1 #3 #2 ~
+ #1
+ \else:
+ #2
+ \fi:
+ }
+\cs_new:Npn \dim_ratio:nn #1#2
+ { \__dim_ratio:n {#1} / \__dim_ratio:n {#2} }
+\cs_new:Npn \__dim_ratio:n #1
+ { \int_value:w \__dim_eval:w (#1) \__dim_eval_end: }
+\prg_new_conditional:Npnn \dim_compare:nNn #1#2#3 { p , T , F , TF }
+ {
+ \if_dim:w \__dim_eval:w #1 #2 \__dim_eval:w #3 \__dim_eval_end:
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+\prg_new_conditional:Npnn \dim_compare:n #1 { p , T , F , TF }
+ {
+ \exp_after:wN \__dim_compare:w
+ \dim_use:N \__dim_eval:w #1 \__dim_compare_error:
+ }
+\cs_new:Npn \__dim_compare:w #1 \__dim_compare_error:
+ {
+ \exp_after:wN \if_false: \exp:w \exp_end_continue_f:w
+ \__dim_compare:wNN #1 ? { = \__dim_compare_end:w \else: } \s__dim_stop
+ }
+\exp_args:Nno \use:nn
+ { \cs_new:Npn \__dim_compare:wNN #1 } { \tl_to_str:n {pt} #2#3 }
+ {
+ \if_meaning:w = #3
+ \use:c { __dim_compare_#2:w }
+ \fi:
+ #1 pt \exp_stop_f:
+ \prg_return_false:
+ \exp_after:wN \__dim_use_none_delimit_by_s_stop:w
+ \fi:
+ \reverse_if:N \if_dim:w #1 pt #2
+ \exp_after:wN \__dim_compare:wNN
+ \dim_use:N \__dim_eval:w #3
+ }
+\cs_new:cpn { __dim_compare_ ! :w }
+ #1 \reverse_if:N #2 ! #3 = { #1 #2 = #3 }
+\cs_new:cpn { __dim_compare_ = :w }
+ #1 \__dim_eval:w = { #1 \__dim_eval:w }
+\cs_new:cpn { __dim_compare_ < :w }
+ #1 \reverse_if:N #2 < #3 = { #1 #2 > #3 }
+\cs_new:cpn { __dim_compare_ > :w }
+ #1 \reverse_if:N #2 > #3 = { #1 #2 < #3 }
+\cs_new:Npn \__dim_compare_end:w #1 \prg_return_false: #2 \s__dim_stop
+ { #1 \prg_return_false: \else: \prg_return_true: \fi: }
+\cs_new_protected:Npn \__dim_compare_error:
+ {
+ \if_int_compare:w \c_zero_int \c_zero_int \fi:
+ =
+ \__dim_compare_error:
+ }
+\cs_new:Npn \dim_case:nnTF #1
+ {
+ \exp:w
+ \exp_args:Nf \__dim_case:nnTF { \dim_eval:n {#1} }
+ }
+\cs_new:Npn \dim_case:nnT #1#2#3
+ {
+ \exp:w
+ \exp_args:Nf \__dim_case:nnTF { \dim_eval:n {#1} } {#2} {#3} { }
+ }
+\cs_new:Npn \dim_case:nnF #1#2
+ {
+ \exp:w
+ \exp_args:Nf \__dim_case:nnTF { \dim_eval:n {#1} } {#2} { }
+ }
+\cs_new:Npn \dim_case:nn #1#2
+ {
+ \exp:w
+ \exp_args:Nf \__dim_case:nnTF { \dim_eval:n {#1} } {#2} { } { }
+ }
+\cs_new:Npn \__dim_case:nnTF #1#2#3#4
+ { \__dim_case:nw {#1} #2 {#1} { } \s__dim_mark {#3} \s__dim_mark {#4} \s__dim_stop }
+\cs_new:Npn \__dim_case:nw #1#2#3
+ {
+ \dim_compare:nNnTF {#1} = {#2}
+ { \__dim_case_end:nw {#3} }
+ { \__dim_case:nw {#1} }
+ }
+\cs_new:Npn \__dim_case_end:nw #1#2#3 \s__dim_mark #4#5 \s__dim_stop
+ { \exp_end: #1 #4 }
+\cs_new:Npn \dim_while_do:nn #1#2
+ {
+ \dim_compare:nT {#1}
+ {
+ #2
+ \dim_while_do:nn {#1} {#2}
+ }
+ }
+\cs_new:Npn \dim_until_do:nn #1#2
+ {
+ \dim_compare:nF {#1}
+ {
+ #2
+ \dim_until_do:nn {#1} {#2}
+ }
+ }
+\cs_new:Npn \dim_do_while:nn #1#2
+ {
+ #2
+ \dim_compare:nT {#1}
+ { \dim_do_while:nn {#1} {#2} }
+ }
+\cs_new:Npn \dim_do_until:nn #1#2
+ {
+ #2
+ \dim_compare:nF {#1}
+ { \dim_do_until:nn {#1} {#2} }
+ }
+\cs_new:Npn \dim_while_do:nNnn #1#2#3#4
+ {
+ \dim_compare:nNnT {#1} #2 {#3}
+ {
+ #4
+ \dim_while_do:nNnn {#1} #2 {#3} {#4}
+ }
+ }
+\cs_new:Npn \dim_until_do:nNnn #1#2#3#4
+ {
+ \dim_compare:nNnF {#1} #2 {#3}
+ {
+ #4
+ \dim_until_do:nNnn {#1} #2 {#3} {#4}
+ }
+ }
+\cs_new:Npn \dim_do_while:nNnn #1#2#3#4
+ {
+ #4
+ \dim_compare:nNnT {#1} #2 {#3}
+ { \dim_do_while:nNnn {#1} #2 {#3} {#4} }
+ }
+\cs_new:Npn \dim_do_until:nNnn #1#2#3#4
+ {
+ #4
+ \dim_compare:nNnF {#1} #2 {#3}
+ { \dim_do_until:nNnn {#1} #2 {#3} {#4} }
+ }
+\cs_new:Npn \dim_step_function:nnnN #1#2#3
+ {
+ \exp_after:wN \__dim_step:wwwN
+ \tex_the:D \__dim_eval:w #1 \exp_after:wN ;
+ \tex_the:D \__dim_eval:w #2 \exp_after:wN ;
+ \tex_the:D \__dim_eval:w #3 ;
+ }
+\cs_new:Npn \__dim_step:wwwN #1; #2; #3; #4
+ {
+ \dim_compare:nNnTF {#2} > \c_zero_dim
+ { \__dim_step:NnnnN > }
+ {
+ \dim_compare:nNnTF {#2} = \c_zero_dim
+ {
+ \msg_expandable_error:nnn { kernel } { zero-step } {#4}
+ \use_none:nnnn
+ }
+ { \__dim_step:NnnnN < }
+ }
+ {#1} {#2} {#3} #4
+ }
+\cs_new:Npn \__dim_step:NnnnN #1#2#3#4#5
+ {
+ \dim_compare:nNnF {#2} #1 {#4}
+ {
+ #5 {#2}
+ \exp_args:NNf \__dim_step:NnnnN
+ #1 { \dim_eval:n { #2 + #3 } } {#3} {#4} #5
+ }
+ }
+\cs_new_protected:Npn \dim_step_inline:nnnn
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:NNc \__dim_step:NNnnnn
+ \cs_gset_protected:Npn
+ { __dim_map_ \int_use:N \g__kernel_prg_map_int :w }
+ }
+\cs_new_protected:Npn \dim_step_variable:nnnNn #1#2#3#4#5
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:NNc \__dim_step:NNnnnn
+ \cs_gset_protected:Npe
+ { __dim_map_ \int_use:N \g__kernel_prg_map_int :w }
+ {#1}{#2}{#3}
+ {
+ \tl_set:Nn \exp_not:N #4 {##1}
+ \exp_not:n {#5}
+ }
+ }
+\cs_new_protected:Npn \__dim_step:NNnnnn #1#2#3#4#5#6
+ {
+ #1 #2 ##1 {#6}
+ \dim_step_function:nnnN {#3} {#4} {#5} #2
+ \prg_break_point:Nn \scan_stop: { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+\cs_new:Npn \dim_eval:n #1
+ { \dim_use:N \__dim_eval:w #1 \__dim_eval_end: }
+\cs_new:Npn \dim_sign:n #1
+ {
+ \int_value:w \exp_after:wN \__dim_sign:Nw
+ \dim_use:N \__dim_eval:w #1 \__dim_eval_end: ;
+ \exp_stop_f:
+ }
+\cs_new:Npn \__dim_sign:Nw #1#2 ;
+ {
+ \if_dim:w #1#2 > \c_zero_dim
+ 1
+ \else:
+ \if_meaning:w - #1
+ -1
+ \else:
+ 0
+ \fi:
+ \fi:
+ }
+\cs_new_eq:NN \dim_use:N \tex_the:D
+\cs_new:Npn \dim_use:c #1 { \tex_the:D \cs:w #1 \cs_end: }
+\cs_new:Npn \dim_to_decimal:n #1
+ {
+ \exp_after:wN
+ \__dim_to_decimal:w \dim_use:N \__dim_eval:w #1 \__dim_eval_end:
+ }
+\use:e
+ {
+ \cs_new:Npn \exp_not:N \__dim_to_decimal:w
+ #1 . #2 \tl_to_str:n { pt }
+ }
+ {
+ \int_compare:nNnTF {#2} > \c_zero_int
+ { #1 . #2 }
+ { #1 }
+ }
+\cs_new:Npn \dim_to_decimal_in_sp:n #1
+ { \int_value:w \__dim_eval:w #1 \__dim_eval_end: }
+\group_begin:
+ \cs_set_protected:Npn \__dim_tmp:w #1#2
+ {
+ \cs_new:cpn { dim_to_decimal_in_ #1 :n } ##1
+ {
+ \exp_after:wN \__dim_to_decimal_aux:w
+ \int_value:w \__dim_eval:w ##1 \__dim_eval_end: ; #2 ;
+ }
+ }
+ \__dim_tmp:w { in } { 50 / 7227 } % delta = 7227/100
+ \__dim_tmp:w { pc } { 1 / 24 } % delta = 12/1
+ \__dim_tmp:w { cm } { 127 / 7227 } % delta = 7227/254
+ \__dim_tmp:w { mm } { 1270 / 7227 } % delta = 7227/2540
+ \__dim_tmp:w { bp } { 400 / 803 } % delta = 7227/7200
+ \__dim_tmp:w { dd } { 1157 / 2476 } % delta = 1238/1157
+ \__dim_tmp:w { cc } { 1157 / 29712 } % delta = 14856/1157
+\group_end:
+\cs_new:Npn \__dim_to_decimal_aux:w #1#2 ; #3 ;
+ {
+ \dim_to_decimal:n
+ {
+ \int_eval:n
+ { ( 2 * #1#2 \if:w #1 - - \else: + \fi: 1 ) * #3 }
+ sp
+ }
+ }
+\cs_new:Npn \dim_to_decimal_in_unit:nn #1#2
+ {
+ \exp_after:wN \__dim_chk_unit:w
+ \int_value:w \__dim_eval:w #2 \__dim_eval_end: ; {#1}
+ }
+\cs_new:Npn \__dim_chk_unit:w #1#2;#3
+ {
+ \token_if_eq_charcode:NNTF #1 0
+ { \msg_expandable_error:nn { dim } { zero-unit } }
+ {
+ \exp_after:wN \__dim_branch_unit:w
+ \int_value:w \if:w #1 - - \fi: \__dim_eval:w #3 \exp_after:wN ;
+ \int_value:w \if:w #1 - - \fi: #1#2 ;
+ }
+ }
+\cs_new:Npn \__dim_branch_unit:w #1;#2;
+ {
+ \int_compare:nNnTF {#2} > { 65536 }
+ { \__dim_to_decimal_aux:w #1 ; 32768 / #2 ; }
+ {
+ \int_compare:nNnTF {#2} = { 65536 }
+ { \dim_to_decimal:n { #1sp } }
+ { \__dim_get_quotient:w #1 ; #2 ; }
+ }
+ }
+\cs_new:Npn \__dim_get_quotient:w #1#2;#3;
+ {
+ \token_if_eq_charcode:NNTF #1 0
+ { 0 }
+ {
+ \token_if_eq_charcode:NNTF #1 -
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \__dim_get_remainder:w
+ \int_eval:n { ( 2 * #2 - #3 ) / ( 2 * #3 ) } ;
+ #2 ; #3 ; - ;
+ }
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \__dim_get_remainder:w
+ \int_eval:n { ( 2 * #1#2 - #3 ) / ( 2 * #3 ) } ;
+ #1#2 ; #3 ; ;
+ }
+ }
+ }
+\cs_new:Npn \__dim_get_remainder:w #1;#2;#3;
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \__dim_convert_remainder:w
+ \int_eval:n { #2 - #1 * #3 } ;
+ #3 ; #1 ;
+ }
+\cs_new:Npn \__dim_convert_remainder:w #1;#2;
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \__dim_test_candidate:w
+ \int_eval:n { #1 * 65536 / #2 } ;
+ #1 ; #2 ;
+ }
+\cs_new:Npn \__dim_test_candidate:w #1;#2;#3;
+ {
+ \dim_compare:nNnTF { #2sp } =
+ { \dim_to_decimal:n { #1sp } \__dim_eval:w #3sp \__dim_eval_end: }
+ { \__dim_parse_decimal:w #1 ; }
+ {
+ \__dim_parse_decimal:w \int_eval:n { #1 + 1 } ;
+ }
+ }
+\cs_new:Npn \__dim_parse_decimal:w #1;#2;#3;
+ {
+ \exp_after:wN \__dim_parse_decimal_aux:w
+ \int_value:w #3 \int_eval:w #2 + \dim_to_decimal:n { #1sp } ;
+ }
+\cs_new:Npn \__dim_parse_decimal_aux:w #1 ; {#1}
+\cs_new_eq:NN \dim_show:N \__kernel_register_show:N
+\cs_generate_variant:Nn \dim_show:N { c }
+\cs_new_protected:Npn \dim_show:n
+ { \__kernel_msg_show_eval:Nn \dim_eval:n }
+\cs_new_eq:NN \dim_log:N \__kernel_register_log:N
+\cs_new_eq:NN \dim_log:c \__kernel_register_log:c
+\cs_new_protected:Npn \dim_log:n
+ { \__kernel_msg_log_eval:Nn \dim_eval:n }
+\dim_const:Nn \c_zero_dim { 0 pt }
+\dim_const:Nn \c_max_dim { 16383.99999 pt }
+\dim_new:N \l_tmpa_dim
+\dim_new:N \l_tmpb_dim
+\dim_new:N \g_tmpa_dim
+\dim_new:N \g_tmpb_dim
+\scan_new:N \s__skip_stop
+\cs_new_protected:Npn \skip_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs:w newskip \cs_end: #1
+ }
+\cs_generate_variant:Nn \skip_new:N { c }
+\cs_new_protected:Npn \skip_const:Nn #1#2
+ {
+ \skip_new:N #1
+ \tex_global:D #1 = \skip_eval:n {#2} \scan_stop:
+ }
+\cs_generate_variant:Nn \skip_const:Nn { c }
+\cs_new_eq:NN \skip_zero:N \dim_zero:N
+\cs_new_eq:NN \skip_gzero:N \dim_gzero:N
+\cs_generate_variant:Nn \skip_zero:N { c }
+\cs_generate_variant:Nn \skip_gzero:N { c }
+\cs_new_protected:Npn \skip_zero_new:N #1
+ { \skip_if_exist:NTF #1 { \skip_zero:N #1 } { \skip_new:N #1 } }
+\cs_new_protected:Npn \skip_gzero_new:N #1
+ { \skip_if_exist:NTF #1 { \skip_gzero:N #1 } { \skip_new:N #1 } }
+\cs_generate_variant:Nn \skip_zero_new:N { c }
+\cs_generate_variant:Nn \skip_gzero_new:N { c }
+\prg_new_eq_conditional:NNn \skip_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \skip_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+\cs_new_protected:Npn \skip_set:Nn #1#2
+ { #1 = \tex_glueexpr:D #2 \scan_stop: }
+\cs_new_protected:Npn \skip_gset:Nn #1#2
+ { \tex_global:D #1 = \tex_glueexpr:D #2 \scan_stop: }
+\cs_generate_variant:Nn \skip_set:Nn { c }
+\cs_generate_variant:Nn \skip_gset:Nn { c }
+\cs_new_protected:Npn \skip_set_eq:NN #1#2 { #1 = #2 }
+\cs_generate_variant:Nn \skip_set_eq:NN { c , Nc , cc }
+\cs_new_protected:Npn \skip_gset_eq:NN #1#2 { \tex_global:D #1 = #2 }
+\cs_generate_variant:Nn \skip_gset_eq:NN { c , Nc , cc }
+\cs_new_protected:Npn \skip_add:Nn #1#2
+ { \tex_advance:D #1 \tex_glueexpr:D #2 \scan_stop: }
+\cs_new_protected:Npn \skip_gadd:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 \tex_glueexpr:D #2 \scan_stop: }
+\cs_generate_variant:Nn \skip_add:Nn { c }
+\cs_generate_variant:Nn \skip_gadd:Nn { c }
+\cs_new_protected:Npn \skip_sub:Nn #1#2
+ { \tex_advance:D #1 - \tex_glueexpr:D #2 \scan_stop: }
+\cs_new_protected:Npn \skip_gsub:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 - \tex_glueexpr:D #2 \scan_stop: }
+\cs_generate_variant:Nn \skip_sub:Nn { c }
+\cs_generate_variant:Nn \skip_gsub:Nn { c }
+\prg_new_conditional:Npnn \skip_if_eq:nn #1#2 { p , T , F , TF }
+ {
+ \str_if_eq:eeTF { \skip_eval:n {#1} } { \skip_eval:n {#2} }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_set_protected:Npn \__skip_tmp:w #1
+ {
+ \prg_new_conditional:Npnn \skip_if_finite:n ##1 { p , T , F , TF }
+ {
+ \exp_after:wN \__skip_if_finite:wwNw
+ \skip_use:N \tex_glueexpr:D ##1 ; \prg_return_false:
+ #1 ; \prg_return_true: \s__skip_stop
+ }
+ \cs_new:Npn \__skip_if_finite:wwNw ##1 #1 ##2 ; ##3 ##4 \s__skip_stop {##3}
+ }
+\exp_args:No \__skip_tmp:w { \tl_to_str:n { fil } }
+\cs_new:Npn \skip_eval:n #1
+ { \skip_use:N \tex_glueexpr:D #1 \scan_stop: }
+\cs_new_eq:NN \skip_use:N \dim_use:N
+\cs_new_eq:NN \skip_use:c \dim_use:c
+\cs_new_eq:NN \skip_horizontal:N \tex_hskip:D
+\cs_new:Npn \skip_horizontal:n #1
+ { \skip_horizontal:N \tex_glueexpr:D #1 \scan_stop: }
+\cs_new_eq:NN \skip_vertical:N \tex_vskip:D
+\cs_new:Npn \skip_vertical:n #1
+ { \skip_vertical:N \tex_glueexpr:D #1 \scan_stop: }
+\cs_generate_variant:Nn \skip_horizontal:N { c }
+\cs_generate_variant:Nn \skip_vertical:N { c }
+\cs_new_eq:NN \skip_show:N \__kernel_register_show:N
+\cs_generate_variant:Nn \skip_show:N { c }
+\cs_new_protected:Npn \skip_show:n
+ { \__kernel_msg_show_eval:Nn \skip_eval:n }
+\cs_new_eq:NN \skip_log:N \__kernel_register_log:N
+\cs_new_eq:NN \skip_log:c \__kernel_register_log:c
+\cs_new_protected:Npn \skip_log:n
+ { \__kernel_msg_log_eval:Nn \skip_eval:n }
+\skip_const:Nn \c_zero_skip { \c_zero_dim }
+\skip_const:Nn \c_max_skip { \c_max_dim }
+\skip_new:N \l_tmpa_skip
+\skip_new:N \l_tmpb_skip
+\skip_new:N \g_tmpa_skip
+\skip_new:N \g_tmpb_skip
+\cs_new_protected:Npn \muskip_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs:w newmuskip \cs_end: #1
+ }
+\cs_generate_variant:Nn \muskip_new:N { c }
+\cs_new_protected:Npn \muskip_const:Nn #1#2
+ {
+ \muskip_new:N #1
+ \tex_global:D #1 = \muskip_eval:n {#2} \scan_stop:
+ }
+\cs_generate_variant:Nn \muskip_const:Nn { c }
+\cs_new_protected:Npn \muskip_zero:N #1
+ { #1 = \c_zero_muskip }
+\cs_new_protected:Npn \muskip_gzero:N #1
+ { \tex_global:D #1 = \c_zero_muskip }
+\cs_generate_variant:Nn \muskip_zero:N { c }
+\cs_generate_variant:Nn \muskip_gzero:N { c }
+\cs_new_protected:Npn \muskip_zero_new:N #1
+ { \muskip_if_exist:NTF #1 { \muskip_zero:N #1 } { \muskip_new:N #1 } }
+\cs_new_protected:Npn \muskip_gzero_new:N #1
+ { \muskip_if_exist:NTF #1 { \muskip_gzero:N #1 } { \muskip_new:N #1 } }
+\cs_generate_variant:Nn \muskip_zero_new:N { c }
+\cs_generate_variant:Nn \muskip_gzero_new:N { c }
+\prg_new_eq_conditional:NNn \muskip_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \muskip_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+\cs_new_protected:Npn \muskip_set:Nn #1#2
+ { #1 = \tex_muexpr:D #2 \scan_stop: }
+\cs_new_protected:Npn \muskip_gset:Nn #1#2
+ { \tex_global:D #1 = \tex_muexpr:D #2 \scan_stop: }
+\cs_generate_variant:Nn \muskip_set:Nn { c }
+\cs_generate_variant:Nn \muskip_gset:Nn { c }
+\cs_new_protected:Npn \muskip_set_eq:NN #1#2 { #1 = #2 }
+\cs_generate_variant:Nn \muskip_set_eq:NN { c , Nc , cc }
+\cs_new_protected:Npn \muskip_gset_eq:NN #1#2 { \tex_global:D #1 = #2 }
+\cs_generate_variant:Nn \muskip_gset_eq:NN { c , Nc , cc }
+\cs_new_protected:Npn \muskip_add:Nn #1#2
+ { \tex_advance:D #1 \tex_muexpr:D #2 \scan_stop: }
+\cs_new_protected:Npn \muskip_gadd:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 \tex_muexpr:D #2 \scan_stop: }
+\cs_generate_variant:Nn \muskip_add:Nn { c }
+\cs_generate_variant:Nn \muskip_gadd:Nn { c }
+\cs_new_protected:Npn \muskip_sub:Nn #1#2
+ { \tex_advance:D #1 - \tex_muexpr:D #2 \scan_stop: }
+\cs_new_protected:Npn \muskip_gsub:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 - \tex_muexpr:D #2 \scan_stop: }
+\cs_generate_variant:Nn \muskip_sub:Nn { c }
+\cs_generate_variant:Nn \muskip_gsub:Nn { c }
+\cs_new:Npn \muskip_eval:n #1
+ { \muskip_use:N \tex_muexpr:D #1 \scan_stop: }
+\cs_new_eq:NN \muskip_use:N \dim_use:N
+\cs_new_eq:NN \muskip_use:c \dim_use:c
+\cs_new_eq:NN \muskip_show:N \__kernel_register_show:N
+\cs_generate_variant:Nn \muskip_show:N { c }
+\cs_new_protected:Npn \muskip_show:n
+ { \__kernel_msg_show_eval:Nn \muskip_eval:n }
+\cs_new_eq:NN \muskip_log:N \__kernel_register_log:N
+\cs_new_eq:NN \muskip_log:c \__kernel_register_log:c
+\cs_new_protected:Npn \muskip_log:n
+ { \__kernel_msg_log_eval:Nn \muskip_eval:n }
+\muskip_const:Nn \c_zero_muskip { 0 mu }
+\muskip_const:Nn \c_max_muskip { 16383.99999 mu }
+\muskip_new:N \l_tmpa_muskip
+\muskip_new:N \l_tmpb_muskip
+\muskip_new:N \g_tmpa_muskip
+\muskip_new:N \g_tmpb_muskip
+%% File: l3keys.dtx
+\scan_new:N \s__keyval_nil
+\scan_new:N \s__keyval_mark
+\scan_new:N \s__keyval_stop
+\scan_new:N \s__keyval_tail
+\bool_new:N \l__kernel_keyval_allow_blank_keys_bool
+\group_begin:
+ \cs_set_protected:Npn \__keyval_tmp:w #1#2
+ {
+ \cs_new:Npn \keyval_parse:nnn ##1 ##2 ##3
+ {
+ \__kernel_exp_not:w \tex_expanded:D
+ {
+ {
+ \__keyval_loop_active:nnw {##1} {##2}
+ \s__keyval_mark ##3 #1 \s__keyval_tail #1
+ }
+ }
+ }
+ \cs_new_eq:NN \keyval_parse:NNn \keyval_parse:nnn
+ \cs_new:Npn \__keyval_loop_active:nnw ##1 ##2 ##3 #1
+ {
+ \__keyval_if_recursion_tail:w ##3
+ \__keyval_end_loop_active:w \s__keyval_tail
+ \__keyval_loop_other:nnw {##1} {##2} ##3 , \s__keyval_tail ,
+ }
+ \cs_new:Npn \__keyval_split_other:w ##1 = ##2 \s__keyval_mark ##3
+ { ##3 ##1 \s__keyval_stop \s__keyval_mark ##2 }
+ \cs_new:Npn \__keyval_split_active:w ##1 #2 ##2 \s__keyval_mark ##3
+ { ##3 ##1 \s__keyval_stop \s__keyval_mark ##2 }
+ \cs_new:Npn \__keyval_loop_other:nnw ##1 ##2 ##3 ,
+ {
+ \__keyval_if_recursion_tail:w ##3
+ \__keyval_end_loop_other:w \s__keyval_tail
+ \__keyval_split_active:w ##3 \s__keyval_nil
+ \s__keyval_mark \__keyval_split_active_auxi:w
+ #2 \s__keyval_mark \__keyval_clean_up_active:w
+ {##1} {##2}
+ \s__keyval_mark
+ }
+ \cs_new:Npn \__keyval_split_active_auxi:w ##1 \s__keyval_stop
+ {
+ \__keyval_split_other:w ##1 \s__keyval_nil
+ \s__keyval_mark \__keyval_misplaced_equal_after_active_error:w
+ = \s__keyval_mark \__keyval_split_active_auxii:w
+ }
+ \cs_new:Npn \__keyval_split_active_auxii:w
+ ##1 \s__keyval_nil \s__keyval_mark \__keyval_misplaced_equal_after_active_error:w
+ \s__keyval_stop \s__keyval_mark
+ ##2 \s__keyval_nil #2 \s__keyval_mark \__keyval_clean_up_active:w
+ { \__keyval_trim:nN {##1} \__keyval_split_active_auxiii:w ##2 \s__keyval_nil }
+ \cs_new:Npn \__keyval_split_active_auxiii:w ##1 ##2 \s__keyval_nil
+ {
+ \__keyval_split_active:w ##2 \s__keyval_nil
+ \s__keyval_mark \__keyval_misplaced_equal_in_split_error:w
+ #2 \s__keyval_mark \__keyval_split_active_auxiv:w
+ {##1}
+ }
+ \cs_new:Npn \__keyval_split_active_auxiv:w
+ ##1 \s__keyval_nil \s__keyval_mark \__keyval_misplaced_equal_in_split_error:w
+ \s__keyval_stop \s__keyval_mark
+ {
+ \__keyval_split_other:w ##1 \s__keyval_nil
+ \s__keyval_mark \__keyval_misplaced_equal_in_split_error:w
+ = \s__keyval_mark \__keyval_split_active_auxv:w
+ }
+ \cs_new:Npn \__keyval_split_active_auxv:w
+ ##1 \s__keyval_nil \s__keyval_mark \__keyval_misplaced_equal_in_split_error:w
+ \s__keyval_stop \s__keyval_mark
+ { \__keyval_trim:nN { ##1 } \__keyval_pair:nnnn }
+ \cs_new:Npn \__keyval_clean_up_active:w
+ ##1 \s__keyval_nil \s__keyval_mark \__keyval_split_active_auxi:w \s__keyval_stop \s__keyval_mark
+ {
+ \__keyval_split_other:w ##1 \s__keyval_nil
+ \s__keyval_mark \__keyval_split_other_auxi:w
+ = \s__keyval_mark \__keyval_clean_up_other:w
+ }
+ \cs_new:Npn \__keyval_split_other_auxi:w ##1 \s__keyval_stop
+ { \__keyval_trim:nN { ##1 } \__keyval_split_other_auxii:w }
+ \cs_new:Npn \__keyval_split_other_auxii:w
+ ##1 ##2 \s__keyval_nil = \s__keyval_mark \__keyval_clean_up_other:w
+ {
+ \__keyval_split_other:w ##2 \s__keyval_nil
+ \s__keyval_mark \__keyval_misplaced_equal_in_split_error:w
+ = \s__keyval_mark \__keyval_split_other_auxiii:w
+ { ##1 }
+ }
+ \cs_new:Npn \__keyval_split_other_auxiii:w
+ ##1 \s__keyval_nil \s__keyval_mark \__keyval_misplaced_equal_in_split_error:w
+ \s__keyval_stop \s__keyval_mark
+ { \__keyval_trim:nN { ##1 } \__keyval_pair:nnnn }
+ \cs_new:Npn \__keyval_clean_up_other:w
+ ##1 \s__keyval_nil \s__keyval_mark \__keyval_split_other_auxi:w \s__keyval_stop \s__keyval_mark
+ {
+ \__keyval_if_blank:w ##1 \s__keyval_nil \s__keyval_stop \__keyval_blank_true:w
+ \s__keyval_mark \s__keyval_stop
+ \__keyval_trim:nN { ##1 } \__keyval_key:nn
+ }
+ \cs_new:Npn \__keyval_misplaced_equal_after_active_error:w
+ \s__keyval_mark ##1 \s__keyval_stop \s__keyval_mark ##2 \s__keyval_nil
+ = \s__keyval_mark \__keyval_split_active_auxii:w
+ \s__keyval_mark ##3 \s__keyval_nil
+ #2 \s__keyval_mark \__keyval_clean_up_active:w
+ {
+ \msg_expandable_error:nn
+ { keyval } { misplaced-equals-sign }
+ \__keyval_loop_other:nnw
+ }
+ \cs_new:Npn \__keyval_misplaced_equal_in_split_error:w
+ \s__keyval_mark ##1 \s__keyval_stop \s__keyval_mark ##2 \s__keyval_nil
+ ##3 \s__keyval_mark ##4 ##5
+ {
+ \msg_expandable_error:nn
+ { keyval } { misplaced-equals-sign }
+ \__keyval_loop_other:nnw
+ }
+ \cs_new:Npn \__keyval_end_loop_other:w
+ \s__keyval_tail
+ \__keyval_split_active:w
+ \s__keyval_mark \s__keyval_tail
+ \s__keyval_nil \s__keyval_mark
+ \__keyval_split_active_auxi:w
+ #2 \s__keyval_mark \__keyval_clean_up_active:w
+ { \__keyval_loop_active:nnw }
+ \cs_new:Npn \__keyval_end_loop_active:w
+ \s__keyval_tail
+ \__keyval_loop_other:nnw ##1 \s__keyval_mark \s__keyval_tail , \s__keyval_tail ,
+ { }
+ }
+ \char_set_catcode_active:n { `\, }
+ \char_set_catcode_active:n { `\= }
+ \__keyval_tmp:w , =
+\group_end:
+\cs_generate_variant:Nn \keyval_parse:NNn { NNV , NNv }
+\cs_generate_variant:Nn \keyval_parse:nnn { nnV , nnv }
+\group_begin:
+ \cs_set_protected:Npn \__keyval_tmp:w #1#2
+ {
+ \cs_new:Npn \__keyval_pair:nnnn ##1 ##2 ##3 ##4
+ {
+ \__keyval_if_blank:w \s__keyval_mark ##2 \s__keyval_nil \s__keyval_stop \__keyval_blank_key_error:w
+ \s__keyval_mark \s__keyval_stop
+ #1
+ \exp_not:n { ##4 {##2} {##1} }
+ #2
+ \__keyval_loop_other:nnw {##3} {##4}
+ }
+ \cs_new:Npn \__keyval_key:nn ##1 ##2
+ {
+ \__keyval_if_blank:w \s__keyval_mark ##1 \s__keyval_nil \s__keyval_stop \__keyval_blank_key_error:w
+ \s__keyval_mark \s__keyval_stop
+ #1
+ \exp_not:n { ##2 {##1} }
+ #2
+ \__keyval_loop_other:nnw {##2}
+ }
+ }
+ \__keyval_tmp:w { } { }
+\group_end:
+\cs_new:Npn \__keyval_if_empty:w #1 \s__keyval_mark \s__keyval_stop { }
+\cs_new:Npn \__keyval_if_blank:w \s__keyval_mark #1 { \__keyval_if_empty:w \s__keyval_mark }
+\cs_new:Npn \__keyval_if_recursion_tail:w \s__keyval_mark #1 \s__keyval_tail { }
+\cs_new:Npn \__keyval_blank_true:w \s__keyval_mark \s__keyval_stop \__keyval_trim:nN #1 \__keyval_key:nn
+ { \__keyval_loop_other:nnw }
+\cs_new:Npn \__keyval_blank_key_error:w \s__keyval_mark \s__keyval_stop #1 \__keyval_loop_other:nnw
+ {
+ \bool_if:NTF \l__kernel_keyval_allow_blank_keys_bool
+ { #1 }
+ { \msg_expandable_error:nn { keyval } { blank-key-name } }
+ \__keyval_loop_other:nnw
+ }
+\msg_new:nnn { keyval } { misplaced-equals-sign }
+ { Misplaced~'='~in~key-value~input~\msg_line_context: }
+\msg_new:nnn { keyval } { blank-key-name }
+ { Blank~key~name~in~key-value~input~\msg_line_context: }
+\prop_gput:Nnn \g_msg_module_name_prop { keyval } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { keyval } { }
+\group_begin:
+ \cs_set_protected:Npn \__keyval_tmp:w #1
+ {
+ \cs_new:Npn \__keyval_trim:nN ##1
+ {
+ \__keyval_trim_auxi:w
+ ##1
+ \s__keyval_nil
+ \s__keyval_mark #1 { }
+ \s__keyval_mark \__keyval_trim_auxii:w
+ \__keyval_trim_auxiii:w
+ #1 \s__keyval_nil
+ \__keyval_trim_auxiv:w
+ }
+ \cs_new:Npn \__keyval_trim_auxi:w ##1 \s__keyval_mark #1 ##2 \s__keyval_mark ##3
+ {
+ ##3
+ \__keyval_trim_auxi:w
+ \s__keyval_mark
+ ##2
+ \s__keyval_mark #1 {##1}
+ }
+ \cs_new:Npn \__keyval_trim_auxii:w \__keyval_trim_auxi:w \s__keyval_mark \s__keyval_mark ##1
+ {
+ \__keyval_trim_auxiii:w
+ ##1
+ }
+ \cs_new:Npn \__keyval_trim_auxiii:w ##1 #1 \s__keyval_nil ##2
+ {
+ ##2
+ ##1 \s__keyval_nil
+ \__keyval_trim_auxiii:w
+ }
+ \cs_new:Npn \__keyval_trim_auxiv:w
+ \s__keyval_mark ##1 \s__keyval_nil
+ \__keyval_trim_auxiii:w \s__keyval_nil \__keyval_trim_auxiii:w
+ ##2
+ { ##2 { ##1 } }
+ }
+ \__keyval_tmp:w { ~ }
+\group_end:
+\str_const:Nn \c__keys_code_root_str { key~code~>~ }
+\str_const:Nn \c__keys_check_root_str { key~check~>~ }
+\str_const:Nn \c__keys_default_root_str { key~default~>~ }
+\str_const:Nn \c__keys_groups_root_str { key~groups~>~ }
+\str_const:Nn \c__keys_inherit_root_str { key~inherit~>~ }
+\str_const:Nn \c__keys_type_root_str { key~type~>~ }
+\str_const:Nn \c__keys_props_root_str { key~prop~>~ }
+\int_new:N \l_keys_choice_int
+\tl_new:N \l_keys_choice_tl
+\clist_new:N \l__keys_groups_clist
+\clist_new:N \l__keys_inherit_clist
+\str_new:N \l_keys_key_str
+\tl_new:N \l_keys_key_tl
+\str_new:N \l__keys_module_str
+\bool_new:N \l__keys_no_value_bool
+\bool_new:N \l__keys_only_known_bool
+\str_new:N \l_keys_path_str
+\tl_new:N \l_keys_path_tl
+\str_new:N \l__keys_inherit_str
+\tl_new:N \l__keys_relative_tl
+\tl_set:Nn \l__keys_relative_tl { \q__keys_no_value }
+\str_new:N \l__keys_property_str
+\bool_new:N \l__keys_selective_bool
+\bool_new:N \l__keys_filtered_bool
+\seq_new:N \l__keys_selective_seq
+\clist_new:N \l__keys_unused_clist
+\tl_new:N \l_keys_value_tl
+\bool_new:N \l__keys_tmp_bool
+\tl_new:N \l__keys_tmpa_tl
+\tl_new:N \l__keys_tmpb_tl
+\bool_new:N \l__keys_precompile_bool
+\tl_new:N \l__keys_precompile_tl
+\prop_new:N \l_keys_usage_load_prop
+\prop_new:N \l_keys_usage_preamble_prop
+\scan_new:N \s__keys_nil
+\scan_new:N \s__keys_mark
+\scan_new:N \s__keys_stop
+\quark_new:N \q__keys_no_value
+\__kernel_quark_new_conditional:Nn \__keys_quark_if_no_value:N { TF }
+\cs_new_protected:Npn \__keys_precompile:n #1
+ {
+ \bool_if:NTF \l__keys_precompile_bool
+ { \tl_put_right:Nn \l__keys_precompile_tl }
+ { \use:n }
+ {#1}
+ }
+\cs_new_protected:Npn \__keys_cs_undefine:c #1
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \else:
+ \use_i:nnnn
+ \fi:
+ \cs_set_eq:cN {#1} \tex_undefined:D
+ }
+\cs_new_protected:Npn \keys_define:nn
+ { \__keys_define:onn \l__keys_module_str }
+\cs_generate_variant:Nn \keys_define:nn { ne , nx }
+\cs_new_protected:Npn \__keys_define:nnn #1#2#3
+ {
+ \str_set:Ne \l__keys_module_str { \__keys_trim_spaces:n {#2} }
+ \keyval_parse:NNn \__keys_define:n \__keys_define:nn {#3}
+ \str_set:Nn \l__keys_module_str {#1}
+ }
+\cs_generate_variant:Nn \__keys_define:nnn { o }
+\cs_new_protected:Npn \__keys_define:n #1
+ {
+ \bool_set_true:N \l__keys_no_value_bool
+ \__keys_define_aux:nn {#1} { }
+ }
+\cs_new_protected:Npn \__keys_define:nn #1#2
+ {
+ \bool_set_false:N \l__keys_no_value_bool
+ \__keys_define_aux:nn {#1} {#2}
+ }
+\cs_new_protected:Npn \__keys_define_aux:nn #1#2
+ {
+ \__keys_property_find:n {#1}
+ \cs_if_exist:cTF { \c__keys_props_root_str \l__keys_property_str }
+ { \__keys_define_code:n {#2} }
+ {
+ \str_if_empty:NF \l__keys_property_str
+ {
+ \msg_error:nnee { keys } { property-unknown }
+ \l__keys_property_str \l_keys_path_str
+ }
+ }
+ }
+\cs_new_protected:Npn \__keys_property_find:n #1
+ {
+ \exp_after:wN \__keys_property_find_auxi:w \tl_to_str:n {#1}
+ \s__keys_nil \__keys_property_find_auxii:w
+ . \s__keys_nil \__keys_property_find_err:w
+ }
+\cs_new:Npn \__keys_property_find_auxi:w #1 . #2 \s__keys_nil #3
+ {
+ #3 #1 \s__keys_mark #2 \s__keys_nil #3
+ }
+\cs_new_protected:Npn \__keys_property_find_auxii:w
+ #1 \s__keys_mark #2 \s__keys_nil \__keys_property_find_auxii:w . \s__keys_nil
+ \__keys_property_find_err:w
+ {
+ \cs_set_nopar:Npe \l_keys_path_str
+ {
+ \str_if_empty:NF \l__keys_module_str { \l__keys_module_str / }
+ \exp_after:wN \__keys_trim_spaces:n \tex_expanded:D {{
+ #1
+ \if_false: }}} \fi:
+ \__keys_property_find_auxi:w #2 \s__keys_nil \__keys_property_find_auxiii:w
+ . \s__keys_nil \__keys_property_find_auxiv:w
+ }
+\cs_new:Npn \__keys_property_find_auxiii:w #1 \s__keys_mark #2 . #3 \s__keys_nil #4
+ {
+ . #1 #4 #2 \s__keys_mark #3 \s__keys_nil #4
+ }
+\cs_new:Npn \__keys_property_find_auxiv:w
+ #1 \s__keys_nil \__keys_property_find_auxiii:w
+ \s__keys_mark \s__keys_nil \__keys_property_find_auxiv:w
+ {
+ \if_false: {{{ \fi: }}}
+ \cs_set_nopar:Npe \l__keys_property_str { . #1 }
+ \tl_set_eq:NN \l_keys_path_tl \l_keys_path_str
+ }
+\cs_new_protected:Npn \__keys_property_find_err:w
+ #1 \s__keys_nil #2 \__keys_property_find_err:w
+ {
+ \str_clear:N \l__keys_property_str
+ \msg_error:nnn { keys } { no-property } {#1}
+ }
+\cs_new_protected:Npn \__keys_define_code:n #1
+ {
+ \bool_if:NTF \l__keys_no_value_bool
+ {
+ \exp_after:wN \__keys_define_code:w
+ \l__keys_property_str \s__keys_stop
+ { \use:c { \c__keys_props_root_str \l__keys_property_str } }
+ {
+ \msg_error:nnee { keys } { property-requires-value }
+ \l__keys_property_str \l_keys_path_str
+ }
+ }
+ { \use:c { \c__keys_props_root_str \l__keys_property_str } {#1} }
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new:Npn \__keys_define_code:w #1 \c_colon_str #2 \s__keys_stop
+ { \tl_if_empty:nTF {#2} }
+\cs_new_protected:Npn \__keys_bool_set:Nn #1#2
+ { \__keys_bool_set:Nnnn #1 {#2} { true } { false } }
+\cs_generate_variant:Nn \__keys_bool_set:Nn { c }
+\cs_new_protected:Npn \__keys_bool_set_inverse:Nn #1#2
+ { \__keys_bool_set:Nnnn #1 {#2} { false } { true } }
+\cs_generate_variant:Nn \__keys_bool_set_inverse:Nn { c }
+\cs_new_protected:Npn \__keys_bool_set:Nnnn #1#2#3#4
+ {
+ \bool_if_exist:NF #1 { \bool_new:N #1 }
+ \__keys_choice_make:
+ \__keys_cmd_set:ne { \l_keys_path_str / true }
+ { \exp_not:c { bool_ #2 set_ #3 :N } \exp_not:N #1 }
+ \__keys_cmd_set:ne { \l_keys_path_str / false }
+ { \exp_not:c { bool_ #2 set_ #4 :N } \exp_not:N #1 }
+ \__keys_cmd_set_direct:nn { \l_keys_path_str / unknown }
+ {
+ \msg_error:nne { keys } { boolean-values-only }
+ \l_keys_path_str
+ }
+ \__keys_default_set:n { true }
+ }
+\cs_generate_variant:Nn \__keys_bool_set:Nn { c }
+\cs_new_protected:Npn \__keys_choice_make:
+ { \__keys_choice_make:N \__keys_choice_find:n }
+\cs_new_protected:Npn \__keys_multichoice_make:
+ { \__keys_choice_make:N \__keys_multichoice_find:n }
+\cs_new_protected:Npn \__keys_choice_make:N #1
+ {
+ \cs_if_exist:cTF
+ { \c__keys_type_root_str \__keys_parent:o \l_keys_path_str }
+ {
+ \str_if_eq:vnTF
+ { \c__keys_type_root_str \__keys_parent:o \l_keys_path_str }
+ { choice }
+ {
+ \msg_error:nnee { keys } { nested-choice-key }
+ \l_keys_path_tl { \__keys_parent:o \l_keys_path_str }
+ }
+ { \__keys_choice_make_aux:N #1 }
+ }
+ { \__keys_choice_make_aux:N #1 }
+ }
+\cs_new_protected:Npn \__keys_choice_make_aux:N #1
+ {
+ \cs_set_nopar:cpn { \c__keys_type_root_str \l_keys_path_str }
+ { choice }
+ \__keys_cmd_set_direct:nn \l_keys_path_str { #1 {##1} }
+ \__keys_cmd_set_direct:nn { \l_keys_path_str / unknown }
+ {
+ \msg_error:nnee { keys } { choice-unknown }
+ \l_keys_path_str {##1}
+ }
+ }
+\cs_new_protected:Npn \__keys_choices_make:nn
+ { \__keys_choices_make:Nnn \__keys_choice_make: }
+\cs_new_protected:Npn \__keys_multichoices_make:nn
+ { \__keys_choices_make:Nnn \__keys_multichoice_make: }
+\cs_new_protected:Npn \__keys_choices_make:Nnn #1#2#3
+ {
+ #1
+ \int_zero:N \l_keys_choice_int
+ \clist_map_inline:nn {#2}
+ {
+ \int_incr:N \l_keys_choice_int
+ \__keys_cmd_set:ne
+ { \l_keys_path_str / \__keys_trim_spaces:n {##1} }
+ {
+ \tl_set:Nn \exp_not:N \l_keys_choice_tl {##1}
+ \int_set:Nn \exp_not:N \l_keys_choice_int
+ { \int_use:N \l_keys_choice_int }
+ \exp_not:n {#3}
+ }
+ }
+ }
+\cs_new_protected:Npn \__keys_cmd_set:nn #1#2
+ { \__keys_cmd_set_direct:nn {#1} { \__keys_precompile:n {#2} } }
+\cs_generate_variant:Nn \__keys_cmd_set:nn { ne , Vn , Vo }
+\cs_new_protected:Npn \__keys_cmd_set_direct:nn #1#2
+ { \cs_set_protected:cpn { \c__keys_code_root_str #1 } ##1 {#2} }
+\cs_new_protected:Npn \__keys_cs_set:NNpn #1#2#3#
+ {
+ \cs_set_protected:cpe { \c__keys_code_root_str \l_keys_path_str } ##1
+ {
+ \__keys_precompile:n
+ { #1 \exp_not:N #2 \exp_not:n {#3} {##1} }
+ }
+ \use_none:n
+ }
+\cs_generate_variant:Nn \__keys_cs_set:NNpn { Nc }
+\cs_new_protected:Npn \__keys_default_set:n #1
+ {
+ \tl_if_empty:nTF {#1}
+ {
+ \__keys_cs_undefine:c
+ { \c__keys_default_root_str \l_keys_path_str }
+ }
+ {
+ \cs_set_nopar:cpe
+ { \c__keys_default_root_str \l_keys_path_str }
+ { \exp_not:n {#1} }
+ \__keys_value_requirement:nn { required } { false }
+ }
+ }
+\cs_new_protected:Npn \__keys_groups_set:n #1
+ {
+ \clist_set:Ne \l__keys_groups_clist { \tl_to_str:n {#1} }
+ \clist_if_empty:NTF \l__keys_groups_clist
+ {
+ \__keys_cs_undefine:c
+ { \c__keys_groups_root_str \l_keys_path_str }
+ }
+ {
+ \cs_set_eq:cN { \c__keys_groups_root_str \l_keys_path_str }
+ \l__keys_groups_clist
+ }
+ }
+\cs_new_protected:Npn \__keys_inherit:n #1
+ {
+ \__keys_undefine:
+ \clist_set:Nn \l__keys_inherit_clist {#1}
+ \cs_set_eq:cN { \c__keys_inherit_root_str \l_keys_path_str }
+ \l__keys_inherit_clist
+ }
+\cs_new_protected:Npn \__keys_initialise:n #1
+ {
+ \cs_if_exist:cTF
+ { \c__keys_inherit_root_str \__keys_parent:o \l_keys_path_str }
+ { \__keys_execute_inherit: }
+ {
+ \str_clear:N \l__keys_inherit_str
+ \cs_if_exist:cT { \c__keys_code_root_str \l_keys_path_str }
+ {
+ \exp_after:wN \__keys_find_key_module:wNN
+ \l_keys_path_str \s__keys_stop
+ \l_keys_key_tl \l_keys_key_str
+ \tl_set_eq:NN \l_keys_key_tl \l_keys_key_str
+ \tl_set:Nn \l_keys_value_tl {#1}
+ \__keys_execute:no \l_keys_path_str \l_keys_value_tl
+ }
+ }
+ }
+\cs_new_protected:Npn \__keys_legacy_if_set:nn #1#2
+ { \__keys_legacy_if_set:nnnn {#1} {#2} { true } { false } }
+\cs_new_protected:Npn \__keys_legacy_if_set_inverse:nn #1#2
+ { \__keys_legacy_if_set:nnnn {#1} {#2} { false } { true } }
+\cs_new_protected:Npn \__keys_legacy_if_set:nnnn #1#2#3#4
+ {
+ \__keys_choice_make:
+ \__keys_cmd_set:ne { \l_keys_path_str / true }
+ { \exp_not:c { legacy_if_#2 set_ #3 :n } { \exp_not:n {#1} } }
+ \__keys_cmd_set:ne { \l_keys_path_str / false }
+ { \exp_not:c { legacy_if_#2 set_ #4 :n } { \exp_not:n {#1} } }
+ \__keys_cmd_set:nn { \l_keys_path_str / unknown }
+ {
+ \msg_error:nne { keys } { boolean-values-only }
+ \l_keys_path_str
+ }
+ \__keys_default_set:n { true }
+ \cs_if_exist:cF { if#1 }
+ {
+ \cs:w newif \exp_after:wN \cs_end:
+ \cs:w if#1 \cs_end:
+ }
+ }
+\cs_new_protected:Npn \__keys_meta_make:n #1
+ {
+ \exp_args:NVo \__keys_cmd_set_direct:nn \l_keys_path_str
+ {
+ \exp_after:wN \__keys_set:nn \exp_after:wN
+ { \l__keys_module_str } {#1}
+ }
+ }
+\cs_new_protected:Npn \__keys_meta_make:nn #1#2
+ {
+ \exp_args:NV \__keys_cmd_set_direct:nn
+ \l_keys_path_str { \__keys_set:nn {#1} {#2} }
+ }
+\cs_new_protected:Npn \__keys_prop_put:Nn #1#2
+ {
+ \prop_if_exist:NF #1 { \prop_new:N #1 }
+ \exp_after:wN \__keys_find_key_module:wNN \l_keys_path_str \s__keys_stop
+ \l__keys_tmpa_tl \l__keys_tmpb_tl
+ \__keys_cmd_set:ne \l_keys_path_str
+ {
+ \exp_not:c { prop_ #2 put:Nnn }
+ \exp_not:N #1
+ { \l__keys_tmpb_tl }
+ \exp_not:n { {##1} }
+ }
+ }
+\cs_generate_variant:Nn \__keys_prop_put:Nn { c }
+\cs_new_protected:Npn \__keys_undefine:
+ {
+ \clist_map_inline:nn
+ { code , default , groups , inherit , type , check }
+ {
+ \__keys_cs_undefine:c
+ { \tl_use:c { c__keys_ ##1 _root_str } \l_keys_path_str }
+ }
+ }
+\cs_new_protected:Npn \__keys_value_requirement:nn #1#2
+ {
+ \str_case:nnF {#2}
+ {
+ { true }
+ {
+ \cs_set_eq:cc
+ { \c__keys_check_root_str \l_keys_path_str }
+ { __keys_check_ #1 : }
+ }
+ { false }
+ {
+ \cs_if_eq:ccT
+ { \c__keys_check_root_str \l_keys_path_str }
+ { __keys_check_ #1 : }
+ {
+ \__keys_cs_undefine:c
+ { \c__keys_check_root_str \l_keys_path_str }
+ }
+ }
+ }
+ {
+ \msg_error:nne { keys }
+ { boolean-values-only }
+ { .value_ #1 :n }
+ }
+ }
+\cs_new_protected:Npn \__keys_check_forbidden:
+ {
+ \bool_if:NF \l__keys_no_value_bool
+ {
+ \msg_error:nnee { keys } { value-forbidden }
+ \l_keys_path_str \l_keys_value_tl
+ \use_none:nnn
+ }
+ }
+\cs_new_protected:Npn \__keys_check_required:
+ {
+ \bool_if:NT \l__keys_no_value_bool
+ {
+ \msg_error:nne { keys } { value-required }
+ \l_keys_path_str
+ \use_none:nnn
+ }
+ }
+\cs_new_protected:Npn \__keys_usage:n #1
+ {
+ \str_case:nnF {#1}
+ {
+ { general }
+ {
+ \__keys_usage:NN \l_keys_usage_load_prop
+ \c_false_bool
+ \__keys_usage:NN \l_keys_usage_preamble_prop
+ \c_false_bool
+ }
+ { load }
+ {
+ \__keys_usage:NN \l_keys_usage_load_prop
+ \c_true_bool
+ \__keys_usage:NN \l_keys_usage_preamble_prop
+ \c_false_bool
+ }
+ { preamble }
+ {
+ \__keys_usage:NN \l_keys_usage_load_prop
+ \c_false_bool
+ \__keys_usage:NN \l_keys_usage_preamble_prop
+ \c_true_bool
+ }
+ }
+ {
+ \msg_error:nnnn { keys }
+ { choice-unknown }
+ { .usage:n }
+ {#1}
+ }
+ }
+\cs_new_protected:Npn \__keys_usage:NN #1#2
+ {
+ \prop_get:NVNF #1 \l__keys_module_str \l__keys_tmpa_tl
+ { \tl_clear:N \l__keys_tmpa_tl }
+ \tl_set:Ne \l__keys_tmpb_tl
+ { \exp_after:wN \__keys_usage:w \l_keys_path_str \s__keys_stop }
+ \bool_if:NTF #2
+ { \clist_put_right:NV \l__keys_tmpa_tl \l__keys_tmpb_tl }
+ { \clist_remove_all:NV \l__keys_tmpa_tl \l__keys_tmpb_tl }
+ \prop_put:NVV #1 \l__keys_module_str
+ \l__keys_tmpa_tl
+ }
+\cs_new:Npn \__keys_usage:w #1 / #2 \s__keys_stop {#2}
+\cs_new_protected:Npn \__keys_variable_set:NnnN #1#2#3#4
+ {
+ \use:c { #2_if_exist:NF } #1 { \use:c { #2 _new:N } #1 }
+ \__keys_cmd_set:ne \l_keys_path_str
+ {
+ \exp_not:c { #2 _ #3 set:N #4 }
+ \exp_not:N #1
+ \exp_not:n { {##1} }
+ }
+ }
+\cs_generate_variant:Nn \__keys_variable_set:NnnN { c }
+\cs_new_protected:Npn \__keys_variable_set_required:NnnN #1#2#3#4
+ {
+ \__keys_variable_set:NnnN #1 {#2} {#3} #4
+ \__keys_value_requirement:nn { required } { true }
+ }
+\cs_generate_variant:Nn \__keys_variable_set_required:NnnN { c }
+\cs_new_protected:cpn { \c__keys_props_root_str .bool_set:N } #1
+ { \__keys_bool_set:Nn #1 { } }
+\cs_new_protected:cpn { \c__keys_props_root_str .bool_set:c } #1
+ { \__keys_bool_set:cn {#1} { } }
+\cs_new_protected:cpn { \c__keys_props_root_str .bool_gset:N } #1
+ { \__keys_bool_set:Nn #1 { g } }
+\cs_new_protected:cpn { \c__keys_props_root_str .bool_gset:c } #1
+ { \__keys_bool_set:cn {#1} { g } }
+\cs_new_protected:cpn { \c__keys_props_root_str .bool_set_inverse:N } #1
+ { \__keys_bool_set_inverse:Nn #1 { } }
+\cs_new_protected:cpn { \c__keys_props_root_str .bool_set_inverse:c } #1
+ { \__keys_bool_set_inverse:cn {#1} { } }
+\cs_new_protected:cpn { \c__keys_props_root_str .bool_gset_inverse:N } #1
+ { \__keys_bool_set_inverse:Nn #1 { g } }
+\cs_new_protected:cpn { \c__keys_props_root_str .bool_gset_inverse:c } #1
+ { \__keys_bool_set_inverse:cn {#1} { g } }
+\cs_new_protected:cpn { \c__keys_props_root_str .choice: }
+ { \__keys_choice_make: }
+\cs_new_protected:cpn { \c__keys_props_root_str .choices:nn } #1
+ { \__keys_choices_make:nn #1 }
+\cs_new_protected:cpn { \c__keys_props_root_str .choices:Vn } #1
+ { \exp_args:NV \__keys_choices_make:nn #1 }
+\cs_new_protected:cpn { \c__keys_props_root_str .choices:en } #1
+ { \exp_args:Ne \__keys_choices_make:nn #1 }
+\cs_new_protected:cpn { \c__keys_props_root_str .choices:on } #1
+ { \exp_args:No \__keys_choices_make:nn #1 }
+\cs_new_protected:cpn { \c__keys_props_root_str .choices:xn } #1
+ { \exp_args:Nx \__keys_choices_make:nn #1 }
+\cs_new_protected:cpn { \c__keys_props_root_str .code:n } #1
+ { \__keys_cmd_set:nn \l_keys_path_str {#1} }
+\cs_new_protected:cpn { \c__keys_props_root_str .clist_set:N } #1
+ { \__keys_variable_set:NnnN #1 { clist } { } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .clist_set:c } #1
+ { \__keys_variable_set:cnnN {#1} { clist } { } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .clist_gset:N } #1
+ { \__keys_variable_set:NnnN #1 { clist } { g } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .clist_gset:c } #1
+ { \__keys_variable_set:cnnN {#1} { clist } { g } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .cs_set:Np } #1
+ { \__keys_cs_set:NNpn \cs_set:Npn #1 { } }
+\cs_new_protected:cpn { \c__keys_props_root_str .cs_set:cp } #1
+ { \__keys_cs_set:Ncpn \cs_set:Npn #1 { } }
+\cs_new_protected:cpn { \c__keys_props_root_str .cs_set_protected:Np } #1
+ { \__keys_cs_set:NNpn \cs_set_protected:Npn #1 { } }
+\cs_new_protected:cpn { \c__keys_props_root_str .cs_set_protected:cp } #1
+ { \__keys_cs_set:Ncpn \cs_set_protected:Npn #1 { } }
+\cs_new_protected:cpn { \c__keys_props_root_str .cs_gset:Np } #1
+ { \__keys_cs_set:NNpn \cs_gset:Npn #1 { } }
+\cs_new_protected:cpn { \c__keys_props_root_str .cs_gset:cp } #1
+ { \__keys_cs_set:Ncpn \cs_gset:Npn #1 { } }
+\cs_new_protected:cpn { \c__keys_props_root_str .cs_gset_protected:Np } #1
+ { \__keys_cs_set:NNpn \cs_gset_protected:Npn #1 { } }
+\cs_new_protected:cpn { \c__keys_props_root_str .cs_gset_protected:cp } #1
+ { \__keys_cs_set:Ncpn \cs_gset_protected:Npn #1 { } }
+\cs_new_protected:cpn { \c__keys_props_root_str .default:n } #1
+ { \__keys_default_set:n {#1} }
+\cs_new_protected:cpn { \c__keys_props_root_str .default:V } #1
+ { \exp_args:NV \__keys_default_set:n #1 }
+\cs_new_protected:cpn { \c__keys_props_root_str .default:e } #1
+ { \exp_args:Ne \__keys_default_set:n {#1} }
+\cs_new_protected:cpn { \c__keys_props_root_str .default:o } #1
+ { \exp_args:No \__keys_default_set:n {#1} }
+\cs_new_protected:cpn { \c__keys_props_root_str .default:x } #1
+ { \exp_args:Nx \__keys_default_set:n {#1} }
+\cs_new_protected:cpn { \c__keys_props_root_str .dim_set:N } #1
+ { \__keys_variable_set_required:NnnN #1 { dim } { } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .dim_set:c } #1
+ { \__keys_variable_set_required:cnnN {#1} { dim } { } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .dim_gset:N } #1
+ { \__keys_variable_set_required:NnnN #1 { dim } { g } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .dim_gset:c } #1
+ { \__keys_variable_set_required:cnnN {#1} { dim } { g } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .fp_set:N } #1
+ { \__keys_variable_set_required:NnnN #1 { fp } { } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .fp_set:c } #1
+ { \__keys_variable_set_required:cnnN {#1} { fp } { } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .fp_gset:N } #1
+ { \__keys_variable_set_required:NnnN #1 { fp } { g } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .fp_gset:c } #1
+ { \__keys_variable_set_required:cnnN {#1} { fp } { g } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .groups:n } #1
+ { \__keys_groups_set:n {#1} }
+\cs_new_protected:cpn { \c__keys_props_root_str .inherit:n } #1
+ { \__keys_inherit:n {#1} }
+\cs_new_protected:cpn { \c__keys_props_root_str .initial:n } #1
+ { \__keys_initialise:n {#1} }
+\cs_new_protected:cpn { \c__keys_props_root_str .initial:V } #1
+ { \exp_args:NV \__keys_initialise:n #1 }
+\cs_new_protected:cpn { \c__keys_props_root_str .initial:e } #1
+ { \exp_args:Ne \__keys_initialise:n {#1} }
+\cs_new_protected:cpn { \c__keys_props_root_str .initial:o } #1
+ { \exp_args:No \__keys_initialise:n {#1} }
+\cs_new_protected:cpn { \c__keys_props_root_str .initial:x } #1
+ { \exp_args:Nx \__keys_initialise:n {#1} }
+\cs_new_protected:cpn { \c__keys_props_root_str .int_set:N } #1
+ { \__keys_variable_set_required:NnnN #1 { int } { } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .int_set:c } #1
+ { \__keys_variable_set_required:cnnN {#1} { int } { } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .int_gset:N } #1
+ { \__keys_variable_set_required:NnnN #1 { int } { g } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .int_gset:c } #1
+ { \__keys_variable_set_required:cnnN {#1} { int } { g } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .legacy_if_set:n } #1
+ { \__keys_legacy_if_set:nn {#1} { } }
+\cs_new_protected:cpn { \c__keys_props_root_str .legacy_if_gset:n } #1
+ { \__keys_legacy_if_set:nn {#1} { g } }
+\cs_new_protected:cpn { \c__keys_props_root_str .legacy_if_set_inverse:n } #1
+ { \__keys_legacy_if_set_inverse:nn {#1} { } }
+\cs_new_protected:cpn { \c__keys_props_root_str .legacy_if_gset_inverse:n } #1
+ { \__keys_legacy_if_set_inverse:nn {#1} { g } }
+\cs_new_protected:cpn { \c__keys_props_root_str .meta:n } #1
+ { \__keys_meta_make:n {#1} }
+\cs_new_protected:cpn { \c__keys_props_root_str .meta:nn } #1
+ { \__keys_meta_make:nn #1 }
+\cs_new_protected:cpn { \c__keys_props_root_str .multichoice: }
+ { \__keys_multichoice_make: }
+\cs_new_protected:cpn { \c__keys_props_root_str .multichoices:nn } #1
+ { \__keys_multichoices_make:nn #1 }
+\cs_new_protected:cpn { \c__keys_props_root_str .multichoices:Vn } #1
+ { \exp_args:NV \__keys_multichoices_make:nn #1 }
+\cs_new_protected:cpn { \c__keys_props_root_str .multichoices:en } #1
+ { \exp_args:Ne \__keys_multichoices_make:nn #1 }
+\cs_new_protected:cpn { \c__keys_props_root_str .multichoices:on } #1
+ { \exp_args:No \__keys_multichoices_make:nn #1 }
+\cs_new_protected:cpn { \c__keys_props_root_str .multichoices:xn } #1
+ { \exp_args:Nx \__keys_multichoices_make:nn #1 }
+\cs_new_protected:cpn { \c__keys_props_root_str .muskip_set:N } #1
+ { \__keys_variable_set_required:NnnN #1 { muskip } { } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .muskip_set:c } #1
+ { \__keys_variable_set_required:cnnN {#1} { muskip } { } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .muskip_gset:N } #1
+ { \__keys_variable_set_required:NnnN #1 { muskip } { g } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .muskip_gset:c } #1
+ { \__keys_variable_set_required:cnnN {#1} { muskip } { g } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .prop_put:N } #1
+ { \__keys_prop_put:Nn #1 { } }
+\cs_new_protected:cpn { \c__keys_props_root_str .prop_put:c } #1
+ { \__keys_prop_put:cn {#1} { } }
+\cs_new_protected:cpn { \c__keys_props_root_str .prop_gput:N } #1
+ { \__keys_prop_put:Nn #1 { g } }
+\cs_new_protected:cpn { \c__keys_props_root_str .prop_gput:c } #1
+ { \__keys_prop_put:cn {#1} { g } }
+\cs_new_protected:cpn { \c__keys_props_root_str .skip_set:N } #1
+ { \__keys_variable_set_required:NnnN #1 { skip } { } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .skip_set:c } #1
+ { \__keys_variable_set_required:cnnN {#1} { skip } { } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .skip_gset:N } #1
+ { \__keys_variable_set_required:NnnN #1 { skip } { g } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .skip_gset:c } #1
+ { \__keys_variable_set_required:cnnN {#1} { skip } { g } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .str_set:N } #1
+ { \__keys_variable_set:NnnN #1 { str } { } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .str_set:c } #1
+ { \__keys_variable_set:cnnN {#1} { str } { } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .str_set_e:N } #1
+ { \__keys_variable_set:NnnN #1 { str } { } e }
+\cs_new_protected:cpn { \c__keys_props_root_str .str_set_e:c } #1
+ { \__keys_variable_set:cnnN {#1} { str } { } e }
+\cs_new_protected:cpn { \c__keys_props_root_str .str_gset:N } #1
+ { \__keys_variable_set:NnnN #1 { str } { g } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .str_gset:c } #1
+ { \__keys_variable_set:cnnN {#1} { str } { g } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .str_gset_e:N } #1
+ { \__keys_variable_set:NnnN #1 { str } { g } e }
+\cs_new_protected:cpn { \c__keys_props_root_str .str_gset_e:c } #1
+ { \__keys_variable_set:cnnN {#1} { str } { g } e }
+\cs_new_protected:cpn { \c__keys_props_root_str .tl_set:N } #1
+ { \__keys_variable_set:NnnN #1 { tl } { } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .tl_set:c } #1
+ { \__keys_variable_set:cnnN {#1} { tl } { } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .tl_set_e:N } #1
+ { \__keys_variable_set:NnnN #1 { tl } { } e }
+\cs_new_protected:cpn { \c__keys_props_root_str .tl_set_e:c } #1
+ { \__keys_variable_set:cnnN {#1} { tl } { } e }
+\cs_new_protected:cpn { \c__keys_props_root_str .tl_gset:N } #1
+ { \__keys_variable_set:NnnN #1 { tl } { g } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .tl_gset:c } #1
+ { \__keys_variable_set:cnnN {#1} { tl } { g } n }
+\cs_new_protected:cpn { \c__keys_props_root_str .tl_gset_e:N } #1
+ { \__keys_variable_set:NnnN #1 { tl } { g } e }
+\cs_new_protected:cpn { \c__keys_props_root_str .tl_gset_e:c } #1
+ { \__keys_variable_set:cnnN {#1} { tl } { g } e }
+\cs_new_protected:cpn { \c__keys_props_root_str .undefine: }
+ { \__keys_undefine: }
+\cs_new_protected:cpn { \c__keys_props_root_str .usage:n } #1
+ { \__keys_usage:n {#1} }
+\cs_new_protected:cpn { \c__keys_props_root_str .value_forbidden:n } #1
+ { \__keys_value_requirement:nn { forbidden } {#1} }
+\cs_new_protected:cpn { \c__keys_props_root_str .value_required:n } #1
+ { \__keys_value_requirement:nn { required } {#1} }
+\cs_new_protected:Npn \keys_set:nn #1#2
+ {
+ \use:e
+ {
+ \bool_set_false:N \exp_not:N \l__keys_only_known_bool
+ \bool_set_false:N \exp_not:N \l__keys_filtered_bool
+ \bool_set_false:N \exp_not:N \l__keys_selective_bool
+ \tl_set:Nn \exp_not:N \l__keys_relative_tl
+ { \exp_not:N \q__keys_no_value }
+ \__keys_set:nn \exp_not:n { {#1} {#2} }
+ \bool_if:NT \l__keys_only_known_bool
+ { \bool_set_true:N \exp_not:N \l__keys_only_known_bool }
+ \bool_if:NT \l__keys_filtered_bool
+ { \bool_set_true:N \exp_not:N \l__keys_filtered_bool }
+ \bool_if:NT \l__keys_selective_bool
+ { \bool_set_true:N \exp_not:N \l__keys_selective_bool }
+ \tl_set:Nn \exp_not:N \l__keys_relative_tl
+ { \exp_not:o \l__keys_relative_tl }
+ }
+ }
+\cs_generate_variant:Nn \keys_set:nn { nV , nv , ne , no , nx }
+\cs_new_protected:Npn \__keys_set:nn #1#2
+ { \exp_args:No \__keys_set:nnn \l__keys_module_str {#1} {#2} }
+\cs_new_protected:Npn \__keys_set:nnn #1#2#3
+ {
+ \str_set:Ne \l__keys_module_str { \__keys_trim_spaces:n {#2} }
+ \keyval_parse:NNn \__keys_set_keyval:n \__keys_set_keyval:nn {#3}
+ \str_set:Nn \l__keys_module_str {#1}
+ }
+\cs_new_protected:Npn \keys_set_known:nnN #1#2#3
+ {
+ \exp_args:No \__keys_set_known:nnnnN
+ \l__keys_unused_clist \q__keys_no_value {#1} {#2} #3
+ }
+\cs_generate_variant:Nn \keys_set_known:nnN { nV , nv , ne , no }
+\cs_new_protected:Npn \keys_set_known:nnnN #1#2#3#4
+ {
+ \exp_args:No \__keys_set_known:nnnnN
+ \l__keys_unused_clist {#3} {#1} {#2} #4
+ }
+\cs_generate_variant:Nn \keys_set_known:nnnN { nV , nv , ne , no }
+\cs_new_protected:Npn \__keys_set_known:nnnnN #1#2#3#4#5
+ {
+ \clist_clear:N \l__keys_unused_clist
+ \__keys_set_known:nnn {#2} {#3} {#4}
+ \__kernel_tl_set:Nx #5 { \exp_not:o \l__keys_unused_clist }
+ \__kernel_tl_set:Nx \l__keys_unused_clist { \exp_not:n {#1} }
+ }
+\cs_new_protected:Npn \keys_set_known:nn #1#2
+ { \__keys_set_known:nnn \q__keys_no_value {#1} {#2} }
+\cs_generate_variant:Nn \keys_set_known:nn { nV , nv , ne , no }
+\cs_new_protected:Npn \__keys_set_known:nnn #1#2#3
+ {
+ \use:e
+ {
+ \bool_set_true:N \exp_not:N \l__keys_only_known_bool
+ \bool_set_false:N \exp_not:N \l__keys_filtered_bool
+ \bool_set_false:N \exp_not:N \l__keys_selective_bool
+ \tl_set:Nn \exp_not:N \l__keys_relative_tl { \exp_not:n {#1} }
+ \__keys_set:nn \exp_not:n { {#2} {#3} }
+ \bool_if:NF \l__keys_only_known_bool
+ { \bool_set_false:N \exp_not:N \l__keys_only_known_bool }
+ \bool_if:NT \l__keys_filtered_bool
+ { \bool_set_true:N \exp_not:N \l__keys_filtered_bool }
+ \bool_if:NT \l__keys_selective_bool
+ { \bool_set_true:N \exp_not:N \l__keys_selective_bool }
+ \tl_set:Nn \exp_not:N \l__keys_relative_tl
+ { \exp_not:o \l__keys_relative_tl }
+ }
+ }
+\cs_new_protected:Npn \keys_set_exclude_groups:nnnN #1#2#3#4
+ {
+ \exp_args:No \__keys_set_exclude_groups:nnnnnN
+ \l__keys_unused_clist
+ \q__keys_no_value {#1} {#2} {#3} #4
+ }
+\cs_generate_variant:Nn \keys_set_exclude_groups:nnnN { nnV , nnv , nno }
+\cs_new_protected:Npn \keys_set_exclude_groups:nnnnN #1#2#3#4#5
+ {
+ \exp_args:No \__keys_set_exclude_groups:nnnnnN
+ \l__keys_unused_clist {#4} {#1} {#2} {#3} #5
+ }
+\cs_generate_variant:Nn \keys_set_exclude_groups:nnnnN { nnV , nnv , nno }
+\cs_new_protected:Npn \__keys_set_exclude_groups:nnnnnN #1#2#3#4#5#6
+ {
+ \clist_clear:N \l__keys_unused_clist
+ \__keys_set_exclude_groups:nnnn {#2} {#3} {#4} {#5}
+ \__kernel_tl_set:Nx #6 { \exp_not:o \l__keys_unused_clist }
+ \__kernel_tl_set:Nx \l__keys_unused_clist { \exp_not:n {#1} }
+ }
+\cs_new_protected:Npn \keys_set_exclude_groups:nnn #1#2#3
+ {\__keys_set_exclude_groups:nnnn \q__keys_no_value {#1} {#2} {#3} }
+\cs_generate_variant:Nn \keys_set_exclude_groups:nnn { nnV , nnv , nno }
+\cs_new_protected:Npn \__keys_set_exclude_groups:nnnn #1#2#3#4
+ {
+ \use:e
+ {
+ \bool_set_false:N \exp_not:N \l__keys_only_known_bool
+ \bool_set_true:N \exp_not:N \l__keys_filtered_bool
+ \bool_set_true:N \exp_not:N \l__keys_selective_bool
+ \tl_set:Nn \exp_not:N \l__keys_relative_tl { \exp_not:n {#1} }
+ \__keys_set_selective:nnn \exp_not:n { {#2} {#3} {#4} }
+ \bool_if:NT \l__keys_only_known_bool
+ { \bool_set_true:N \exp_not:N \l__keys_only_known_bool }
+ \bool_if:NF \l__keys_filtered_bool
+ { \bool_set_false:N \exp_not:N \l__keys_filtered_bool }
+ \bool_if:NF \l__keys_selective_bool
+ { \bool_set_false:N \exp_not:N \l__keys_selective_bool }
+ \tl_set:Nn \exp_not:N \l__keys_relative_tl
+ { \exp_not:o \l__keys_relative_tl }
+ }
+ }
+\cs_new_protected:Npn \keys_set_groups:nnn #1#2#3
+ {
+ \use:e
+ {
+ \bool_set_false:N \exp_not:N \l__keys_only_known_bool
+ \bool_set_false:N \exp_not:N \l__keys_filtered_bool
+ \bool_set_true:N \exp_not:N \l__keys_selective_bool
+ \tl_set:Nn \exp_not:N \l__keys_relative_tl
+ { \exp_not:N \q__keys_no_value }
+ \__keys_set_selective:nnn \exp_not:n { {#1} {#2} {#3} }
+ \bool_if:NT \l__keys_only_known_bool
+ { \bool_set_true:N \exp_not:N \l__keys_only_known_bool }
+ \bool_if:NF \l__keys_filtered_bool
+ { \bool_set_true:N \exp_not:N \l__keys_filtered_bool }
+ \bool_if:NF \l__keys_selective_bool
+ { \bool_set_false:N \exp_not:N \l__keys_selective_bool }
+ \tl_set:Nn \exp_not:N \l__keys_relative_tl
+ { \exp_not:o \l__keys_relative_tl }
+ }
+ }
+\cs_generate_variant:Nn \keys_set_groups:nnn { nnV , nnv , nno }
+\cs_new_protected:Npn \__keys_set_selective:nnn
+ { \exp_args:No \__keys_set_selective:nnnn \l__keys_selective_seq }
+\cs_new_protected:Npn \__keys_set_selective:nnnn #1#2#3#4
+ {
+ \exp_args:NNe \seq_set_from_clist:Nn
+ \l__keys_selective_seq { \tl_to_str:n {#3} }
+ \__keys_set:nn {#2} {#4}
+ \tl_set:Nn \l__keys_selective_seq {#1}
+ }
+\cs_new_protected:Npn \keys_precompile:nnN #1#2#3
+ {
+ \bool_set_true:N \l__keys_precompile_bool
+ \tl_clear:N \l__keys_precompile_tl
+ \keys_set:nn {#1} {#2}
+ \bool_set_false:N \l__keys_precompile_bool
+ \tl_set_eq:NN #3 \l__keys_precompile_tl
+ }
+\cs_new_protected:Npn \__keys_set_keyval:n #1
+ {
+ \bool_set_true:N \l__keys_no_value_bool
+ \__keys_set_keyval:onn \l__keys_module_str {#1} { }
+ }
+\cs_new_protected:Npn \__keys_set_keyval:nn #1#2
+ {
+ \bool_set_false:N \l__keys_no_value_bool
+ \__keys_set_keyval:onn \l__keys_module_str {#1} {#2}
+ }
+\cs_new_protected:Npn \__keys_set_keyval:nnn #1#2#3
+ {
+ \__kernel_tl_set:Nx \l_keys_path_str
+ {
+ \tl_if_blank:nF {#1}
+ { #1 / }
+ \__keys_trim_spaces:n {#2}
+ }
+ \str_clear:N \l__keys_module_str
+ \str_clear:N \l__keys_inherit_str
+ \exp_after:wN \__keys_find_key_module:wNN \l_keys_path_str \s__keys_stop
+ \l__keys_module_str \l_keys_key_str
+ \tl_set_eq:NN \l_keys_key_tl \l_keys_key_str
+ \__keys_value_or_default:n {#3}
+ \bool_if:NTF \l__keys_selective_bool
+ \__keys_set_selective:
+ \__keys_execute:
+ \str_set:Nn \l__keys_module_str {#1}
+ }
+\cs_generate_variant:Nn \__keys_set_keyval:nnn { o }
+\cs_new_protected:Npn \__keys_find_key_module:wNN #1 \s__keys_stop #2 #3
+ {
+ \__keys_find_key_module_auxi:Nw #2 #1 \s__keys_nil \__keys_find_key_module_auxii:Nw
+ / \s__keys_nil \__keys_find_key_module_auxiv:Nw #3
+ }
+\cs_new_protected:Npn \__keys_find_key_module_auxi:Nw #1 #2 / #3 \s__keys_nil #4
+ {
+ #4 #1 #2 \s__keys_mark #3 \s__keys_nil #4
+ }
+\cs_new_protected:Npn \__keys_find_key_module_auxii:Nw
+ #1 #2 \s__keys_mark #3 \s__keys_nil \__keys_find_key_module_auxii:Nw
+ {
+ \cs_set_nopar:Npe #1 { \tl_if_empty:NF #1 { #1 / } #2 }
+ \__keys_find_key_module_auxi:Nw #1 #3 \s__keys_nil \__keys_find_key_module_auxiii:Nw
+ }
+\cs_new_protected:Npn \__keys_find_key_module_auxiii:Nw #1 #2 \s__keys_mark
+ {
+ \cs_set_nopar:Npe #1 { \tl_if_empty:NF #1 { #1 / } #2 }
+ \__keys_find_key_module_auxi:Nw #1
+ }
+\cs_new_protected:Npn \__keys_find_key_module_auxiv:Nw
+ #1 #2 \s__keys_nil #3 \s__keys_mark
+ \s__keys_nil \__keys_find_key_module_auxiv:Nw #4
+ {
+ \cs_set_nopar:Npn #4 { #2 }
+ }
+\cs_new_protected:Npn \__keys_set_selective:
+ {
+ \cs_if_exist:cTF { \c__keys_groups_root_str \l_keys_path_str }
+ {
+ \clist_set_eq:Nc \l__keys_groups_clist
+ { \c__keys_groups_root_str \l_keys_path_str }
+ \__keys_check_groups:
+ }
+ {
+ \bool_if:NTF \l__keys_filtered_bool
+ \__keys_execute:
+ \__keys_store_unused:
+ }
+ }
+\cs_new_protected:Npn \__keys_check_groups:
+ {
+ \bool_set_false:N \l__keys_tmp_bool
+ \seq_map_inline:Nn \l__keys_selective_seq
+ {
+ \clist_if_in:NnT \l__keys_groups_clist {##1}
+ {
+ \bool_set_true:N \l__keys_tmp_bool
+ \seq_map_break:
+ }
+ }
+ \bool_if:NTF \l__keys_tmp_bool
+ {
+ \bool_if:NTF \l__keys_filtered_bool
+ \__keys_store_unused:
+ \__keys_execute:
+ }
+ {
+ \bool_if:NTF \l__keys_filtered_bool
+ \__keys_execute:
+ \__keys_store_unused:
+ }
+ }
+\cs_new_protected:Npn \__keys_value_or_default:n #1
+ {
+ \bool_if:NTF \l__keys_no_value_bool
+ {
+ \cs_if_exist:cTF { \c__keys_default_root_str \l_keys_path_str }
+ {
+ \tl_set_eq:Nc
+ \l_keys_value_tl
+ { \c__keys_default_root_str \l_keys_path_str }
+ }
+ {
+ \tl_clear:N \l_keys_value_tl
+ \cs_if_exist:cT
+ { \c__keys_inherit_root_str \__keys_parent:o \l_keys_path_str }
+ { \__keys_default_inherit: }
+ }
+ }
+ { \tl_set:Nn \l_keys_value_tl {#1} }
+ }
+\cs_new_protected:Npn \__keys_default_inherit:
+ {
+ \clist_map_inline:cn
+ { \c__keys_inherit_root_str \__keys_parent:o \l_keys_path_str }
+ {
+ \cs_if_exist:cT
+ { \c__keys_default_root_str ##1 / \l_keys_key_str }
+ {
+ \tl_set_eq:Nc
+ \l_keys_value_tl
+ { \c__keys_default_root_str ##1 / \l_keys_key_str }
+ \clist_map_break:
+ }
+ }
+ }
+\cs_new_protected:Npn \__keys_execute:
+ {
+ \cs_if_exist:cTF { \c__keys_code_root_str \l_keys_path_str }
+ {
+ \cs_if_exist_use:c { \c__keys_check_root_str \l_keys_path_str }
+ \__keys_execute:no \l_keys_path_str \l_keys_value_tl
+ }
+ {
+ \cs_if_exist:cTF
+ { \c__keys_inherit_root_str \__keys_parent:o \l_keys_path_str }
+ { \__keys_execute_inherit: }
+ { \__keys_execute_unknown: }
+ }
+ }
+\cs_new_protected:Npn \__keys_execute_inherit:
+ {
+ \clist_map_inline:cn
+ { \c__keys_inherit_root_str \__keys_parent:o \l_keys_path_str }
+ {
+ \cs_if_exist:cT
+ { \c__keys_code_root_str ##1 / \l_keys_key_str }
+ {
+ \str_set:Nn \l__keys_inherit_str {##1}
+ \cs_if_exist_use:c { \c__keys_check_root_str ##1 / \l_keys_key_str }
+ \__keys_execute:no { ##1 / \l_keys_key_str } \l_keys_value_tl
+ \clist_map_break:n \use_none:n
+ }
+ }
+ \__keys_execute_unknown:
+ }
+\cs_new_protected:Npn \__keys_execute_unknown:
+ {
+ \bool_if:NTF \l__keys_only_known_bool
+ { \__keys_store_unused: }
+ {
+ \cs_if_exist:cTF
+ { \c__keys_code_root_str \l__keys_module_str / unknown }
+ {
+ \bool_if:NT \l__keys_no_value_bool
+ {
+ \cs_if_exist:cT
+ { \c__keys_default_root_str \l__keys_module_str / unknown }
+ {
+ \tl_set_eq:Nc
+ \l_keys_value_tl
+ { \c__keys_default_root_str \l__keys_module_str / unknown }
+ }
+ }
+ \__keys_execute:no { \l__keys_module_str / unknown } \l_keys_value_tl
+ }
+ {
+ \msg_error:nnee { keys } { unknown }
+ \l_keys_path_str \l__keys_module_str
+ }
+ }
+ }
+\cs_new:Npn \__keys_execute:nn #1#2
+ { \__keys_execute:no {#1} { \prg_do_nothing: #2 } }
+\cs_new:Npn \__keys_execute:no #1#2
+ {
+ \exp_args:NNo \exp_args:No \use:n
+ {
+ \cs:w \c__keys_code_root_str #1 \exp_after:wN \cs_end:
+ \exp_after:wN {#2}
+ }
+ }
+\cs_new_protected:Npn \__keys_store_unused:
+ {
+ \__keys_quark_if_no_value:NTF \l__keys_relative_tl
+ {
+ \clist_put_right:Ne \l__keys_unused_clist
+ {
+ \l_keys_key_str
+ \bool_if:NF \l__keys_no_value_bool
+ { = { \exp_not:o \l_keys_value_tl } }
+ }
+ }
+ {
+ \tl_if_empty:NTF \l__keys_relative_tl
+ {
+ \clist_put_right:Ne \l__keys_unused_clist
+ {
+ \l_keys_path_str
+ \bool_if:NF \l__keys_no_value_bool
+ { = { \exp_not:o \l_keys_value_tl } }
+ }
+ }
+ { \__keys_store_unused_aux: }
+ }
+ }
+\cs_new_protected:Npn \__keys_store_unused_aux:
+ {
+ \__kernel_tl_set:Nx \l__keys_relative_tl
+ { \exp_args:No \__keys_trim_spaces:n \l__keys_relative_tl }
+ \use:e
+ {
+ \cs_set_protected:Npn \__keys_store_unused:w
+ ##1 \l__keys_relative_tl /
+ ##2 \l__keys_relative_tl /
+ ##3 \s__keys_stop
+ }
+ {
+ \tl_if_blank:nF {##1}
+ {
+ \msg_error:nnee { keys } { bad-relative-key-path }
+ \l_keys_path_str
+ \l__keys_relative_tl
+ }
+ \clist_put_right:Ne \l__keys_unused_clist
+ {
+ \exp_not:n {##2}
+ \bool_if:NF \l__keys_no_value_bool
+ { = { \exp_not:o \l_keys_value_tl } }
+ }
+ }
+ \use:e
+ {
+ \__keys_store_unused:w \l_keys_path_str
+ \l__keys_relative_tl / \l__keys_relative_tl /
+ \s__keys_stop
+ }
+ }
+\cs_new_protected:Npn \__keys_store_unused:w { }
+\cs_new:Npn \__keys_choice_find:n #1
+ {
+ \str_if_empty:NTF \l__keys_inherit_str
+ { \__keys_choice_find:nn \l_keys_path_str {#1} }
+ {
+ \__keys_choice_find:nn
+ { \l__keys_inherit_str / \l_keys_key_str } {#1}
+ }
+ }
+\cs_new:Npn \__keys_choice_find:nn #1#2
+ {
+ \cs_if_exist:cTF { \c__keys_code_root_str #1 / \__keys_trim_spaces:n {#2} }
+ { \__keys_execute:nn { #1 / \__keys_trim_spaces:n {#2} } {#2} }
+ { \__keys_execute:nn { #1 / unknown } {#2} }
+ }
+\cs_new:Npn \__keys_multichoice_find:n #1
+ { \clist_map_function:nN {#1} \__keys_choice_find:n }
+\cs_new:Npn \__keys_parent:o #1
+ {
+ \exp_after:wN \__keys_parent_auxi:w #1 \q_nil \__keys_parent_auxii:w
+ / \q_nil \__keys_parent_auxiv:w
+ }
+\cs_new:Npn \__keys_parent_auxi:w #1 / #2 \q_nil #3
+ {
+ #3 { #1 } #2 \q_nil #3
+ }
+\cs_new:Npn \__keys_parent_auxii:w #1 #2 \q_nil \__keys_parent_auxii:w
+ {
+ #1 \__keys_parent_auxi:w #2 \q_nil \__keys_parent_auxiii:n
+ }
+\cs_new:Npn \__keys_parent_auxiii:n #1
+ {
+ / #1 \__keys_parent_auxi:w
+ }
+\cs_new:Npn \__keys_parent_auxiv:w #1 \q_nil \__keys_parent_auxiv:w
+ {
+ }
+\group_begin:
+ \cs_set:Npn \__keys_tmp:w #1
+ {
+ \cs_new:Npn \__keys_trim_spaces:n ##1
+ {
+ \exp_after:wN \__keys_trim_spaces_auxi:w \tl_to_str:n { / ##1 } /
+ \s__keys_nil \__keys_trim_spaces_auxi:w
+ \s__keys_mark \__keys_trim_spaces_auxii:w
+ #1 / #1
+ \s__keys_nil \__keys_trim_spaces_auxii:w
+ \s__keys_mark \__keys_trim_spaces_auxiii:w
+ }
+ }
+ \__keys_tmp:w { ~ }
+\group_end:
+\cs_new:Npn \__keys_trim_spaces_auxi:w #1 ~ / #2 \s__keys_nil #3
+ {
+ #3 #1 / #2 \s__keys_nil #3
+ }
+\cs_new:Npn \__keys_trim_spaces_auxii:w #1 / ~ #2 \s__keys_mark #3
+ {
+ #3 #1 / #2 \s__keys_mark #3
+ }
+\cs_new:Npn \__keys_trim_spaces_auxiii:w
+ / #1 /
+ \s__keys_nil \__keys_trim_spaces_auxi:w
+ \s__keys_mark \__keys_trim_spaces_auxii:w
+ /
+ \s__keys_nil \__keys_trim_spaces_auxii:w
+ \s__keys_mark \__keys_trim_spaces_auxiii:w
+ {
+ #1
+ }
+\prg_new_conditional:Npnn \keys_if_exist:nn #1#2 { p , T , F , TF }
+ {
+ \cs_if_exist:cTF
+ { \c__keys_code_root_str \__keys_trim_spaces:n { #1 / #2 } }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\prg_generate_conditional_variant:Nnn \keys_if_exist:nn { ne } { p , T , F , TF }
+\prg_new_conditional:Npnn \keys_if_choice_exist:nnn #1#2#3
+ { p , T , F , TF }
+ {
+ \cs_if_exist:cTF
+ { \c__keys_code_root_str \__keys_trim_spaces:n { #1 / #2 / #3 } }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_new_protected:Npn \keys_show:nn
+ { \__keys_show:Nnn \msg_show:nneeee }
+\cs_new_protected:Npn \keys_log:nn
+ { \__keys_show:Nnn \msg_log:nneeee }
+\cs_new_protected:Npn \__keys_show:Nnn #1#2#3
+ {
+ #1 { keys } { show-key }
+ { \__keys_trim_spaces:n { #2 / #3 } }
+ {
+ \keys_if_exist:nnT {#2} {#3}
+ {
+ \exp_args:Nnf \msg_show_item_unbraced:nn { code }
+ {
+ \exp_args:Ne \__keys_show:n
+ {
+ \exp_args:Nc \cs_replacement_spec:N
+ {
+ \c__keys_code_root_str
+ \__keys_trim_spaces:n { #2 / #3 }
+ }
+ }
+ }
+ }
+ }
+ { } { }
+ }
+\cs_new:Npe \__keys_show:n #1
+ {
+ \exp_not:N \__keys_show:w
+ #1
+ \tl_to_str:n { \__keys_precompile:n }
+ #1
+ \tl_to_str:n { \__keys_precompile:n }
+ \exp_not:N \s__keys_stop
+ }
+\use:e
+ {
+ \cs_new:Npn \exp_not:N \__keys_show:w
+ #1 \tl_to_str:n { \__keys_precompile:n }
+ #2 \tl_to_str:n { \__keys_precompile:n }
+ #3 \exp_not:N \s__keys_stop
+ }
+ {
+ \tl_if_blank:nTF {#2}
+ {#1}
+ { \__keys_show:Nw #2 \s__keys_stop }
+ }
+\use:e
+ {
+ \cs_new:Npn \exp_not:N \__keys_show:Nw #1#2
+ \c_right_brace_str \exp_not:N \s__keys_stop
+ }
+ {#2}
+\msg_new:nnnn { keys } { bad-relative-key-path }
+ { The~key~'#1'~is~not~inside~the~'#2'~path. }
+ { The~key~'#1'~cannot~be~expressed~relative~to~path~'#2'. }
+\msg_new:nnnn { keys } { boolean-values-only }
+ { Key~'#1'~accepts~boolean~values~only. }
+ { The~key~'#1'~only~accepts~the~values~'true'~and~'false'. }
+\msg_new:nnnn { keys } { choice-unknown }
+ { Key~'#1'~accepts~only~a~fixed~set~of~choices. }
+ {
+ The~key~'#1'~only~accepts~predefined~values,~
+ and~'#2'~is~not~one~of~these.
+ }
+\msg_new:nnnn { keys } { unknown }
+ { The~key~'#1'~is~unknown~and~is~being~ignored. }
+ {
+ The~module~'#2'~does~not~have~a~key~called~'#1'.\\
+ Check~that~you~have~spelled~the~key~name~correctly.
+ }
+\msg_new:nnnn { keys } { nested-choice-key }
+ { Attempt~to~define~'#1'~as~a~nested~choice~key. }
+ {
+ The~key~'#1'~cannot~be~defined~as~a~choice~as~the~parent~key~'#2'~is~
+ itself~a~choice.
+ }
+\msg_new:nnnn { keys } { value-forbidden }
+ { The~key~'#1'~does~not~take~a~value. }
+ {
+ The~key~'#1'~should~be~given~without~a~value.\\
+ The~value~'#2'~was~present:~the~key~will~be~ignored.
+ }
+\msg_new:nnnn { keys } { value-required }
+ { The~key~'#1'~requires~a~value. }
+ {
+ The~key~'#1'~must~have~a~value.\\
+ No~value~was~present:~the~key~will~be~ignored.
+ }
+\msg_new:nnn { keys } { show-key }
+ {
+ The~key~#1~
+ \tl_if_empty:nTF {#2}
+ { is~undefined. }
+ { has~the~properties: #2 . }
+ }
+\prop_gput:Nnn \g_msg_module_name_prop { keys } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { keys } { }
+%% File: l3intarray.dtx
+\msg_new:nnn { kernel } { negative-array-size }
+ { Size~of~array~may~not~be~negative:~#1 }
+\int_new:N \l__intarray_loop_int
+\cs_if_exist:NTF \__intarray_gset_count:Nw
+ {
+ \int_new:N \g__intarray_table_int
+ \int_new:N \l__intarray_bad_index_int
+ \cs_new_protected:Npn \__intarray_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \int_gincr:N \g__intarray_table_int
+ \cs_gset_nopar:Npe #1 { \__intarray:w \int_use:N \g__intarray_table_int \c_space_tl }
+ }
+ \cs_new_protected:Npn \intarray_new:Nn #1#2
+ {
+ \__intarray_new:N #1
+ \__intarray_gset_count:Nw #1 \int_eval:n {#2} \scan_stop:
+ \int_compare:nNnT { \intarray_count:N #1 } < 0
+ {
+ \msg_error:nne { kernel } { negative-array-size }
+ { \intarray_count:N #1 }
+ }
+ }
+ \cs_generate_variant:Nn \intarray_new:Nn { c }
+ \cs_generate_variant:Nn \intarray_count:N { c }
+ \cs_new_protected:Npn \__kernel_intarray_gset:Nnn #1#2#3
+ { \__intarray_gset:w #2 #1 #3 \scan_stop: }
+ \cs_new_protected:Npn \intarray_gset:Nnn #1#2#3
+ {
+ \__intarray_gset:wF \int_eval:n {#2} #1 \int_eval:n{#3}
+ {
+ \msg_error:nneee { kernel } { out-of-bounds }
+ { \token_to_str:N #1 } { \int_use:N \l__intarray_bad_index_int } { \intarray_count:N #1 }
+ }
+ }
+ \cs_generate_variant:Nn \intarray_gset:Nnn { c }
+ \cs_generate_variant:Nn \intarray_gzero:N { c }
+ \cs_new:Npn \__kernel_intarray_item:Nn #1#2
+ { \__intarray_item:w #2 #1 }
+ \cs_new:Npn \intarray_item:Nn #1#2
+ {
+ \__intarray_item:wF \int_eval:n {#2} #1
+ {
+ \msg_expandable_error:nnfff { kernel } { out-of-bounds }
+ { \token_to_str:N #1 } { \int_use:N \l__intarray_bad_index_int } { \intarray_count:N #1 }
+ 0
+ }
+ }
+ \cs_generate_variant:Nn \intarray_item:Nn { c }
+ \cs_new:Npn \intarray_rand_item:N #1
+ { \intarray_item:Nn #1 { \int_rand:n { \intarray_count:N #1 } } }
+ \cs_generate_variant:Nn \intarray_rand_item:N { c }
+ \cs_new_protected:Npn \intarray_const_from_clist:Nn #1#2
+ {
+ \__intarray_new:N #1
+ \int_zero:N \l__intarray_loop_int
+ \clist_map_inline:nn {#2}
+ {
+ \int_incr:N \l__intarray_loop_int
+ \__kernel_intarray_gset:Nnn #1 \l__intarray_loop_int { \int_eval:n {##1} } }
+ }
+ \cs_generate_variant:Nn \intarray_const_from_clist:Nn { c }
+ \cs_new:Npn \__kernel_intarray_range_to_clist:Nnn #1#2#3
+ {
+ \__intarray_range_to_clist:w #1
+ \int_eval:n {#2} ~ \int_eval:n {#3} ~
+ }
+ \cs_new_protected:Npn \__kernel_intarray_gset_range_from_clist:Nnn #1#2#3
+ {
+ \__intarray_gset_range:w \int_eval:w #2 #1 #3 , , \scan_stop:
+ }
+ \cs_new_protected:Npn \__intarray_gset_overflow_test:nw #1
+ {
+ }
+ }
+ {
+ \cs_new_eq:NN \__intarray_entry:w \tex_fontdimen:D
+ \cs_new_eq:NN \__intarray_count:w \tex_hyphenchar:D
+ \dim_const:Nn \c__intarray_sp_dim { 1 sp }
+ \int_new:N \g__intarray_font_int
+ \cs_new_protected:Npn \__intarray_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \int_gincr:N \g__intarray_font_int
+ \tex_global:D \tex_font:D #1
+ = cmr10~at~ \g__intarray_font_int \c__intarray_sp_dim \scan_stop:
+ \int_step_inline:nn { 8 }
+ { \__kernel_intarray_gset:Nnn #1 {##1} \c_zero_int }
+ }
+ \cs_new_protected:Npn \intarray_new:Nn #1#2
+ {
+ \__intarray_new:N #1
+ \__intarray_count:w #1 = \int_eval:n {#2} \scan_stop:
+ \int_compare:nNnT { \intarray_count:N #1 } < 0
+ {
+ \msg_error:nne { kernel } { negative-array-size }
+ { \intarray_count:N #1 }
+ }
+ \int_compare:nNnT { \intarray_count:N #1 } > 0
+ { \__kernel_intarray_gset:Nnn #1 { \intarray_count:N #1 } { 0 } }
+ }
+ \cs_generate_variant:Nn \intarray_new:Nn { c }
+ \cs_new:Npn \intarray_count:N #1 { \int_value:w \__intarray_count:w #1 }
+ \cs_generate_variant:Nn \intarray_count:N { c }
+ \cs_new:Npn \__intarray_signed_max_dim:n #1
+ { \int_value:w \int_compare:nNnT {#1} < 0 { - } \c_max_dim }
+ \cs_new:Npn \__intarray_bounds:NNnTF #1#2#3
+ {
+ \if_int_compare:w 1 > #3 \exp_stop_f:
+ \__intarray_bounds_error:NNnw #1 #2 {#3}
+ \else:
+ \if_int_compare:w #3 > \intarray_count:N #2 \exp_stop_f:
+ \__intarray_bounds_error:NNnw #1 #2 {#3}
+ \fi:
+ \fi:
+ \use_i:nn
+ }
+ \cs_new:Npn \__intarray_bounds_error:NNnw #1#2#3#4 \use_i:nn #5#6
+ {
+ #4
+ #1 { kernel } { out-of-bounds }
+ { \token_to_str:N #2 } {#3} { \intarray_count:N #2 }
+ #6
+ }
+ \cs_new_protected:Npn \__kernel_intarray_gset:Nnn #1#2#3
+ { \__intarray_entry:w #2 #1 #3 \c__intarray_sp_dim }
+ \cs_new_protected:Npn \intarray_gset:Nnn #1#2#3
+ {
+ \exp_after:wN \__intarray_gset:Nww
+ \exp_after:wN #1
+ \int_value:w \int_eval:n {#2} \exp_after:wN ;
+ \int_value:w \int_eval:n {#3} ;
+ }
+ \cs_generate_variant:Nn \intarray_gset:Nnn { c }
+ \cs_new_protected:Npn \__intarray_gset:Nww #1#2 ; #3 ;
+ {
+ \__intarray_bounds:NNnTF \msg_error:nneee #1 {#2}
+ {
+ \__intarray_gset_overflow_test:nw {#3}
+ \__kernel_intarray_gset:Nnn #1 {#2} {#3}
+ }
+ { }
+ }
+ \cs_if_exist:NTF \tex_ifabsnum:D
+ {
+ \cs_new_protected:Npn \__intarray_gset_overflow_test:nw #1
+ {
+ \tex_ifabsnum:D #1 > \c_max_dim
+ \exp_after:wN \__intarray_gset_overflow:NNnn
+ \fi:
+ }
+ }
+ {
+ \cs_new_protected:Npn \__intarray_gset_overflow_test:nw #1
+ {
+ \if_int_compare:w \int_abs:n {#1} > \c_max_dim
+ \exp_after:wN \__intarray_gset_overflow:NNnn
+ \fi:
+ }
+ }
+ \cs_new_protected:Npn \__intarray_gset_overflow:NNnn #1#2#3#4
+ {
+ \msg_error:nneeee { kernel } { overflow }
+ { \token_to_str:N #2 } {#3} {#4} { \__intarray_signed_max_dim:n {#4} }
+ #1 #2 {#3} { \__intarray_signed_max_dim:n {#4} }
+ }
+ \cs_new_protected:Npn \intarray_gzero:N #1
+ {
+ \int_zero:N \l__intarray_loop_int
+ \prg_replicate:nn { \intarray_count:N #1 }
+ {
+ \int_incr:N \l__intarray_loop_int
+ \__intarray_entry:w \l__intarray_loop_int #1 \c_zero_dim
+ }
+ }
+ \cs_generate_variant:Nn \intarray_gzero:N { c }
+ \cs_new:Npn \__kernel_intarray_item:Nn #1#2
+ { \int_value:w \__intarray_entry:w #2 #1 }
+ \cs_new:Npn \intarray_item:Nn #1#2
+ {
+ \exp_after:wN \__intarray_item:Nw
+ \exp_after:wN #1
+ \int_value:w \int_eval:n {#2} ;
+ }
+ \cs_generate_variant:Nn \intarray_item:Nn { c }
+ \cs_new:Npn \__intarray_item:Nw #1#2 ;
+ {
+ \__intarray_bounds:NNnTF \msg_expandable_error:nnfff #1 {#2}
+ { \__kernel_intarray_item:Nn #1 {#2} }
+ { 0 }
+ }
+ \cs_new:Npn \intarray_rand_item:N #1
+ { \intarray_item:Nn #1 { \int_rand:n { \intarray_count:N #1 } } }
+ \cs_generate_variant:Nn \intarray_rand_item:N { c }
+ \cs_new_protected:Npn \intarray_const_from_clist:Nn #1#2
+ {
+ \__intarray_new:N #1
+ \int_zero:N \l__intarray_loop_int
+ \clist_map_inline:nn {#2}
+ { \exp_args:Nf \__intarray_const_from_clist:nN { \int_eval:n {##1} } #1 }
+ \__intarray_count:w #1 \l__intarray_loop_int
+ }
+ \cs_generate_variant:Nn \intarray_const_from_clist:Nn { c }
+ \cs_new_protected:Npn \__intarray_const_from_clist:nN #1#2
+ {
+ \int_incr:N \l__intarray_loop_int
+ \__intarray_gset_overflow_test:nw {#1}
+ \__kernel_intarray_gset:Nnn #2 \l__intarray_loop_int {#1}
+ }
+ \cs_new:Npn \__intarray_to_clist:Nn #1#2
+ {
+ \int_compare:nNnF { \intarray_count:N #1 } = \c_zero_int
+ {
+ \exp_last_unbraced:Nf \use_none:n
+ { \__intarray_to_clist:w 1 ; #1 {#2} \prg_break_point: }
+ }
+ }
+ \cs_new:Npn \__intarray_to_clist:w #1 ; #2#3
+ {
+ \if_int_compare:w #1 > \__intarray_count:w #2
+ \prg_break:n
+ \fi:
+ #3 \__kernel_intarray_item:Nn #2 {#1}
+ \exp_after:wN \__intarray_to_clist:w
+ \int_value:w \int_eval:w #1 + \c_one_int ; #2 {#3}
+ }
+ \cs_new:Npn \__kernel_intarray_range_to_clist:Nnn #1#2#3
+ {
+ \exp_last_unbraced:Nf \use_none:n
+ {
+ \exp_after:wN \__intarray_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 \__intarray_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 \__intarray_range_to_clist:ww
+ \int_value:w \int_eval:w #1 + \c_one_int ; #2 ; #3
+ }
+ \cs_new_protected:Npn \__kernel_intarray_gset_range_from_clist:Nnn #1#2#3
+ {
+ \int_set:Nn \l__intarray_loop_int {#2}
+ \__intarray_gset_range:Nw #1 #3 , , \prg_break_point:
+ }
+ \cs_new_protected:Npn \__intarray_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__intarray_loop_int {#2}
+ \int_incr:N \l__intarray_loop_int
+ \__intarray_gset_range:Nw #1
+ }
+ }
+\prg_new_eq_conditional:NNn \intarray_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \intarray_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+\cs_new_protected:Npn \intarray_show:N { \__intarray_show:NN \msg_show:nneeee }
+\cs_generate_variant:Nn \intarray_show:N { c }
+\cs_new_protected:Npn \intarray_log:N { \__intarray_show:NN \msg_log:nneeee }
+\cs_generate_variant:Nn \intarray_log:N { c }
+\cs_new_protected:Npn \__intarray_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ #1 { intarray } { show }
+ { \token_to_str:N #2 }
+ { \intarray_count:N #2 }
+ { >~ \__intarray_to_clist:Nn #2 { , ~ } }
+ { }
+ }
+ }
+%% File: l3fp.dtx
+%% File: l3fp-aux.dtx
+\cs_new_eq:NN \__fp_int_eval:w \tex_numexpr:D
+\cs_new_eq:NN \__fp_int_eval_end: \scan_stop:
+\cs_new_eq:NN \__fp_int_to_roman:w \tex_romannumeral:D
+\cs_new:Npn \__fp_use_none_stop_f:n #1 { \exp_stop_f: }
+\cs_new:Npn \__fp_use_s:n #1 { #1; }
+\cs_new:Npn \__fp_use_s:nn #1#2 { #1#2; }
+\cs_new:Npn \__fp_use_none_until_s:w #1; { }
+\cs_new:Npn \__fp_use_i_until_s:nw #1#2; {#1}
+\cs_new:Npn \__fp_use_ii_until_s:nnw #1#2#3; {#2}
+\cs_new:Npn \__fp_reverse_args:Nww #1 #2; #3; { #1 #3; #2; }
+\cs_new:Npn \__fp_rrot:www #1; #2; #3; { #2; #3; #1; }
+\cs_new:Npn \__fp_use_i:ww #1; #2; { #1; }
+\cs_new:Npn \__fp_use_i:www #1; #2; #3; { #1; }
+\cs_new_protected:Npn \__fp_misused:n #1
+ { \msg_error:nne { fp } { misused } { \fp_to_tl:n {#1} } }
+\scan_new:N \s__fp
+\cs_new_protected:Npn \__fp_chk:w #1 ;
+ { \__fp_misused:n { \s__fp \__fp_chk:w #1 ; } }
+\scan_new:N \s__fp_expr_mark
+\scan_new:N \s__fp_expr_stop
+\scan_new:N \s__fp_mark
+\scan_new:N \s__fp_stop
+\cs_new:Npn \__fp_use_i_delimit_by_s_stop:nw #1 #2 \s__fp_stop {#1}
+\scan_new:N \s__fp_invalid
+\scan_new:N \s__fp_underflow
+\scan_new:N \s__fp_overflow
+\scan_new:N \s__fp_division
+\scan_new:N \s__fp_exact
+\tl_const:Nn \c_zero_fp { \s__fp \__fp_chk:w 0 0 \s__fp_exact ; }
+\tl_const:Nn \c_minus_zero_fp { \s__fp \__fp_chk:w 0 2 \s__fp_exact ; }
+\tl_const:Nn \c_inf_fp { \s__fp \__fp_chk:w 2 0 \s__fp_exact ; }
+\tl_const:Nn \c_minus_inf_fp { \s__fp \__fp_chk:w 2 2 \s__fp_exact ; }
+\tl_const:Nn \c_nan_fp { \s__fp \__fp_chk:w 3 1 \s__fp_exact ; }
+\int_const:Nn \c__fp_prec_int { 16 }
+\int_const:Nn \c__fp_half_prec_int { 8 }
+\int_const:Nn \c__fp_block_int { 4 }
+\int_const:Nn \c__fp_myriad_int { 10000 }
+\int_const:Nn \c__fp_minus_min_exponent_int { 10000 }
+\int_const:Nn \c__fp_max_exponent_int { 10000 }
+\int_const:Nn \c__fp_max_exp_exponent_int { 5 }
+\tl_const:Ne \c__fp_overflowing_fp
+ {
+ \s__fp \__fp_chk:w 1 0
+ { \int_eval:n { \c__fp_max_exponent_int + 1 } }
+ {1000} {0000} {0000} {0000} ;
+ }
+\cs_new:Npn \__fp_zero_fp:N #1
+ { \s__fp \__fp_chk:w 0 #1 \s__fp_underflow ; }
+\cs_new:Npn \__fp_inf_fp:N #1
+ { \s__fp \__fp_chk:w 2 #1 \s__fp_overflow ; }
+\cs_new:Npn \__fp_exponent:w \s__fp \__fp_chk:w #1
+ {
+ \if_meaning:w 1 #1
+ \exp_after:wN \__fp_use_ii_until_s:nnw
+ \else:
+ \exp_after:wN \__fp_use_i_until_s:nw
+ \exp_after:wN 0
+ \fi:
+ }
+\cs_new:Npn \__fp_neg_sign:N #1
+ { \__fp_int_eval:w 2 - #1 \__fp_int_eval_end: }
+\cs_new:Npn \__fp_kind:w #1
+ {
+ \__fp_if_type_fp:NTwFw
+ #1 \__fp_use_ii_until_s:nnw
+ \s__fp { \__fp_use_i_until_s:nw 4 }
+ \s__fp_stop
+ }
+\cs_new:Npn \__fp_sanitize:Nw #1 #2;
+ {
+ \if_case:w
+ \if_int_compare:w #2 > \c__fp_max_exponent_int 1 ~ \else:
+ \if_int_compare:w #2 < - \c__fp_minus_min_exponent_int 2 ~ \else:
+ \if_meaning:w 1 #1 3 ~ \fi: \fi: \fi: 0 ~
+ \or: \exp_after:wN \__fp_overflow:w
+ \or: \exp_after:wN \__fp_underflow:w
+ \or: \exp_after:wN \__fp_sanitize_zero:w
+ \fi:
+ \s__fp \__fp_chk:w 1 #1 {#2}
+ }
+\cs_new:Npn \__fp_sanitize:wN #1; #2 { \__fp_sanitize:Nw #2 #1; }
+\cs_new:Npn \__fp_sanitize_zero:w \s__fp \__fp_chk:w #1 #2 #3;
+ { \c_zero_fp }
+\cs_new:Npn \__fp_exp_after_o:w \s__fp \__fp_chk:w #1
+ {
+ \if_meaning:w 1 #1
+ \exp_after:wN \__fp_exp_after_normal:nNNw
+ \else:
+ \exp_after:wN \__fp_exp_after_special:nNNw
+ \fi:
+ { }
+ #1
+ }
+\cs_new:Npn \__fp_exp_after_f:nw #1 \s__fp \__fp_chk:w #2
+ {
+ \if_meaning:w 1 #2
+ \exp_after:wN \__fp_exp_after_normal:nNNw
+ \else:
+ \exp_after:wN \__fp_exp_after_special:nNNw
+ \fi:
+ { \exp:w \exp_end_continue_f:w #1 }
+ #2
+ }
+\cs_new:Npn \__fp_exp_after_special:nNNw #1#2#3#4;
+ {
+ \exp_after:wN \s__fp
+ \exp_after:wN \__fp_chk:w
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN #4
+ \exp_after:wN ;
+ #1
+ }
+\cs_new:Npn \__fp_exp_after_normal:nNNw #1 1 #2 #3 #4#5#6#7;
+ {
+ \exp_after:wN \__fp_exp_after_normal:Nwwwww
+ \exp_after:wN #2
+ \int_value:w #3 \exp_after:wN ;
+ \int_value:w 1 #4 \exp_after:wN ;
+ \int_value:w 1 #5 \exp_after:wN ;
+ \int_value:w 1 #6 \exp_after:wN ;
+ \int_value:w 1 #7 \exp_after:wN ; #1
+ }
+\cs_new:Npn \__fp_exp_after_normal:Nwwwww
+ #1 #2; 1 #3 ; 1 #4 ; 1 #5 ; 1 #6 ;
+ { \s__fp \__fp_chk:w 1 #1 {#2} {#3} {#4} {#5} {#6} ; }
+\scan_new:N \s__fp_tuple
+\cs_new_protected:Npn \__fp_tuple_chk:w #1 ;
+ { \__fp_misused:n { \s__fp_tuple \__fp_tuple_chk:w #1 ; } }
+\tl_const:Nn \c__fp_empty_tuple_fp
+ { \s__fp_tuple \__fp_tuple_chk:w { } ; }
+\cs_new:Npn \__fp_array_count:n #1
+ { \__fp_tuple_count:w \s__fp_tuple \__fp_tuple_chk:w {#1} ; }
+\cs_new:Npn \__fp_tuple_count:w \s__fp_tuple \__fp_tuple_chk:w #1 ;
+ {
+ \int_value:w \__fp_int_eval:w 0
+ \__fp_tuple_count_loop:Nw #1 { ? \prg_break: } ;
+ \prg_break_point:
+ \__fp_int_eval_end:
+ }
+\cs_new:Npn \__fp_tuple_count_loop:Nw #1#2;
+ { \use_none:n #1 + 1 \__fp_tuple_count_loop:Nw }
+\cs_new:Npn \__fp_if_type_fp:NTwFw #1 \s__fp #2 #3 \s__fp_stop {#2}
+\cs_new:Npn \__fp_array_if_all_fp:nTF #1
+ {
+ \__fp_array_if_all_fp_loop:w #1 { \s__fp \prg_break: } ;
+ \prg_break_point: \use_i:nn
+ }
+\cs_new:Npn \__fp_array_if_all_fp_loop:w #1#2 ;
+ {
+ \__fp_if_type_fp:NTwFw
+ #1 \__fp_array_if_all_fp_loop:w
+ \s__fp { \prg_break:n \use_iii:nnn }
+ \s__fp_stop
+ }
+\cs_new:Npn \__fp_type_from_scan:N #1
+ {
+ \__fp_if_type_fp:NTwFw
+ #1 { }
+ \s__fp { \__fp_type_from_scan_other:N #1 }
+ \s__fp_stop
+ }
+\cs_new:Npe \__fp_type_from_scan_other:N #1
+ {
+ \exp_not:N \exp_after:wN \exp_not:N \__fp_type_from_scan:w
+ \exp_not:N \token_to_str:N #1 \s__fp_mark
+ \tl_to_str:n { s__fp _? } \s__fp_mark \s__fp_stop
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new:Npn \__fp_type_from_scan:w #1
+ { \tl_to_str:n { s__fp } } #2 \s__fp_mark #3 \s__fp_stop {#2}
+\cs_new:Npn \__fp_change_func_type:NNN #1#2#3
+ {
+ \__fp_if_type_fp:NTwFw
+ #1 #2
+ \s__fp
+ {
+ \exp_after:wN \__fp_change_func_type_chk:NNN
+ \cs:w
+ __fp \__fp_type_from_scan_other:N #1
+ \exp_after:wN \__fp_change_func_type_aux:w \token_to_str:N #2
+ \cs_end:
+ #2 #3
+ }
+ \s__fp_stop
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new:Npn \__fp_change_func_type_aux:w #1 { \tl_to_str:n { __fp } } { }
+\cs_new:Npn \__fp_change_func_type_chk:NNN #1#2#3
+ {
+ \if_meaning:w \scan_stop: #1
+ \exp_after:wN #3 \exp_after:wN #2
+ \else:
+ \exp_after:wN #1
+ \fi:
+ }
+\cs_new:Npn \__fp_exp_after_any_f:Nnw #1
+ { \cs:w __fp_exp_after \__fp_type_from_scan_other:N #1 _f:nw \cs_end: }
+\cs_new:Npn \__fp_exp_after_any_f:nw #1#2
+ {
+ \__fp_if_type_fp:NTwFw
+ #2 \__fp_exp_after_f:nw
+ \s__fp { \__fp_exp_after_any_f:Nnw #2 }
+ \s__fp_stop
+ {#1} #2
+ }
+\cs_new_eq:NN \__fp_exp_after_expr_stop_f:nw \use_none:nn
+\cs_new:Npn \__fp_exp_after_tuple_o:w
+ { \__fp_exp_after_tuple_f:nw { \exp_after:wN \exp_stop_f: } }
+\cs_new:Npn \__fp_exp_after_tuple_f:nw
+ #1 \s__fp_tuple \__fp_tuple_chk:w #2 ;
+ {
+ \exp_after:wN \s__fp_tuple
+ \exp_after:wN \__fp_tuple_chk:w
+ \exp_after:wN {
+ \exp:w \exp_end_continue_f:w
+ \__fp_exp_after_array_f:w #2 \s__fp_expr_stop
+ \exp_after:wN }
+ \exp_after:wN ;
+ \exp:w \exp_end_continue_f:w #1
+ }
+\cs_new:Npn \__fp_exp_after_array_f:w
+ { \__fp_exp_after_any_f:nw { \__fp_exp_after_array_f:w } }
+\int_const:Nn \c__fp_leading_shift_int { - 5 0000 }
+\int_const:Nn \c__fp_middle_shift_int { 5 0000 * 9999 }
+\int_const:Nn \c__fp_trailing_shift_int { 5 0000 * 10000 }
+\cs_new:Npn \__fp_pack:NNNNNw #1 #2#3#4#5 #6; { + #1#2#3#4#5 ; {#6} }
+\int_const:Nn \c__fp_big_leading_shift_int { - 15 2374 }
+\int_const:Nn \c__fp_big_middle_shift_int { 15 2374 * 9999 }
+\int_const:Nn \c__fp_big_trailing_shift_int { 15 2374 * 10000 }
+\cs_new:Npn \__fp_pack_big:NNNNNNw #1#2 #3#4#5#6 #7;
+ { + #1#2#3#4#5#6 ; {#7} }
+\int_const:Nn \c__fp_Bigg_leading_shift_int { - 20 0000 }
+\int_const:Nn \c__fp_Bigg_middle_shift_int { 20 0000 * 9999 }
+\int_const:Nn \c__fp_Bigg_trailing_shift_int { 20 0000 * 10000 }
+\cs_new:Npn \__fp_pack_Bigg:NNNNNNw #1#2 #3#4#5#6 #7;
+ { + #1#2#3#4#5#6 ; {#7} }
+\cs_new:Npn \__fp_pack_twice_four:wNNNNNNNN #1; #2#3#4#5 #6#7#8#9
+ { #1 {#2#3#4#5} {#6#7#8#9} ; }
+\cs_new:Npn \__fp_pack_eight:wNNNNNNNN #1; #2#3#4#5 #6#7#8#9
+ { #1 {#2#3#4#5#6#7#8#9} ; }
+\cs_new:Npn \__fp_basics_pack_low:NNNNNw #1 #2#3#4#5 #6;
+ { + #1 - 1 ; {#2#3#4#5} {#6} ; }
+\cs_new:Npn \__fp_basics_pack_high:NNNNNw #1 #2#3#4#5 #6;
+ {
+ \if_meaning:w 2 #1
+ \__fp_basics_pack_high_carry:w
+ \fi:
+ ; {#2#3#4#5} {#6}
+ }
+\cs_new:Npn \__fp_basics_pack_high_carry:w \fi: ; #1
+ { \fi: + 1 ; {1000} }
+\cs_new:Npn \__fp_basics_pack_weird_low:NNNNw #1 #2#3#4 #5;
+ {
+ \if_meaning:w 2 #1
+ + 1
+ \fi:
+ \__fp_int_eval_end:
+ #2#3#4; {#5} ;
+ }
+\cs_new:Npn \__fp_basics_pack_weird_high:NNNNNNNNw
+ 1 #1#2#3#4 #5#6#7#8 #9; { ; {#1#2#3#4} {#5#6#7#8} {#9} }
+\cs_new:Npn \__fp_decimate:nNnnnn #1
+ {
+ \cs:w
+ __fp_decimate_
+ \if_int_compare:w \__fp_int_eval:w #1 > \c__fp_prec_int
+ tiny
+ \else:
+ \__fp_int_to_roman:w \__fp_int_eval:w #1
+ \fi:
+ :Nnnnn
+ \cs_end:
+ }
+\cs_new:Npn \__fp_decimate_:Nnnnn #1 #2#3#4#5
+ { #1 0 {#2#3} {#4#5} ; }
+\cs_new:Npn \__fp_decimate_tiny:Nnnnn #1 #2#3#4#5
+ { #1 1 { 0000 0000 } { 0000 0000 } 0 #2#3#4#5 ; }
+\cs_new:Npn \__fp_tmp:w #1 #2 #3
+ {
+ \cs_new:cpn { __fp_decimate_ #1 :Nnnnn } ##1 ##2##3##4##5
+ {
+ \exp_after:wN ##1
+ \int_value:w
+ \exp_after:wN \__fp_round_digit:Nw #2 ;
+ \__fp_decimate_pack:nnnnnnnnnnw #3 ;
+ }
+ }
+\__fp_tmp:w {i} {\use_none:nnn #50}{ 0{#2}#3{#4}#5 }
+\__fp_tmp:w {ii} {\use_none:nn #5 }{ 00{#2}#3{#4}#5 }
+\__fp_tmp:w {iii} {\use_none:n #5 }{ 000{#2}#3{#4}#5 }
+\__fp_tmp:w {iv} { #5 }{ {0000}#2{#3}#4 #5 }
+\__fp_tmp:w {v} {\use_none:nnn #4#5 }{ 0{0000}#2{#3}#4 #5 }
+\__fp_tmp:w {vi} {\use_none:nn #4#5 }{ 00{0000}#2{#3}#4 #5 }
+\__fp_tmp:w {vii} {\use_none:n #4#5 }{ 000{0000}#2{#3}#4 #5 }
+\__fp_tmp:w {viii}{ #4#5 }{ {0000}0000{#2}#3 #4 #5 }
+\__fp_tmp:w {ix} {\use_none:nnn #3#4+#5}{ 0{0000}0000{#2}#3 #4 #5 }
+\__fp_tmp:w {x} {\use_none:nn #3#4+#5}{ 00{0000}0000{#2}#3 #4 #5 }
+\__fp_tmp:w {xi} {\use_none:n #3#4+#5}{ 000{0000}0000{#2}#3 #4 #5 }
+\__fp_tmp:w {xii} { #3#4+#5}{ {0000}0000{0000}#2 #3 #4 #5 }
+\__fp_tmp:w {xiii}{\use_none:nnn#2#3+#4#5}{ 0{0000}0000{0000}#2 #3 #4 #5 }
+\__fp_tmp:w {xiv} {\use_none:nn #2#3+#4#5}{ 00{0000}0000{0000}#2 #3 #4 #5 }
+\__fp_tmp:w {xv} {\use_none:n #2#3+#4#5}{ 000{0000}0000{0000}#2 #3 #4 #5 }
+\__fp_tmp:w {xvi} { #2#3+#4#5}{{0000}0000{0000}0000 #2 #3 #4 #5}
+\cs_new:Npn \__fp_decimate_pack:nnnnnnnnnnw #1#2#3#4#5
+ { \__fp_decimate_pack:nnnnnnw { #1#2#3#4#5 } }
+\cs_new:Npn \__fp_decimate_pack:nnnnnnw #1 #2#3#4#5#6
+ { {#1} {#2#3#4#5#6} }
+\cs_new:Npn \__fp_case_use:nw #1#2 \fi: #3 \s__fp { \fi: #1 \s__fp }
+\cs_new:Npn \__fp_case_return:nw #1#2 \fi: #3 ; { \fi: #1 }
+\cs_new:Npn \__fp_case_return_o:Nw #1#2 \fi: #3 \s__fp #4 ;
+ { \fi: \exp_after:wN #1 }
+\cs_new:Npn \__fp_case_return_same_o:w #1 \fi: #2 \s__fp
+ { \fi: \__fp_exp_after_o:w \s__fp }
+\cs_new:Npn \__fp_case_return_o:Nww #1#2 \fi: #3 \s__fp #4 ; #5 ;
+ { \fi: \exp_after:wN #1 }
+\cs_new:Npn \__fp_case_return_i_o:ww #1 \fi: #2 \s__fp #3 ; \s__fp #4 ;
+ { \fi: \__fp_exp_after_o:w \s__fp #3 ; }
+\cs_new:Npn \__fp_case_return_ii_o:ww #1 \fi: #2 \s__fp #3 ;
+ { \fi: \__fp_exp_after_o:w }
+\prg_new_conditional:Npnn \__fp_int:w \s__fp \__fp_chk:w #1 #2 #3 #4;
+ { TF , T , F , p }
+ {
+ \if_case:w #1 \exp_stop_f:
+ \prg_return_true:
+ \or:
+ \if_charcode:w 0
+ \__fp_decimate:nNnnnn { \c__fp_prec_int - #3 }
+ \__fp_use_i_until_s:nw #4
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \else: \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \__fp_small_int:wTF \s__fp \__fp_chk:w #1#2
+ {
+ \if_case:w #1 \exp_stop_f:
+ \__fp_case_return:nw { \__fp_small_int_true:wTF 0 ; }
+ \or: \exp_after:wN \__fp_small_int_normal:NnwTF
+ \or:
+ \__fp_case_return:nw
+ {
+ \exp_after:wN \__fp_small_int_true:wTF \int_value:w
+ \if_meaning:w 2 #2 - \fi: 1 0000 0000 ;
+ }
+ \else: \__fp_case_return:nw \use_ii:nn
+ \fi:
+ #2
+ }
+\cs_new:Npn \__fp_small_int_true:wTF #1; #2#3 { #2 {#1} }
+\cs_new:Npn \__fp_small_int_normal:NnwTF #1#2#3;
+ {
+ \__fp_decimate:nNnnnn { \c__fp_prec_int - #2 }
+ \__fp_small_int_test:NnnwNw
+ #3 #1
+ }
+\cs_new:Npn \__fp_small_int_test:NnnwNw #1#2#3#4; #5
+ {
+ \if_meaning:w 0 #1
+ \exp_after:wN \__fp_small_int_true:wTF
+ \int_value:w \if_meaning:w 2 #5 - \fi:
+ \if_int_compare:w #2 > \c_zero_int
+ 1 0000 0000
+ \else:
+ #3
+ \fi:
+ \exp_after:wN ;
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ }
+\cs_new_eq:NN \__fp_str_if_eq:nn \tex_strcmp:D
+\cs_new:Npn \__fp_func_to_name:N #1
+ {
+ \exp_last_unbraced:Nf
+ \__fp_func_to_name_aux:w { \cs_to_str:N #1 } X
+ }
+\cs_set_protected:Npn \__fp_tmp:w #1 #2
+ { \cs_new:Npn \__fp_func_to_name_aux:w ##1 #1 ##2 #2 ##3 X {##2} }
+\exp_args:Nff \__fp_tmp:w { \tl_to_str:n { __fp_ } }
+ { \tl_to_str:n { _o: } }
+\msg_new:nnnn { fp } { misused }
+ { A~floating~point~with~value~'#1'~was~misused. }
+ {
+ To~obtain~the~value~of~a~floating~point~variable,~use~
+ '\token_to_str:N \fp_to_decimal:N',~
+ '\token_to_str:N \fp_to_tl:N',~or~other~
+ conversion~functions.
+ }
+\prop_gput:Nnn \g_msg_module_name_prop { fp } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { fp } { }
+%% File: l3fp-traps.dtx
+\flag_new:N \l_fp_invalid_operation_flag
+\flag_new:N \l_fp_division_by_zero_flag
+\flag_new:N \l_fp_overflow_flag
+\flag_new:N \l_fp_underflow_flag
+\cs_new_protected:Npn \fp_trap:nn #1#2
+ {
+ \cs_if_exist_use:cF { __fp_trap_#1_set_#2: }
+ {
+ \clist_if_in:nnTF
+ { invalid_operation , division_by_zero , overflow , underflow }
+ {#1}
+ {
+ \msg_error:nnee { fp }
+ { unknown-fpu-trap-type } {#1} {#2}
+ }
+ {
+ \msg_error:nne
+ { fp } { unknown-fpu-exception } {#1}
+ }
+ }
+ }
+\cs_new_protected:Npn \__fp_trap_invalid_operation_set_error:
+ { \__fp_trap_invalid_operation_set:N \prg_do_nothing: }
+\cs_new_protected:Npn \__fp_trap_invalid_operation_set_flag:
+ { \__fp_trap_invalid_operation_set:N \use_none:nnnnn }
+\cs_new_protected:Npn \__fp_trap_invalid_operation_set_none:
+ { \__fp_trap_invalid_operation_set:N \use_none:nnnnnnn }
+\cs_new_protected:Npn \__fp_trap_invalid_operation_set:N #1
+ {
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \__fp_invalid_operation:nnw ##1##2##3; }
+ {
+ #1
+ \__fp_error:nnfn { invalid } {##2} { \fp_to_tl:n { ##3; } } { }
+ \flag_ensure_raised:N \l_fp_invalid_operation_flag
+ ##1
+ }
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \__fp_invalid_operation_o:Nww ##1##2; ##3; }
+ {
+ #1
+ \__fp_error:nffn { invalid-ii }
+ { \fp_to_tl:n { ##2; } } { \fp_to_tl:n { ##3; } } {##1}
+ \flag_ensure_raised:N \l_fp_invalid_operation_flag
+ \exp_after:wN \c_nan_fp
+ }
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \__fp_invalid_operation_tl_o:ff ##1##2 }
+ {
+ #1
+ \__fp_error:nffn { invalid } {##1} {##2} { }
+ \flag_ensure_raised:N \l_fp_invalid_operation_flag
+ \exp_after:wN \c_nan_fp
+ }
+ }
+\cs_new_protected:Npn \__fp_trap_division_by_zero_set_error:
+ { \__fp_trap_division_by_zero_set:N \prg_do_nothing: }
+\cs_new_protected:Npn \__fp_trap_division_by_zero_set_flag:
+ { \__fp_trap_division_by_zero_set:N \use_none:nnnnn }
+\cs_new_protected:Npn \__fp_trap_division_by_zero_set_none:
+ { \__fp_trap_division_by_zero_set:N \use_none:nnnnnnn }
+\cs_new_protected:Npn \__fp_trap_division_by_zero_set:N #1
+ {
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \__fp_division_by_zero_o:Nnw ##1##2##3; }
+ {
+ #1
+ \__fp_error:nnfn { zero-div } {##2} { \fp_to_tl:n { ##3; } } { }
+ \flag_ensure_raised:N \l_fp_division_by_zero_flag
+ \exp_after:wN ##1
+ }
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \__fp_division_by_zero_o:NNww ##1##2##3; ##4; }
+ {
+ #1
+ \__fp_error:nffn { zero-div-ii }
+ { \fp_to_tl:n { ##3; } } { \fp_to_tl:n { ##4; } } {##2}
+ \flag_ensure_raised:N \l_fp_division_by_zero_flag
+ \exp_after:wN ##1
+ }
+ }
+\cs_new_protected:Npn \__fp_trap_overflow_set_error:
+ { \__fp_trap_overflow_set:N \prg_do_nothing: }
+\cs_new_protected:Npn \__fp_trap_overflow_set_flag:
+ { \__fp_trap_overflow_set:N \use_none:nnnnn }
+\cs_new_protected:Npn \__fp_trap_overflow_set_none:
+ { \__fp_trap_overflow_set:N \use_none:nnnnnnn }
+\cs_new_protected:Npn \__fp_trap_overflow_set:N #1
+ { \__fp_trap_overflow_set:NnNn #1 { overflow } \__fp_inf_fp:N { inf } }
+\cs_new_protected:Npn \__fp_trap_underflow_set_error:
+ { \__fp_trap_underflow_set:N \prg_do_nothing: }
+\cs_new_protected:Npn \__fp_trap_underflow_set_flag:
+ { \__fp_trap_underflow_set:N \use_none:nnnnn }
+\cs_new_protected:Npn \__fp_trap_underflow_set_none:
+ { \__fp_trap_underflow_set:N \use_none:nnnnnnn }
+\cs_new_protected:Npn \__fp_trap_underflow_set:N #1
+ { \__fp_trap_overflow_set:NnNn #1 { underflow } \__fp_zero_fp:N { 0 } }
+\cs_new_protected:Npn \__fp_trap_overflow_set:NnNn #1#2#3#4
+ {
+ \exp_args:Nno \use:n
+ { \cs_set:cpn { __fp_ #2 :w } \s__fp \__fp_chk:w ##1##2##3; }
+ {
+ #1
+ \__fp_error:nffn
+ { flow \if_meaning:w 1 ##1 -to \fi: }
+ { \fp_to_tl:n { \s__fp \__fp_chk:w ##1##2##3; } }
+ { \token_if_eq_meaning:NNF 0 ##2 { - } #4 }
+ {#2}
+ \flag_ensure_raised:c { l_fp_#2_flag }
+ #3 ##2
+ }
+ }
+\cs_new:Npn \__fp_invalid_operation:nnw #1#2#3; { }
+\cs_new:Npn \__fp_invalid_operation_o:Nww #1#2; #3; { }
+\cs_new:Npn \__fp_invalid_operation_tl_o:ff #1 #2 { }
+\cs_new:Npn \__fp_division_by_zero_o:Nnw #1#2#3; { }
+\cs_new:Npn \__fp_division_by_zero_o:NNww #1#2#3; #4; { }
+\cs_new:Npn \__fp_overflow:w { }
+\cs_new:Npn \__fp_underflow:w { }
+\fp_trap:nn { invalid_operation } { error }
+\fp_trap:nn { division_by_zero } { flag }
+\fp_trap:nn { overflow } { flag }
+\fp_trap:nn { underflow } { flag }
+\cs_new:Npn \__fp_invalid_operation_o:nw
+ { \__fp_invalid_operation:nnw { \exp_after:wN \c_nan_fp } }
+\cs_generate_variant:Nn \__fp_invalid_operation_o:nw { f }
+\cs_new:Npn \__fp_error:nnnn
+ { \msg_expandable_error:nnnnn { fp } }
+\cs_generate_variant:Nn \__fp_error:nnnn { nnf, nff , nfff }
+\msg_new:nnnn { fp } { unknown-fpu-exception }
+ {
+ The~FPU~exception~'#1'~is~not~known:~
+ that~trap~will~never~be~triggered.
+ }
+ {
+ The~only~exceptions~to~which~traps~can~be~attached~are \\
+ \iow_indent:n
+ {
+ * ~ invalid_operation \\
+ * ~ division_by_zero \\
+ * ~ overflow \\
+ * ~ underflow
+ }
+ }
+\msg_new:nnnn { fp } { unknown-fpu-trap-type }
+ { The~FPU~trap~type~'#2'~is~not~known. }
+ {
+ The~trap~type~must~be~one~of \\
+ \iow_indent:n
+ {
+ * ~ error \\
+ * ~ flag \\
+ * ~ none
+ }
+ }
+\msg_new:nnn { fp } { flow }
+ { An ~ #3 ~ occurred. }
+\msg_new:nnn { fp } { flow-to }
+ { #1 ~ #3 ed ~ to ~ #2 . }
+\msg_new:nnn { fp } { zero-div }
+ { Division~by~zero~in~ #1 (#2) }
+\msg_new:nnn { fp } { zero-div-ii }
+ { Division~by~zero~in~ (#1) #3 (#2) }
+\msg_new:nnn { fp } { invalid }
+ { Invalid~operation~ #1 (#2) }
+\msg_new:nnn { fp } { invalid-ii }
+ { Invalid~operation~ (#1) #3 (#2) }
+\msg_new:nnn { fp } { unknown-type }
+ { Unknown~type~for~'#1' }
+%% File: l3fp-round.dtx
+\cs_new:Npn \__fp_parse_word_trunc:N
+ { \__fp_parse_function:NNN \__fp_round_o:Nw \__fp_round_to_zero:NNN }
+\cs_new:Npn \__fp_parse_word_floor:N
+ { \__fp_parse_function:NNN \__fp_round_o:Nw \__fp_round_to_ninf:NNN }
+\cs_new:Npn \__fp_parse_word_ceil:N
+ { \__fp_parse_function:NNN \__fp_round_o:Nw \__fp_round_to_pinf:NNN }
+\cs_new:Npn \__fp_parse_word_round:N #1#2
+ {
+ \__fp_parse_function:NNN
+ \__fp_round_o:Nw \__fp_round_to_nearest:NNN #1
+ #2
+ }
+\cs_new:Npn \__fp_parse_round:Nw #1 #2 \__fp_round_to_nearest:NNN #3#4
+ { #2 #1 #3 }
+
+\int_const:Nn \c__fp_five_int { 5 }
+\cs_new:Npn \__fp_round_return_one:
+ { \exp_after:wN 1 \exp_after:wN \exp_stop_f: \exp:w }
+\cs_new:Npn \__fp_round_to_ninf:NNN #1 #2 #3
+ {
+ \if_meaning:w 2 #1
+ \if_int_compare:w #3 > \c_zero_int
+ \__fp_round_return_one:
+ \fi:
+ \fi:
+ \c_zero_int
+ }
+\cs_new:Npn \__fp_round_to_zero:NNN #1 #2 #3 { \c_zero_int }
+\cs_new:Npn \__fp_round_to_pinf:NNN #1 #2 #3
+ {
+ \if_meaning:w 0 #1
+ \if_int_compare:w #3 > \c_zero_int
+ \__fp_round_return_one:
+ \fi:
+ \fi:
+ \c_zero_int
+ }
+\cs_new:Npn \__fp_round_to_nearest:NNN #1 #2 #3
+ {
+ \if_int_compare:w #3 > \c__fp_five_int
+ \__fp_round_return_one:
+ \else:
+ \if_meaning:w 5 #3
+ \if_int_odd:w #2 \exp_stop_f:
+ \__fp_round_return_one:
+ \fi:
+ \fi:
+ \fi:
+ \c_zero_int
+ }
+\cs_new:Npn \__fp_round_to_nearest_ninf:NNN #1 #2 #3
+ {
+ \if_int_compare:w #3 > \c__fp_five_int
+ \__fp_round_return_one:
+ \else:
+ \if_meaning:w 5 #3
+ \if_meaning:w 2 #1
+ \__fp_round_return_one:
+ \fi:
+ \fi:
+ \fi:
+ \c_zero_int
+ }
+\cs_new:Npn \__fp_round_to_nearest_zero:NNN #1 #2 #3
+ {
+ \if_int_compare:w #3 > \c__fp_five_int
+ \__fp_round_return_one:
+ \fi:
+ \c_zero_int
+ }
+\cs_new:Npn \__fp_round_to_nearest_pinf:NNN #1 #2 #3
+ {
+ \if_int_compare:w #3 > \c__fp_five_int
+ \__fp_round_return_one:
+ \else:
+ \if_meaning:w 5 #3
+ \if_meaning:w 0 #1
+ \__fp_round_return_one:
+ \fi:
+ \fi:
+ \fi:
+ \c_zero_int
+ }
+\cs_new_eq:NN \__fp_round:NNN \__fp_round_to_nearest:NNN
+\cs_new:Npn \__fp_round_s:NNNw #1 #2 #3 #4;
+ {
+ \exp_after:wN \__fp_round:NNN
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \int_value:w \__fp_int_eval:w
+ \if_int_odd:w 0 \if_meaning:w 0 #3 1 \fi:
+ \if_meaning:w 5 #3 1 \fi:
+ \exp_stop_f:
+ \if_int_compare:w \__fp_int_eval:w #4 > \c_zero_int
+ 1 +
+ \fi:
+ \fi:
+ #3
+ ;
+ }
+\cs_new:Npn \__fp_round_digit:Nw #1 #2;
+ {
+ \if_int_odd:w \if_meaning:w 0 #1 1 \else:
+ \if_meaning:w 5 #1 1 \else:
+ 0 \fi: \fi: \exp_stop_f:
+ \if_int_compare:w \__fp_int_eval:w #2 > \c_zero_int
+ \__fp_int_eval:w 1 +
+ \fi:
+ \fi:
+ #1
+ }
+\cs_new_eq:NN \__fp_round_to_ninf_neg:NNN \__fp_round_to_pinf:NNN
+\cs_new:Npn \__fp_round_to_zero_neg:NNN #1 #2 #3
+ {
+ \if_int_compare:w #3 > \c_zero_int
+ \__fp_round_return_one:
+ \fi:
+ \c_zero_int
+ }
+\cs_new_eq:NN \__fp_round_to_pinf_neg:NNN \__fp_round_to_ninf:NNN
+\cs_new_eq:NN \__fp_round_to_nearest_neg:NNN \__fp_round_to_nearest:NNN
+\cs_new_eq:NN \__fp_round_to_nearest_ninf_neg:NNN
+ \__fp_round_to_nearest_pinf:NNN
+\cs_new:Npn \__fp_round_to_nearest_zero_neg:NNN #1 #2 #3
+ {
+ \if_int_compare:w #3 < \c__fp_five_int \else:
+ \__fp_round_return_one:
+ \fi:
+ \c_zero_int
+ }
+\cs_new_eq:NN \__fp_round_to_nearest_pinf_neg:NNN
+ \__fp_round_to_nearest_ninf:NNN
+\cs_new_eq:NN \__fp_round_neg:NNN \__fp_round_to_nearest_neg:NNN
+\cs_new:Npn \__fp_round_o:Nw #1
+ {
+ \__fp_parse_function_all_fp_o:fnw
+ { \__fp_round_name_from_cs:N #1 }
+ { \__fp_round_aux_o:Nw #1 }
+ }
+\cs_new:Npn \__fp_round_aux_o:Nw #1#2 @
+ {
+ \if_case:w
+ \__fp_int_eval:w \__fp_array_count:n {#2} \__fp_int_eval_end:
+ \__fp_round_no_arg_o:Nw #1 \exp:w
+ \or: \__fp_round:Nwn #1 #2 {0} \exp:w
+ \or: \__fp_round:Nww #1 #2 \exp:w
+ \else: \__fp_round:Nwww #1 #2 @ \exp:w
+ \fi:
+ \exp_after:wN \exp_end:
+ }
+\cs_new:Npn \__fp_round_no_arg_o:Nw #1
+ {
+ \cs_if_eq:NNTF #1 \__fp_round_to_nearest:NNN
+ { \__fp_error:nnnn { num-args } { round () } { 1 } { 3 } }
+ {
+ \__fp_error:nffn { num-args }
+ { \__fp_round_name_from_cs:N #1 () } { 1 } { 2 }
+ }
+ \exp_after:wN \c_nan_fp
+ }
+\cs_new:Npn \__fp_round:Nwww #1#2 ; #3 ; \s__fp \__fp_chk:w #4#5#6 ; #7 @
+ {
+ \cs_if_eq:NNTF #1 \__fp_round_to_nearest:NNN
+ {
+ \tl_if_empty:nTF {#7}
+ {
+ \exp_args:Nc \__fp_round:Nww
+ {
+ __fp_round_to_nearest
+ \if_meaning:w 0 #4 _zero \else:
+ \if_case:w #5 \exp_stop_f: _pinf \or: \else: _ninf \fi: \fi:
+ :NNN
+ }
+ #2 ; #3 ;
+ }
+ {
+ \__fp_error:nnnn { num-args } { round () } { 1 } { 3 }
+ \exp_after:wN \c_nan_fp
+ }
+ }
+ {
+ \__fp_error:nffn { num-args }
+ { \__fp_round_name_from_cs:N #1 () } { 1 } { 2 }
+ \exp_after:wN \c_nan_fp
+ }
+ }
+\cs_new:Npn \__fp_round_name_from_cs:N #1
+ {
+ \cs_if_eq:NNTF #1 \__fp_round_to_zero:NNN { trunc }
+ {
+ \cs_if_eq:NNTF #1 \__fp_round_to_ninf:NNN { floor }
+ {
+ \cs_if_eq:NNTF #1 \__fp_round_to_pinf:NNN { ceil }
+ { round }
+ }
+ }
+ }
+\cs_new:Npn \__fp_round:Nww #1#2 ; #3 ;
+ {
+ \__fp_small_int:wTF #3; { \__fp_round:Nwn #1#2; }
+ {
+ \if:w 3 \__fp_kind:w #3 ;
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ { \exp_after:wN \c_nan_fp }
+ {
+ \__fp_invalid_operation_tl_o:ff
+ { \__fp_round_name_from_cs:N #1 }
+ { \__fp_array_to_clist:n { #2; #3; } }
+ }
+ }
+ }
+\cs_new:Npn \__fp_round:Nwn #1 \s__fp \__fp_chk:w #2#3#4; #5
+ {
+ \if_meaning:w 1 #2
+ \exp_after:wN \__fp_round_normal:NwNNnw
+ \exp_after:wN #1
+ \int_value:w #5
+ \else:
+ \exp_after:wN \__fp_exp_after_o:w
+ \fi:
+ \s__fp \__fp_chk:w #2#3#4;
+ }
+\cs_new:Npn \__fp_round_normal:NwNNnw #1#2 \s__fp \__fp_chk:w 1#3#4#5;
+ {
+ \__fp_decimate:nNnnnn { \c__fp_prec_int - #4 - #2 }
+ \__fp_round_normal:NnnwNNnn #5 #1 #3 {#4} {#2}
+ }
+\cs_new:Npn \__fp_round_normal:NnnwNNnn #1#2#3#4; #5#6
+ {
+ \exp_after:wN \__fp_round_normal:NNwNnn
+ \int_value:w \__fp_int_eval:w
+ \if_int_compare:w #2 > \c_zero_int
+ 1 \int_value:w #2
+ \exp_after:wN \__fp_round_pack:Nw
+ \int_value:w \__fp_int_eval:w 1#3 +
+ \else:
+ \if_int_compare:w #3 > \c_zero_int
+ 1 \int_value:w #3 +
+ \fi:
+ \fi:
+ \exp_after:wN #5
+ \exp_after:wN #6
+ \use_none:nnnnnnn #3
+ #1
+ \__fp_int_eval_end:
+ 0000 0000 0000 0000 ; #6
+ }
+\cs_new:Npn \__fp_round_pack:Nw #1
+ { \if_meaning:w 2 #1 + 1 \fi: \__fp_int_eval_end: }
+\cs_new:Npn \__fp_round_normal:NNwNnn #1 #2
+ {
+ \if_meaning:w 0 #2
+ \exp_after:wN \__fp_round_special:NwwNnn
+ \exp_after:wN #1
+ \fi:
+ \__fp_pack_twice_four:wNNNNNNNN
+ \__fp_pack_twice_four:wNNNNNNNN
+ \__fp_round_normal_end:wwNnn
+ ; #2
+ }
+\cs_new:Npn \__fp_round_normal_end:wwNnn #1;#2;#3#4#5
+ {
+ \exp_after:wN \__fp_exp_after_o:w \exp:w \exp_end_continue_f:w
+ \__fp_sanitize:Nw #3 #4 ; #1 ;
+ }
+\cs_new:Npn \__fp_round_special:NwwNnn #1#2;#3;#4#5#6
+ {
+ \if_meaning:w 0 #1
+ \__fp_case_return:nw
+ { \exp_after:wN \__fp_zero_fp:N \exp_after:wN #4 }
+ \else:
+ \exp_after:wN \__fp_round_special_aux:Nw
+ \exp_after:wN #4
+ \int_value:w \__fp_int_eval:w 1
+ \if_meaning:w 1 #1 -#6 \else: +#5 \fi:
+ \fi:
+ ;
+ }
+\cs_new:Npn \__fp_round_special_aux:Nw #1#2;
+ {
+ \exp_after:wN \__fp_exp_after_o:w \exp:w \exp_end_continue_f:w
+ \__fp_sanitize:Nw #1#2; {1000}{0000}{0000}{0000};
+ }
+%% File: l3fp-parse.dtx
+\int_const:Nn \c__fp_prec_func_int { 16 }
+\int_const:Nn \c__fp_prec_hatii_int { 14 }
+\int_const:Nn \c__fp_prec_hat_int { 13 }
+\int_const:Nn \c__fp_prec_not_int { 12 }
+\int_const:Nn \c__fp_prec_juxt_int { 11 }
+\int_const:Nn \c__fp_prec_times_int { 10 }
+\int_const:Nn \c__fp_prec_plus_int { 9 }
+\int_const:Nn \c__fp_prec_comp_int { 7 }
+\int_const:Nn \c__fp_prec_and_int { 6 }
+\int_const:Nn \c__fp_prec_or_int { 5 }
+\int_const:Nn \c__fp_prec_quest_int { 4 }
+\int_const:Nn \c__fp_prec_colon_int { 3 }
+\int_const:Nn \c__fp_prec_comma_int { 2 }
+\int_const:Nn \c__fp_prec_tuple_int { 1 }
+\int_const:Nn \c__fp_prec_end_int { 0 }
+\cs_new:Npn \__fp_parse_expand:w #1 { \exp_end_continue_f:w #1 }
+\cs_new:Npn \__fp_parse_return_semicolon:w
+ #1 \fi: \__fp_parse_expand:w { \fi: ; #1 }
+\cs_set_protected:Npn \__fp_tmp:w #1 #2 #3
+ {
+ \cs_new:cpn { __fp_parse_digits_ #1 :N } ##1
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N ##1 \exp_stop_f:
+ \token_to_str:N ##1 \exp_after:wN #2 \exp:w
+ \else:
+ \__fp_parse_return_semicolon:w #3 ##1
+ \fi:
+ \__fp_parse_expand:w
+ }
+ }
+\__fp_tmp:w {vii} \__fp_parse_digits_vi:N { 0000000 ; 7 }
+\__fp_tmp:w {vi} \__fp_parse_digits_v:N { 000000 ; 6 }
+\__fp_tmp:w {v} \__fp_parse_digits_iv:N { 00000 ; 5 }
+\__fp_tmp:w {iv} \__fp_parse_digits_iii:N { 0000 ; 4 }
+\__fp_tmp:w {iii} \__fp_parse_digits_ii:N { 000 ; 3 }
+\__fp_tmp:w {ii} \__fp_parse_digits_i:N { 00 ; 2 }
+\__fp_tmp:w {i} \__fp_parse_digits_:N { 0 ; 1 }
+\cs_new:Npn \__fp_parse_digits_:N { ; ; 0 }
+\cs_new:Npn \__fp_parse_one:Nw #1 #2
+ {
+ \if_catcode:w \scan_stop: \exp_not:N #2
+ \exp_after:wN \if_meaning:w \exp_not:N #2 #2 \else:
+ \exp_after:wN \reverse_if:N
+ \fi:
+ \if_meaning:w \scan_stop: #2
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__fp_parse_one_fp:NN
+ \else:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__fp_parse_one_register:NN
+ \fi:
+ \else:
+ \if_int_compare:w 9 < 1 \token_to_str:N #2 \exp_stop_f:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__fp_parse_one_digit:NN
+ \else:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__fp_parse_one_other:NN
+ \fi:
+ \fi:
+ #1 #2
+ }
+\cs_new:Npn \__fp_parse_one_fp:NN #1
+ {
+ \__fp_exp_after_any_f:nw
+ {
+ \exp_after:wN \__fp_parse_infix:NN
+ \exp_after:wN #1 \exp:w \__fp_parse_expand:w
+ }
+ }
+\cs_new:Npn \__fp_exp_after_expr_mark_f:nw #1
+ {
+ \int_case:nnF { \exp_after:wN \use_i:nnn \use_none:nnn #1 }
+ {
+ \c__fp_prec_comma_int { }
+ \c__fp_prec_tuple_int { }
+ \c__fp_prec_end_int
+ {
+ \exp_after:wN \c__fp_empty_tuple_fp
+ \exp:w \exp_end_continue_f:w
+ }
+ }
+ {
+ \msg_expandable_error:nn { fp } { early-end }
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w
+ }
+ #1
+ }
+\cs_new:cpn { __fp_exp_after_?_f:nw } #1#2
+ {
+ \msg_expandable_error:nnn { kernel } { bad-variable }
+ {#2}
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w #1
+ }
+\cs_set_protected:Npn \__fp_tmp:w #1
+ {
+ \cs_if_exist:NT #1
+ {
+ \cs_gset:cpn { __fp_exp_after_?_f:nw } ##1##2
+ {
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w ##1
+ \str_if_eq:nnTF {##2} { \protect }
+ {
+ \cs_if_eq:NNTF ##2 #1 { \use_i:nn } { \use:n }
+ {
+ \msg_expandable_error:nnn { fp }
+ { robust-cmd }
+ }
+ }
+ {
+ \msg_expandable_error:nnn { kernel }
+ { bad-variable } {##2}
+ }
+ }
+ }
+ }
+\exp_args:Nc \__fp_tmp:w { @unexpandable@protect }
+\cs_new:Npn \__fp_parse_one_register:NN #1#2
+ {
+ \exp_after:wN \__fp_parse_infix_after_operand:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \__fp_parse_one_register_special:N #2
+ \exp_after:wN \__fp_parse_one_register_aux:Nw
+ \exp_after:wN #2
+ \int_value:w
+ \exp_after:wN \__fp_parse_exponent:N
+ \exp:w \__fp_parse_expand:w
+ }
+\cs_new:Npe \__fp_parse_one_register_aux:Nw #1
+ {
+ \exp_not:n
+ {
+ \exp_after:wN \use:nn
+ \exp_after:wN \__fp_parse_one_register_auxii:wwwNw
+ }
+ \exp_not:N \exp_after:wN { \exp_not:N \tex_the:D #1 }
+ ; \exp_not:N \__fp_parse_one_register_dim:ww
+ \tl_to_str:n { pt } ; \exp_not:N \__fp_parse_one_register_mu:www
+ . \tl_to_str:n { pt } ; \exp_not:N \__fp_parse_one_register_int:www
+ \s__fp_stop
+ }
+\exp_args:Nno \use:nn
+ { \cs_new:Npn \__fp_parse_one_register_auxii:wwwNw #1 . #2 }
+ { \tl_to_str:n { pt } #3 ; #4#5 \s__fp_stop }
+ { #4 #1.#2; }
+\exp_args:Nno \use:nn
+ { \cs_new:Npn \__fp_parse_one_register_mu:www #1 }
+ { \tl_to_str:n { mu } ; #2 ; }
+ { \__fp_parse_one_register_dim:ww #1 ; }
+\cs_new:Npn \__fp_parse_one_register_int:www #1; #2.; #3;
+ { \__fp_parse:n { #1 e #3 } }
+\cs_new:Npn \__fp_parse_one_register_dim:ww #1; #2;
+ {
+ \exp_after:wN \__fp_from_dim_test:ww
+ \int_value:w #2 \exp_after:wN ,
+ \int_value:w \dim_to_decimal_in_sp:n { #1 pt } ;
+ }
+\cs_new:Npn \__fp_parse_one_register_special:N #1
+ {
+ \if_meaning:w \box_wd:N #1 \__fp_parse_one_register_wd:w \fi:
+ \if_meaning:w \box_ht:N #1 \__fp_parse_one_register_wd:w \fi:
+ \if_meaning:w \box_dp:N #1 \__fp_parse_one_register_wd:w \fi:
+ \if_meaning:w \infty #1
+ \__fp_parse_one_register_math:NNw \infty #1
+ \fi:
+ \if_meaning:w \pi #1
+ \__fp_parse_one_register_math:NNw \pi #1
+ \fi:
+ }
+\cs_new:Npn \__fp_parse_one_register_math:NNw
+ #1#2#3#4 \__fp_parse_expand:w
+ {
+ #3
+ \str_if_eq:nnTF {#1} {#2}
+ {
+ \msg_expandable_error:nnn
+ { fp } { infty-pi } {#1}
+ \c_nan_fp
+ }
+ { #4 \__fp_parse_expand:w }
+ }
+\cs_new:Npn \__fp_parse_one_register_wd:w
+ #1#2 \exp_after:wN #3#4 \__fp_parse_expand:w
+ {
+ #1
+ \exp_after:wN \__fp_parse_one_register_wd:Nw
+ #4 \__fp_parse_expand:w e
+ }
+\cs_new:Npn \__fp_parse_one_register_wd:Nw #1#2 ;
+ {
+ \exp_after:wN \__fp_from_dim_test:ww
+ \exp_after:wN 0 \exp_after:wN ,
+ \int_value:w \dim_to_decimal_in_sp:n { #1 #2 } ;
+ }
+\cs_new:Npn \__fp_parse_one_digit:NN #1
+ {
+ \exp_after:wN \__fp_parse_infix_after_operand:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN \__fp_sanitize:wN
+ \int_value:w \__fp_int_eval:w 0 \__fp_parse_trim_zeros:N
+ }
+\cs_new:Npn \__fp_parse_one_other:NN #1 #2
+ {
+ \if_int_compare:w
+ \__fp_int_eval:w
+ ( `#2 \if_int_compare:w `#2 > `Z - 32 \fi: ) / 26
+ = 3 \exp_stop_f:
+ \exp_after:wN \__fp_parse_word:Nw
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp:w \exp_after:wN \__fp_parse_letters:N
+ \exp:w
+ \else:
+ \exp_after:wN \__fp_parse_prefix:NNN
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \cs:w
+ __fp_parse_prefix_ \token_to_str:N #2 :Nw
+ \exp_after:wN
+ \cs_end:
+ \exp:w
+ \fi:
+ \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_word:Nw #1#2;
+ {
+ \cs_if_exist_use:cF { __fp_parse_word_#2:N }
+ {
+ \cs_if_exist_use:cF
+ { __fp_parse_caseless_ \str_casefold:n {#2} :N }
+ {
+ \msg_expandable_error:nnn
+ { fp } { unknown-fp-word } {#2}
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w
+ \__fp_parse_infix:NN
+ }
+ }
+ #1
+ }
+\cs_new:Npn \__fp_parse_letters:N #1
+ {
+ \exp_end_continue_f:w
+ \if_int_compare:w
+ \if_catcode:w \scan_stop: \exp_not:N #1
+ 0
+ \else:
+ \__fp_int_eval:w
+ ( `#1 \if_int_compare:w `#1 > `Z - 32 \fi: ) / 26
+ \fi:
+ = 3 \exp_stop_f:
+ \exp_after:wN #1
+ \exp:w \exp_after:wN \__fp_parse_letters:N
+ \exp:w
+ \else:
+ \__fp_parse_return_semicolon:w #1
+ \fi:
+ \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_prefix:NNN #1#2#3
+ {
+ \if_meaning:w \scan_stop: #3
+ \exp_after:wN \__fp_parse_prefix_unknown:NNN
+ \exp_after:wN #2
+ \fi:
+ #3 #1
+ }
+\cs_new:Npn \__fp_parse_prefix_unknown:NNN #1#2#3
+ {
+ \cs_if_exist:cTF { __fp_parse_infix_ \token_to_str:N #1 :N }
+ {
+ \msg_expandable_error:nnn
+ { fp } { missing-number } {#1}
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w
+ \__fp_parse_infix:NN #3 #1
+ }
+ {
+ \msg_expandable_error:nnn
+ { fp } { unknown-symbol } {#1}
+ \__fp_parse_one:Nw #3
+ }
+ }
+\cs_new:Npn \__fp_parse_trim_zeros:N #1
+ {
+ \if:w 0 \exp_not:N #1
+ \exp_after:wN \__fp_parse_trim_zeros:N
+ \exp:w
+ \else:
+ \if:w . \exp_not:N #1
+ \exp_after:wN \__fp_parse_strim_zeros:N
+ \exp:w
+ \else:
+ \__fp_parse_trim_end:w #1
+ \fi:
+ \fi:
+ \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_trim_end:w #1 \fi: \fi: \__fp_parse_expand:w
+ {
+ \fi:
+ \fi:
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ \exp_after:wN \__fp_parse_large:N
+ \else:
+ \exp_after:wN \__fp_parse_zero:
+ \fi:
+ #1
+ }
+\cs_new:Npn \__fp_parse_strim_zeros:N #1
+ {
+ \if:w 0 \exp_not:N #1
+ - 1
+ \exp_after:wN \__fp_parse_strim_zeros:N \exp:w
+ \else:
+ \__fp_parse_strim_end:w #1
+ \fi:
+ \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_strim_end:w #1 \fi: \__fp_parse_expand:w
+ {
+ \fi:
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ \exp_after:wN \__fp_parse_small:N
+ \else:
+ \exp_after:wN \__fp_parse_zero:
+ \fi:
+ #1
+ }
+\cs_new:Npn \__fp_parse_zero:
+ {
+ \exp_after:wN ; \exp_after:wN 1
+ \int_value:w \__fp_parse_exponent:N
+ }
+\cs_new:Npn \__fp_parse_small:N #1
+ {
+ \exp_after:wN \__fp_parse_pack_leading:NNNNNww
+ \int_value:w \__fp_int_eval:w 1 \token_to_str:N #1
+ \exp_after:wN \__fp_parse_small_leading:wwNN
+ \int_value:w 1
+ \exp_after:wN \__fp_parse_digits_vii:N
+ \exp:w \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_small_leading:wwNN 1 #1 ; #2; #3 #4
+ {
+ #1 #2
+ \exp_after:wN \__fp_parse_pack_trailing:NNNNNNww
+ \exp_after:wN 0
+ \int_value:w \__fp_int_eval:w 1
+ \if_int_compare:w 9 < 1 \token_to_str:N #4 \exp_stop_f:
+ \token_to_str:N #4
+ \exp_after:wN \__fp_parse_small_trailing:wwNN
+ \int_value:w 1
+ \exp_after:wN \__fp_parse_digits_vi:N
+ \exp:w
+ \else:
+ 0000 0000 \__fp_parse_exponent:Nw #4
+ \fi:
+ \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_small_trailing:wwNN 1 #1 ; #2; #3 #4
+ {
+ #1 #2
+ \if_int_compare:w 9 < 1 \token_to_str:N #4 \exp_stop_f:
+ \token_to_str:N #4
+ \exp_after:wN \__fp_parse_small_round:NN
+ \exp_after:wN #4
+ \exp:w
+ \else:
+ 0 \__fp_parse_exponent:Nw #4
+ \fi:
+ \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_pack_trailing:NNNNNNww #1 #2 #3#4#5#6 #7; #8 ;
+ {
+ \if_meaning:w 2 #2 + 1 \fi:
+ ; #8 + #1 ; {#3#4#5#6} {#7};
+ }
+\cs_new:Npn \__fp_parse_pack_leading:NNNNNww #1 #2#3#4#5 #6; #7;
+ {
+ + #7
+ \if_meaning:w 2 #1 \__fp_parse_pack_carry:w \fi:
+ ; 0 {#2#3#4#5} {#6}
+ }
+\cs_new:Npn \__fp_parse_pack_carry:w \fi: ; 0 #1
+ { \fi: + 1 ; 0 {1000} }
+\cs_new:Npn \__fp_parse_large:N #1
+ {
+ \exp_after:wN \__fp_parse_large_leading:wwNN
+ \int_value:w 1 \token_to_str:N #1
+ \exp_after:wN \__fp_parse_digits_vii:N
+ \exp:w \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_large_leading:wwNN 1 #1 ; #2; #3 #4
+ {
+ + \c__fp_half_prec_int - #3
+ \exp_after:wN \__fp_parse_pack_leading:NNNNNww
+ \int_value:w \__fp_int_eval:w 1 #1
+ \if_int_compare:w 9 < 1 \token_to_str:N #4 \exp_stop_f:
+ \exp_after:wN \__fp_parse_large_trailing:wwNN
+ \int_value:w 1 \token_to_str:N #4
+ \exp_after:wN \__fp_parse_digits_vi:N
+ \exp:w
+ \else:
+ \if:w . \exp_not:N #4
+ \exp_after:wN \__fp_parse_small_leading:wwNN
+ \int_value:w 1
+ \cs:w
+ __fp_parse_digits_
+ \__fp_int_to_roman:w #3
+ :N \exp_after:wN
+ \cs_end:
+ \exp:w
+ \else:
+ #2
+ \exp_after:wN \__fp_parse_pack_trailing:NNNNNNww
+ \exp_after:wN 0
+ \int_value:w 1 0000 0000
+ \__fp_parse_exponent:Nw #4
+ \fi:
+ \fi:
+ \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_large_trailing:wwNN 1 #1 ; #2; #3 #4
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #4 \exp_stop_f:
+ \exp_after:wN \__fp_parse_pack_trailing:NNNNNNww
+ \exp_after:wN \c__fp_half_prec_int
+ \int_value:w \__fp_int_eval:w 1 #1 \token_to_str:N #4
+ \exp_after:wN \__fp_parse_large_round:NN
+ \exp_after:wN #4
+ \exp:w
+ \else:
+ \exp_after:wN \__fp_parse_pack_trailing:NNNNNNww
+ \int_value:w \__fp_int_eval:w 7 - #3 \exp_stop_f:
+ \int_value:w \__fp_int_eval:w 1 #1
+ \if:w . \exp_not:N #4
+ \exp_after:wN \__fp_parse_small_trailing:wwNN
+ \int_value:w 1
+ \cs:w
+ __fp_parse_digits_
+ \__fp_int_to_roman:w #3
+ :N \exp_after:wN
+ \cs_end:
+ \exp:w
+ \else:
+ #2 0 \__fp_parse_exponent:Nw #4
+ \fi:
+ \fi:
+ \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_round_loop:N #1
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ + 1
+ \if:w 0 \token_to_str:N #1
+ \exp_after:wN \__fp_parse_round_loop:N
+ \exp:w
+ \else:
+ \exp_after:wN \__fp_parse_round_up:N
+ \exp:w
+ \fi:
+ \else:
+ \__fp_parse_return_semicolon:w 0 #1
+ \fi:
+ \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_round_up:N #1
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ + 1
+ \exp_after:wN \__fp_parse_round_up:N
+ \exp:w
+ \else:
+ \__fp_parse_return_semicolon:w 1 #1
+ \fi:
+ \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_round_after:wN #1; #2
+ {
+ + #2 \exp_after:wN ;
+ \int_value:w \__fp_int_eval:w #1 + \__fp_parse_exponent:N
+ }
+\cs_new:Npn \__fp_parse_small_round:NN #1#2
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #2 \exp_stop_f:
+ +
+ \exp_after:wN \__fp_round_s:NNNw
+ \exp_after:wN 0
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \int_value:w \__fp_int_eval:w
+ \exp_after:wN \__fp_parse_round_after:wN
+ \int_value:w \__fp_int_eval:w 0 * \__fp_int_eval:w 0
+ \exp_after:wN \__fp_parse_round_loop:N
+ \exp:w
+ \else:
+ \__fp_parse_exponent:Nw #2
+ \fi:
+ \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_large_round:NN #1#2
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #2 \exp_stop_f:
+ +
+ \exp_after:wN \__fp_round_s:NNNw
+ \exp_after:wN 0
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \int_value:w \__fp_int_eval:w
+ \exp_after:wN \__fp_parse_large_round_aux:wNN
+ \int_value:w \__fp_int_eval:w 1
+ \exp_after:wN \__fp_parse_round_loop:N
+ \else: %^^A could be dot, or e, or other
+ \exp_after:wN \__fp_parse_large_round_test:NN
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \fi:
+ }
+\cs_new:Npn \__fp_parse_large_round_test:NN #1#2
+ {
+ \if:w . \exp_not:N #2
+ \exp_after:wN \__fp_parse_small_round:NN
+ \exp_after:wN #1
+ \exp:w
+ \else:
+ \__fp_parse_exponent:Nw #2
+ \fi:
+ \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_large_round_aux:wNN #1 ; #2 #3
+ {
+ + #2
+ \exp_after:wN \__fp_parse_round_after:wN
+ \int_value:w \__fp_int_eval:w #1
+ \if:w . \exp_not:N #3
+ + 0 * \__fp_int_eval:w 0
+ \exp_after:wN \__fp_parse_round_loop:N
+ \exp:w \exp_after:wN \__fp_parse_expand:w
+ \else:
+ \exp_after:wN ;
+ \exp_after:wN 0
+ \exp_after:wN #3
+ \fi:
+ }
+\cs_new:Npn \__fp_parse_exponent:Nw #1 #2 \__fp_parse_expand:w
+ {
+ \exp_after:wN ;
+ \int_value:w #2 \__fp_parse_exponent:N #1
+ }
+\cs_new:Npn \__fp_parse_exponent:N #1
+ {
+ \if:w e \if:w E \exp_not:N #1 e \else: \exp_not:N #1 \fi:
+ \exp_after:wN \__fp_parse_exponent_aux:NN
+ \exp_after:wN #1
+ \exp:w
+ \else:
+ 0 \__fp_parse_return_semicolon:w #1
+ \fi:
+ \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_exponent_aux:NN #1#2
+ {
+ \if_int_compare:w \if_catcode:w \scan_stop: \exp_not:N #2
+ 0 \else: `#2 \fi: > `9 \exp_stop_f:
+ 0 \exp_after:wN ; \exp_after:wN #1
+ \else:
+ \exp_after:wN \__fp_parse_exponent_sign:N
+ \fi:
+ #2
+ }
+\cs_new:Npn \__fp_parse_exponent_sign:N #1
+ {
+ \if:w + \if:w - \exp_not:N #1 + \fi: \token_to_str:N #1
+ \exp_after:wN \__fp_parse_exponent_sign:N
+ \exp:w \exp_after:wN \__fp_parse_expand:w
+ \else:
+ \exp_after:wN \__fp_parse_exponent_body:N
+ \exp_after:wN #1
+ \fi:
+ }
+\cs_new:Npn \__fp_parse_exponent_body:N #1
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ \token_to_str:N #1
+ \exp_after:wN \__fp_parse_exponent_digits:N
+ \exp:w
+ \else:
+ \__fp_parse_exponent_keep:NTF #1
+ { \__fp_parse_return_semicolon:w #1 }
+ {
+ \exp_after:wN ;
+ \exp:w
+ }
+ \fi:
+ \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_exponent_digits:N #1
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ \token_to_str:N #1
+ \exp_after:wN \__fp_parse_exponent_digits:N
+ \exp:w
+ \else:
+ \__fp_parse_return_semicolon:w #1
+ \fi:
+ \__fp_parse_expand:w
+ }
+\prg_new_conditional:Npnn \__fp_parse_exponent_keep:N #1 { TF }
+ {
+ \if_catcode:w \scan_stop: \exp_not:N #1
+ \if_meaning:w \scan_stop: #1
+ \if:w 0 \__fp_str_if_eq:nn { \s__fp } { \exp_not:N #1 }
+ 0
+ \msg_expandable_error:nnn
+ { fp } { after-e } { floating~point~ }
+ \prg_return_true:
+ \else:
+ 0
+ \msg_expandable_error:nnn
+ { kernel } { bad-variable } {#1}
+ \prg_return_false:
+ \fi:
+ \else:
+ \if:w 0 \__fp_str_if_eq:nn { \int_value:w #1 } { \tex_the:D #1 }
+ \int_value:w #1
+ \else:
+ 0
+ \msg_expandable_error:nnn
+ { fp } { after-e } { dimension~#1 }
+ \fi:
+ \prg_return_false:
+ \fi:
+ \else:
+ 0
+ \msg_expandable_error:nnn
+ { fp } { missing } { exponent }
+ \prg_return_true:
+ \fi:
+ }
+\cs_new_eq:cN { __fp_parse_prefix_+:Nw } \__fp_parse_one:Nw
+\cs_new:Npn \__fp_parse_apply_function:NNNwN #1#2#3#4@#5
+ {
+ #3 #2 #4 @
+ \exp:w \exp_end_continue_f:w #5 #1
+ }
+\cs_new:Npn \__fp_parse_apply_unary:NNNwN #1#2#3#4@#5
+ {
+ \__fp_parse_apply_unary_chk:NwNw #4 @ ; . \s__fp_stop
+ \__fp_parse_apply_unary_type:NNN
+ #3 #2 #4 @
+ \exp:w \exp_end_continue_f:w #5 #1
+ }
+\cs_new:Npn \__fp_parse_apply_unary_chk:NwNw #1#2 ; #3#4 \s__fp_stop
+ {
+ \if_meaning:w @ #3 \else:
+ \token_if_eq_meaning:NNTF . #3
+ { \__fp_parse_apply_unary_chk:nNNNNw { no } }
+ { \__fp_parse_apply_unary_chk:nNNNNw { multi } }
+ \fi:
+ }
+\cs_new:Npn \__fp_parse_apply_unary_chk:nNNNNw #1#2#3#4#5#6 @
+ {
+ #2
+ \__fp_error:nffn { #1-arg } { \__fp_func_to_name:N #4 } { } { }
+ \exp_after:wN #4 \exp_after:wN #5 \c_nan_fp @
+ }
+\cs_new:Npn \__fp_parse_apply_unary_type:NNN #1#2#3
+ {
+ \__fp_change_func_type:NNN #3 #1 \__fp_parse_apply_unary_error:NNw
+ #2 #3
+ }
+\cs_new:Npn \__fp_parse_apply_unary_error:NNw #1#2#3 @
+ { \__fp_invalid_operation_o:fw { \__fp_func_to_name:N #1 } #3 }
+\cs_set_protected:Npn \__fp_tmp:w #1#2#3#4
+ {
+ \cs_new:cpn { __fp_parse_prefix_ #1 :Nw } ##1
+ {
+ \exp_after:wN \__fp_parse_apply_unary:NNNwN
+ \exp_after:wN ##1
+ \exp_after:wN #4
+ \exp_after:wN #3
+ \exp:w
+ \if_int_compare:w #2 < ##1
+ \__fp_parse_operand:Nw ##1
+ \else:
+ \__fp_parse_operand:Nw #2
+ \fi:
+ \__fp_parse_expand:w
+ }
+ }
+\__fp_tmp:w - \c__fp_prec_not_int \__fp_set_sign_o:w 2
+\__fp_tmp:w ! \c__fp_prec_not_int \__fp_not_o:w ?
+\cs_new:cpn { __fp_parse_prefix_.:Nw } #1
+ {
+ \exp_after:wN \__fp_parse_infix_after_operand:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN \__fp_sanitize:wN
+ \int_value:w \__fp_int_eval:w 0 \__fp_parse_strim_zeros:N
+ }
+\cs_new:cpn { __fp_parse_prefix_(:Nw } #1
+ {
+ \exp_after:wN \__fp_parse_lparen_after:NwN
+ \exp_after:wN #1
+ \exp:w
+ \if_int_compare:w #1 = \c__fp_prec_func_int
+ \__fp_parse_operand:Nw \c__fp_prec_comma_int
+ \else:
+ \__fp_parse_operand:Nw \c__fp_prec_tuple_int
+ \fi:
+ \__fp_parse_expand:w
+ }
+\cs_new:Npe \__fp_parse_lparen_after:NwN #1#2 @ #3
+ {
+ \exp_not:N \token_if_eq_meaning:NNTF #3
+ \exp_not:c { __fp_parse_infix_):N }
+ {
+ \exp_not:N \__fp_exp_after_array_f:w #2 \s__fp_expr_stop
+ \exp_not:N \exp_after:wN
+ \exp_not:N \__fp_parse_infix_after_paren:NN
+ \exp_not:N \exp_after:wN #1
+ \exp_not:N \exp:w
+ \exp_not:N \__fp_parse_expand:w
+ }
+ {
+ \exp_not:N \msg_expandable_error:nnn
+ { fp } { missing } { ) }
+ \exp_not:N \tl_if_empty:nT {#2} \exp_not:N \c__fp_empty_tuple_fp
+ #2 @
+ \exp_not:N \use_none:n #3
+ }
+ }
+\cs_new:cpn { __fp_parse_prefix_):Nw } #1
+ {
+ \if_int_compare:w #1 = \c__fp_prec_comma_int
+ \else:
+ \if_int_compare:w #1 = \c__fp_prec_tuple_int
+ \exp_after:wN \c__fp_empty_tuple_fp \exp:w
+ \else:
+ \msg_expandable_error:nnn
+ { fp } { missing-number } { ) }
+ \exp_after:wN \c_nan_fp \exp:w
+ \fi:
+ \exp_end_continue_f:w
+ \fi:
+ \__fp_parse_infix_after_paren:NN #1 )
+ }
+\cs_set_protected:Npn \__fp_tmp:w #1 #2
+ {
+ \cs_new:cpn { __fp_parse_word_#1:N }
+ { \exp_after:wN #2 \exp:w \exp_end_continue_f:w \__fp_parse_infix:NN }
+ }
+\__fp_tmp:w { inf } \c_inf_fp
+\__fp_tmp:w { nan } \c_nan_fp
+\__fp_tmp:w { pi } \c_pi_fp
+\__fp_tmp:w { deg } \c_one_degree_fp
+\__fp_tmp:w { true } \c_one_fp
+\__fp_tmp:w { false } \c_zero_fp
+\cs_new_eq:NN \__fp_parse_caseless_inf:N \__fp_parse_word_inf:N
+\cs_new_eq:NN \__fp_parse_caseless_infinity:N \__fp_parse_word_inf:N
+\cs_new_eq:NN \__fp_parse_caseless_nan:N \__fp_parse_word_nan:N
+\cs_set_protected:Npn \__fp_tmp:w #1 #2
+ {
+ \cs_new:cpn { __fp_parse_word_#1:N }
+ {
+ \__fp_exp_after_f:nw { \__fp_parse_infix:NN }
+ \s__fp \__fp_chk:w 10 #2 ;
+ }
+ }
+\__fp_tmp:w {pt} { {1} {1000} {0000} {0000} {0000} }
+\__fp_tmp:w {in} { {2} {7227} {0000} {0000} {0000} }
+\__fp_tmp:w {pc} { {2} {1200} {0000} {0000} {0000} }
+\__fp_tmp:w {cm} { {2} {2845} {2755} {9055} {1181} }
+\__fp_tmp:w {mm} { {1} {2845} {2755} {9055} {1181} }
+\__fp_tmp:w {dd} { {1} {1070} {0085} {6496} {0630} }
+\__fp_tmp:w {cc} { {2} {1284} {0102} {7795} {2756} }
+\__fp_tmp:w {nd} { {1} {1066} {9783} {4645} {6693} }
+\__fp_tmp:w {nc} { {2} {1280} {3740} {1574} {8031} }
+\__fp_tmp:w {bp} { {1} {1003} {7500} {0000} {0000} }
+\__fp_tmp:w {sp} { {-4} {1525} {8789} {0625} {0000} }
+\tl_map_inline:nn { {em} {ex} }
+ {
+ \cs_new:cpn { __fp_parse_word_#1:N }
+ {
+ \exp_after:wN \__fp_from_dim_test:ww
+ \exp_after:wN 0 \exp_after:wN ,
+ \int_value:w \dim_to_decimal_in_sp:n { 1 #1 } \exp_after:wN ;
+ \exp:w \exp_end_continue_f:w \__fp_parse_infix:NN
+ }
+ }
+\cs_new:Npn \__fp_parse_unary_function:NNN #1#2#3
+ {
+ \exp_after:wN \__fp_parse_apply_unary:NNNwN
+ \exp_after:wN #3
+ \exp_after:wN #2
+ \exp_after:wN #1
+ \exp:w
+ \__fp_parse_operand:Nw \c__fp_prec_func_int \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_function:NNN #1#2#3
+ {
+ \exp_after:wN \__fp_parse_apply_function:NNNwN
+ \exp_after:wN #3
+ \exp_after:wN #2
+ \exp_after:wN #1
+ \exp:w
+ \__fp_parse_operand:Nw \c__fp_prec_func_int \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse:n #1
+ {
+ \exp:w
+ \exp_after:wN \__fp_parse_after:ww
+ \exp:w
+ \__fp_parse_operand:Nw \c__fp_prec_end_int
+ \__fp_parse_expand:w #1
+ \s__fp_expr_mark \__fp_parse_infix_end:N
+ \s__fp_expr_stop
+ \exp_end:
+ }
+\cs_new:Npn \__fp_parse_after:ww
+ #1@ \__fp_parse_infix_end:N \s__fp_expr_stop #2 { #2 #1 }
+\cs_new:Npn \__fp_parse_o:n #1
+ {
+ \exp:w
+ \exp_after:wN \__fp_parse_after:ww
+ \exp:w
+ \__fp_parse_operand:Nw \c__fp_prec_end_int
+ \__fp_parse_expand:w #1
+ \s__fp_expr_mark \__fp_parse_infix_end:N
+ \s__fp_expr_stop
+ {
+ \exp_end_continue_f:w
+ \__fp_exp_after_any_f:nw { \exp_after:wN \exp_stop_f: }
+ }
+ }
+\cs_new:Npn \__fp_parse_operand:Nw #1
+ {
+ \exp_end_continue_f:w
+ \exp_after:wN \__fp_parse_continue:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN \__fp_parse_one:Nw
+ \exp_after:wN #1
+ \exp:w
+ }
+\cs_new:Npn \__fp_parse_continue:NwN #1 #2 @ #3 { #3 #1 #2 @ }
+\cs_new:Npn \__fp_parse_apply_binary:NwNwN #1 #2#3@ #4 #5#6@ #7
+ {
+ \exp_after:wN \__fp_parse_continue:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN \__fp_parse_apply_binary_chk:NN
+ \cs:w
+ __fp
+ \__fp_type_from_scan:N #2
+ _#4
+ \__fp_type_from_scan:N #5
+ _o:ww
+ \cs_end:
+ #4
+ #2#3 #5#6
+ \exp:w \exp_end_continue_f:w #7 #1
+ }
+\cs_new:Npn \__fp_parse_apply_binary_chk:NN #1#2
+ {
+ \if_meaning:w \scan_stop: #1
+ \__fp_parse_apply_binary_error:NNN #2
+ \fi:
+ #1
+ }
+\cs_new:Npn \__fp_parse_apply_binary_error:NNN #1#2#3
+ {
+ #2
+ \__fp_invalid_operation_o:Nww #1
+ }
+\cs_new:Npn \__fp_binary_type_o:Nww #1 #2#3 ; #4
+ {
+ \exp_after:wN \__fp_parse_apply_binary_chk:NN
+ \cs:w
+ __fp
+ \__fp_type_from_scan:N #2
+ _ #1
+ \__fp_type_from_scan:N #4
+ _o:ww
+ \cs_end:
+ #1
+ #2 #3 ; #4
+ }
+\cs_new:Npn \__fp_binary_rev_type_o:Nww #1 #2#3 ; #4#5 ;
+ {
+ \exp_after:wN \__fp_parse_apply_binary_chk:NN
+ \cs:w
+ __fp
+ \__fp_type_from_scan:N #4
+ _ #1
+ \__fp_type_from_scan:N #2
+ _o:ww
+ \cs_end:
+ #1
+ #4 #5 ; #2 #3 ;
+ }
+\cs_new:Npn \__fp_parse_infix_after_operand:NwN #1 #2;
+ {
+ \__fp_exp_after_f:nw { \__fp_parse_infix:NN #1 }
+ #2;
+ }
+\cs_new:Npn \__fp_parse_infix:NN #1 #2
+ {
+ \if_catcode:w \scan_stop: \exp_not:N #2
+ \if:w 0 \__fp_str_if_eq:nn { \s__fp_expr_mark } { \exp_not:N #2 }
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__fp_parse_infix_mark:NNN
+ \else:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__fp_parse_infix_juxt:N
+ \fi:
+ \else:
+ \if_int_compare:w
+ \__fp_int_eval:w
+ ( `#2 \if_int_compare:w `#2 > `Z - 32 \fi: ) / 26
+ = 3 \exp_stop_f:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__fp_parse_infix_juxt:N
+ \else:
+ \exp_after:wN \__fp_parse_infix_check:NNN
+ \cs:w
+ __fp_parse_infix_ \token_to_str:N #2 :N
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \cs_end:
+ \fi:
+ \fi:
+ #1
+ #2
+ }
+\cs_new:Npn \__fp_parse_infix_check:NNN #1#2#3
+ {
+ \if_meaning:w \scan_stop: #1
+ \msg_expandable_error:nnn
+ { fp } { missing } { * }
+ \exp_after:wN \__fp_parse_infix_mul:N
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \else:
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp:w \exp_after:wN \__fp_parse_expand:w
+ \fi:
+ }
+\cs_new:Npn \__fp_parse_infix_after_paren:NN #1 #2
+ {
+ \if_catcode:w \scan_stop: \exp_not:N #2
+ \if:w 0 \__fp_str_if_eq:nn { \s__fp_expr_mark } { \exp_not:N #2 }
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__fp_parse_infix_mark:NNN
+ \else:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__fp_parse_infix_mul:N
+ \fi:
+ \else:
+ \if_int_compare:w
+ \__fp_int_eval:w
+ ( `#2 \if_int_compare:w `#2 > `Z - 32 \fi: ) / 26
+ = 3 \exp_stop_f:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__fp_parse_infix_mul:N
+ \else:
+ \exp_after:wN \__fp_parse_infix_check:NNN
+ \cs:w
+ __fp_parse_infix_ \token_to_str:N #2 :N
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \cs_end:
+ \fi:
+ \fi:
+ #1
+ #2
+ }
+\cs_new:Npn \__fp_parse_infix_mark:NNN #1#2#3 { #3 #1 }
+\cs_new:Npn \__fp_parse_infix_end:N #1
+ { @ \use_none:n \__fp_parse_infix_end:N }
+\cs_set_protected:Npn \__fp_tmp:w #1
+ {
+ \cs_new:Npn #1 ##1
+ {
+ \if_int_compare:w ##1 > \c__fp_prec_end_int
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN #1
+ \else:
+ \msg_expandable_error:nnn { fp } { extra } { ) }
+ \exp_after:wN \__fp_parse_infix:NN
+ \exp_after:wN ##1
+ \exp:w \exp_after:wN \__fp_parse_expand:w
+ \fi:
+ }
+ }
+\exp_args:Nc \__fp_tmp:w { __fp_parse_infix_):N }
+\cs_set_protected:Npn \__fp_tmp:w #1
+ {
+ \cs_new:Npn #1 ##1
+ {
+ \if_int_compare:w ##1 > \c__fp_prec_comma_int
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN #1
+ \else:
+ \if_int_compare:w ##1 < \c__fp_prec_comma_int
+ \exp_after:wN @
+ \exp_after:wN \__fp_parse_apply_comma:NwNwN
+ \exp_after:wN ,
+ \exp:w
+ \else:
+ \exp_after:wN \__fp_parse_infix_comma:w
+ \exp:w
+ \fi:
+ \__fp_parse_operand:Nw \c__fp_prec_comma_int
+ \exp_after:wN \__fp_parse_expand:w
+ \fi:
+ }
+ }
+\exp_args:Nc \__fp_tmp:w { __fp_parse_infix_,:N }
+\cs_new:Npn \__fp_parse_infix_comma:w #1 @
+ { #1 @ \use_none:n }
+\cs_new:Npn \__fp_parse_apply_comma:NwNwN #1 #2@ #3 #4@ #5
+ {
+ \exp_after:wN \__fp_parse_continue:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \__fp_exp_after_tuple_f:nw { }
+ \s__fp_tuple \__fp_tuple_chk:w { #2 #4 } ;
+ #5 #1
+ }
+\cs_set_protected:Npn \__fp_tmp:w #1#2#3#4
+ {
+ \cs_new:Npn #1 ##1
+ {
+ \if_int_compare:w ##1 < #3
+ \exp_after:wN @
+ \exp_after:wN \__fp_parse_apply_binary:NwNwN
+ \exp_after:wN #2
+ \exp:w
+ \__fp_parse_operand:Nw #4
+ \exp_after:wN \__fp_parse_expand:w
+ \else:
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN #1
+ \fi:
+ }
+ }
+\exp_args:Nc \__fp_tmp:w { __fp_parse_infix_^:N } ^
+ \c__fp_prec_hatii_int \c__fp_prec_hat_int
+\exp_args:Nc \__fp_tmp:w { __fp_parse_infix_juxt:N } *
+ \c__fp_prec_juxt_int \c__fp_prec_juxt_int
+\exp_args:Nc \__fp_tmp:w { __fp_parse_infix_/:N } /
+ \c__fp_prec_times_int \c__fp_prec_times_int
+\exp_args:Nc \__fp_tmp:w { __fp_parse_infix_mul:N } *
+ \c__fp_prec_times_int \c__fp_prec_times_int
+\exp_args:Nc \__fp_tmp:w { __fp_parse_infix_-:N } -
+ \c__fp_prec_plus_int \c__fp_prec_plus_int
+\exp_args:Nc \__fp_tmp:w { __fp_parse_infix_+:N } +
+ \c__fp_prec_plus_int \c__fp_prec_plus_int
+\exp_args:Nc \__fp_tmp:w { __fp_parse_infix_and:N } &
+ \c__fp_prec_and_int \c__fp_prec_and_int
+\exp_args:Nc \__fp_tmp:w { __fp_parse_infix_or:N } |
+ \c__fp_prec_or_int \c__fp_prec_or_int
+\cs_new:cpn { __fp_parse_infix_(:N } #1
+ { \__fp_parse_infix_mul:N #1 ( }
+\cs_set_protected:Npn \__fp_tmp:w #1
+ {
+ \cs_new:cpn { __fp_parse_infix_*:N } ##1##2
+ {
+ \if:w * \exp_not:N ##2
+ \exp_after:wN #1
+ \exp_after:wN ##1
+ \else:
+ \exp_after:wN \__fp_parse_infix_mul:N
+ \exp_after:wN ##1
+ \exp_after:wN ##2
+ \fi:
+ }
+ }
+\exp_args:Nc \__fp_tmp:w { __fp_parse_infix_^:N }
+\cs_set_protected:Npn \__fp_tmp:w #1#2#3
+ {
+ \cs_new:Npn #1 ##1##2
+ {
+ \if:w #2 \exp_not:N ##2
+ \exp_after:wN #1
+ \exp_after:wN ##1
+ \exp:w \exp_after:wN \__fp_parse_expand:w
+ \else:
+ \exp_after:wN #3
+ \exp_after:wN ##1
+ \exp_after:wN ##2
+ \fi:
+ }
+ }
+\exp_args:Nc \__fp_tmp:w { __fp_parse_infix_|:N } | \__fp_parse_infix_or:N
+\exp_args:Nc \__fp_tmp:w { __fp_parse_infix_&:N } & \__fp_parse_infix_and:N
+\cs_set_protected:Npn \__fp_tmp:w #1#2#3#4
+ {
+ \cs_new:Npn #1 ##1
+ {
+ \if_int_compare:w ##1 < \c__fp_prec_quest_int
+ #4
+ \exp_after:wN @
+ \exp_after:wN #2
+ \exp:w
+ \__fp_parse_operand:Nw #3
+ \exp_after:wN \__fp_parse_expand:w
+ \else:
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN #1
+ \fi:
+ }
+ }
+\exp_args:Nc \__fp_tmp:w { __fp_parse_infix_?:N }
+ \__fp_ternary:NwwN \c__fp_prec_quest_int { }
+\exp_args:Nc \__fp_tmp:w { __fp_parse_infix_::N }
+ \__fp_ternary_auxii:NwwN \c__fp_prec_colon_int
+ {
+ \msg_expandable_error:nnnn
+ { fp } { missing } { ? } { ~for~?: }
+ }
+\cs_new:cpn { __fp_parse_infix_<:N } #1
+ { \__fp_parse_compare:NNNNNNN #1 1 0 0 0 0 < }
+\cs_new:cpn { __fp_parse_infix_=:N } #1
+ { \__fp_parse_compare:NNNNNNN #1 1 0 0 0 0 = }
+\cs_new:cpn { __fp_parse_infix_>:N } #1
+ { \__fp_parse_compare:NNNNNNN #1 1 0 0 0 0 > }
+\cs_new:cpn { __fp_parse_infix_!:N } #1
+ {
+ \exp_after:wN \__fp_parse_compare:NNNNNNN
+ \exp_after:wN #1
+ \exp_after:wN 0
+ \exp_after:wN 1
+ \exp_after:wN 1
+ \exp_after:wN 1
+ \exp_after:wN 1
+ }
+\cs_new:Npn \__fp_parse_excl_error:
+ {
+ \msg_expandable_error:nnnn
+ { fp } { missing } { = } { ~after~!. }
+ }
+\cs_new:Npn \__fp_parse_compare:NNNNNNN #1
+ {
+ \if_int_compare:w #1 < \c__fp_prec_comp_int
+ \exp_after:wN \__fp_parse_compare_auxi:NNNNNNN
+ \exp_after:wN \__fp_parse_excl_error:
+ \else:
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN \__fp_parse_compare:NNNNNNN
+ \fi:
+ }
+\cs_new:Npn \__fp_parse_compare_auxi:NNNNNNN #1#2#3#4#5#6#7
+ {
+ \if_case:w
+ \__fp_int_eval:w \exp_after:wN ` \token_to_str:N #7 - `<
+ \__fp_int_eval_end:
+ \__fp_parse_compare_auxii:NNNNN #2#2#4#5#6
+ \or: \__fp_parse_compare_auxii:NNNNN #2#3#2#5#6
+ \or: \__fp_parse_compare_auxii:NNNNN #2#3#4#2#6
+ \or: \__fp_parse_compare_auxii:NNNNN #2#3#4#5#2
+ \else: #1 \__fp_parse_compare_end:NNNNw #3#4#5#6#7
+ \fi:
+ }
+\cs_new:Npn \__fp_parse_compare_auxii:NNNNN #1#2#3#4#5
+ {
+ \exp_after:wN \__fp_parse_compare_auxi:NNNNNNN
+ \exp_after:wN \prg_do_nothing:
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN #4
+ \exp_after:wN #5
+ \exp:w \exp_after:wN \__fp_parse_expand:w
+ }
+\cs_new:Npn \__fp_parse_compare_end:NNNNw #1#2#3#4#5 \fi:
+ {
+ \fi:
+ \exp_after:wN @
+ \exp_after:wN \__fp_parse_apply_compare:NwNNNNNwN
+ \exp_after:wN \c_one_fp
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN #4
+ \exp:w
+ \__fp_parse_operand:Nw \c__fp_prec_comp_int \__fp_parse_expand:w #5
+ }
+\cs_new:Npn \__fp_parse_apply_compare:NwNNNNNwN
+ #1 #2@ #3 #4#5#6#7 #8@ #9
+ {
+ \if_int_odd:w
+ \if_meaning:w \c_zero_fp #3
+ 0
+ \else:
+ \if_case:w \__fp_compare_back_any:ww #8 #2 \exp_stop_f:
+ #5 \or: #6 \or: #7 \else: #4
+ \fi:
+ \fi:
+ \exp_stop_f:
+ \exp_after:wN \__fp_parse_apply_compare_aux:NNwN
+ \exp_after:wN \c_one_fp
+ \else:
+ \exp_after:wN \__fp_parse_apply_compare_aux:NNwN
+ \exp_after:wN \c_zero_fp
+ \fi:
+ #1 #8 #9
+ }
+\cs_new:Npn \__fp_parse_apply_compare_aux:NNwN #1 #2 #3; #4
+ {
+ \if_meaning:w \__fp_parse_compare:NNNNNNN #4
+ \exp_after:wN \__fp_parse_continue_compare:NNwNN
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp:w \exp_end_continue_f:w
+ \__fp_exp_after_o:w #3;
+ \exp:w \exp_end_continue_f:w
+ \else:
+ \exp_after:wN \__fp_parse_continue:NwN
+ \exp_after:wN #2
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \fi:
+ #4 #2
+ }
+\cs_new:Npn \__fp_parse_continue_compare:NNwNN #1#2 #3@ #4#5
+ { #4 #2 #3@ #1 }
+\cs_new:Npn \__fp_parse_function_all_fp_o:fnw #1#2#3 @
+ {
+ \__fp_array_if_all_fp:nTF {#3}
+ { #2 #3 @ }
+ {
+ \__fp_error:nffn { bad-args }
+ {#1}
+ { \fp_to_tl:n { \s__fp_tuple \__fp_tuple_chk:w {#3} ; } }
+ { }
+ \exp_after:wN \c_nan_fp
+ }
+ }
+\cs_new:Npn \__fp_parse_function_one_two:nnw #1#2#3
+ {
+ \__fp_if_type_fp:NTwFw
+ #3 { } \s__fp \__fp_parse_function_one_two_error_o:w \s__fp_stop
+ \__fp_parse_function_one_two_aux:nnw {#1} {#2} #3
+ }
+\cs_new:Npn \__fp_parse_function_one_two_error_o:w #1#2#3#4 @
+ {
+ \__fp_error:nffn { bad-args }
+ {#2}
+ { \fp_to_tl:n { \s__fp_tuple \__fp_tuple_chk:w {#4} ; } }
+ { }
+ \exp_after:wN \c_nan_fp
+ }
+\cs_new:Npn \__fp_parse_function_one_two_aux:nnw #1#2 #3; #4
+ {
+ \__fp_if_type_fp:NTwFw
+ #4 { }
+ \s__fp
+ {
+ \if_meaning:w @ #4
+ \exp_after:wN \use_iv:nnnn
+ \fi:
+ \__fp_parse_function_one_two_error_o:w
+ }
+ \s__fp_stop
+ \__fp_parse_function_one_two_auxii:nnw {#1} {#2} #3; #4
+ }
+\cs_new:Npn \__fp_parse_function_one_two_auxii:nnw #1#2#3; #4; #5
+ {
+ \if_meaning:w @ #5 \else:
+ \exp_after:wN \__fp_parse_function_one_two_error_o:w
+ \fi:
+ \use_ii:nn {#1} { \use_none:n #2 } #3; #4; #5
+ }
+\cs_new:Npn \__fp_tuple_map_o:nw #1 \s__fp_tuple \__fp_tuple_chk:w #2 ;
+ {
+ \exp_after:wN \s__fp_tuple
+ \exp_after:wN \__fp_tuple_chk:w
+ \exp_after:wN {
+ \exp:w \exp_end_continue_f:w
+ \__fp_tuple_map_loop_o:nw {#1} #2
+ { \s__fp \prg_break: } ;
+ \prg_break_point:
+ \exp_after:wN } \exp_after:wN ;
+ }
+\cs_new:Npn \__fp_tuple_map_loop_o:nw #1#2#3 ;
+ {
+ \use_none:n #2
+ #1 #2 #3 ;
+ \exp:w \exp_end_continue_f:w
+ \__fp_tuple_map_loop_o:nw {#1}
+ }
+\cs_new:Npn \__fp_tuple_mapthread_o:nww #1
+ \s__fp_tuple \__fp_tuple_chk:w #2 ;
+ \s__fp_tuple \__fp_tuple_chk:w #3 ;
+ {
+ \exp_after:wN \s__fp_tuple
+ \exp_after:wN \__fp_tuple_chk:w
+ \exp_after:wN {
+ \exp:w \exp_end_continue_f:w
+ \__fp_tuple_mapthread_loop_o:nw {#1}
+ #2 { \s__fp \prg_break: } ; @
+ #3 { \s__fp \prg_break: } ;
+ \prg_break_point:
+ \exp_after:wN } \exp_after:wN ;
+ }
+\cs_new:Npn \__fp_tuple_mapthread_loop_o:nw #1#2#3 ; #4 @ #5#6 ;
+ {
+ \use_none:n #2
+ \use_none:n #5
+ #1 #2 #3 ; #5 #6 ;
+ \exp:w \exp_end_continue_f:w
+ \__fp_tuple_mapthread_loop_o:nw {#1} #4 @
+ }
+\msg_new:nnn { fp } { deprecated }
+ { '#1'~deprecated;~use~'#2' }
+\msg_new:nnn { fp } { unknown-fp-word }
+ { Unknown~fp~word~#1. }
+\msg_new:nnn { fp } { missing }
+ { Missing~#1~inserted #2. }
+\msg_new:nnn { fp } { extra }
+ { Extra~#1~ignored. }
+\msg_new:nnn { fp } { early-end }
+ { Premature~end~in~fp~expression. }
+\msg_new:nnn { fp } { after-e }
+ { Cannot~use~#1 after~'e'. }
+\msg_new:nnn { fp } { missing-number }
+ { Missing~number~before~'#1'. }
+\msg_new:nnn { fp } { unknown-symbol }
+ { Unknown~symbol~#1~ignored. }
+\msg_new:nnn { fp } { extra-comma }
+ { Unexpected~comma~turned~to~nan~result. }
+\msg_new:nnn { fp } { no-arg }
+ { #1~got~no~argument;~used~nan. }
+\msg_new:nnn { fp } { multi-arg }
+ { #1~got~more~than~one~argument;~used~nan. }
+\msg_new:nnn { fp } { num-args }
+ { #1~expects~between~#2~and~#3~arguments. }
+\msg_new:nnn { fp } { bad-args }
+ { Arguments~in~#1#2~are~invalid. }
+\msg_new:nnn { fp } { infty-pi }
+ { Math~command~#1 is~not~an~fp }
+\cs_if_exist:cT { @unexpandable@protect }
+ {
+ \msg_new:nnn { fp } { robust-cmd }
+ { Robust~command~#1 invalid~in~fp~expression! }
+ }
+%% File: l3fp-assign.dtx
+\cs_new_protected:Npn \fp_new:N #1
+ { \cs_new_eq:NN #1 \c_zero_fp }
+\cs_generate_variant:Nn \fp_new:N {c}
+\cs_new_protected:Npn \fp_set:Nn #1#2
+ { \__kernel_tl_set:Nx #1 { \exp_not:f { \__fp_parse:n {#2} } } }
+\cs_new_protected:Npn \fp_gset:Nn #1#2
+ { \__kernel_tl_gset:Nx #1 { \exp_not:f { \__fp_parse:n {#2} } } }
+\cs_new_protected:Npn \fp_const:Nn #1#2
+ { \tl_const:Ne #1 { \exp_not:f { \__fp_parse:n {#2} } } }
+\cs_generate_variant:Nn \fp_set:Nn {c}
+\cs_generate_variant:Nn \fp_gset:Nn {c}
+\cs_generate_variant:Nn \fp_const:Nn {c}
+\cs_new_eq:NN \fp_set_eq:NN \tl_set_eq:NN
+\cs_new_eq:NN \fp_gset_eq:NN \tl_gset_eq:NN
+\cs_generate_variant:Nn \fp_set_eq:NN { c , Nc , cc }
+\cs_generate_variant:Nn \fp_gset_eq:NN { c , Nc , cc }
+\cs_new_protected:Npn \fp_zero:N #1 { \fp_set_eq:NN #1 \c_zero_fp }
+\cs_new_protected:Npn \fp_gzero:N #1 { \fp_gset_eq:NN #1 \c_zero_fp }
+\cs_generate_variant:Nn \fp_zero:N { c }
+\cs_generate_variant:Nn \fp_gzero:N { c }
+\cs_new_protected:Npn \fp_zero_new:N #1
+ { \fp_if_exist:NTF #1 { \fp_zero:N #1 } { \fp_new:N #1 } }
+\cs_new_protected:Npn \fp_gzero_new:N #1
+ { \fp_if_exist:NTF #1 { \fp_gzero:N #1 } { \fp_new:N #1 } }
+\cs_generate_variant:Nn \fp_zero_new:N { c }
+\cs_generate_variant:Nn \fp_gzero_new:N { c }
+\cs_new_protected:Npn \fp_add:Nn { \__fp_add:NNNn \fp_set:Nn + }
+\cs_new_protected:Npn \fp_gadd:Nn { \__fp_add:NNNn \fp_gset:Nn + }
+\cs_new_protected:Npn \fp_sub:Nn { \__fp_add:NNNn \fp_set:Nn - }
+\cs_new_protected:Npn \fp_gsub:Nn { \__fp_add:NNNn \fp_gset:Nn - }
+\cs_new_protected:Npn \__fp_add:NNNn #1#2#3#4
+ { #1 #3 { #3 #2 \__fp_parse:n {#4} } }
+\cs_generate_variant:Nn \fp_add:Nn { c }
+\cs_generate_variant:Nn \fp_gadd:Nn { c }
+\cs_generate_variant:Nn \fp_sub:Nn { c }
+\cs_generate_variant:Nn \fp_gsub:Nn { c }
+\cs_new_protected:Npn \fp_show:N { \__fp_show:NN \tl_show:n }
+\cs_generate_variant:Nn \fp_show:N { c }
+\cs_new_protected:Npn \fp_log:N { \__fp_show:NN \tl_log:n }
+\cs_generate_variant:Nn \fp_log:N { c }
+\cs_new_protected:Npn \__fp_show:NN #1#2
+ {
+ \__kernel_chk_tl_type:NnnT #2 { fp }
+ { \exp_args:No \__fp_show_validate:n #2 }
+ { \exp_args:Ne #1 { \token_to_str:N #2 = \fp_to_tl:N #2 } }
+ }
+\cs_new:Npn \__fp_show_validate:n #1
+ {
+ \__fp_show_validate:nn { #1 }
+ {
+ \s__fp \__fp_chk:w ??? ;~ or \iow_newline:
+ \s__fp_tuple \__fp_tuple_chk:w ? ;~ or \iow_newline:
+ \s__fp_symbolic \__fp_symbolic_chk:w ? , ? ;
+ }
+ }
+\cs_new:Npn \__fp_show_validate_aux:n #1
+ {
+ \__fp_show_validate:nn { #1 } { }
+ }
+\cs_new:Npn \__fp_show_validate:nn #1#2
+ {
+ \tl_if_empty:nF { #1 }
+ {
+ \str_case:enF { \tl_head:n { #1 } }
+ {
+ { \s__fp }
+ {
+ \__fp_show_validate:w #1 \s__fp
+ \__fp_chk:w ??? ; \s__fp_stop
+ }
+ { \s__fp_tuple }
+ {
+ \__fp_tuple_show_validate:w #1
+ \s__fp_tuple \__fp_tuple_chk:w ?? ; \s__fp_stop
+ }
+ { \s__fp_symbolic }
+ {
+ \__fp_symbolic_show_validate:w #1
+ \s__fp_symbolic \__fp_symbolic_chk:w ? , ?? ; \s__fp_stop
+ }
+ }
+ { #2 }
+ }
+ }
+\cs_new:Npn \__fp_show_validate:w
+ #1 \s__fp \__fp_chk:w #2#3#4#5 ; #6 \s__fp_stop
+ {
+ \str_if_eq:nnF { #2 } {?}
+ {
+ \token_if_eq_meaning:NNTF #2 1
+ { \s__fp \__fp_chk:w #2 #3 { #4 } #5 ; }
+ { \s__fp \__fp_chk:w #2 #3 #4 #5 ; }
+ \__fp_show_validate_aux:n { #6 }
+ }
+ }
+\cs_new:Npn \__fp_tuple_show_validate:w
+ #1 \s__fp_tuple \__fp_tuple_chk:w #2#3 ; #4 \s__fp_stop
+ {
+ \str_if_eq:nnF { #2 } {?}
+ { \s__fp_tuple \__fp_tuple_chk:w { \__fp_show_validate_aux:n { #2 } } ; }
+ }
+\cs_new:Npn \__fp_symbolic_show_validate:w
+ #1 \s__fp_symbolic \__fp_symbolic_chk:w #2 , #3#4 ; #5 \s__fp_stop
+ {
+ \str_if_eq:nnF { #2 } {?}
+ {
+ \s__fp_symbolic \__fp_symbolic_chk:w \exp_not:n { #2 } ,
+ { \__fp_show_validate_aux:n { #3 } };
+ \__fp_show_validate_aux:n { #5 }
+ }
+ }
+\cs_new_protected:Npn \fp_show:n
+ { \__kernel_msg_show_eval:Nn \fp_to_tl:n }
+\cs_new_protected:Npn \fp_log:n
+ { \__kernel_msg_log_eval:Nn \fp_to_tl:n }
+\fp_const:Nn \c_e_fp { 2.718 2818 2845 9045 }
+\fp_const:Nn \c_one_fp { 1 }
+\fp_const:Nn \c_pi_fp { 3.141 5926 5358 9793 }
+\fp_const:Nn \c_one_degree_fp { 0.0 1745 3292 5199 4330 }
+\fp_new:N \l_tmpa_fp
+\fp_new:N \l_tmpb_fp
+\fp_new:N \g_tmpa_fp
+\fp_new:N \g_tmpb_fp
+%% File: l3fp-logic.dtx
+\cs_new:Npn \__fp_parse_word_max:N
+ { \__fp_parse_function:NNN \__fp_minmax_o:Nw 2 }
+\cs_new:Npn \__fp_parse_word_min:N
+ { \__fp_parse_function:NNN \__fp_minmax_o:Nw 0 }
+\prg_new_eq_conditional:NNn \fp_if_exist:N \cs_if_exist:N { TF , T , F , p }
+\prg_new_eq_conditional:NNn \fp_if_exist:c \cs_if_exist:c { TF , T , F , p }
+\prg_new_conditional:Npnn \fp_if_nan:n #1 { TF , T , F , p }
+ {
+ \if:w 3 \exp_last_unbraced:Nf \__fp_kind:w { \__fp_parse:n {#1} }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_new_conditional:Npnn \fp_compare:n #1 { p , T , F , TF }
+ {
+ \exp_after:wN \__fp_compare_return:w
+ \exp:w \exp_end_continue_f:w \__fp_parse:n {#1}
+ }
+\cs_new:Npn \__fp_compare_return:w #1#2#3;
+ {
+ \if_charcode:w 0
+ \__fp_if_type_fp:NTwFw
+ #1 { \__fp_use_i_delimit_by_s_stop:nw #3 \s__fp_stop }
+ \s__fp 1 \s__fp_stop
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ }
+\prg_new_conditional:Npnn \fp_compare:nNn #1#2#3 { p , T , F , TF }
+ {
+ \if_int_compare:w
+ \exp_after:wN \__fp_compare_aux:wn
+ \exp:w \exp_end_continue_f:w \__fp_parse:n {#1} {#3}
+ = \__fp_int_eval:w `#2 - `= \__fp_int_eval_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \__fp_compare_aux:wn #1; #2
+ {
+ \exp_after:wN \__fp_compare_back_any:ww
+ \exp:w \exp_end_continue_f:w \__fp_parse:n {#2} #1;
+ }
+\cs_new:Npn \__fp_compare_back:ww #1#2; #3#4;
+ {
+ \cs:w
+ __fp
+ \__fp_type_from_scan:N #1
+ _bcmp
+ \__fp_type_from_scan:N #3
+ :ww
+ \cs_end:
+ #1#2; #3#4;
+ }
+\cs_new:Npn \__fp_compare_back_any:ww #1#2; #3
+ {
+ \__fp_if_type_fp:NTwFw
+ #1 { \__fp_if_type_fp:NTwFw #3 \use_i:nn \s__fp \use_ii:nn \s__fp_stop }
+ \s__fp \use_ii:nn \s__fp_stop
+ \__fp_compare_back:ww
+ {
+ \cs:w
+ __fp
+ \__fp_type_from_scan:N #1
+ _compare_back
+ \__fp_type_from_scan:N #3
+ :ww
+ \cs_end:
+ }
+ #1#2 ; #3
+ }
+\cs_new:Npn \__fp_bcmp:ww
+ \s__fp \__fp_chk:w #1 #2 #3;
+ \s__fp \__fp_chk:w #4 #5 #6;
+ {
+ \int_value:w
+ \if_meaning:w 3 #1 \exp_after:wN \__fp_compare_nan:w \fi:
+ \if_meaning:w 3 #4 \exp_after:wN \__fp_compare_nan:w \fi:
+ \if_meaning:w 2 #5 - \fi:
+ \if_meaning:w #2 #5
+ \if_meaning:w #1 #4
+ \if_meaning:w 1 #1
+ \__fp_compare_npos:nwnw #6; #3;
+ \else:
+ 0
+ \fi:
+ \else:
+ \if_int_compare:w #4 < #1 - \fi: 1
+ \fi:
+ \else:
+ \if_int_compare:w #1#4 = \c_zero_int
+ 0
+ \else:
+ 1
+ \fi:
+ \fi:
+ \exp_stop_f:
+ }
+\cs_new:Npn \__fp_compare_nan:w #1 \fi: \exp_stop_f: { 2 \exp_stop_f: }
+\cs_new:Npn \__fp_compare_back_tuple:ww #1; #2; { 2 }
+\cs_new:Npn \__fp_tuple_compare_back:ww #1; #2; { 2 }
+\cs_new:Npn \__fp_tuple_compare_back_tuple:ww
+ \s__fp_tuple \__fp_tuple_chk:w #1;
+ \s__fp_tuple \__fp_tuple_chk:w #2;
+ {
+ \int_compare:nNnTF { \__fp_array_count:n {#1} } =
+ { \__fp_array_count:n {#2} }
+ {
+ \int_value:w 0
+ \__fp_tuple_compare_back_loop:w
+ #1 { \s__fp \prg_break: } ; @
+ #2 { \s__fp \prg_break: } ;
+ \prg_break_point:
+ \exp_stop_f:
+ }
+ { 2 }
+ }
+\cs_new:Npn \__fp_tuple_compare_back_loop:w #1#2 ; #3 @ #4#5 ;
+ {
+ \use_none:n #1
+ \use_none:n #4
+ \if_int_compare:w
+ \__fp_compare_back_any:ww #1 #2 ; #4 #5 ; = \c_zero_int
+ \else:
+ 2 \exp_after:wN \prg_break:
+ \fi:
+ \__fp_tuple_compare_back_loop:w #3 @
+ }
+\cs_new:Npn \__fp_compare_npos:nwnw #1#2; #3#4;
+ {
+ \if_int_compare:w #1 = #3 \exp_stop_f:
+ \__fp_compare_significand:nnnnnnnn #2 #4
+ \else:
+ \if_int_compare:w #1 < #3 - \fi: 1
+ \fi:
+ }
+\cs_new:Npn \__fp_compare_significand:nnnnnnnn #1#2#3#4#5#6#7#8
+ {
+ \if_int_compare:w #1#2 = #5#6 \exp_stop_f:
+ \if_int_compare:w #3#4 = #7#8 \exp_stop_f:
+ 0
+ \else:
+ \if_int_compare:w #3#4 < #7#8 - \fi: 1
+ \fi:
+ \else:
+ \if_int_compare:w #1#2 < #5#6 - \fi: 1
+ \fi:
+ }
+\cs_new:Npn \fp_do_until:nn #1#2
+ {
+ #2
+ \fp_compare:nF {#1}
+ { \fp_do_until:nn {#1} {#2} }
+ }
+\cs_new:Npn \fp_do_while:nn #1#2
+ {
+ #2
+ \fp_compare:nT {#1}
+ { \fp_do_while:nn {#1} {#2} }
+ }
+\cs_new:Npn \fp_until_do:nn #1#2
+ {
+ \fp_compare:nF {#1}
+ {
+ #2
+ \fp_until_do:nn {#1} {#2}
+ }
+ }
+\cs_new:Npn \fp_while_do:nn #1#2
+ {
+ \fp_compare:nT {#1}
+ {
+ #2
+ \fp_while_do:nn {#1} {#2}
+ }
+ }
+\cs_new:Npn \fp_do_until:nNnn #1#2#3#4
+ {
+ #4
+ \fp_compare:nNnF {#1} #2 {#3}
+ { \fp_do_until:nNnn {#1} #2 {#3} {#4} }
+ }
+\cs_new:Npn \fp_do_while:nNnn #1#2#3#4
+ {
+ #4
+ \fp_compare:nNnT {#1} #2 {#3}
+ { \fp_do_while:nNnn {#1} #2 {#3} {#4} }
+ }
+\cs_new:Npn \fp_until_do:nNnn #1#2#3#4
+ {
+ \fp_compare:nNnF {#1} #2 {#3}
+ {
+ #4
+ \fp_until_do:nNnn {#1} #2 {#3} {#4}
+ }
+ }
+\cs_new:Npn \fp_while_do:nNnn #1#2#3#4
+ {
+ \fp_compare:nNnT {#1} #2 {#3}
+ {
+ #4
+ \fp_while_do:nNnn {#1} #2 {#3} {#4}
+ }
+ }
+\cs_new:Npn \fp_step_function:nnnN #1#2#3
+ {
+ \exp_after:wN \__fp_step:wwwN
+ \exp:w \exp_end_continue_f:w \__fp_parse_o:n {#1}
+ \exp:w \exp_end_continue_f:w \__fp_parse_o:n {#2}
+ \exp:w \exp_end_continue_f:w \__fp_parse:n {#3}
+ }
+\cs_generate_variant:Nn \fp_step_function:nnnN { nnnc }
+\cs_new:Npn \__fp_step:wwwN #1#2; #3#4; #5#6; #7
+ {
+ \__fp_if_type_fp:NTwFw #1 { } \s__fp \prg_break: \s__fp_stop
+ \__fp_if_type_fp:NTwFw #3 { } \s__fp \prg_break: \s__fp_stop
+ \__fp_if_type_fp:NTwFw #5 { } \s__fp \prg_break: \s__fp_stop
+ \use_i:nnnn { \__fp_step_fp:wwwN #1#2; #3#4; #5#6; #7 }
+ \prg_break_point:
+ \use:n
+ {
+ \__fp_error:nfff { step-tuple } { \fp_to_tl:n { #1#2 ; } }
+ { \fp_to_tl:n { #3#4 ; } } { \fp_to_tl:n { #5#6 ; } }
+ }
+ }
+\cs_new:Npn \__fp_step_fp:wwwN #1 ; \s__fp \__fp_chk:w #2#3#4 ; #5; #6
+ {
+ \token_if_eq_meaning:NNTF #2 1
+ {
+ \token_if_eq_meaning:NNTF #3 0
+ { \__fp_step:NnnnnN > }
+ { \__fp_step:NnnnnN < }
+ }
+ {
+ \token_if_eq_meaning:NNTF #2 0
+ {
+ \msg_expandable_error:nnn { kernel }
+ { zero-step } {#6}
+ }
+ {
+ \__fp_error:nnfn { bad-step } { }
+ { \fp_to_tl:n { \s__fp \__fp_chk:w #2#3#4 ; } } {#6}
+ }
+ \use_none:nnnnn
+ }
+ { #1 ; } { \c_nan_fp } { \s__fp \__fp_chk:w #2#3#4 ; } { #5 ; } #6
+ }
+\cs_new:Npn \__fp_step:NnnnnN #1#2#3#4#5#6
+ {
+ \fp_compare:nNnTF {#2} = {#3}
+ {
+ \__fp_error:nffn { tiny-step }
+ { \fp_to_tl:n {#3} } { \fp_to_tl:n {#4} } {#6}
+ }
+ {
+ \fp_compare:nNnF {#2} #1 {#5}
+ {
+ \exp_args:Nf #6 { \__fp_to_decimal_dispatch:w #2 }
+ \__fp_step:NfnnnN
+ #1 { \__fp_parse:n { #2 + #4 } } {#2} {#4} {#5} #6
+ }
+ }
+ }
+\cs_generate_variant:Nn \__fp_step:NnnnnN { Nf }
+\cs_new_protected:Npn \fp_step_inline:nnnn
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:NNc \__fp_step:NNnnnn
+ \cs_gset_protected:Npn
+ { __fp_map_ \int_use:N \g__kernel_prg_map_int :w }
+ }
+\cs_new_protected:Npn \fp_step_variable:nnnNn #1#2#3#4#5
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:NNc \__fp_step:NNnnnn
+ \cs_gset_protected:Npe
+ { __fp_map_ \int_use:N \g__kernel_prg_map_int :w }
+ {#1} {#2} {#3}
+ {
+ \tl_set:Nn \exp_not:N #4 {##1}
+ \exp_not:n {#5}
+ }
+ }
+\cs_new_protected:Npn \__fp_step:NNnnnn #1#2#3#4#5#6
+ {
+ #1 #2 ##1 {#6}
+ \fp_step_function:nnnN {#3} {#4} {#5} #2
+ \prg_break_point:Nn \scan_stop: { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+\msg_new:nnn { fp } { step-tuple }
+ { Tuple~argument~in~fp_step_...~{#1}{#2}{#3}. }
+\msg_new:nnn { fp } { bad-step }
+ { Invalid~step~size~#2~for~function~#3. }
+\msg_new:nnn { fp } { tiny-step }
+ { Tiny~step~size~(#1+#2=#1)~for~function~#3. }
+\cs_new:Npn \__fp_minmax_o:Nw #1
+ {
+ \__fp_parse_function_all_fp_o:fnw
+ { \token_if_eq_meaning:NNTF 0 #1 { min } { max } }
+ { \__fp_minmax_aux_o:Nw #1 }
+ }
+\cs_new:Npn \__fp_minmax_aux_o:Nw #1#2 @
+ {
+ \if_meaning:w 0 #1
+ \exp_after:wN \__fp_minmax_loop:Nww \exp_after:wN +
+ \else:
+ \exp_after:wN \__fp_minmax_loop:Nww \exp_after:wN -
+ \fi:
+ #2
+ \s__fp \__fp_chk:w 2 #1 \s__fp_exact ;
+ \s__fp \__fp_chk:w { 3 \__fp_minmax_break_o:w } ;
+ }
+\cs_new:Npn \__fp_minmax_loop:Nww
+ #1 \s__fp \__fp_chk:w #2#3; \s__fp \__fp_chk:w #4#5;
+ {
+ \if_meaning:w 3 #4
+ \if_meaning:w 3 #2
+ \__fp_minmax_auxi:ww
+ \else:
+ \__fp_minmax_auxii:ww
+ \fi:
+ \else:
+ \if_int_compare:w
+ \__fp_compare_back:ww
+ \s__fp \__fp_chk:w #4#5;
+ \s__fp \__fp_chk:w #2#3;
+ = #1 1 \exp_stop_f:
+ \__fp_minmax_auxii:ww
+ \else:
+ \__fp_minmax_auxi:ww
+ \fi:
+ \fi:
+ \__fp_minmax_loop:Nww #1
+ \s__fp \__fp_chk:w #2#3;
+ \s__fp \__fp_chk:w #4#5;
+ }
+\cs_new:Npn \__fp_minmax_auxi:ww #1 \fi: \fi: #2 \s__fp #3 ; \s__fp #4;
+ { \fi: \fi: #2 \s__fp #3 ; }
+\cs_new:Npn \__fp_minmax_auxii:ww #1 \fi: \fi: #2 \s__fp #3 ;
+ { \fi: \fi: #2 }
+\cs_new:Npn \__fp_minmax_break_o:w #1 \fi: \fi: #2 \s__fp #3; #4;
+ { \fi: \__fp_exp_after_o:w \s__fp #3; }
+\cs_new:Npn \__fp_not_o:w #1 \s__fp \__fp_chk:w #2#3; @
+ {
+ \if_meaning:w 0 #2
+ \exp_after:wN \exp_after:wN \exp_after:wN \c_one_fp
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \c_zero_fp
+ \fi:
+ }
+\cs_new:Npn \__fp_tuple_not_o:w #1 @ { \exp_after:wN \c_zero_fp }
+\group_begin:
+ \char_set_catcode_letter:N &
+ \char_set_catcode_letter:N |
+ \cs_new:Npn \__fp_&_o:ww #1 \s__fp \__fp_chk:w #2#3;
+ {
+ \if_meaning:w 0 #2 #1
+ \__fp_and_return:wNw \s__fp \__fp_chk:w #2#3;
+ \fi:
+ \__fp_exp_after_o:w
+ }
+ \cs_new:Npn \__fp_&_tuple_o:ww #1 \s__fp \__fp_chk:w #2#3;
+ {
+ \if_meaning:w 0 #2 #1
+ \__fp_and_return:wNw \s__fp \__fp_chk:w #2#3;
+ \fi:
+ \__fp_exp_after_tuple_o:w
+ }
+ \cs_new:Npn \__fp_tuple_&_o:ww #1; { \__fp_exp_after_o:w }
+ \cs_new:Npn \__fp_tuple_&_tuple_o:ww #1; { \__fp_exp_after_tuple_o:w }
+ \cs_new:Npn \__fp_|_o:ww { \__fp_&_o:ww \else: }
+ \cs_new:Npn \__fp_|_tuple_o:ww { \__fp_&_tuple_o:ww \else: }
+ \cs_new:Npn \__fp_tuple_|_o:ww #1; #2; { \__fp_exp_after_tuple_o:w #1; }
+ \cs_new:Npn \__fp_tuple_|_tuple_o:ww #1; #2;
+ { \__fp_exp_after_tuple_o:w #1; }
+\group_end:
+\cs_new:Npn \__fp_and_return:wNw #1; \fi: #2;
+ { \fi: \__fp_exp_after_o:w #1; }
+\cs_new:Npn \__fp_ternary:NwwN #1 #2#3@ #4@ #5
+ {
+ \if_meaning:w \__fp_parse_infix_::N #5
+ \if_charcode:w 0
+ \__fp_if_type_fp:NTwFw
+ #2 { \use_i:nn \__fp_use_i_delimit_by_s_stop:nw #3 \s__fp_stop }
+ \s__fp 1 \s__fp_stop
+ \exp_after:wN \exp_after:wN \exp_after:wN \__fp_ternary_auxii:NwwN
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \__fp_ternary_auxi:NwwN
+ \fi:
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \__fp_exp_after_array_f:w #4 \s__fp_expr_stop
+ \exp_after:wN @
+ \exp:w
+ \__fp_parse_operand:Nw \c__fp_prec_colon_int
+ \__fp_parse_expand:w
+ \else:
+ \msg_expandable_error:nnnn
+ { fp } { missing } { : } { ~for~?: }
+ \exp_after:wN \__fp_parse_continue:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \__fp_exp_after_array_f:w #4 \s__fp_expr_stop
+ \exp_after:wN #5
+ \exp_after:wN #1
+ \fi:
+ }
+\cs_new:Npn \__fp_ternary_auxi:NwwN #1#2@#3@#4
+ {
+ \exp_after:wN \__fp_parse_continue:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \__fp_exp_after_array_f:w #2 \s__fp_expr_stop
+ #4 #1
+ }
+\cs_new:Npn \__fp_ternary_auxii:NwwN #1#2@#3@#4
+ {
+ \exp_after:wN \__fp_parse_continue:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \__fp_exp_after_array_f:w #3 \s__fp_expr_stop
+ #4 #1
+ }
+%% File: l3fp-basics.dtx
+\cs_new:Npn \__fp_parse_word_abs:N
+ { \__fp_parse_unary_function:NNN \__fp_set_sign_o:w 0 }
+\cs_new:Npn \__fp_parse_word_logb:N
+ { \__fp_parse_unary_function:NNN \__fp_logb_o:w ? }
+\cs_new:Npn \__fp_parse_word_sign:N
+ { \__fp_parse_unary_function:NNN \__fp_sign_o:w ? }
+\cs_new:Npn \__fp_parse_word_sqrt:N
+ { \__fp_parse_unary_function:NNN \__fp_sqrt_o:w ? }
+\cs_new:cpe { __fp_-_o:ww } \s__fp
+ {
+ \exp_not:c { __fp_+_o:ww }
+ \exp_not:n { \s__fp \__fp_neg_sign:N }
+ }
+\cs_new:cpn { __fp_+_o:ww }
+ \s__fp #1 \__fp_chk:w #2 #3 ; \s__fp \__fp_chk:w #4 #5
+ {
+ \if_case:w
+ \if_meaning:w #2 #4
+ #2
+ \else:
+ \if_int_compare:w #2 > #4 \exp_stop_f:
+ 3
+ \else:
+ 4
+ \fi:
+ \fi:
+ \exp_stop_f:
+ \exp_after:wN \__fp_add_zeros_o:Nww \int_value:w
+ \or: \exp_after:wN \__fp_add_normal_o:Nww \int_value:w
+ \or: \exp_after:wN \__fp_add_inf_o:Nww \int_value:w
+ \or: \__fp_case_return_i_o:ww
+ \else: \exp_after:wN \__fp_add_return_ii_o:Nww \int_value:w
+ \fi:
+ #1 #5
+ \s__fp \__fp_chk:w #2 #3 ;
+ \s__fp \__fp_chk:w #4 #5
+ }
+\cs_new:Npn \__fp_add_return_ii_o:Nww #1 #2 ; \s__fp \__fp_chk:w #3 #4
+ { \__fp_exp_after_o:w \s__fp \__fp_chk:w #3 #1 }
+\cs_new:Npn \__fp_add_zeros_o:Nww #1 \s__fp \__fp_chk:w 0 #2
+ {
+ \if_int_compare:w #2 #1 = 20 \exp_stop_f:
+ \exp_after:wN \__fp_add_return_ii_o:Nww
+ \else:
+ \__fp_case_return_i_o:ww
+ \fi:
+ #1
+ \s__fp \__fp_chk:w 0 #2
+ }
+\cs_new:Npn \__fp_add_inf_o:Nww
+ #1 \s__fp \__fp_chk:w 2 #2 #3; \s__fp \__fp_chk:w 2 #4
+ {
+ \if_meaning:w #1 #2
+ \__fp_case_return_i_o:ww
+ \else:
+ \__fp_case_use:nw
+ {
+ \exp_last_unbraced:Nf \__fp_invalid_operation_o:Nww
+ { \token_if_eq_meaning:NNTF #1 #4 + - }
+ }
+ \fi:
+ \s__fp \__fp_chk:w 2 #2 #3;
+ \s__fp \__fp_chk:w 2 #4
+ }
+\cs_new:Npn \__fp_add_normal_o:Nww #1 \s__fp \__fp_chk:w 1 #2
+ {
+ \if_meaning:w #1#2
+ \exp_after:wN \__fp_add_npos_o:NnwNnw
+ \else:
+ \exp_after:wN \__fp_sub_npos_o:NnwNnw
+ \fi:
+ #2
+ }
+\cs_new:Npn \__fp_add_npos_o:NnwNnw #1#2#3 ; \s__fp \__fp_chk:w 1 #4 #5
+ {
+ \exp_after:wN \__fp_sanitize:Nw
+ \exp_after:wN #1
+ \int_value:w \__fp_int_eval:w
+ \if_int_compare:w #2 > #5 \exp_stop_f:
+ #2
+ \exp_after:wN \__fp_add_big_i_o:wNww \int_value:w -
+ \else:
+ #5
+ \exp_after:wN \__fp_add_big_ii_o:wNww \int_value:w
+ \fi:
+ \__fp_int_eval:w #5 - #2 ; #1 #3;
+ }
+\cs_new:Npn \__fp_add_big_i_o:wNww #1; #2 #3; #4;
+ {
+ \__fp_decimate:nNnnnn {#1}
+ \__fp_add_significand_o:NnnwnnnnN
+ #4
+ #3
+ #2
+ }
+\cs_new:Npn \__fp_add_big_ii_o:wNww #1; #2 #3; #4;
+ {
+ \__fp_decimate:nNnnnn {#1}
+ \__fp_add_significand_o:NnnwnnnnN
+ #3
+ #4
+ #2
+ }
+\cs_new:Npn \__fp_add_significand_o:NnnwnnnnN #1 #2#3 #4; #5#6#7#8
+ {
+ \exp_after:wN \__fp_add_significand_test_o:N
+ \int_value:w \__fp_int_eval:w 1#5#6 + #2
+ \exp_after:wN \__fp_add_significand_pack:NNNNNNN
+ \int_value:w \__fp_int_eval:w 1#7#8 + #3 ; #1
+ }
+\cs_new:Npn \__fp_add_significand_pack:NNNNNNN #1 #2#3#4#5#6#7
+ {
+ \if_meaning:w 2 #1
+ + 1
+ \fi:
+ ; #2 #3 #4 #5 #6 #7 ;
+ }
+\cs_new:Npn \__fp_add_significand_test_o:N #1
+ {
+ \if_meaning:w 2 #1
+ \exp_after:wN \__fp_add_significand_carry_o:wwwNN
+ \else:
+ \exp_after:wN \__fp_add_significand_no_carry_o:wwwNN
+ \fi:
+ }
+\cs_new:Npn \__fp_add_significand_no_carry_o:wwwNN
+ #1; #2; #3#4 ; #5#6
+ {
+ \exp_after:wN \__fp_basics_pack_high:NNNNNw
+ \int_value:w \__fp_int_eval:w 1 #1
+ \exp_after:wN \__fp_basics_pack_low:NNNNNw
+ \int_value:w \__fp_int_eval:w 1 #2 #3#4
+ + \__fp_round:NNN #6 #4 #5
+ \exp_after:wN ;
+ }
+\cs_new:Npn \__fp_add_significand_carry_o:wwwNN
+ #1; #2; #3#4; #5#6
+ {
+ + 1
+ \exp_after:wN \__fp_basics_pack_weird_high:NNNNNNNNw
+ \int_value:w \__fp_int_eval:w 1 1 #1
+ \exp_after:wN \__fp_basics_pack_weird_low:NNNNw
+ \int_value:w \__fp_int_eval:w 1 #2#3 +
+ \exp_after:wN \__fp_round:NNN
+ \exp_after:wN #6
+ \exp_after:wN #3
+ \int_value:w \__fp_round_digit:Nw #4 #5 ;
+ \exp_after:wN ;
+ }
+\cs_new:Npn \__fp_sub_npos_o:NnwNnw #1#2#3; \s__fp \__fp_chk:w 1 #4#5#6;
+ {
+ \if_case:w \__fp_compare_npos:nwnw {#2} #3; {#5} #6; \exp_stop_f:
+ \exp_after:wN \__fp_sub_eq_o:Nnwnw
+ \or:
+ \exp_after:wN \__fp_sub_npos_i_o:Nnwnw
+ \else:
+ \exp_after:wN \__fp_sub_npos_ii_o:Nnwnw
+ \fi:
+ #1 {#2} #3; {#5} #6;
+ }
+\cs_new:Npn \__fp_sub_eq_o:Nnwnw #1#2; #3; { \exp_after:wN \c_zero_fp }
+\cs_new:Npn \__fp_sub_npos_ii_o:Nnwnw #1 #2; #3;
+ {
+ \exp_after:wN \__fp_sub_npos_i_o:Nnwnw
+ \int_value:w \__fp_neg_sign:N #1
+ #3; #2;
+ }
+\cs_new:Npn \__fp_sub_npos_i_o:Nnwnw #1 #2#3; #4#5;
+ {
+ \exp_after:wN \__fp_sanitize:Nw
+ \exp_after:wN #1
+ \int_value:w \__fp_int_eval:w
+ #2
+ \if_int_compare:w #2 = #4 \exp_stop_f:
+ \exp_after:wN \__fp_sub_back_near_o:nnnnnnnnN
+ \else:
+ \exp_after:wN \__fp_decimate:nNnnnn \exp_after:wN
+ { \int_value:w \__fp_int_eval:w #2 - #4 - 1 \exp_after:wN }
+ \exp_after:wN \__fp_sub_back_far_o:NnnwnnnnN
+ \fi:
+ #5
+ #3
+ #1
+ }
+\cs_new:Npn \__fp_sub_back_near_o:nnnnnnnnN #1#2#3#4 #5#6#7#8 #9
+ {
+ \exp_after:wN \__fp_sub_back_near_after:wNNNNw
+ \int_value:w \__fp_int_eval:w 10#5#6 - #1#2 - 11
+ \exp_after:wN \__fp_sub_back_near_pack:NNNNNNw
+ \int_value:w \__fp_int_eval:w 11#7#8 - #3#4 \exp_after:wN ;
+ }
+\cs_new:Npn \__fp_sub_back_near_pack:NNNNNNw #1#2#3#4#5#6#7 ;
+ { + #1#2 ; {#3#4#5#6} {#7} ; }
+\cs_new:Npn \__fp_sub_back_near_after:wNNNNw 10 #1#2#3#4 #5 ;
+ {
+ \if_meaning:w 0 #1
+ \exp_after:wN \__fp_sub_back_shift:wnnnn
+ \fi:
+ ; {#1#2#3#4} {#5}
+ }
+\cs_new:Npn \__fp_sub_back_shift:wnnnn ; #1#2
+ {
+ \exp_after:wN \__fp_sub_back_shift_ii:ww
+ \int_value:w #1 #2 0 ;
+ }
+\cs_new:Npn \__fp_sub_back_shift_ii:ww #1 0 ; #2#3 ;
+ {
+ \if_meaning:w @ #1 @
+ - 7
+ - \exp_after:wN \use_i:nnn
+ \exp_after:wN \__fp_sub_back_shift_iii:NNNNNNNNw
+ \int_value:w #2#3 0 ~ 123456789;
+ \else:
+ - \__fp_sub_back_shift_iii:NNNNNNNNw #1 123456789;
+ \fi:
+ \exp_after:wN \__fp_pack_twice_four:wNNNNNNNN
+ \exp_after:wN \__fp_pack_twice_four:wNNNNNNNN
+ \exp_after:wN \__fp_sub_back_shift_iv:nnnnw
+ \exp_after:wN ;
+ \int_value:w
+ #1 ~ #2#3 0 ~ 0000 0000 0000 000 ;
+ }
+\cs_new:Npn \__fp_sub_back_shift_iii:NNNNNNNNw #1#2#3#4#5#6#7#8#9; {#8}
+\cs_new:Npn \__fp_sub_back_shift_iv:nnnnw #1 ; #2 ; { ; #1 ; }
+\cs_new:Npn \__fp_sub_back_far_o:NnnwnnnnN #1 #2#3 #4; #5#6#7#8
+ {
+ \if_case:w
+ \if_int_compare:w 1 #2 = #5#6 \use_i:nnnn #7 \exp_stop_f:
+ \if_int_compare:w #3 = \use_none:n #7#8 0 \exp_stop_f:
+ 0
+ \else:
+ \if_int_compare:w #3 > \use_none:n #7#8 0 - \fi: 1
+ \fi:
+ \else:
+ \if_int_compare:w 1 #2 > #5#6 \use_i:nnnn #7 - \fi: 1
+ \fi:
+ \exp_stop_f:
+ \exp_after:wN \__fp_sub_back_quite_far_o:wwNN
+ \or: \exp_after:wN \__fp_sub_back_very_far_o:wwwwNN
+ \else: \exp_after:wN \__fp_sub_back_not_far_o:wwwwNN
+ \fi:
+ #2 ~ #3 ; #5 #6 ~ #7 #8 ; #1
+ }
+\cs_new:Npn \__fp_sub_back_quite_far_o:wwNN #1; #2; #3#4
+ {
+ \exp_after:wN \__fp_sub_back_quite_far_ii:NN
+ \exp_after:wN #3
+ \exp_after:wN #4
+ }
+\cs_new:Npn \__fp_sub_back_quite_far_ii:NN #1#2
+ {
+ \if_case:w \__fp_round_neg:NNN #2 0 #1
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ { ; {1000} {0000} {0000} {0000} ; }
+ { - 1 ; {9999} {9999} {9999} {9999} ; }
+ }
+\cs_new:Npn \__fp_sub_back_not_far_o:wwwwNN #1 ~ #2; #3 ~ #4; #5#6
+ {
+ - 1
+ \exp_after:wN \__fp_sub_back_near_after:wNNNNw
+ \int_value:w \__fp_int_eval:w 1#30 - #1 - 11
+ \exp_after:wN \__fp_sub_back_near_pack:NNNNNNw
+ \int_value:w \__fp_int_eval:w 11 0000 0000 + #40 - #2
+ - \exp_after:wN \__fp_round_neg:NNN
+ \exp_after:wN #6
+ \use_none:nnnnnnn #2 #5
+ \exp_after:wN ;
+ }
+\cs_new:Npn \__fp_sub_back_very_far_o:wwwwNN #1#2#3#4#5#6#7
+ {
+ \__fp_pack_eight:wNNNNNNNN
+ \__fp_sub_back_very_far_ii_o:nnNwwNN
+ { 0 #1#2#3 #4#5#6#7 }
+ ;
+ }
+\cs_new:Npn \__fp_sub_back_very_far_ii_o:nnNwwNN #1#2 ; #3 ; #4 ~ #5; #6#7
+ {
+ \exp_after:wN \__fp_basics_pack_high:NNNNNw
+ \int_value:w \__fp_int_eval:w 1#4 - #1 - 1
+ \exp_after:wN \__fp_basics_pack_low:NNNNNw
+ \int_value:w \__fp_int_eval:w 2#5 - #2
+ - \exp_after:wN \__fp_round_neg:NNN
+ \exp_after:wN #7
+ \int_value:w
+ \if_int_odd:w \__fp_int_eval:w #5 - #2 \__fp_int_eval_end:
+ 1 \else: 2 \fi:
+ \int_value:w \__fp_round_digit:Nw #3 #6 ;
+ \exp_after:wN ;
+ }
+\cs_new:cpn { __fp_*_o:ww }
+ {
+ \__fp_mul_cases_o:NnNnww
+ *
+ { - 2 + }
+ \__fp_mul_npos_o:Nww
+ { }
+ }
+\cs_new:Npn \__fp_mul_cases_o:NnNnww
+ #1#2#3#4 \s__fp \__fp_chk:w #5#6#7; \s__fp \__fp_chk:w #8#9
+ {
+ \if_case:w \__fp_int_eval:w
+ \if_int_compare:w #5 #8 = 11 ~
+ 1
+ \else:
+ \if_meaning:w 3 #8
+ 3
+ \else:
+ \if_meaning:w 3 #5
+ 2
+ \else:
+ \if_int_compare:w #5 #8 = 10 ~
+ 9 #2 - 2
+ \else:
+ (#5 #2 #8) / 2 * 2 + 7
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \if_meaning:w #6 #9 - 1 \fi:
+ \__fp_int_eval_end:
+ \__fp_case_use:nw { #3 0 }
+ \or: \__fp_case_use:nw { #3 2 }
+ \or: \__fp_case_return_i_o:ww
+ \or: \__fp_case_return_ii_o:ww
+ \or: \__fp_case_return_o:Nww \c_zero_fp
+ \or: \__fp_case_return_o:Nww \c_minus_zero_fp
+ \or: \__fp_case_use:nw { \__fp_invalid_operation_o:Nww #1 }
+ \or: \__fp_case_use:nw { \__fp_invalid_operation_o:Nww #1 }
+ \or: \__fp_case_return_o:Nww \c_inf_fp
+ \or: \__fp_case_return_o:Nww \c_minus_inf_fp
+ #4
+ \fi:
+ \s__fp \__fp_chk:w #5 #6 #7;
+ \s__fp \__fp_chk:w #8 #9
+ }
+\cs_new:Npn \__fp_mul_npos_o:Nww
+ #1 \s__fp \__fp_chk:w #2 #3 #4 #5 ; \s__fp \__fp_chk:w #6 #7 #8 #9 ;
+ {
+ \exp_after:wN \__fp_sanitize:Nw
+ \exp_after:wN #1
+ \int_value:w \__fp_int_eval:w
+ #4 + #8
+ \__fp_mul_significand_o:nnnnNnnnn #5 #1 #9
+ }
+\cs_new:Npn \__fp_mul_significand_o:nnnnNnnnn #1#2#3#4 #5 #6#7#8#9
+ {
+ \exp_after:wN \__fp_mul_significand_test_f:NNN
+ \exp_after:wN #5
+ \int_value:w \__fp_int_eval:w 99990000 + #1*#6 +
+ \exp_after:wN \__fp_mul_significand_keep:NNNNNw
+ \int_value:w \__fp_int_eval:w 99990000 + #1*#7 + #2*#6 +
+ \exp_after:wN \__fp_mul_significand_keep:NNNNNw
+ \int_value:w \__fp_int_eval:w 99990000 + #1*#8 + #2*#7 + #3*#6 +
+ \exp_after:wN \__fp_mul_significand_drop:NNNNNw
+ \int_value:w \__fp_int_eval:w 99990000 + #1*#9 + #2*#8 +
+ #3*#7 + #4*#6 +
+ \exp_after:wN \__fp_mul_significand_drop:NNNNNw
+ \int_value:w \__fp_int_eval:w 99990000 + #2*#9 + #3*#8 +
+ #4*#7 +
+ \exp_after:wN \__fp_mul_significand_drop:NNNNNw
+ \int_value:w \__fp_int_eval:w 99990000 + #3*#9 + #4*#8 +
+ \exp_after:wN \__fp_mul_significand_drop:NNNNNw
+ \int_value:w \__fp_int_eval:w 100000000 + #4*#9 ;
+ ; \exp_after:wN ;
+ }
+\cs_new:Npn \__fp_mul_significand_drop:NNNNNw #1#2#3#4#5 #6;
+ { #1#2#3#4#5 ; + #6 }
+\cs_new:Npn \__fp_mul_significand_keep:NNNNNw #1#2#3#4#5 #6;
+ { #1#2#3#4#5 ; #6 ; }
+\cs_new:Npn \__fp_mul_significand_test_f:NNN #1 #2 #3
+ {
+ \if_meaning:w 0 #3
+ \exp_after:wN \__fp_mul_significand_small_f:NNwwwN
+ \else:
+ \exp_after:wN \__fp_mul_significand_large_f:NwwNNNN
+ \fi:
+ #1 #3
+ }
+\cs_new:Npn \__fp_mul_significand_large_f:NwwNNNN #1 #2; #3; #4#5#6#7; +
+ {
+ \exp_after:wN \__fp_basics_pack_high:NNNNNw
+ \int_value:w \__fp_int_eval:w 1#2
+ \exp_after:wN \__fp_basics_pack_low:NNNNNw
+ \int_value:w \__fp_int_eval:w 1#3#4#5#6#7
+ + \exp_after:wN \__fp_round:NNN
+ \exp_after:wN #1
+ \exp_after:wN #7
+ \int_value:w \__fp_round_digit:Nw
+ }
+\cs_new:Npn \__fp_mul_significand_small_f:NNwwwN #1 #2#3; #4#5; #6; + #7
+ {
+ - 1
+ \exp_after:wN \__fp_basics_pack_high:NNNNNw
+ \int_value:w \__fp_int_eval:w 1#3#4
+ \exp_after:wN \__fp_basics_pack_low:NNNNNw
+ \int_value:w \__fp_int_eval:w 1#5#6#7
+ + \exp_after:wN \__fp_round:NNN
+ \exp_after:wN #1
+ \exp_after:wN #7
+ \int_value:w \__fp_round_digit:Nw
+ }
+\cs_new:cpn { __fp_/_o:ww }
+ {
+ \__fp_mul_cases_o:NnNnww
+ /
+ { - }
+ \__fp_div_npos_o:Nww
+ {
+ \or:
+ \__fp_case_use:nw
+ { \__fp_division_by_zero_o:NNww \c_inf_fp / }
+ \or:
+ \__fp_case_use:nw
+ { \__fp_division_by_zero_o:NNww \c_minus_inf_fp / }
+ }
+ }
+\cs_new:Npn \__fp_div_npos_o:Nww
+ #1 \s__fp \__fp_chk:w 1 #2 #3 #4 ; \s__fp \__fp_chk:w 1 #5 #6 #7#8#9;
+ {
+ \exp_after:wN \__fp_sanitize:Nw
+ \exp_after:wN #1
+ \int_value:w \__fp_int_eval:w
+ #3 - #6
+ \exp_after:wN \__fp_div_significand_i_o:wnnw
+ \int_value:w \__fp_int_eval:w #7 \use_i:nnnn #8 + 1 ;
+ #4
+ {#7}{#8}#9 ;
+ #1
+ }
+\cs_new:Npn \__fp_div_significand_i_o:wnnw #1 ; #2#3 #4 ;
+ {
+ \exp_after:wN \__fp_div_significand_test_o:w
+ \int_value:w \__fp_int_eval:w
+ \exp_after:wN \__fp_div_significand_calc:wwnnnnnnn
+ \int_value:w \__fp_int_eval:w 999999 + #2 #3 0 / #1 ;
+ #2 #3 ;
+ #4
+ { \exp_after:wN \__fp_div_significand_ii:wwn \int_value:w #1 }
+ { \exp_after:wN \__fp_div_significand_ii:wwn \int_value:w #1 }
+ { \exp_after:wN \__fp_div_significand_ii:wwn \int_value:w #1 }
+ { \exp_after:wN \__fp_div_significand_iii:wwnnnnn \int_value:w #1 }
+ }
+\cs_new:Npn \__fp_div_significand_calc:wwnnnnnnn 1#1
+ {
+ \if_meaning:w 1 #1
+ \exp_after:wN \__fp_div_significand_calc_i:wwnnnnnnn
+ \else:
+ \exp_after:wN \__fp_div_significand_calc_ii:wwnnnnnnn
+ \fi:
+ }
+\cs_new:Npn \__fp_div_significand_calc_i:wwnnnnnnn
+ #1; #2;#3#4 #5#6#7#8 #9
+ {
+ 1 1 #1
+ #9 \exp_after:wN ;
+ \int_value:w \__fp_int_eval:w \c__fp_Bigg_leading_shift_int
+ + #2 - #1 * #5 - #5#60
+ \exp_after:wN \__fp_pack_Bigg:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_Bigg_middle_shift_int
+ + #3 - #1 * #6 - #70
+ \exp_after:wN \__fp_pack_Bigg:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_Bigg_middle_shift_int
+ + #4 - #1 * #7 - #80
+ \exp_after:wN \__fp_pack_Bigg:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_Bigg_trailing_shift_int
+ - #1 * #8 ;
+ {#5}{#6}{#7}{#8}
+ }
+\cs_new:Npn \__fp_div_significand_calc_ii:wwnnnnnnn
+ #1; #2;#3#4 #5#6#7#8 #9
+ {
+ 1 0 #1
+ #9 \exp_after:wN ;
+ \int_value:w \__fp_int_eval:w \c__fp_Bigg_leading_shift_int
+ + #2 - #1 * #5
+ \exp_after:wN \__fp_pack_Bigg:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_Bigg_middle_shift_int
+ + #3 - #1 * #6
+ \exp_after:wN \__fp_pack_Bigg:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_Bigg_middle_shift_int
+ + #4 - #1 * #7
+ \exp_after:wN \__fp_pack_Bigg:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_Bigg_trailing_shift_int
+ - #1 * #8 ;
+ {#5}{#6}{#7}{#8}
+ }
+\cs_new:Npn \__fp_div_significand_ii:wwn #1; #2;#3
+ {
+ \exp_after:wN \__fp_div_significand_pack:NNN
+ \int_value:w \__fp_int_eval:w
+ \exp_after:wN \__fp_div_significand_calc:wwnnnnnnn
+ \int_value:w \__fp_int_eval:w 999999 + #2 #3 0 / #1 ; #2 #3 ;
+ }
+\cs_new:Npn \__fp_div_significand_iii:wwnnnnn #1; #2;#3#4#5 #6#7
+ {
+ 0
+ \exp_after:wN \__fp_div_significand_iv:wwnnnnnnn
+ \int_value:w \__fp_int_eval:w ( 2 * #2 #3) / #6 #7 ; % <- P
+ #2 ; {#3} {#4} {#5}
+ {#6} {#7}
+ }
+\cs_new:Npn \__fp_div_significand_iv:wwnnnnnnn #1; #2;#3#4#5 #6#7#8#9
+ {
+ + 5 * #1
+ \exp_after:wN \__fp_div_significand_vi:Nw
+ \int_value:w \__fp_int_eval:w -50 + 2*#2#3 - #1*#6#7 +
+ \exp_after:wN \__fp_div_significand_v:NN
+ \int_value:w \__fp_int_eval:w 499950 + 2*#4 - #1*#8 +
+ \exp_after:wN \__fp_div_significand_v:NN
+ \int_value:w \__fp_int_eval:w 500000 + 2*#5 - #1*#9 ;
+ }
+\cs_new:Npn \__fp_div_significand_v:NN #1#2 { #1#2 \__fp_int_eval_end: + }
+\cs_new:Npn \__fp_div_significand_vi:Nw #1#2;
+ {
+ \if_meaning:w 0 #1
+ \if_int_compare:w \__fp_int_eval:w #2 > 0 + 1 \fi:
+ \else:
+ \if_meaning:w - #1 - \else: + \fi: 1
+ \fi:
+ ;
+ }
+\cs_new:Npn \__fp_div_significand_pack:NNN 1 #1 #2 { + #1 #2 ; }
+\cs_new:Npn \__fp_div_significand_test_o:w 10 #1
+ {
+ \if_meaning:w 0 #1
+ \exp_after:wN \__fp_div_significand_small_o:wwwNNNNwN
+ \else:
+ \exp_after:wN \__fp_div_significand_large_o:wwwNNNNwN
+ \fi:
+ #1
+ }
+\cs_new:Npn \__fp_div_significand_small_o:wwwNNNNwN
+ 0 #1; #2; #3; #4#5#6#7#8; #9
+ {
+ \exp_after:wN \__fp_basics_pack_high:NNNNNw
+ \int_value:w \__fp_int_eval:w 1 #1#2
+ \exp_after:wN \__fp_basics_pack_low:NNNNNw
+ \int_value:w \__fp_int_eval:w 1 #3#4#5#6#7
+ + \__fp_round:NNN #9 #7 #8
+ \exp_after:wN ;
+ }
+\cs_new:Npn \__fp_div_significand_large_o:wwwNNNNwN
+ #1; #2; #3; #4#5#6#7#8; #9
+ {
+ + 1
+ \exp_after:wN \__fp_basics_pack_weird_high:NNNNNNNNw
+ \int_value:w \__fp_int_eval:w 1 #1 #2
+ \exp_after:wN \__fp_basics_pack_weird_low:NNNNw
+ \int_value:w \__fp_int_eval:w 1 #3 #4 #5 #6 +
+ \exp_after:wN \__fp_round:NNN
+ \exp_after:wN #9
+ \exp_after:wN #6
+ \int_value:w \__fp_round_digit:Nw #7 #8 ;
+ \exp_after:wN ;
+ }
+\cs_new:Npn \__fp_sqrt_o:w #1 \s__fp \__fp_chk:w #2#3#4; @
+ {
+ \if_meaning:w 0 #2 \__fp_case_return_same_o:w \fi:
+ \if_meaning:w 2 #3
+ \__fp_case_use:nw { \__fp_invalid_operation_o:nw { sqrt } }
+ \fi:
+ \if_meaning:w 1 #2 \else: \__fp_case_return_same_o:w \fi:
+ \__fp_sqrt_npos_o:w
+ \s__fp \__fp_chk:w #2 #3 #4;
+ }
+\cs_new:Npn \__fp_sqrt_npos_o:w \s__fp \__fp_chk:w 1 0 #1#2#3#4#5;
+ {
+ \exp_after:wN \__fp_sanitize:Nw
+ \exp_after:wN 0
+ \int_value:w \__fp_int_eval:w
+ \if_int_odd:w #1 \exp_stop_f:
+ \exp_after:wN \__fp_sqrt_npos_auxi_o:wwnnN
+ \fi:
+ #1 / 2
+ \__fp_sqrt_Newton_o:wwn 56234133; 0; {#2#3} {#4#5} 0
+ }
+\cs_new:Npn \__fp_sqrt_npos_auxi_o:wwnnN #1 / 2 #2; 0; #3#4#5
+ {
+ ( #1 + 1 ) / 2
+ \__fp_pack_eight:wNNNNNNNN
+ \__fp_sqrt_npos_auxii_o:wNNNNNNNN
+ ;
+ 0 #3 #4
+ }
+\cs_new:Npn \__fp_sqrt_npos_auxii_o:wNNNNNNNN #1; #2#3#4#5#6#7#8#9
+ { \__fp_sqrt_Newton_o:wwn 17782794; 0; {#1} {#2#3#4#5#6#7#8#9} }
+\cs_new:Npn \__fp_sqrt_Newton_o:wwn #1; #2; #3
+ {
+ \if_int_compare:w #1 = #2 \exp_stop_f:
+ \exp_after:wN \__fp_sqrt_auxi_o:NNNNwnnN
+ \int_value:w \__fp_int_eval:w 9999 9999 +
+ \exp_after:wN \__fp_use_none_until_s:w
+ \fi:
+ \exp_after:wN \__fp_sqrt_Newton_o:wwn
+ \int_value:w \__fp_int_eval:w (#1 + #3 * 1 0000 0000 / #1) / 2 ;
+ #1; {#3}
+ }
+\cs_new:Npn \__fp_sqrt_auxi_o:NNNNwnnN 1 #1#2#3#4#5;
+ {
+ \__fp_sqrt_auxii_o:NnnnnnnnN
+ \__fp_sqrt_auxiii_o:wnnnnnnnn
+ {#1#2#3#4} {#5} {2499} {9988} {7500}
+ }
+\cs_new:Npn \__fp_sqrt_auxii_o:NnnnnnnnN #1 #2#3#4#5#6 #7#8#9
+ {
+ \exp_after:wN #1
+ \int_value:w \__fp_int_eval:w \c__fp_big_leading_shift_int
+ + #7 - #2 * #2
+ \exp_after:wN \__fp_pack_big:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_big_middle_shift_int
+ - 2 * #2 * #3
+ \exp_after:wN \__fp_pack_big:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_big_middle_shift_int
+ + #8 - #3 * #3 - 2 * #2 * #4
+ \exp_after:wN \__fp_pack_big:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_big_middle_shift_int
+ - 2 * #3 * #4 - 2 * #2 * #5
+ \exp_after:wN \__fp_pack_big:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_big_middle_shift_int
+ + #9 000 0000 - #4 * #4 - 2 * #3 * #5 - 2 * #2 * #6
+ \exp_after:wN \__fp_pack_big:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_big_middle_shift_int
+ - 2 * #4 * #5 - 2 * #3 * #6
+ \exp_after:wN \__fp_pack_big:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_big_middle_shift_int
+ - #5 * #5 - 2 * #4 * #6
+ \exp_after:wN \__fp_pack_big:NNNNNNw
+ \int_value:w \__fp_int_eval:w
+ \c__fp_big_middle_shift_int
+ - 2 * #5 * #6
+ \exp_after:wN \__fp_pack_big:NNNNNNw
+ \int_value:w \__fp_int_eval:w
+ \c__fp_big_trailing_shift_int
+ - #6 * #6 ;
+ % (
+ - 257 ) * 5000 0000 / (#2#3 + 1) + 10 0000 0000 ;
+ {#2}{#3}{#4}{#5}{#6} {#7}{#8}#9
+ }
+\cs_new:Npn \__fp_sqrt_auxiii_o:wnnnnnnnn
+ #1; #2#3#4#5#6#7#8#9
+ {
+ \if_int_compare:w #1 > \c_one_int
+ \exp_after:wN \__fp_sqrt_auxiv_o:NNNNNw
+ \int_value:w \__fp_int_eval:w (#1#2 %)
+ \else:
+ \if_int_compare:w #1#2 > \c_one_int
+ \exp_after:wN \__fp_sqrt_auxv_o:NNNNNw
+ \int_value:w \__fp_int_eval:w (#1#2#3 %)
+ \else:
+ \if_int_compare:w #1#2#3 > \c_one_int
+ \exp_after:wN \__fp_sqrt_auxvi_o:NNNNNw
+ \int_value:w \__fp_int_eval:w (#1#2#3#4 %)
+ \else:
+ \exp_after:wN \__fp_sqrt_auxvii_o:NNNNNw
+ \int_value:w \__fp_int_eval:w (#1#2#3#4#5 %)
+ \fi:
+ \fi:
+ \fi:
+ }
+\cs_new:Npn \__fp_sqrt_auxiv_o:NNNNNw 1#1#2#3#4#5#6;
+ { \__fp_sqrt_auxviii_o:nnnnnnn {#1#2#3#4#5#6} {00000000} }
+\cs_new:Npn \__fp_sqrt_auxv_o:NNNNNw 1#1#2#3#4#5#6;
+ { \__fp_sqrt_auxviii_o:nnnnnnn {000#1#2#3#4#5} {#60000} }
+\cs_new:Npn \__fp_sqrt_auxvi_o:NNNNNw 1#1#2#3#4#5#6;
+ { \__fp_sqrt_auxviii_o:nnnnnnn {0000000#1} {#2#3#4#5#6} }
+\cs_new:Npn \__fp_sqrt_auxvii_o:NNNNNw 1#1#2#3#4#5#6;
+ {
+ \if_int_compare:w #1#2 = \c_zero_int
+ \exp_after:wN \__fp_sqrt_auxx_o:Nnnnnnnn
+ \fi:
+ \__fp_sqrt_auxviii_o:nnnnnnn {00000000} {000#1#2#3#4#5}
+ }
+\cs_new:Npn \__fp_sqrt_auxviii_o:nnnnnnn #1#2 #3#4#5#6#7
+ {
+ \exp_after:wN \__fp_sqrt_auxix_o:wnwnw
+ \int_value:w \__fp_int_eval:w #3
+ \exp_after:wN \__fp_basics_pack_low:NNNNNw
+ \int_value:w \__fp_int_eval:w #1 + 1#4#5
+ \exp_after:wN \__fp_basics_pack_low:NNNNNw
+ \int_value:w \__fp_int_eval:w #2 + 1#6#7 ;
+ }
+\cs_new:Npn \__fp_sqrt_auxix_o:wnwnw #1; #2#3; #4#5;
+ {
+ \__fp_sqrt_auxii_o:NnnnnnnnN
+ \__fp_sqrt_auxiii_o:wnnnnnnnn {#1}{#2}{#3}{#4}{#5}
+ }
+\cs_new:Npn \__fp_sqrt_auxx_o:Nnnnnnnn #1#2#3 #4#5#6#7#8
+ {
+ \exp_after:wN \__fp_sqrt_auxxi_o:wwnnN
+ \int_value:w \__fp_int_eval:w
+ (#8 + 2499) / 5000 * 5000 ;
+ {#4} {#5} {#6} {#7} ;
+ }
+\cs_new:Npn \__fp_sqrt_auxxi_o:wwnnN #1; #2; #3#4#5
+ {
+ \__fp_sqrt_auxii_o:NnnnnnnnN
+ \__fp_sqrt_auxxii_o:nnnnnnnnw
+ #2 {#1}
+ {#3} { #4 + 1 } #5
+ }
+\cs_new:Npn \__fp_sqrt_auxxii_o:nnnnnnnnw 0; #1#2#3#4#5#6#7#8 #9;
+ {
+ \if_int_compare:w #1#2 > \c_zero_int
+ \if_int_compare:w #1#2 = \c_one_int
+ \if_int_compare:w #3#4 = \c_zero_int
+ \if_int_compare:w #5#6 = \c_zero_int
+ \if_int_compare:w #7#8 = \c_zero_int
+ \__fp_sqrt_auxxiii_o:w
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \exp_after:wN \__fp_sqrt_auxxiv_o:wnnnnnnnN
+ \int_value:w 9998
+ \else:
+ \exp_after:wN \__fp_sqrt_auxxiv_o:wnnnnnnnN
+ \int_value:w 10000
+ \fi:
+ ;
+ }
+\cs_new:Npn \__fp_sqrt_auxxiii_o:w \fi: \fi: \fi: \fi: #1 \fi: ;
+ {
+ \fi: \fi: \fi: \fi: \fi:
+ \__fp_sqrt_auxxiv_o:wnnnnnnnN 9999 ;
+ }
+\cs_new:Npn \__fp_sqrt_auxxiv_o:wnnnnnnnN #1; #2#3#4#5#6 #7#8#9
+ {
+ \exp_after:wN \__fp_basics_pack_high:NNNNNw
+ \int_value:w \__fp_int_eval:w 1 0000 0000 + #2#3
+ \exp_after:wN \__fp_basics_pack_low:NNNNNw
+ \int_value:w \__fp_int_eval:w 1 0000 0000
+ + #4#5
+ \if_int_compare:w #6 > #1 \exp_stop_f: + 1 \fi:
+ + \exp_after:wN \__fp_round:NNN
+ \exp_after:wN 0
+ \exp_after:wN 0
+ \int_value:w
+ \exp_after:wN \use_i:nn
+ \exp_after:wN \__fp_round_digit:Nw
+ \int_value:w \__fp_int_eval:w #6 + 19999 - #1 ;
+ \exp_after:wN ;
+ }
+\cs_new:Npn \__fp_logb_o:w ? \s__fp \__fp_chk:w #1#2; @
+ {
+ \if_case:w #1 \exp_stop_f:
+ \__fp_case_use:nw
+ { \__fp_division_by_zero_o:Nnw \c_minus_inf_fp { logb } }
+ \or: \exp_after:wN \__fp_logb_aux_o:w
+ \or: \__fp_case_return_o:Nw \c_inf_fp
+ \else: \__fp_case_return_same_o:w
+ \fi:
+ \s__fp \__fp_chk:w #1 #2;
+ }
+\cs_new:Npn \__fp_logb_aux_o:w \s__fp \__fp_chk:w #1 #2 #3 #4 ;
+ {
+ \exp_after:wN \__fp_parse:n \exp_after:wN
+ { \int_value:w \int_eval:w #3 - 1 \exp_after:wN }
+ }
+\cs_new:Npn \__fp_sign_o:w ? \s__fp \__fp_chk:w #1#2; @
+ {
+ \if_case:w #1 \exp_stop_f:
+ \__fp_case_return_same_o:w
+ \or: \exp_after:wN \__fp_sign_aux_o:w
+ \or: \exp_after:wN \__fp_sign_aux_o:w
+ \else: \__fp_case_return_same_o:w
+ \fi:
+ \s__fp \__fp_chk:w #1 #2;
+ }
+\cs_new:Npn \__fp_sign_aux_o:w \s__fp \__fp_chk:w #1 #2 #3 ;
+ { \exp_after:wN \__fp_set_sign_o:w \exp_after:wN #2 \c_one_fp @ }
+\cs_new:Npn \__fp_set_sign_o:w #1 \s__fp \__fp_chk:w #2#3#4; @
+ {
+ \exp_after:wN \__fp_exp_after_o:w
+ \exp_after:wN \s__fp
+ \exp_after:wN \__fp_chk:w
+ \exp_after:wN #2
+ \int_value:w
+ \if_case:w #3 \exp_stop_f: #1 \or: 1 \or: 0 \fi: \exp_stop_f:
+ #4;
+ }
+\cs_new:Npn \__fp_tuple_set_sign_o:w #1#2 @
+ {
+ \if_meaning:w 2 #1
+ \exp_after:wN \__fp_tuple_set_sign_aux_o:Nnw
+ \fi:
+ \__fp_invalid_operation_o:nw { abs }
+ #2
+ }
+\cs_new:Npn \__fp_tuple_set_sign_aux_o:Nnw #1#2
+ { \__fp_tuple_map_o:nw \__fp_tuple_set_sign_aux_o:w }
+\cs_new:Npn \__fp_tuple_set_sign_aux_o:w #1#2 ;
+ {
+ \__fp_change_func_type:NNN #1 \__fp_set_sign_o:w
+ \__fp_parse_apply_unary_error:NNw
+ 2 #1 #2 ; @
+ }
+\cs_new:cpn { __fp_*_tuple_o:ww } #1 ;
+ { \__fp_tuple_map_o:nw { \__fp_binary_type_o:Nww * #1 ; } }
+\cs_new:cpn { __fp_tuple_*_o:ww } #1 ; #2 ;
+ { \__fp_tuple_map_o:nw { \__fp_binary_rev_type_o:Nww * #2 ; } #1 ; }
+\cs_new:cpn { __fp_tuple_/_o:ww } #1 ; #2 ;
+ { \__fp_tuple_map_o:nw { \__fp_binary_rev_type_o:Nww / #2 ; } #1 ; }
+\cs_set_protected:Npn \__fp_tmp:w #1
+ {
+ \cs_new:cpn { __fp_tuple_#1_tuple_o:ww }
+ \s__fp_tuple \__fp_tuple_chk:w ##1 ;
+ \s__fp_tuple \__fp_tuple_chk:w ##2 ;
+ {
+ \int_compare:nNnTF
+ { \__fp_array_count:n {##1} } = { \__fp_array_count:n {##2} }
+ { \__fp_tuple_mapthread_o:nww { \__fp_binary_type_o:Nww #1 } }
+ { \__fp_invalid_operation_o:nww #1 }
+ \s__fp_tuple \__fp_tuple_chk:w {##1} ;
+ \s__fp_tuple \__fp_tuple_chk:w {##2} ;
+ }
+ }
+\__fp_tmp:w +
+\__fp_tmp:w -
+%% File: l3fp-extended.dtx
+\tl_const:Nn \c__fp_one_fixed_tl
+ { {10000} {0000} {0000} {0000} {0000} {0000} ; }
+\cs_new:Npn \__fp_fixed_continue:wn #1; #2 { #2 #1; }
+\cs_new:Npn \__fp_fixed_add_one:wN #1#2; #3
+ {
+ \exp_after:wN #3 \exp_after:wN
+ { \int_value:w \__fp_int_eval:w \c__fp_myriad_int + #1 } #2 ;
+ }
+\cs_new:Npn \__fp_fixed_div_myriad:wn #1#2#3#4#5#6;
+ {
+ \exp_after:wN \__fp_fixed_mul_after:wwn
+ \int_value:w \__fp_int_eval:w \c__fp_leading_shift_int
+ \exp_after:wN \__fp_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_trailing_shift_int
+ + #1 ; {#2}{#3}{#4}{#5};
+ }
+\cs_new:Npn \__fp_fixed_mul_after:wwn #1; #2; #3 { #3 {#1} #2; }
+\cs_new:Npn \__fp_fixed_mul_short:wwn #1#2#3#4#5#6; #7#8#9;
+ {
+ \exp_after:wN \__fp_fixed_mul_after:wwn
+ \int_value:w \__fp_int_eval:w \c__fp_leading_shift_int
+ + #1*#7
+ \exp_after:wN \__fp_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_middle_shift_int
+ + #1*#8 + #2*#7
+ \exp_after:wN \__fp_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_middle_shift_int
+ + #1*#9 + #2*#8 + #3*#7
+ \exp_after:wN \__fp_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_middle_shift_int
+ + #2*#9 + #3*#8 + #4*#7
+ \exp_after:wN \__fp_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_middle_shift_int
+ + #3*#9 + #4*#8 + #5*#7
+ \exp_after:wN \__fp_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_trailing_shift_int
+ + #4*#9 + #5*#8 + #6*#7
+ + ( #5*#9 + #6*#8 + #6*#9 / \c__fp_myriad_int )
+ / \c__fp_myriad_int ; ;
+ }
+\cs_new:Npn \__fp_fixed_div_int:wwN #1#2#3#4#5#6 ; #7 ; #8
+ {
+ \exp_after:wN \__fp_fixed_div_int_after:Nw
+ \exp_after:wN #8
+ \int_value:w \__fp_int_eval:w - 1
+ \__fp_fixed_div_int:wnN
+ #1; {#7} \__fp_fixed_div_int_auxi:wnn
+ #2; {#7} \__fp_fixed_div_int_auxi:wnn
+ #3; {#7} \__fp_fixed_div_int_auxi:wnn
+ #4; {#7} \__fp_fixed_div_int_auxi:wnn
+ #5; {#7} \__fp_fixed_div_int_auxi:wnn
+ #6; {#7} \__fp_fixed_div_int_auxii:wnn ;
+ }
+\cs_new:Npn \__fp_fixed_div_int:wnN #1; #2 #3
+ {
+ \exp_after:wN #3
+ \int_value:w \__fp_int_eval:w #1 / #2 - 1 ;
+ {#2}
+ {#1}
+ }
+\cs_new:Npn \__fp_fixed_div_int_auxi:wnn #1; #2 #3
+ {
+ + #1
+ \exp_after:wN \__fp_fixed_div_int_pack:Nw
+ \int_value:w \__fp_int_eval:w 9999
+ \exp_after:wN \__fp_fixed_div_int:wnN
+ \int_value:w \__fp_int_eval:w #3 - #1*#2 \__fp_int_eval_end:
+ }
+\cs_new:Npn \__fp_fixed_div_int_auxii:wnn #1; #2 #3 { + #1 + 2 ; }
+\cs_new:Npn \__fp_fixed_div_int_pack:Nw #1 #2; { + #1; {#2} }
+\cs_new:Npn \__fp_fixed_div_int_after:Nw #1 #2; { #1 {#2} }
+\cs_new:Npn \__fp_fixed_add:wwn { \__fp_fixed_add:Nnnnnwnn + }
+\cs_new:Npn \__fp_fixed_sub:wwn { \__fp_fixed_add:Nnnnnwnn - }
+\cs_new:Npn \__fp_fixed_add:Nnnnnwnn #1 #2#3#4#5 #6; #7#8
+ {
+ \exp_after:wN \__fp_fixed_add_after:NNNNNwn
+ \int_value:w \__fp_int_eval:w 9 9999 9998 + #2#3 #1 #7#8
+ \exp_after:wN \__fp_fixed_add_pack:NNNNNwn
+ \int_value:w \__fp_int_eval:w 1 9999 9998 + #4#5
+ \__fp_fixed_add:nnNnnnwn #6 #1
+ }
+\cs_new:Npn \__fp_fixed_add:nnNnnnwn #1#2 #3 #4#5 #6#7 ; #8
+ {
+ #3 #4#5
+ \exp_after:wN \__fp_fixed_add_pack:NNNNNwn
+ \int_value:w \__fp_int_eval:w 2 0000 0000 #3 #6#7 + #1#2 ; {#8} ;
+ }
+\cs_new:Npn \__fp_fixed_add_pack:NNNNNwn #1 #2#3#4#5 #6; #7
+ { + #1 ; {#7} {#2#3#4#5} {#6} }
+\cs_new:Npn \__fp_fixed_add_after:NNNNNwn 1 #1 #2#3#4#5 #6; #7
+ { #7 {#1#2#3#4#5} {#6} }
+\cs_new:Npn \__fp_fixed_mul:wwn #1#2#3#4 #5; #6#7#8#9
+ {
+ \exp_after:wN \__fp_fixed_mul_after:wwn
+ \int_value:w \__fp_int_eval:w \c__fp_leading_shift_int
+ \exp_after:wN \__fp_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_middle_shift_int
+ + #1*#6
+ \exp_after:wN \__fp_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_middle_shift_int
+ + #1*#7 + #2*#6
+ \exp_after:wN \__fp_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_middle_shift_int
+ + #1*#8 + #2*#7 + #3*#6
+ \exp_after:wN \__fp_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_middle_shift_int
+ + #1*#9 + #2*#8 + #3*#7 + #4*#6
+ \exp_after:wN \__fp_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_trailing_shift_int
+ + #2*#9 + #3*#8 + #4*#7
+ + ( #3*#9 + #4*#8
+ + \__fp_fixed_mul:nnnnnnnw #5 {#6}{#7} {#1}{#2}
+ }
+\cs_new:Npn \__fp_fixed_mul:nnnnnnnw #1#2 #3#4 #5#6 #7#8 ;
+ {
+ #1*#4 + #2*#3 + #5*#8 + #6*#7 ) / \c__fp_myriad_int
+ + #1*#3 + #5*#7 ; ;
+ }
+\cs_new:Npn \__fp_fixed_mul_add:wwwn #1; #2; #3#4#5#6#7#8;
+ {
+ \exp_after:wN \__fp_fixed_mul_after:wwn
+ \int_value:w \__fp_int_eval:w \c__fp_big_leading_shift_int
+ \exp_after:wN \__fp_pack_big:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_big_middle_shift_int + #3 #4
+ \__fp_fixed_mul_add:Nwnnnwnnn +
+ + #5 #6 ; #2 ; #1 ; #2 ; +
+ + #7 #8 ; ;
+ }
+\cs_new:Npn \__fp_fixed_mul_sub_back:wwwn #1; #2; #3#4#5#6#7#8;
+ {
+ \exp_after:wN \__fp_fixed_mul_after:wwn
+ \int_value:w \__fp_int_eval:w \c__fp_big_leading_shift_int
+ \exp_after:wN \__fp_pack_big:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_big_middle_shift_int + #3 #4
+ \__fp_fixed_mul_add:Nwnnnwnnn -
+ + #5 #6 ; #2 ; #1 ; #2 ; -
+ + #7 #8 ; ;
+ }
+\cs_new:Npn \__fp_fixed_one_minus_mul:wwn #1; #2;
+ {
+ \exp_after:wN \__fp_fixed_mul_after:wwn
+ \int_value:w \__fp_int_eval:w \c__fp_big_leading_shift_int
+ \exp_after:wN \__fp_pack_big:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_big_middle_shift_int +
+ 1 0000 0000
+ \__fp_fixed_mul_add:Nwnnnwnnn -
+ ; #2 ; #1 ; #2 ; -
+ ; ;
+ }
+\cs_new:Npn \__fp_fixed_mul_add:Nwnnnwnnn #1 #2; #3#4#5#6; #7#8#9
+ {
+ #1 #7*#3
+ \exp_after:wN \__fp_pack_big:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_big_middle_shift_int
+ #1 #7*#4 #1 #8*#3
+ \exp_after:wN \__fp_pack_big:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_big_middle_shift_int
+ #1 #7*#5 #1 #8*#4 #1 #9*#3 #2
+ \exp_after:wN \__fp_pack_big:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_big_middle_shift_int
+ #1 \__fp_fixed_mul_add:nnnnwnnnn {#7}{#8}{#9}
+ }
+\cs_new:Npn \__fp_fixed_mul_add:nnnnwnnnn #1#2#3#4#5; #6#7#8#9
+ {
+ ( #1*#9 + #2*#8 + #3*#7 + #4*#6 )
+ \exp_after:wN \__fp_pack_big:NNNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_big_trailing_shift_int
+ \__fp_fixed_mul_add:nnnnwnnwN
+ { #6 + #4*#7 + #3*#8 + #2*#9 + #1 }
+ { #7 + #4*#8 + #3*#9 + #2 }
+ {#1} #5;
+ {#6}
+ }
+\cs_new:Npn \__fp_fixed_mul_add:nnnnwnnwN #1#2 #3#4#5; #6#7#8; #9
+ {
+ #9 (#4* #1 *#7)
+ #9 (#5*#6+#4* #2 *#7+#3*#8) / \c__fp_myriad_int
+ }
+\cs_new:Npn \__fp_ep_to_fixed:wwn #1,#2
+ {
+ \exp_after:wN \__fp_ep_to_fixed_auxi:www
+ \int_value:w \__fp_int_eval:w 1 0000 0000 + #2 \exp_after:wN ;
+ \exp:w \exp_end_continue_f:w
+ \prg_replicate:nn { 4 - \int_max:nn {#1} { -32 } } { 0 } ;
+ }
+\cs_new:Npn \__fp_ep_to_fixed_auxi:www 1#1; #2; #3#4#5#6#7;
+ {
+ \__fp_pack_eight:wNNNNNNNN
+ \__fp_pack_twice_four:wNNNNNNNN
+ \__fp_pack_twice_four:wNNNNNNNN
+ \__fp_pack_twice_four:wNNNNNNNN
+ \__fp_ep_to_fixed_auxii:nnnnnnnwn ;
+ #2 #1#3#4#5#6#7 0000 !
+ }
+\cs_new:Npn \__fp_ep_to_fixed_auxii:nnnnnnnwn #1#2#3#4#5#6#7; #8! #9
+ { #9 {#1#2}{#3}{#4}{#5}{#6}{#7}; }
+\cs_new:Npn \__fp_ep_to_ep:wwN #1,#2#3#4#5#6#7; #8
+ {
+ \exp_after:wN #8
+ \int_value:w \__fp_int_eval:w #1 + 4
+ \exp_after:wN \use_i:nn
+ \exp_after:wN \__fp_ep_to_ep_loop:N
+ \int_value:w \__fp_int_eval:w 1 0000 0000 + #2 \__fp_int_eval_end:
+ #3#4#5#6#7 ; ; !
+ }
+\cs_new:Npn \__fp_ep_to_ep_loop:N #1
+ {
+ \if_meaning:w 0 #1
+ - 1
+ \else:
+ \__fp_ep_to_ep_end:www #1
+ \fi:
+ \__fp_ep_to_ep_loop:N
+ }
+\cs_new:Npn \__fp_ep_to_ep_end:www
+ #1 \fi: \__fp_ep_to_ep_loop:N #2; #3!
+ {
+ \fi:
+ \if_meaning:w ; #1
+ - 2 * \c__fp_max_exponent_int
+ \__fp_ep_to_ep_zero:ww
+ \fi:
+ \__fp_pack_twice_four:wNNNNNNNN
+ \__fp_pack_twice_four:wNNNNNNNN
+ \__fp_pack_twice_four:wNNNNNNNN
+ \__fp_use_i:ww , ;
+ #1 #2 0000 0000 0000 0000 0000 0000 ;
+ }
+\cs_new:Npn \__fp_ep_to_ep_zero:ww \fi: #1; #2; #3;
+ { \fi: , {1000}{0000}{0000}{0000}{0000}{0000} ; }
+\cs_new:Npn \__fp_ep_compare:wwww #1,#2#3#4#5#6#7;
+ { \__fp_ep_compare_aux:wwww {#1}{#2}{#3}{#4}{#5}; #6#7; }
+\cs_new:Npn \__fp_ep_compare_aux:wwww #1;#2;#3,#4#5#6#7#8#9;
+ {
+ \if_case:w
+ \__fp_compare_npos:nwnw #1; {#3}{#4}{#5}{#6}{#7}; \exp_stop_f:
+ \if_int_compare:w #2 = #8#9 \exp_stop_f:
+ 0
+ \else:
+ \if_int_compare:w #2 < #8#9 - \fi: 1
+ \fi:
+ \or: 1
+ \else: -1
+ \fi:
+ }
+\cs_new:Npn \__fp_ep_mul:wwwwn #1,#2; #3,#4;
+ {
+ \__fp_ep_to_ep:wwN #3,#4;
+ \__fp_fixed_continue:wn
+ {
+ \__fp_ep_to_ep:wwN #1,#2;
+ \__fp_ep_mul_raw:wwwwN
+ }
+ \__fp_fixed_continue:wn
+ }
+\cs_new:Npn \__fp_ep_mul_raw:wwwwN #1,#2; #3,#4; #5
+ {
+ \__fp_fixed_mul:wwn #2; #4;
+ { \exp_after:wN #5 \int_value:w \__fp_int_eval:w #1 + #3 , }
+ }
+\cs_new:Npn \__fp_ep_div:wwwwn #1,#2; #3,#4;
+ {
+ \__fp_ep_to_ep:wwN #1,#2;
+ \__fp_fixed_continue:wn
+ {
+ \__fp_ep_to_ep:wwN #3,#4;
+ \__fp_ep_div_esti:wwwwn
+ }
+ }
+\cs_new:Npn \__fp_ep_div_esti:wwwwn #1,#2#3; #4,
+ {
+ \exp_after:wN \__fp_ep_div_estii:wwnnwwn
+ \int_value:w \__fp_int_eval:w 10 0000 0000 / ( #2 + 1 )
+ \exp_after:wN ;
+ \int_value:w \__fp_int_eval:w #4 - #1 + 1 ,
+ {#2} #3;
+ }
+\cs_new:Npn \__fp_ep_div_estii:wwnnwwn #1; #2,#3#4#5; #6; #7
+ {
+ \exp_after:wN \__fp_ep_div_estiii:NNNNNwwwn
+ \int_value:w \__fp_int_eval:w 10 0000 0000 - 1750
+ + #1 000 + (10 0000 0000 / #3 - #1) * (1000 - #4 / 10) ;
+ {#3}{#4}#5; #6; { #7 #2, }
+ }
+\cs_new:Npn \__fp_ep_div_estiii:NNNNNwwwn 1#1#2#3#4#5#6; #7;
+ {
+ \__fp_fixed_mul_short:wwn #7; {#1}{#2#3#4#5}{#6};
+ \__fp_ep_div_epsi:wnNNNNNn {#1#2#3#4}#5#6
+ \__fp_fixed_mul:wwn
+ }
+\cs_new:Npn \__fp_ep_div_epsi:wnNNNNNn #1#2#3#4#5#6;
+ {
+ \exp_after:wN \__fp_ep_div_epsii:wwnNNNNNn
+ \int_value:w \__fp_int_eval:w 1 9998 - #2
+ \exp_after:wN \__fp_ep_div_eps_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w 1 9999 9998 - #3#4
+ \exp_after:wN \__fp_ep_div_eps_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w 2 0000 0000 - #5#6 ; ;
+ }
+\cs_new:Npn \__fp_ep_div_eps_pack:NNNNNw #1#2#3#4#5#6;
+ { + #1 ; {#2#3#4#5} {#6} }
+\cs_new:Npn \__fp_ep_div_epsii:wwnNNNNNn 1#1; #2; #3#4#5#6#7#8
+ {
+ \__fp_fixed_mul:wwn {0000}{#1}#2; {0000}{#1}#2;
+ \__fp_fixed_add_one:wN
+ \__fp_fixed_mul:wwn {10000} {#1} #2 ;
+ {
+ \__fp_fixed_mul_short:wwn {0000}{#1}#2; {#3}{#4#5#6#7}{#8000};
+ \__fp_fixed_div_myriad:wn
+ \__fp_fixed_mul:wwn
+ }
+ \__fp_fixed_add:wwn {#3}{#4#5#6#7}{#8000}{0000}{0000}{0000};
+ }
+\cs_new:Npn \__fp_ep_isqrt:wwn #1,#2;
+ {
+ \__fp_ep_to_ep:wwN #1,#2;
+ \__fp_ep_isqrt_auxi:wwn
+ }
+\cs_new:Npn \__fp_ep_isqrt_auxi:wwn #1,
+ {
+ \exp_after:wN \__fp_ep_isqrt_auxii:wwnnnwn
+ \int_value:w \__fp_int_eval:w
+ \int_if_odd:nTF {#1}
+ { (1 - #1) / 2 , 535 , { 0 } { } }
+ { 1 - #1 / 2 , 168 , { } { 0 } }
+ }
+\cs_new:Npn \__fp_ep_isqrt_auxii:wwnnnwn #1, #2, #3#4 #5#6; #7
+ {
+ \__fp_ep_isqrt_esti:wwwnnwn #2, 0, #5, {#3} {#4}
+ {#5} #6 ; { #7 #1 , }
+ }
+\cs_new:Npn \__fp_ep_isqrt_esti:wwwnnwn #1, #2, #3, #4
+ {
+ \if_int_compare:w #1 = #2 \exp_stop_f:
+ \exp_after:wN \__fp_ep_isqrt_estii:wwwnnwn
+ \fi:
+ \exp_after:wN \__fp_ep_isqrt_esti:wwwnnwn
+ \int_value:w \__fp_int_eval:w
+ (#1 + 1 0050 0000 #4 / (#1 * #3)) / 2 ,
+ #1, #3, {#4}
+ }
+\cs_new:Npn \__fp_ep_isqrt_estii:wwwnnwn #1, #2, #3, #4#5
+ {
+ \exp_after:wN \__fp_ep_isqrt_estiii:NNNNNwwwn
+ \int_value:w \__fp_int_eval:w 1000 0000 + #2 * #2 #5 * 5
+ \exp_after:wN , \int_value:w \__fp_int_eval:w 10000 + #2 ;
+ }
+\cs_new:Npn \__fp_ep_isqrt_estiii:NNNNNwwwn 1#1#2#3#4#5#6, 1#7#8; #9;
+ {
+ \__fp_fixed_mul_short:wwn #9; {#1} {#2#3#4#5} {#600} ;
+ \__fp_ep_isqrt_epsi:wN
+ \__fp_fixed_mul_short:wwn {#7} {#80} {0000} ;
+ }
+\cs_new:Npn \__fp_ep_isqrt_epsi:wN #1;
+ {
+ \__fp_fixed_sub:wwn {15000}{0000}{0000}{0000}{0000}{0000}; #1;
+ \__fp_ep_isqrt_epsii:wwN #1;
+ \__fp_ep_isqrt_epsii:wwN #1;
+ \__fp_ep_isqrt_epsii:wwN #1;
+ }
+\cs_new:Npn \__fp_ep_isqrt_epsii:wwN #1; #2;
+ {
+ \__fp_fixed_mul:wwn #1; #1;
+ \__fp_fixed_mul_sub_back:wwwn #2;
+ {15000}{0000}{0000}{0000}{0000}{0000};
+ \__fp_fixed_mul:wwn #1;
+ }
+\cs_new:Npn \__fp_ep_to_float_o:wwN #1,
+ { + \__fp_int_eval:w #1 \__fp_fixed_to_float_o:wN }
+\cs_new:Npn \__fp_ep_inv_to_float_o:wwN #1,#2;
+ {
+ \__fp_ep_div:wwwwn 1,{1000}{0000}{0000}{0000}{0000}{0000}; #1,#2;
+ \__fp_ep_to_float_o:wwN
+ }
+\cs_new:Npn \__fp_fixed_inv_to_float_o:wN
+ { \__fp_ep_inv_to_float_o:wwN 0, }
+\cs_new:Npn \__fp_fixed_to_float_rad_o:wN #1;
+ {
+ \__fp_fixed_mul:wwn #1; {5729}{5779}{5130}{8232}{0876}{7981};
+ { \__fp_ep_to_float_o:wwN 2, }
+ }
+\cs_new:Npn \__fp_fixed_to_float_o:Nw #1#2;
+ { \__fp_fixed_to_float_o:wN #2; #1 }
+\cs_new:Npn \__fp_fixed_to_float_o:wN #1#2#3#4#5#6; #7
+ { % for the 8-digit-at-the-start thing
+ + \__fp_int_eval:w \c__fp_block_int
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__fp_fixed_to_loop:N
+ \exp_after:wN \use_none:n
+ \int_value:w \__fp_int_eval:w
+ 1 0000 0000 + #1 \exp_after:wN \__fp_use_none_stop_f:n
+ \int_value:w 1#2 \exp_after:wN \__fp_use_none_stop_f:n
+ \int_value:w 1#3#4 \exp_after:wN \__fp_use_none_stop_f:n
+ \int_value:w 1#5#6
+ \exp_after:wN ;
+ \exp_after:wN ;
+ }
+\cs_new:Npn \__fp_fixed_to_loop:N #1
+ {
+ \if_meaning:w 0 #1
+ - 1
+ \exp_after:wN \__fp_fixed_to_loop:N
+ \else:
+ \exp_after:wN \__fp_fixed_to_loop_end:w
+ \exp_after:wN #1
+ \fi:
+ }
+\cs_new:Npn \__fp_fixed_to_loop_end:w #1 #2 ;
+ {
+ \if_meaning:w ; #1
+ \exp_after:wN \__fp_fixed_to_float_zero:w
+ \else:
+ \exp_after:wN \__fp_pack_twice_four:wNNNNNNNN
+ \exp_after:wN \__fp_pack_twice_four:wNNNNNNNN
+ \exp_after:wN \__fp_fixed_to_float_pack:ww
+ \exp_after:wN ;
+ \fi:
+ #1 #2 0000 0000 0000 0000 ;
+ }
+\cs_new:Npn \__fp_fixed_to_float_zero:w ; 0000 0000 0000 0000 ;
+ {
+ - 2 * \c__fp_max_exponent_int ;
+ {0000} {0000} {0000} {0000} ;
+ }
+\cs_new:Npn \__fp_fixed_to_float_pack:ww #1 ; #2#3 ; ;
+ {
+ \if_int_compare:w #2 > 4 \exp_stop_f:
+ \exp_after:wN \__fp_fixed_to_float_round_up:wnnnnw
+ \fi:
+ ; #1 ;
+ }
+\cs_new:Npn \__fp_fixed_to_float_round_up:wnnnnw ; #1#2#3#4 ;
+ {
+ \exp_after:wN \__fp_basics_pack_high:NNNNNw
+ \int_value:w \__fp_int_eval:w 1 #1#2
+ \exp_after:wN \__fp_basics_pack_low:NNNNNw
+ \int_value:w \__fp_int_eval:w 1 #3#4 + 1 ;
+ }
+%% File: l3fp-expo.dtx
+\cs_new:Npn \__fp_parse_word_exp:N
+ { \__fp_parse_unary_function:NNN \__fp_exp_o:w ? }
+\cs_new:Npn \__fp_parse_word_ln:N
+ { \__fp_parse_unary_function:NNN \__fp_ln_o:w ? }
+\cs_new:Npn \__fp_parse_word_fact:N
+ { \__fp_parse_unary_function:NNN \__fp_fact_o:w ? }
+\tl_const:Nn \c__fp_ln_i_fixed_tl { {0000}{0000}{0000}{0000}{0000}{0000};}
+\tl_const:Nn \c__fp_ln_ii_fixed_tl { {6931}{4718}{0559}{9453}{0941}{7232};}
+\tl_const:Nn \c__fp_ln_iii_fixed_tl {{10986}{1228}{8668}{1096}{9139}{5245};}
+\tl_const:Nn \c__fp_ln_iv_fixed_tl {{13862}{9436}{1119}{8906}{1883}{4464};}
+\tl_const:Nn \c__fp_ln_vi_fixed_tl {{17917}{5946}{9228}{0550}{0081}{2477};}
+\tl_const:Nn \c__fp_ln_vii_fixed_tl {{19459}{1014}{9055}{3133}{0510}{5353};}
+\tl_const:Nn \c__fp_ln_viii_fixed_tl{{20794}{4154}{1679}{8359}{2825}{1696};}
+\tl_const:Nn \c__fp_ln_ix_fixed_tl {{21972}{2457}{7336}{2193}{8279}{0490};}
+\tl_const:Nn \c__fp_ln_x_fixed_tl {{23025}{8509}{2994}{0456}{8401}{7991};}
+\cs_new:Npn \__fp_ln_o:w #1 \s__fp \__fp_chk:w #2#3#4; @
+ {
+ \if_meaning:w 2 #3
+ \__fp_case_use:nw { \__fp_invalid_operation_o:nw { ln } }
+ \fi:
+ \if_case:w #2 \exp_stop_f:
+ \__fp_case_use:nw
+ { \__fp_division_by_zero_o:Nnw \c_minus_inf_fp { ln } }
+ \or:
+ \else:
+ \__fp_case_return_same_o:w
+ \fi:
+ \__fp_ln_npos_o:w \s__fp \__fp_chk:w #2#3#4;
+ }
+\cs_new:Npn \__fp_ln_npos_o:w \s__fp \__fp_chk:w 10#1#2#3;
+ { %^^A todo: ln(1) should be "exact zero", not "underflow"
+ \exp_after:wN \__fp_sanitize:Nw
+ \int_value:w % for the overall sign
+ \if_int_compare:w #1 < \c_one_int
+ 2
+ \else:
+ 0
+ \fi:
+ \exp_after:wN \exp_stop_f:
+ \int_value:w \__fp_int_eval:w % for the exponent
+ \__fp_ln_significand:NNNNnnnN #2#3
+ \__fp_ln_exponent:wn {#1}
+ }
+\cs_new:Npn \__fp_ln_significand:NNNNnnnN #1#2#3#4
+ {
+ \exp_after:wN \__fp_ln_x_ii:wnnnn
+ \int_value:w
+ \if_case:w #1 \exp_stop_f:
+ \or:
+ \if_int_compare:w #2 < 4 \exp_stop_f:
+ \__fp_int_eval:w 10 - #2
+ \else:
+ 6
+ \fi:
+ \or: 4
+ \or: 3
+ \or: 2
+ \or: 2
+ \or: 2
+ \else: 1
+ \fi:
+ ; { #1 #2 #3 #4 }
+ }
+\cs_new:Npn \__fp_ln_x_ii:wnnnn #1; #2#3#4#5
+ {
+ \exp_after:wN \__fp_ln_div_after:Nw
+ \cs:w c__fp_ln_ \__fp_int_to_roman:w #1 _fixed_tl \exp_after:wN \cs_end:
+ \int_value:w
+ \exp_after:wN \__fp_ln_x_iv:wnnnnnnnn
+ \int_value:w \__fp_int_eval:w
+ \exp_after:wN \__fp_ln_x_iii_var:NNNNNw
+ \int_value:w \__fp_int_eval:w 9999 9990 + #1*#2#3 +
+ \exp_after:wN \__fp_ln_x_iii:NNNNNNw
+ \int_value:w \__fp_int_eval:w 10 0000 0000 + #1*#4#5 ;
+ {20000} {0000} {0000} {0000}
+ } %^^A todo: reoptimize (a generalization attempt failed).
+\cs_new:Npn \__fp_ln_x_iii:NNNNNNw #1#2 #3#4#5#6 #7;
+ { #1#2; {#3#4#5#6} {#7} }
+\cs_new:Npn \__fp_ln_x_iii_var:NNNNNw #1 #2#3#4#5 #6;
+ {
+ #1#2#3#4#5 + 1 ;
+ {#1#2#3#4#5} {#6}
+ }
+\cs_new:Npn \__fp_ln_x_iv:wnnnnnnnn #1; #2#3#4#5 #6#7#8#9
+ {
+ \exp_after:wN \__fp_div_significand_pack:NNN
+ \int_value:w \__fp_int_eval:w
+ \__fp_ln_div_i:w #1 ;
+ #6 #7 ; {#8} {#9}
+ {#2} {#3} {#4} {#5}
+ { \exp_after:wN \__fp_ln_div_ii:wwn \int_value:w #1 }
+ { \exp_after:wN \__fp_ln_div_ii:wwn \int_value:w #1 }
+ { \exp_after:wN \__fp_ln_div_ii:wwn \int_value:w #1 }
+ { \exp_after:wN \__fp_ln_div_ii:wwn \int_value:w #1 }
+ { \exp_after:wN \__fp_ln_div_vi:wwn \int_value:w #1 }
+ }
+\cs_new:Npn \__fp_ln_div_i:w #1;
+ {
+ \exp_after:wN \__fp_div_significand_calc:wwnnnnnnn
+ \int_value:w \__fp_int_eval:w 999999 + 2 0000 0000 / #1 ; % Q1
+ }
+\cs_new:Npn \__fp_ln_div_ii:wwn #1; #2;#3 % y; B1;B2 <- for k=1
+ {
+ \exp_after:wN \__fp_div_significand_pack:NNN
+ \int_value:w \__fp_int_eval:w
+ \exp_after:wN \__fp_div_significand_calc:wwnnnnnnn
+ \int_value:w \__fp_int_eval:w 999999 + #2 #3 / #1 ; % Q2
+ #2 #3 ;
+ }
+\cs_new:Npn \__fp_ln_div_vi:wwn #1; #2;#3#4#5 #6#7#8#9 %y;F1;F2F3F4x1x2x3x4
+ {
+ \exp_after:wN \__fp_div_significand_pack:NNN
+ \int_value:w \__fp_int_eval:w 1000000 + #2 #3 / #1 ; % Q6
+ }
+\cs_new:Npn \__fp_ln_div_after:Nw #1#2;
+ {
+ \if_meaning:w 0 #2
+ \exp_after:wN \__fp_ln_t_small:Nw
+ \else:
+ \exp_after:wN \__fp_ln_t_large:NNw
+ \exp_after:wN -
+ \fi:
+ #1
+ }
+\cs_new:Npn \__fp_ln_t_small:Nw #1 #2; #3; #4; #5; #6; #7;
+ {
+ \exp_after:wN \__fp_ln_t_large:NNw
+ \exp_after:wN + % <sign>
+ \exp_after:wN #1
+ \int_value:w \__fp_int_eval:w 9999 - #2 \exp_after:wN ;
+ \int_value:w \__fp_int_eval:w 9999 - #3 \exp_after:wN ;
+ \int_value:w \__fp_int_eval:w 9999 - #4 \exp_after:wN ;
+ \int_value:w \__fp_int_eval:w 9999 - #5 \exp_after:wN ;
+ \int_value:w \__fp_int_eval:w 9999 - #6 \exp_after:wN ;
+ \int_value:w \__fp_int_eval:w 1 0000 - #7 ;
+ }
+\cs_new:Npn \__fp_ln_t_large:NNw #1 #2 #3; #4; #5; #6; #7; #8;
+ {
+ \exp_after:wN \__fp_ln_square_t_after:w
+ \int_value:w \__fp_int_eval:w 9999 0000 + #3*#3
+ \exp_after:wN \__fp_ln_square_t_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w 9999 0000 + 2*#3*#4
+ \exp_after:wN \__fp_ln_square_t_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w 9999 0000 + 2*#3*#5 + #4*#4
+ \exp_after:wN \__fp_ln_square_t_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w 9999 0000 + 2*#3*#6 + 2*#4*#5
+ \exp_after:wN \__fp_ln_square_t_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w
+ 1 0000 0000 + 2*#3*#7 + 2*#4*#6 + #5*#5
+ + (2*#3*#8 + 2*#4*#7 + 2*#5*#6) / 1 0000
+ % ; ; ;
+ \exp_after:wN \__fp_ln_twice_t_after:w
+ \int_value:w \__fp_int_eval:w -1 + 2*#3
+ \exp_after:wN \__fp_ln_twice_t_pack:Nw
+ \int_value:w \__fp_int_eval:w 9999 + 2*#4
+ \exp_after:wN \__fp_ln_twice_t_pack:Nw
+ \int_value:w \__fp_int_eval:w 9999 + 2*#5
+ \exp_after:wN \__fp_ln_twice_t_pack:Nw
+ \int_value:w \__fp_int_eval:w 9999 + 2*#6
+ \exp_after:wN \__fp_ln_twice_t_pack:Nw
+ \int_value:w \__fp_int_eval:w 9999 + 2*#7
+ \exp_after:wN \__fp_ln_twice_t_pack:Nw
+ \int_value:w \__fp_int_eval:w 10000 + 2*#8 ; ;
+ { \__fp_ln_c:NwNw #1 }
+ #2
+ }
+\cs_new:Npn \__fp_ln_twice_t_pack:Nw #1 #2; { + #1 ; {#2} }
+\cs_new:Npn \__fp_ln_twice_t_after:w #1; { ;;; {#1} }
+\cs_new:Npn \__fp_ln_square_t_pack:NNNNNw #1 #2#3#4#5 #6;
+ { + #1#2#3#4#5 ; {#6} }
+\cs_new:Npn \__fp_ln_square_t_after:w 1 0 #1#2#3 #4;
+ { \__fp_ln_Taylor:wwNw {0#1#2#3} {#4} }
+\cs_new:Npn \__fp_ln_Taylor:wwNw
+ { \__fp_ln_Taylor_loop:www 21 ; {0000}{0000}{0000}{0000}{0000}{0000} ; }
+\cs_new:Npn \__fp_ln_Taylor_loop:www #1; #2; #3;
+ {
+ \if_int_compare:w #1 = \c_one_int
+ \__fp_ln_Taylor_break:w
+ \fi:
+ \exp_after:wN \__fp_fixed_div_int:wwN \c__fp_one_fixed_tl #1;
+ \__fp_fixed_add:wwn #2;
+ \__fp_fixed_mul:wwn #3;
+ {
+ \exp_after:wN \__fp_ln_Taylor_loop:www
+ \int_value:w \__fp_int_eval:w #1 - 2 ;
+ }
+ #3;
+ }
+\cs_new:Npn \__fp_ln_Taylor_break:w \fi: #1 \__fp_fixed_add:wwn #2#3; #4 ;;
+ {
+ \fi:
+ \exp_after:wN \__fp_fixed_mul:wwn
+ \exp_after:wN { \int_value:w \__fp_int_eval:w 10000 + #2 } #3;
+ }
+\cs_new:Npn \__fp_ln_c:NwNw #1 #2; #3
+ {
+ \if_meaning:w + #1
+ \exp_after:wN \exp_after:wN \exp_after:wN \__fp_fixed_sub:wwn
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \__fp_fixed_add:wwn
+ \fi:
+ #3 #2 ;
+ }
+\cs_new:Npn \__fp_ln_exponent:wn #1; #2
+ {
+ \if_case:w #2 \exp_stop_f:
+ 0 \__fp_case_return:nw { \__fp_fixed_to_float_o:Nw 2 }
+ \or:
+ \exp_after:wN \__fp_ln_exponent_one:ww \int_value:w
+ \else:
+ \if_int_compare:w #2 > \c_zero_int
+ \exp_after:wN \__fp_ln_exponent_small:NNww
+ \exp_after:wN 0
+ \exp_after:wN \__fp_fixed_sub:wwn \int_value:w
+ \else:
+ \exp_after:wN \__fp_ln_exponent_small:NNww
+ \exp_after:wN 2
+ \exp_after:wN \__fp_fixed_add:wwn \int_value:w -
+ \fi:
+ \fi:
+ #2; #1;
+ }
+\cs_new:Npn \__fp_ln_exponent_one:ww 1; #1;
+ {
+ 0
+ \exp_after:wN \__fp_fixed_sub:wwn \c__fp_ln_x_fixed_tl #1;
+ \__fp_fixed_to_float_o:wN 0
+ }
+\cs_new:Npn \__fp_ln_exponent_small:NNww #1#2#3; #4#5#6#7#8#9;
+ {
+ 4
+ \exp_after:wN \__fp_fixed_mul:wwn
+ \c__fp_ln_x_fixed_tl
+ {#3}{0000}{0000}{0000}{0000}{0000} ;
+ #2
+ {0000}{#4}{#5}{#6}{#7}{#8};
+ \__fp_fixed_to_float_o:wN #1
+ }
+\cs_new:Npn \__fp_exp_o:w #1 \s__fp \__fp_chk:w #2#3#4; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \__fp_case_return_o:Nw \c_one_fp
+ \or:
+ \exp_after:wN \__fp_exp_normal_o:w
+ \or:
+ \if_meaning:w 0 #3
+ \exp_after:wN \__fp_case_return_o:Nw
+ \exp_after:wN \c_inf_fp
+ \else:
+ \exp_after:wN \__fp_case_return_o:Nw
+ \exp_after:wN \c_zero_fp
+ \fi:
+ \or:
+ \__fp_case_return_same_o:w
+ \fi:
+ \s__fp \__fp_chk:w #2#3#4;
+ }
+\cs_new:Npn \__fp_exp_normal_o:w \s__fp \__fp_chk:w 1#1
+ {
+ \if_meaning:w 0 #1
+ \__fp_exp_pos_o:NNwnw + \__fp_fixed_to_float_o:wN
+ \else:
+ \__fp_exp_pos_o:NNwnw - \__fp_fixed_inv_to_float_o:wN
+ \fi:
+ }
+\cs_new:Npn \__fp_exp_pos_o:NNwnw #1#2#3 \fi: #4#5;
+ {
+ \fi:
+ \if_int_compare:w #4 > \c__fp_max_exp_exponent_int
+ \token_if_eq_charcode:NNTF + #1
+ { \__fp_exp_overflow:NN \__fp_overflow:w \c_inf_fp }
+ { \__fp_exp_overflow:NN \__fp_underflow:w \c_zero_fp }
+ \exp:w
+ \else:
+ \exp_after:wN \__fp_sanitize:Nw
+ \exp_after:wN 0
+ \int_value:w #1 \__fp_int_eval:w
+ \if_int_compare:w #4 < \c_zero_int
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ {
+ 0
+ \__fp_decimate:nNnnnn { - #4 }
+ \__fp_exp_Taylor:Nnnwn
+ }
+ {
+ \__fp_decimate:nNnnnn { \c__fp_prec_int - #4 }
+ \__fp_exp_pos_large:NnnNwn
+ }
+ #5
+ {#4}
+ #1 #2 0
+ \exp:w
+ \fi:
+ \exp_after:wN \exp_end:
+ }
+\cs_new:Npn \__fp_exp_overflow:NN #1#2
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN #1
+ \exp_after:wN #2
+ }
+\cs_new:Npn \__fp_exp_Taylor:Nnnwn #1#2#3 #4; #5 #6
+ {
+ #6
+ \__fp_pack_twice_four:wNNNNNNNN
+ \__fp_pack_twice_four:wNNNNNNNN
+ \__fp_pack_twice_four:wNNNNNNNN
+ \__fp_exp_Taylor_ii:ww
+ ; #2#3#4 0000 0000 ;
+ }
+\cs_new:Npn \__fp_exp_Taylor_ii:ww #1; #2;
+ { \__fp_exp_Taylor_loop:www 10 ; #1 ; #1 ; \s__fp_stop }
+\cs_new:Npn \__fp_exp_Taylor_loop:www #1; #2; #3;
+ {
+ \if_int_compare:w #1 = \c_one_int
+ \exp_after:wN \__fp_exp_Taylor_break:Nww
+ \fi:
+ \__fp_fixed_div_int:wwN #3 ; #1 ;
+ \__fp_fixed_add_one:wN
+ \__fp_fixed_mul:wwn #2 ;
+ {
+ \exp_after:wN \__fp_exp_Taylor_loop:www
+ \int_value:w \__fp_int_eval:w #1 - 1 ;
+ #2 ;
+ }
+ }
+\cs_new:Npn \__fp_exp_Taylor_break:Nww #1 #2; #3 \s__fp_stop
+ { \__fp_fixed_add_one:wN #2 ; }
+\intarray_const_from_clist:Nn \c__fp_exp_intarray
+ {
+ 1 , 1 1105 1709 , 1 1807 5647 , 1 6248 1171 ,
+ 1 , 1 1221 4027 , 1 5816 0169 , 1 8339 2107 ,
+ 1 , 1 1349 8588 , 1 0757 6003 , 1 1039 8374 ,
+ 1 , 1 1491 8246 , 1 9764 1270 , 1 3178 2485 ,
+ 1 , 1 1648 7212 , 1 7070 0128 , 1 1468 4865 ,
+ 1 , 1 1822 1188 , 1 0039 0508 , 1 9748 7537 ,
+ 1 , 1 2013 7527 , 1 0747 0476 , 1 5216 2455 ,
+ 1 , 1 2225 5409 , 1 2849 2467 , 1 6045 7954 ,
+ 1 , 1 2459 6031 , 1 1115 6949 , 1 6638 0013 ,
+ 1 , 1 2718 2818 , 1 2845 9045 , 1 2353 6029 ,
+ 1 , 1 7389 0560 , 1 9893 0650 , 1 2272 3043 ,
+ 2 , 1 2008 5536 , 1 9231 8766 , 1 7740 9285 ,
+ 2 , 1 5459 8150 , 1 0331 4423 , 1 9078 1103 ,
+ 3 , 1 1484 1315 , 1 9102 5766 , 1 0342 1116 ,
+ 3 , 1 4034 2879 , 1 3492 7351 , 1 2260 8387 ,
+ 4 , 1 1096 6331 , 1 5842 8458 , 1 5992 6372 ,
+ 4 , 1 2980 9579 , 1 8704 1728 , 1 2747 4359 ,
+ 4 , 1 8103 0839 , 1 2757 5384 , 1 0077 1000 ,
+ 5 , 1 2202 6465 , 1 7948 0671 , 1 6516 9579 ,
+ 9 , 1 4851 6519 , 1 5409 7902 , 1 7796 9107 ,
+ 14 , 1 1068 6474 , 1 5815 2446 , 1 2146 9905 ,
+ 18 , 1 2353 8526 , 1 6837 0199 , 1 8540 7900 ,
+ 22 , 1 5184 7055 , 1 2858 7072 , 1 4640 8745 ,
+ 27 , 1 1142 0073 , 1 8981 5684 , 1 2836 6296 ,
+ 31 , 1 2515 4386 , 1 7091 9167 , 1 0062 6578 ,
+ 35 , 1 5540 6223 , 1 8439 3510 , 1 0525 7117 ,
+ 40 , 1 1220 4032 , 1 9431 7840 , 1 8020 0271 ,
+ 44 , 1 2688 1171 , 1 4181 6135 , 1 4484 1263 ,
+ 87 , 1 7225 9737 , 1 6812 5749 , 1 2581 7748 ,
+ 131 , 1 1942 4263 , 1 9524 1255 , 1 9365 8421 ,
+ 174 , 1 5221 4696 , 1 8976 4143 , 1 9505 8876 ,
+ 218 , 1 1403 5922 , 1 1785 2837 , 1 4107 3977 ,
+ 261 , 1 3773 0203 , 1 0092 9939 , 1 8234 0143 ,
+ 305 , 1 1014 2320 , 1 5473 5004 , 1 5094 5533 ,
+ 348 , 1 2726 3745 , 1 7211 2566 , 1 5673 6478 ,
+ 391 , 1 7328 8142 , 1 2230 7421 , 1 7051 8866 ,
+ 435 , 1 1970 0711 , 1 1401 7046 , 1 9938 8888 ,
+ 869 , 1 3881 1801 , 1 9428 4368 , 1 5764 8232 ,
+ 1303 , 1 7646 2009 , 1 8905 4704 , 1 8893 1073 ,
+ 1738 , 1 1506 3559 , 1 7005 0524 , 1 9009 7592 ,
+ 2172 , 1 2967 6283 , 1 8402 3667 , 1 0689 6630 ,
+ 2606 , 1 5846 4389 , 1 5650 2114 , 1 7278 5046 ,
+ 3041 , 1 1151 7900 , 1 5080 6878 , 1 2914 4154 ,
+ 3475 , 1 2269 1083 , 1 0850 6857 , 1 8724 4002 ,
+ 3909 , 1 4470 3047 , 1 3316 5442 , 1 6408 6591 ,
+ 4343 , 1 8806 8182 , 1 2566 2921 , 1 5872 6150 ,
+ 8686 , 1 7756 0047 , 1 2598 6861 , 1 0458 3204 ,
+ 13029 , 1 6830 5723 , 1 7791 4884 , 1 1932 7351 ,
+ 17372 , 1 6015 5609 , 1 3095 3052 , 1 3494 7574 ,
+ 21715 , 1 5297 7951 , 1 6443 0315 , 1 3251 3576 ,
+ 26058 , 1 4665 6719 , 1 0099 3379 , 1 5527 2929 ,
+ 30401 , 1 4108 9724 , 1 3326 3186 , 1 5271 5665 ,
+ 34744 , 1 3618 6973 , 1 3140 0875 , 1 3856 4102 ,
+ 39087 , 1 3186 9209 , 1 6113 3900 , 1 6705 9685 ,
+ }
+\cs_new:Npn \__fp_exp_pos_large:NnnNwn #1#2#3 #4#5; #6
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \__fp_exp_large:NwN
+ \exp_after:wN \exp_after:wN \exp_after:wN #6
+ \exp_after:wN \c__fp_one_fixed_tl
+ \int_value:w #3 #4 \exp_stop_f:
+ #5 00000 ;
+ }
+\cs_new:Npn \__fp_exp_large:NwN #1#2; #3
+ {
+ \if_case:w #3 ~
+ \exp_after:wN \__fp_fixed_continue:wn
+ \else:
+ \exp_after:wN \__fp_exp_intarray:w
+ \int_value:w \__fp_int_eval:w 36 * #1 + 4 * #3 \exp_after:wN ;
+ \fi:
+ #2;
+ {
+ \if_meaning:w 0 #1
+ \exp_after:wN \__fp_exp_large_after:wwn
+ \else:
+ \exp_after:wN \__fp_exp_large:NwN
+ \int_value:w \__fp_int_eval:w #1 - 1 \exp_after:wN \scan_stop:
+ \fi:
+ }
+ }
+\cs_new:Npn \__fp_exp_intarray:w #1 ;
+ {
+ +
+ \__kernel_intarray_item:Nn \c__fp_exp_intarray
+ { \__fp_int_eval:w #1 - 3 \scan_stop: }
+ \exp_after:wN \use_i:nnn
+ \exp_after:wN \__fp_fixed_mul:wwn
+ \int_value:w 0
+ \exp_after:wN \__fp_exp_intarray_aux:w
+ \int_value:w \__kernel_intarray_item:Nn
+ \c__fp_exp_intarray { \__fp_int_eval:w #1 - 2 }
+ \exp_after:wN \__fp_exp_intarray_aux:w
+ \int_value:w \__kernel_intarray_item:Nn
+ \c__fp_exp_intarray { \__fp_int_eval:w #1 - 1 }
+ \exp_after:wN \__fp_exp_intarray_aux:w
+ \int_value:w \__kernel_intarray_item:Nn \c__fp_exp_intarray {#1} ; ;
+ }
+\cs_new:Npn \__fp_exp_intarray_aux:w 1 #1#2#3#4#5 ; { ; {#1#2#3#4} {#5} }
+\cs_new:Npn \__fp_exp_large_after:wwn #1; #2; #3
+ {
+ \__fp_exp_Taylor:Nnnwn ? { } { } 0 #2; {} #3
+ \__fp_fixed_mul:wwn #1;
+ }
+\cs_new:cpn { __fp_ \iow_char:N \^ _o:ww }
+ \s__fp \__fp_chk:w #1#2#3; \s__fp \__fp_chk:w #4#5#6;
+ {
+ \if_meaning:w 0 #4
+ \__fp_case_return_o:Nw \c_one_fp
+ \fi:
+ \if_case:w #2 \exp_stop_f:
+ \exp_after:wN \use_i:nn
+ \or:
+ \__fp_case_return_o:Nw \c_nan_fp
+ \else:
+ \exp_after:wN \__fp_pow_neg:www
+ \exp:w \exp_end_continue_f:w \exp_after:wN \use:nn
+ \fi:
+ {
+ \if_meaning:w 1 #1
+ \exp_after:wN \__fp_pow_normal_o:ww
+ \else:
+ \exp_after:wN \__fp_pow_zero_or_inf:ww
+ \fi:
+ \s__fp \__fp_chk:w #1#2#3;
+ }
+ { \s__fp \__fp_chk:w #4#5#6; \s__fp \__fp_chk:w #1#2#3; }
+ \s__fp \__fp_chk:w #4#5#6;
+ }
+\cs_new:Npn \__fp_pow_zero_or_inf:ww
+ \s__fp \__fp_chk:w #1#2; \s__fp \__fp_chk:w #3#4
+ {
+ \if_meaning:w 1 #4
+ \__fp_case_return_same_o:w
+ \fi:
+ \if_meaning:w #1 #4
+ \__fp_case_return_o:Nw \c_zero_fp
+ \fi:
+ \if_meaning:w 2 #1
+ \__fp_case_return_o:Nw \c_inf_fp
+ \fi:
+ \if_meaning:w 2 #3
+ \__fp_case_return_o:Nw \c_inf_fp
+ \else:
+ \__fp_case_use:nw
+ {
+ \__fp_division_by_zero_o:NNww \c_inf_fp ^
+ \s__fp \__fp_chk:w #1 #2 ;
+ }
+ \fi:
+ \s__fp \__fp_chk:w #3#4
+ }
+\cs_new:Npn \__fp_pow_normal_o:ww
+ \s__fp \__fp_chk:w 1 #1#2#3; \s__fp \__fp_chk:w #4#5
+ {
+ \if:w 0 \__fp_str_if_eq:nn { #2 #3 } { 1 {1000} {0000} {0000} {0000} }
+ \if_int_compare:w #4 #1 = 32 \exp_stop_f:
+ \exp_after:wN \__fp_case_return_ii_o:ww
+ \fi:
+ \__fp_case_return_o:Nww \c_one_fp
+ \fi:
+ \if_case:w #4 \exp_stop_f:
+ \or:
+ \exp_after:wN \__fp_pow_npos_o:Nww
+ \exp_after:wN #5
+ \or:
+ \if_meaning:w 2 #5 \exp_after:wN \reverse_if:N \fi:
+ \if_int_compare:w #2 > \c_zero_int
+ \exp_after:wN \__fp_case_return_o:Nww
+ \exp_after:wN \c_inf_fp
+ \else:
+ \exp_after:wN \__fp_case_return_o:Nww
+ \exp_after:wN \c_zero_fp
+ \fi:
+ \or:
+ \__fp_case_return_ii_o:ww
+ \fi:
+ \s__fp \__fp_chk:w 1 #1 {#2} #3 ;
+ \s__fp \__fp_chk:w #4 #5
+ }
+\cs_new:Npn \__fp_pow_npos_o:Nww #1 \s__fp \__fp_chk:w 1#2#3
+ {
+ \exp_after:wN \__fp_sanitize:Nw
+ \exp_after:wN 0
+ \int_value:w
+ \if:w #1 \if_int_compare:w #3 > \c_zero_int 0 \else: 2 \fi:
+ \exp_after:wN \__fp_pow_npos_aux:NNnww
+ \exp_after:wN +
+ \exp_after:wN \__fp_fixed_to_float_o:wN
+ \else:
+ \exp_after:wN \__fp_pow_npos_aux:NNnww
+ \exp_after:wN -
+ \exp_after:wN \__fp_fixed_inv_to_float_o:wN
+ \fi:
+ {#3}
+ }
+\cs_new:Npn \__fp_pow_npos_aux:NNnww #1#2#3#4#5; \s__fp \__fp_chk:w 1#6#7#8;
+ {
+ #1
+ \__fp_int_eval:w
+ \__fp_ln_significand:NNNNnnnN #4#5
+ \__fp_pow_exponent:wnN {#3}
+ \__fp_fixed_mul:wwn #8 {0000}{0000} ;
+ \__fp_pow_B:wwN #7;
+ #1 #2 0 % fixed_to_float_o:wN
+ }
+\cs_new:Npn \__fp_pow_exponent:wnN #1; #2
+ {
+ \if_int_compare:w #2 > \c_zero_int
+ \exp_after:wN \__fp_pow_exponent:Nwnnnnnw % n\ln(10) - (-\ln(x))
+ \exp_after:wN +
+ \else:
+ \exp_after:wN \__fp_pow_exponent:Nwnnnnnw % -(|n|\ln(10) + (-\ln(x)))
+ \exp_after:wN -
+ \fi:
+ #2; #1;
+ }
+\cs_new:Npn \__fp_pow_exponent:Nwnnnnnw #1#2; #3#4#5#6#7#8;
+ { %^^A todo: use that in ln.
+ \exp_after:wN \__fp_fixed_mul_after:wwn
+ \int_value:w \__fp_int_eval:w \c__fp_leading_shift_int
+ \exp_after:wN \__fp_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_middle_shift_int
+ #1#2*23025 - #1 #3
+ \exp_after:wN \__fp_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_middle_shift_int
+ #1 #2*8509 - #1 #4
+ \exp_after:wN \__fp_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_middle_shift_int
+ #1 #2*2994 - #1 #5
+ \exp_after:wN \__fp_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_middle_shift_int
+ #1 #2*0456 - #1 #6
+ \exp_after:wN \__fp_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_trailing_shift_int
+ #1 #2*8401 - #1 #7
+ #1 ( #2*7991 - #8 ) / 1 0000 ; ;
+ }
+\cs_new:Npn \__fp_pow_B:wwN #1#2#3#4#5#6; #7;
+ {
+ \if_int_compare:w #7 < \c_zero_int
+ \exp_after:wN \__fp_pow_C_neg:w \int_value:w -
+ \else:
+ \if_int_compare:w #7 < 22 \exp_stop_f:
+ \exp_after:wN \__fp_pow_C_pos:w \int_value:w
+ \else:
+ \exp_after:wN \__fp_pow_C_overflow:w \int_value:w
+ \fi:
+ \fi:
+ #7 \exp_after:wN ;
+ \int_value:w \__fp_int_eval:w 10 0000 + #1 \__fp_int_eval_end:
+ #2#3#4#5#6 0000 0000 0000 0000 0000 0000 ; %^^A todo: how many 0?
+ }
+\cs_new:Npn \__fp_pow_C_overflow:w #1; #2; #3
+ {
+ + 2 * \c__fp_max_exponent_int
+ \exp_after:wN \__fp_fixed_continue:wn \c__fp_one_fixed_tl
+ }
+\cs_new:Npn \__fp_pow_C_neg:w #1 ; 1
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \__fp_pow_C_pack:w
+ \prg_replicate:nn {#1} {0}
+ }
+\cs_new:Npn \__fp_pow_C_pos:w #1; 1
+ { \__fp_pow_C_pos_loop:wN #1; }
+\cs_new:Npn \__fp_pow_C_pos_loop:wN #1; #2
+ {
+ \if_meaning:w 0 #1
+ \exp_after:wN \__fp_pow_C_pack:w
+ \exp_after:wN #2
+ \else:
+ \if_meaning:w 0 #2
+ \exp_after:wN \__fp_pow_C_pos_loop:wN \int_value:w
+ \else:
+ \exp_after:wN \__fp_pow_C_overflow:w \int_value:w
+ \fi:
+ \__fp_int_eval:w #1 - 1 \exp_after:wN ;
+ \fi:
+ }
+\cs_new:Npn \__fp_pow_C_pack:w
+ {
+ \exp_after:wN \__fp_exp_large:NwN
+ \exp_after:wN 5
+ \c__fp_one_fixed_tl
+ }
+\cs_new:Npn \__fp_pow_neg:www \s__fp \__fp_chk:w #1#2; #3; #4;
+ {
+ \if_case:w \__fp_pow_neg_case:w #4 ;
+ \exp_after:wN \__fp_pow_neg_aux:wNN
+ \or:
+ \if_int_compare:w \__fp_int_eval:w #1 / 2 = \c_one_int
+ \__fp_invalid_operation_o:Nww ^ #3; #4;
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__fp_use_none_until_s:w
+ \fi:
+ \fi:
+ \__fp_exp_after_o:w
+ \s__fp \__fp_chk:w #1#2;
+ }
+\cs_new:Npn \__fp_pow_neg_aux:wNN #1 \s__fp \__fp_chk:w #2#3
+ {
+ \exp_after:wN \__fp_exp_after_o:w
+ \exp_after:wN \s__fp
+ \exp_after:wN \__fp_chk:w
+ \exp_after:wN #2
+ \int_value:w \__fp_int_eval:w 2 - #3 \__fp_int_eval_end:
+ }
+\cs_new:Npn \__fp_pow_neg_case:w \s__fp \__fp_chk:w #1#2#3;
+ {
+ \if_case:w #1 \exp_stop_f:
+ -1
+ \or: \__fp_pow_neg_case_aux:nnnnn #3
+ \or: -1
+ \else: 1
+ \fi:
+ \exp_stop_f:
+ }
+\cs_new:Npn \__fp_pow_neg_case_aux:nnnnn #1#2#3#4#5
+ {
+ \if_int_compare:w #1 > \c__fp_prec_int
+ -1
+ \else:
+ \__fp_decimate:nNnnnn { \c__fp_prec_int - #1 }
+ \__fp_pow_neg_case_aux:Nnnw
+ {#2} {#3} {#4} {#5}
+ \fi:
+ }
+\cs_new:Npn \__fp_pow_neg_case_aux:Nnnw #1#2#3#4 ;
+ {
+ \if_meaning:w 0 #1
+ \if_int_odd:w #3 \exp_stop_f:
+ 0
+ \else:
+ -1
+ \fi:
+ \else:
+ 1
+ \fi:
+ }
+\int_const:Nn \c__fp_fact_max_arg_int { 3248 }
+\cs_new:Npn \__fp_fact_o:w #1 \s__fp \__fp_chk:w #2#3#4; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \__fp_case_return_o:Nw \c_one_fp
+ \or:
+ \or:
+ \if_meaning:w 0 #3
+ \exp_after:wN \__fp_case_return_same_o:w
+ \fi:
+ \or:
+ \__fp_case_return_same_o:w
+ \fi:
+ \if_meaning:w 2 #3
+ \__fp_case_use:nw { \__fp_invalid_operation_o:fw { fact } }
+ \fi:
+ \__fp_fact_pos_o:w
+ \s__fp \__fp_chk:w #2 #3 #4 ;
+ }
+\cs_new:Npn \__fp_fact_pos_o:w #1;
+ {
+ \__fp_small_int:wTF #1;
+ { \__fp_fact_int_o:n }
+ { \__fp_invalid_operation_o:fw { fact } #1; }
+ }
+\cs_new:Npn \__fp_fact_int_o:n #1
+ {
+ \if_int_compare:w #1 > \c__fp_fact_max_arg_int
+ \__fp_case_return:nw
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \__fp_overflow:w
+ \exp_after:wN \c_inf_fp
+ }
+ \fi:
+ \exp_after:wN \__fp_sanitize:Nw
+ \exp_after:wN 0
+ \int_value:w \__fp_int_eval:w
+ \__fp_fact_loop_o:w #1 . 4 , { 1 } { } { } { } { } { } ;
+ }
+\cs_new:Npn \__fp_fact_loop_o:w #1 . #2 ;
+ {
+ \if_int_compare:w #1 < 12 \exp_stop_f:
+ \__fp_fact_small_o:w #1
+ \fi:
+ \exp_after:wN \__fp_ep_mul:wwwwn
+ \exp_after:wN 4 \exp_after:wN ,
+ \exp_after:wN { \int_value:w \__fp_int_eval:w #1 * (#1 - 1) }
+ { } { } { } { } { } ;
+ #2 ;
+ {
+ \exp_after:wN \__fp_fact_loop_o:w
+ \int_value:w \__fp_int_eval:w #1 - 2 .
+ }
+ }
+\cs_new:Npn \__fp_fact_small_o:w #1 \fi: #2 ; #3 ; #4
+ {
+ \fi:
+ \exp_after:wN \__fp_ep_mul:wwwwn
+ \exp_after:wN 4 \exp_after:wN ,
+ \exp_after:wN
+ {
+ \int_value:w
+ \if_case:w #1 \exp_stop_f:
+ 1 \or: 1 \or: 2 \or: 6 \or: 24 \or: 120 \or: 720 \or: 5040
+ \or: 40320 \or: 362880 \or: 3628800 \or: 39916800
+ \fi:
+ } { } { } { } { } { } ;
+ #3 ;
+ \__fp_ep_to_float_o:wwN 0
+ }
+%% File: l3fp-trig.dtx
+\tl_map_inline:nn
+ {
+ {acos} {acsc} {asec} {asin}
+ {cos} {cot} {csc} {sec} {sin} {tan}
+ }
+ {
+ \cs_new:cpe { __fp_parse_word_#1:N }
+ {
+ \exp_not:N \__fp_parse_unary_function:NNN
+ \exp_not:c { __fp_#1_o:w }
+ \exp_not:N \use_i:nn
+ }
+ \cs_new:cpe { __fp_parse_word_#1d:N }
+ {
+ \exp_not:N \__fp_parse_unary_function:NNN
+ \exp_not:c { __fp_#1_o:w }
+ \exp_not:N \use_ii:nn
+ }
+ }
+\cs_new:Npn \__fp_parse_word_acot:N
+ { \__fp_parse_function:NNN \__fp_acot_o:Nw \use_i:nn }
+\cs_new:Npn \__fp_parse_word_acotd:N
+ { \__fp_parse_function:NNN \__fp_acot_o:Nw \use_ii:nn }
+\cs_new:Npn \__fp_parse_word_atan:N
+ { \__fp_parse_function:NNN \__fp_atan_o:Nw \use_i:nn }
+\cs_new:Npn \__fp_parse_word_atand:N
+ { \__fp_parse_function:NNN \__fp_atan_o:Nw \use_ii:nn }
+\cs_new:Npn \__fp_sin_o:w #1 \s__fp \__fp_chk:w #2#3#4; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \__fp_case_return_same_o:w
+ \or: \__fp_case_use:nw
+ {
+ \__fp_trig:NNNNNwn #1 \__fp_sin_series_o:NNwwww
+ \__fp_ep_to_float_o:wwN #3 0
+ }
+ \or: \__fp_case_use:nw
+ { \__fp_invalid_operation_o:fw { #1 { sin } { sind } } }
+ \else: \__fp_case_return_same_o:w
+ \fi:
+ \s__fp \__fp_chk:w #2 #3 #4;
+ }
+\cs_new:Npn \__fp_cos_o:w #1 \s__fp \__fp_chk:w #2#3; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \__fp_case_return_o:Nw \c_one_fp
+ \or: \__fp_case_use:nw
+ {
+ \__fp_trig:NNNNNwn #1 \__fp_sin_series_o:NNwwww
+ \__fp_ep_to_float_o:wwN 0 2
+ }
+ \or: \__fp_case_use:nw
+ { \__fp_invalid_operation_o:fw { #1 { cos } { cosd } } }
+ \else: \__fp_case_return_same_o:w
+ \fi:
+ \s__fp \__fp_chk:w #2 #3;
+ }
+\cs_new:Npn \__fp_csc_o:w #1 \s__fp \__fp_chk:w #2#3#4; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \__fp_cot_zero_o:Nfw #3 { #1 { csc } { cscd } }
+ \or: \__fp_case_use:nw
+ {
+ \__fp_trig:NNNNNwn #1 \__fp_sin_series_o:NNwwww
+ \__fp_ep_inv_to_float_o:wwN #3 0
+ }
+ \or: \__fp_case_use:nw
+ { \__fp_invalid_operation_o:fw { #1 { csc } { cscd } } }
+ \else: \__fp_case_return_same_o:w
+ \fi:
+ \s__fp \__fp_chk:w #2 #3 #4;
+ }
+\cs_new:Npn \__fp_sec_o:w #1 \s__fp \__fp_chk:w #2#3; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \__fp_case_return_o:Nw \c_one_fp
+ \or: \__fp_case_use:nw
+ {
+ \__fp_trig:NNNNNwn #1 \__fp_sin_series_o:NNwwww
+ \__fp_ep_inv_to_float_o:wwN 0 2
+ }
+ \or: \__fp_case_use:nw
+ { \__fp_invalid_operation_o:fw { #1 { sec } { secd } } }
+ \else: \__fp_case_return_same_o:w
+ \fi:
+ \s__fp \__fp_chk:w #2 #3;
+ }
+\cs_new:Npn \__fp_tan_o:w #1 \s__fp \__fp_chk:w #2#3#4; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \__fp_case_return_same_o:w
+ \or: \__fp_case_use:nw
+ {
+ \__fp_trig:NNNNNwn #1
+ \__fp_tan_series_o:NNwwww 0 #3 1
+ }
+ \or: \__fp_case_use:nw
+ { \__fp_invalid_operation_o:fw { #1 { tan } { tand } } }
+ \else: \__fp_case_return_same_o:w
+ \fi:
+ \s__fp \__fp_chk:w #2 #3 #4;
+ }
+\cs_new:Npn \__fp_cot_o:w #1 \s__fp \__fp_chk:w #2#3#4; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \__fp_cot_zero_o:Nfw #3 { #1 { cot } { cotd } }
+ \or: \__fp_case_use:nw
+ {
+ \__fp_trig:NNNNNwn #1
+ \__fp_tan_series_o:NNwwww 2 #3 3
+ }
+ \or: \__fp_case_use:nw
+ { \__fp_invalid_operation_o:fw { #1 { cot } { cotd } } }
+ \else: \__fp_case_return_same_o:w
+ \fi:
+ \s__fp \__fp_chk:w #2 #3 #4;
+ }
+\cs_new:Npn \__fp_cot_zero_o:Nfw #1#2#3 \fi:
+ {
+ \fi:
+ \token_if_eq_meaning:NNTF 0 #1
+ { \exp_args:NNf \__fp_division_by_zero_o:Nnw \c_inf_fp }
+ { \exp_args:NNf \__fp_division_by_zero_o:Nnw \c_minus_inf_fp }
+ {#2}
+ }
+\cs_new:Npn \__fp_trig:NNNNNwn #1#2#3#4#5 \s__fp \__fp_chk:w 1#6#7#8;
+ {
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN #4
+ \int_value:w \__fp_int_eval:w #5
+ \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN
+ \if_int_compare:w #7 > #1 0 1 \exp_stop_f:
+ #1 \__fp_trig_large:ww \__fp_trigd_large:ww
+ \else:
+ #1 \__fp_trig_small:ww \__fp_trigd_small:ww
+ \fi:
+ #7,#8{0000}{0000};
+ }
+\cs_new:Npn \__fp_trig_small:ww #1,#2;
+ { \__fp_ep_to_fixed:wwn #1,#2; . #1,#2; }
+\cs_new:Npn \__fp_trigd_small:ww #1,#2;
+ {
+ \__fp_ep_mul_raw:wwwwN
+ -1,{1745}{3292}{5199}{4329}{5769}{2369}; #1,#2;
+ \__fp_trig_small:ww
+ }
+\cs_new:Npn \__fp_trigd_large:ww #1, #2#3#4#5#6#7;
+ {
+ \exp_after:wN \__fp_pack_eight:wNNNNNNNN
+ \exp_after:wN \__fp_pack_eight:wNNNNNNNN
+ \exp_after:wN \__fp_pack_twice_four:wNNNNNNNN
+ \exp_after:wN \__fp_pack_twice_four:wNNNNNNNN
+ \exp_after:wN \__fp_trigd_large_auxi:nnnnwNNNN
+ \exp_after:wN ;
+ \exp:w \exp_end_continue_f:w
+ \prg_replicate:nn { \int_max:nn { 22 - #1 } { 0 } } { 0 }
+ #2#3#4#5#6#7 0000 0000 0000 !
+ }
+\cs_new:Npn \__fp_trigd_large_auxi:nnnnwNNNN #1#2#3#4#5; #6#7#8#9
+ {
+ \exp_after:wN \__fp_trigd_large_auxii:wNw
+ \int_value:w \__fp_int_eval:w #1 + #2
+ - (#1 + #2 - 4) / 9 * 9 \__fp_int_eval_end:
+ #3;
+ #4; #5{#6#7#8#9};
+ }
+\cs_new:Npn \__fp_trigd_large_auxii:wNw #1; #2#3;
+ {
+ + (#1#2 - 4) / 9 * 2
+ \exp_after:wN \__fp_trigd_large_auxiii:www
+ \int_value:w \__fp_int_eval:w #1#2
+ - (#1#2 - 4) / 9 * 9 \__fp_int_eval_end: #3 ;
+ }
+\cs_new:Npn \__fp_trigd_large_auxiii:www #1; #2; #3!
+ {
+ \if_int_compare:w #1 < 4500 \exp_stop_f:
+ \exp_after:wN \__fp_use_i_until_s:nw
+ \exp_after:wN \__fp_fixed_continue:wn
+ \else:
+ + 1
+ \fi:
+ \__fp_fixed_sub:wwn {9000}{0000}{0000}{0000}{0000}{0000};
+ {#1}#2{0000}{0000};
+ { \__fp_trigd_small:ww 2, }
+ }
+\intarray_const_from_clist:Nn \c__fp_trig_intarray
+ {
+ 100000000, 100000000, 115915494, 130918953, 135768883, 176337251,
+ 143620344, 159645740, 145644874, 176673440, 158896797, 163422653,
+ 150901138, 102766253, 108595607, 128427267, 157958036, 189291184,
+ 161145786, 152877967, 141073169, 198392292, 139966937, 140907757,
+ 130777463, 196925307, 168871739, 128962173, 197661693, 136239024,
+ 117236290, 111832380, 111422269, 197557159, 140461890, 108690267,
+ 139561204, 189410936, 193784408, 155287230, 199946443, 140024867,
+ 123477394, 159610898, 132309678, 130749061, 166986462, 180469944,
+ 186521878, 181574786, 156696424, 110389958, 174139348, 160998386,
+ 180991999, 162442875, 158517117, 188584311, 117518767, 116054654,
+ 175369880, 109739460, 136475933, 137680593, 102494496, 163530532,
+ 171567755, 103220324, 177781639, 171660229, 146748119, 159816584,
+ 106060168, 103035998, 113391198, 174988327, 186654435, 127975507,
+ 100162406, 177564388, 184957131, 108801221, 199376147, 168137776,
+ 147378906, 133068046, 145797848, 117613124, 127314069, 196077502,
+ 145002977, 159857089, 105690279, 167851315, 125210016, 131774602,
+ 109248116, 106240561, 145620314, 164840892, 148459191, 143521157,
+ 154075562, 100871526, 160680221, 171591407, 157474582, 172259774,
+ 162853998, 175155329, 139081398, 117724093, 158254797, 107332871,
+ 190406999, 175907657, 170784934, 170393589, 182808717, 134256403,
+ 166895116, 162545705, 194332763, 112686500, 126122717, 197115321,
+ 112599504, 138667945, 103762556, 108363171, 116952597, 158128224,
+ 194162333, 143145106, 112353687, 185631136, 136692167, 114206974,
+ 169601292, 150578336, 105311960, 185945098, 139556718, 170995474,
+ 165104316, 123815517, 158083944, 129799709, 199505254, 138756612,
+ 194458833, 106846050, 178529151, 151410404, 189298850, 163881607,
+ 176196993, 107341038, 199957869, 118905980, 193737772, 106187543,
+ 122271893, 101366255, 126123878, 103875388, 181106814, 106765434,
+ 108282785, 126933426, 179955607, 107903860, 160352738, 199624512,
+ 159957492, 176297023, 159409558, 143011648, 129641185, 157771240,
+ 157544494, 157021789, 176979240, 194903272, 194770216, 164960356,
+ 153181535, 144003840, 168987471, 176915887, 163190966, 150696440,
+ 147769706, 187683656, 177810477, 197954503, 153395758, 130188183,
+ 186879377, 166124814, 195305996, 155802190, 183598751, 103512712,
+ 190432315, 180498719, 168687775, 194656634, 162210342, 104440855,
+ 149785037, 192738694, 129353661, 193778292, 187359378, 143470323,
+ 102371458, 137923557, 111863634, 119294601, 183182291, 196416500,
+ 187830793, 131353497, 179099745, 186492902, 167450609, 189368909,
+ 145883050, 133703053, 180547312, 132158094, 131976760, 132283131,
+ 141898097, 149822438, 133517435, 169898475, 101039500, 168388003,
+ 197867235, 199608024, 100273901, 108749548, 154787923, 156826113,
+ 199489032, 168997427, 108349611, 149208289, 103776784, 174303550,
+ 145684560, 183671479, 130845672, 133270354, 185392556, 120208683,
+ 193240995, 162211753, 131839402, 109707935, 170774965, 149880868,
+ 160663609, 168661967, 103747454, 121028312, 119251846, 122483499,
+ 111611495, 166556037, 196967613, 199312829, 196077608, 127799010,
+ 107830360, 102338272, 198790854, 102387615, 157445430, 192601191,
+ 100543379, 198389046, 154921248, 129516070, 172853005, 122721023,
+ 160175233, 113173179, 175931105, 103281551, 109373913, 163964530,
+ 157926071, 180083617, 195487672, 146459804, 173977292, 144810920,
+ 109371257, 186918332, 189588628, 139904358, 168666639, 175673445,
+ 114095036, 137327191, 174311388, 106638307, 125923027, 159734506,
+ 105482127, 178037065, 133778303, 121709877, 134966568, 149080032,
+ 169885067, 141791464, 168350828, 116168533, 114336160, 173099514,
+ 198531198, 119733758, 144420984, 116559541, 152250643, 139431286,
+ 144403838, 183561508, 179771645, 101706470, 167518774, 156059160,
+ 187168578, 157939226, 123475633, 117111329, 198655941, 159689071,
+ 198506887, 144230057, 151919770, 156900382, 118392562, 120338742,
+ 135362568, 108354156, 151729710, 188117217, 195936832, 156488518,
+ 174997487, 108553116, 159830610, 113921445, 144601614, 188452770,
+ 125114110, 170248521, 173974510, 138667364, 103872860, 109967489,
+ 131735618, 112071174, 104788993, 168886556, 192307848, 150230570,
+ 157144063, 163863202, 136852010, 174100574, 185922811, 115721968,
+ 100397824, 175953001, 166958522, 112303464, 118773650, 143546764,
+ 164565659, 171901123, 108476709, 193097085, 191283646, 166919177,
+ 169387914, 133315566, 150669813, 121641521, 100895711, 172862384,
+ 126070678, 145176011, 113450800, 169947684, 122356989, 162488051,
+ 157759809, 153397080, 185475059, 175362656, 149034394, 145420581,
+ 178864356, 183042000, 131509559, 147434392, 152544850, 167491429,
+ 108647514, 142303321, 133245695, 111634945, 167753939, 142403609,
+ 105438335, 152829243, 142203494, 184366151, 146632286, 102477666,
+ 166049531, 140657343, 157553014, 109082798, 180914786, 169343492,
+ 127376026, 134997829, 195701816, 119643212, 133140475, 176289748,
+ 140828911, 174097478, 126378991, 181699939, 148749771, 151989818,
+ 172666294, 160183053, 195832752, 109236350, 168538892, 128468247,
+ 125997252, 183007668, 156937583, 165972291, 198244297, 147406163,
+ 181831139, 158306744, 134851692, 185973832, 137392662, 140243450,
+ 119978099, 140402189, 161348342, 173613676, 144991382, 171541660,
+ 163424829, 136374185, 106122610, 186132119, 198633462, 184709941,
+ 183994274, 129559156, 128333990, 148038211, 175011612, 111667205,
+ 119125793, 103552929, 124113440, 131161341, 112495318, 138592695,
+ 184904438, 146807849, 109739828, 108855297, 104515305, 139914009,
+ 188698840, 188365483, 166522246, 168624087, 125401404, 100911787,
+ 142122045, 123075334, 173972538, 114940388, 141905868, 142311594,
+ 163227443, 139066125, 116239310, 162831953, 123883392, 113153455,
+ 163815117, 152035108, 174595582, 101123754, 135976815, 153401874,
+ 107394340, 136339780, 138817210, 104531691, 182951948, 179591767,
+ 139541778, 179243527, 161740724, 160593916, 102732282, 187946819,
+ 136491289, 149714953, 143255272, 135916592, 198072479, 198580612,
+ 169007332, 118844526, 179433504, 155801952, 149256630, 162048766,
+ 116134365, 133992028, 175452085, 155344144, 109905129, 182727454,
+ 165911813, 122232840, 151166615, 165070983, 175574337, 129548631,
+ 120411217, 116380915, 160616116, 157320000, 183306114, 160618128,
+ 103262586, 195951602, 146321661, 138576614, 180471993, 127077713,
+ 116441201, 159496011, 106328305, 120759583, 148503050, 179095584,
+ 198298218, 167402898, 138551383, 123957020, 180763975, 150429225,
+ 198476470, 171016426, 197438450, 143091658, 164528360, 132493360,
+ 143546572, 137557916, 113663241, 120457809, 196971566, 134022158,
+ 180545794, 131328278, 100552461, 132088901, 187421210, 192448910,
+ 141005215, 149680971, 113720754, 100571096, 134066431, 135745439,
+ 191597694, 135788920, 179342561, 177830222, 137011486, 142492523,
+ 192487287, 113132021, 176673607, 156645598, 127260957, 141566023,
+ 143787436, 129132109, 174858971, 150713073, 191040726, 143541417,
+ 197057222, 165479803, 181512759, 157912400, 125344680, 148220261,
+ 173422990, 101020483, 106246303, 137964746, 178190501, 181183037,
+ 151538028, 179523433, 141955021, 135689770, 191290561, 143178787,
+ 192086205, 174499925, 178975690, 118492103, 124206471, 138519113,
+ 188147564, 102097605, 154895793, 178514140, 141453051, 151583964,
+ 128232654, 106020603, 131189158, 165702720, 186250269, 191639375,
+ 115278873, 160608114, 155694842, 110322407, 177272742, 116513642,
+ 134366992, 171634030, 194053074, 180652685, 109301658, 192136921,
+ 141431293, 171341061, 157153714, 106203978, 147618426, 150297807,
+ 186062669, 169960809, 118422347, 163350477, 146719017, 145045144,
+ 161663828, 146208240, 186735951, 102371302, 190444377, 194085350,
+ 134454426, 133413062, 163074595, 113830310, 122931469, 134466832,
+ 185176632, 182415152, 110179422, 164439571, 181217170, 121756492,
+ 119644493, 196532222, 118765848, 182445119, 109401340, 150443213,
+ 198586286, 121083179, 139396084, 143898019, 114787389, 177233102,
+ 186310131, 148695521, 126205182, 178063494, 157118662, 177825659,
+ 188310053, 151552316, 165984394, 109022180, 163144545, 121212978,
+ 197344714, 188741258, 126822386, 102360271, 109981191, 152056882,
+ 134723983, 158013366, 106837863, 128867928, 161973236, 172536066,
+ 185216856, 132011948, 197807339, 158419190, 166595838, 167852941,
+ 124187182, 117279875, 106103946, 106481958, 157456200, 160892122,
+ 184163943, 173846549, 158993202, 184812364, 133466119, 170732430,
+ 195458590, 173361878, 162906318, 150165106, 126757685, 112163575,
+ 188696307, 145199922, 100107766, 176830946, 198149756, 122682434,
+ 179367131, 108412102, 119520899, 148191244, 140487511, 171059184,
+ 141399078, 189455775, 118462161, 190415309, 134543802, 180893862,
+ 180732375, 178615267, 179711433, 123241969, 185780563, 176301808,
+ 184386640, 160717536, 183213626, 129671224, 126094285, 140110963,
+ 121826276, 151201170, 122552929, 128965559, 146082049, 138409069,
+ 107606920, 103954646, 119164002, 115673360, 117909631, 187289199,
+ 186343410, 186903200, 157966371, 103128612, 135698881, 176403642,
+ 152540837, 109810814, 183519031, 121318624, 172281810, 150845123,
+ 169019064, 166322359, 138872454, 163073727, 128087898, 130041018,
+ 194859136, 173742589, 141812405, 167291912, 138003306, 134499821,
+ 196315803, 186381054, 124578934, 150084553, 128031351, 118843410,
+ 107373060, 159565443, 173624887, 171292628, 198074235, 139074061,
+ 178690578, 144431052, 174262641, 176783005, 182214864, 162289361,
+ 192966929, 192033046, 169332843, 181580535, 164864073, 118444059,
+ 195496893, 153773183, 167266131, 130108623, 158802128, 180432893,
+ 144562140, 147978945, 142337360, 158506327, 104399819, 132635916,
+ 168734194, 136567839, 101281912, 120281622, 195003330, 112236091,
+ 185875592, 101959081, 122415367, 194990954, 148881099, 175891989,
+ 108115811, 163538891, 163394029, 123722049, 184837522, 142362091,
+ 100834097, 156679171, 100841679, 157022331, 178971071, 102928884,
+ 189701309, 195339954, 124415335, 106062584, 139214524, 133864640,
+ 134324406, 157317477, 155340540, 144810061, 177612569, 108474646,
+ 114329765, 143900008, 138265211, 145210162, 136643111, 197987319,
+ 102751191, 144121361, 169620456, 193602633, 161023559, 162140467,
+ 102901215, 167964187, 135746835, 187317233, 110047459, 163339773,
+ 124770449, 118885134, 141536376, 100915375, 164267438, 145016622,
+ 113937193, 106748706, 128815954, 164819775, 119220771, 102367432,
+ 189062690, 170911791, 194127762, 112245117, 123546771, 115640433,
+ 135772061, 166615646, 174474627, 130562291, 133320309, 153340551,
+ 138417181, 194605321, 150142632, 180008795, 151813296, 175497284,
+ 167018836, 157425342, 150169942, 131069156, 134310662, 160434122,
+ 105213831, 158797111, 150754540, 163290657, 102484886, 148697402,
+ 187203725, 198692811, 149360627, 140384233, 128749423, 132178578,
+ 177507355, 171857043, 178737969, 134023369, 102911446, 196144864,
+ 197697194, 134527467, 144296030, 189437192, 154052665, 188907106,
+ 162062575, 150993037, 199766583, 167936112, 181374511, 104971506,
+ 115378374, 135795558, 167972129, 135876446, 130937572, 103221320,
+ 124605656, 161129971, 131027586, 191128460, 143251843, 143269155,
+ 129284585, 173495971, 150425653, 199302112, 118494723, 121323805,
+ 116549802, 190991967, 168151180, 122483192, 151273721, 199792134,
+ 133106764, 121874844, 126215985, 112167639, 167793529, 182985195,
+ 185453921, 106957880, 158685312, 132775454, 133229161, 198905318,
+ 190537253, 191582222, 192325972, 178133427, 181825606, 148823337,
+ 160719681, 101448145, 131983362, 137910767, 112550175, 128826351,
+ 183649210, 135725874, 110356573, 189469487, 154446940, 118175923,
+ 106093708, 128146501, 185742532, 149692127, 164624247, 183221076,
+ 154737505, 168198834, 156410354, 158027261, 125228550, 131543250,
+ 139591848, 191898263, 104987591, 115406321, 103542638, 190012837,
+ 142615518, 178773183, 175862355, 117537850, 169565995, 170028011,
+ 158412588, 170150030, 117025916, 174630208, 142412449, 112839238,
+ 105257725, 114737141, 123102301, 172563968, 130555358, 132628403,
+ 183638157, 168682846, 143304568, 105994018, 170010719, 152092970,
+ 117799058, 132164175, 179868116, 158654714, 177489647, 116547948,
+ 183121404, 131836079, 184431405, 157311793, 149677763, 173989893,
+ 102277656, 107058530, 140837477, 152640947, 143507039, 152145247,
+ 101683884, 107090870, 161471944, 137225650, 128231458, 172995869,
+ 173831689, 171268519, 139042297, 111072135, 107569780, 137262545,
+ 181410950, 138270388, 198736451, 162848201, 180468288, 120582913,
+ 153390138, 135649144, 130040157, 106509887, 192671541, 174507066,
+ 186888783, 143805558, 135011967, 145862340, 180595327, 124727843,
+ 182925939, 157715840, 136885940, 198993925, 152416883, 178793572,
+ 179679516, 154076673, 192703125, 164187609, 162190243, 104699348,
+ 159891990, 160012977, 174692145, 132970421, 167781726, 115178506,
+ 153008552, 155999794, 102099694, 155431545, 127458567, 104403686,
+ 168042864, 184045128, 181182309, 179349696, 127218364, 192935516,
+ 120298724, 169583299, 148193297, 183358034, 159023227, 105261254,
+ 121144370, 184359584, 194433836, 138388317, 175184116, 108817112,
+ 151279233, 137457721, 193398208, 119005406, 132929377, 175306906,
+ 160741530, 149976826, 147124407, 176881724, 186734216, 185881509,
+ 191334220, 175930947, 117385515, 193408089, 157124410, 163472089,
+ 131949128, 180783576, 131158294, 100549708, 191802336, 165960770,
+ 170927599, 101052702, 181508688, 197828549, 143403726, 142729262,
+ 110348701, 139928688, 153550062, 106151434, 130786653, 196085995,
+ 100587149, 139141652, 106530207, 100852656, 124074703, 166073660,
+ 153338052, 163766757, 120188394, 197277047, 122215363, 138511354,
+ 183463624, 161985542, 159938719, 133367482, 104220974, 149956672,
+ 170250544, 164232439, 157506869, 159133019, 137469191, 142980999,
+ 134242305, 150172665, 121209241, 145596259, 160554427, 159095199,
+ 168243130, 184279693, 171132070, 121049823, 123819574, 171759855,
+ 119501864, 163094029, 175943631, 194450091, 191506160, 149228764,
+ 132319212, 197034460, 193584259, 126727638, 168143633, 109856853,
+ 127860243, 132141052, 133076065, 188414958, 158718197, 107124299,
+ 159592267, 181172796, 144388537, 196763139, 127431422, 179531145,
+ 100064922, 112650013, 132686230, 121550837,
+ }
+\cs_new:Npn \__fp_trig_large:ww #1, #2#3#4#5#6;
+ {
+ \exp_after:wN \__fp_trig_large_auxi:w
+ \int_value:w \__fp_int_eval:w (#1 - 4) / 8 \exp_after:wN ,
+ \int_value:w #1 , ;
+ {#2}{#3}{#4}{#5} ;
+ }
+\cs_new:Npn \__fp_trig_large_auxi:w #1, #2,
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__fp_trig_large_auxii:w
+ \cs:w
+ use_none:n \prg_replicate:nn { #2 - #1 * 8 } { n }
+ \exp_after:wN
+ \cs_end:
+ \int_value:w
+ \__kernel_intarray_item:Nn \c__fp_trig_intarray
+ { \__fp_int_eval:w #1 + 1 \scan_stop: }
+ \exp_after:wN \__fp_trig_large_auxiii:w \int_value:w
+ \__kernel_intarray_item:Nn \c__fp_trig_intarray
+ { \__fp_int_eval:w #1 + 2 \scan_stop: }
+ \exp_after:wN \__fp_trig_large_auxiii:w \int_value:w
+ \__kernel_intarray_item:Nn \c__fp_trig_intarray
+ { \__fp_int_eval:w #1 + 3 \scan_stop: }
+ \exp_after:wN \__fp_trig_large_auxiii:w \int_value:w
+ \__kernel_intarray_item:Nn \c__fp_trig_intarray
+ { \__fp_int_eval:w #1 + 4 \scan_stop: }
+ \exp_after:wN \__fp_trig_large_auxiii:w \int_value:w
+ \__kernel_intarray_item:Nn \c__fp_trig_intarray
+ { \__fp_int_eval:w #1 + 5 \scan_stop: }
+ \exp_after:wN \__fp_trig_large_auxiii:w \int_value:w
+ \__kernel_intarray_item:Nn \c__fp_trig_intarray
+ { \__fp_int_eval:w #1 + 6 \scan_stop: }
+ \exp_after:wN \__fp_trig_large_auxiii:w \int_value:w
+ \__kernel_intarray_item:Nn \c__fp_trig_intarray
+ { \__fp_int_eval:w #1 + 7 \scan_stop: }
+ \exp_after:wN \__fp_trig_large_auxiii:w \int_value:w
+ \__kernel_intarray_item:Nn \c__fp_trig_intarray
+ { \__fp_int_eval:w #1 + 8 \scan_stop: }
+ \exp_after:wN \__fp_trig_large_auxiii:w \int_value:w
+ \__kernel_intarray_item:Nn \c__fp_trig_intarray
+ { \__fp_int_eval:w #1 + 9 \scan_stop: }
+ \exp_stop_f:
+ }
+\cs_new:Npn \__fp_trig_large_auxii:w
+ {
+ \__fp_pack_twice_four:wNNNNNNNN \__fp_pack_twice_four:wNNNNNNNN
+ \__fp_pack_twice_four:wNNNNNNNN \__fp_pack_twice_four:wNNNNNNNN
+ \__fp_pack_twice_four:wNNNNNNNN \__fp_pack_twice_four:wNNNNNNNN
+ \__fp_pack_twice_four:wNNNNNNNN \__fp_pack_twice_four:wNNNNNNNN
+ \__fp_trig_large_auxv:www ;
+ }
+\cs_new:Npn \__fp_trig_large_auxiii:w 1 { \exp_stop_f: }
+\cs_new:Npn \__fp_trig_large_auxv:www #1; #2; #3;
+ {
+ \exp_after:wN \__fp_use_i_until_s:nw
+ \exp_after:wN \__fp_trig_large_auxvii:w
+ \int_value:w \__fp_int_eval:w \c__fp_leading_shift_int
+ \prg_replicate:nn { 13 }
+ { \__fp_trig_large_auxvi:wnnnnnnnn }
+ + \c__fp_trailing_shift_int - \c__fp_middle_shift_int
+ \__fp_use_i_until_s:nw
+ ; #3 #1 ; ;
+ }
+\cs_new:Npn \__fp_trig_large_auxvi:wnnnnnnnn #1; #2#3#4#5#6#7#8#9
+ {
+ \exp_after:wN \__fp_trig_large_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_middle_shift_int
+ + #2*#9 + #3*#8 + #4*#7 + #5*#6
+ #1; {#2}{#3}{#4}{#5} {#7}{#8}{#9}
+ }
+\cs_new:Npn \__fp_trig_large_pack:NNNNNw #1#2#3#4#5#6;
+ { + #1#2#3#4#5 ; #6 }
+\cs_new:Npn \__fp_trig_large_auxvii:w #1#2#3
+ {
+ \exp_after:wN \__fp_trig_large_auxviii:ww
+ \int_value:w \__fp_int_eval:w (#1#2#3 - 62) / 125 ;
+ #1#2#3
+ }
+\cs_new:Npn \__fp_trig_large_auxviii:ww #1;
+ {
+ + #1
+ \if_int_odd:w #1 \exp_stop_f:
+ \exp_after:wN \__fp_trig_large_auxix:Nw
+ \exp_after:wN -
+ \else:
+ \exp_after:wN \__fp_trig_large_auxix:Nw
+ \exp_after:wN +
+ \fi:
+ }
+\cs_new:Npn \__fp_trig_large_auxix:Nw
+ {
+ \exp_after:wN \__fp_use_i_until_s:nw
+ \exp_after:wN \__fp_trig_large_auxxi:w
+ \int_value:w \__fp_int_eval:w \c__fp_leading_shift_int
+ \prg_replicate:nn { 13 }
+ { \__fp_trig_large_auxx:wNNNNN }
+ + \c__fp_trailing_shift_int - \c__fp_middle_shift_int
+ ;
+ }
+\cs_new:Npn \__fp_trig_large_auxx:wNNNNN #1; #2 #3#4#5#6
+ {
+ \exp_after:wN \__fp_trig_large_pack:NNNNNw
+ \int_value:w \__fp_int_eval:w \c__fp_middle_shift_int
+ #2 8 * #3#4#5#6
+ #1; #2
+ }
+\cs_new:Npn \__fp_trig_large_auxxi:w #1;
+ {
+ \exp_after:wN \__fp_ep_mul_raw:wwwwN
+ \int_value:w \__fp_int_eval:w 0 \__fp_ep_to_ep_loop:N #1 ; ; !
+ 0,{7853}{9816}{3397}{4483}{0961}{5661};
+ \__fp_trig_small:ww
+ }
+\cs_new:Npn \__fp_sin_series_o:NNwwww #1#2#3. #4;
+ {
+ \__fp_fixed_mul:wwn #4; #4;
+ {
+ \exp_after:wN \__fp_sin_series_aux_o:NNnwww
+ \exp_after:wN #1
+ \int_value:w
+ \if_int_odd:w \__fp_int_eval:w (#3 + 2) / 4 \__fp_int_eval_end:
+ #2
+ \else:
+ \if_meaning:w #2 0 2 \else: 0 \fi:
+ \fi:
+ {#3}
+ }
+ }
+\cs_new:Npn \__fp_sin_series_aux_o:NNnwww #1#2#3 #4; #5,#6;
+ {
+ \if_int_odd:w \__fp_int_eval:w #3 / 2 \__fp_int_eval_end:
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ { % 1/18!
+ \__fp_fixed_mul_sub_back:wwwn {0000}{0000}{0000}{0001}{5619}{2070};
+ #4;{0000}{0000}{0000}{0477}{9477}{3324};
+ \__fp_fixed_mul_sub_back:wwwn #4;{0000}{0000}{0011}{4707}{4559}{7730};
+ \__fp_fixed_mul_sub_back:wwwn #4;{0000}{0000}{2087}{6756}{9878}{6810};
+ \__fp_fixed_mul_sub_back:wwwn #4;{0000}{0027}{5573}{1922}{3985}{8907};
+ \__fp_fixed_mul_sub_back:wwwn #4;{0000}{2480}{1587}{3015}{8730}{1587};
+ \__fp_fixed_mul_sub_back:wwwn #4;{0013}{8888}{8888}{8888}{8888}{8889};
+ \__fp_fixed_mul_sub_back:wwwn #4;{0416}{6666}{6666}{6666}{6666}{6667};
+ \__fp_fixed_mul_sub_back:wwwn #4;{5000}{0000}{0000}{0000}{0000}{0000};
+ \__fp_fixed_mul_sub_back:wwwn#4;{10000}{0000}{0000}{0000}{0000}{0000};
+ { \__fp_fixed_continue:wn 0, }
+ }
+ { % 1/17!
+ \__fp_fixed_mul_sub_back:wwwn {0000}{0000}{0000}{0028}{1145}{7254};
+ #4;{0000}{0000}{0000}{7647}{1637}{3182};
+ \__fp_fixed_mul_sub_back:wwwn #4;{0000}{0000}{0160}{5904}{3836}{8216};
+ \__fp_fixed_mul_sub_back:wwwn #4;{0000}{0002}{5052}{1083}{8544}{1719};
+ \__fp_fixed_mul_sub_back:wwwn #4;{0000}{0275}{5731}{9223}{9858}{9065};
+ \__fp_fixed_mul_sub_back:wwwn #4;{0001}{9841}{2698}{4126}{9841}{2698};
+ \__fp_fixed_mul_sub_back:wwwn #4;{0083}{3333}{3333}{3333}{3333}{3333};
+ \__fp_fixed_mul_sub_back:wwwn #4;{1666}{6666}{6666}{6666}{6666}{6667};
+ \__fp_fixed_mul_sub_back:wwwn#4;{10000}{0000}{0000}{0000}{0000}{0000};
+ { \__fp_ep_mul:wwwwn 0, } #5,#6;
+ }
+ {
+ \exp_after:wN \__fp_sanitize:Nw
+ \exp_after:wN #2
+ \int_value:w \__fp_int_eval:w #1
+ }
+ #2
+ }
+\cs_new:Npn \__fp_tan_series_o:NNwwww #1#2#3. #4;
+ {
+ \__fp_fixed_mul:wwn #4; #4;
+ {
+ \exp_after:wN \__fp_tan_series_aux_o:Nnwww
+ \int_value:w
+ \if_int_odd:w \__fp_int_eval:w #3 / 2 \__fp_int_eval_end:
+ \exp_after:wN \reverse_if:N
+ \fi:
+ \if_meaning:w #1#2 2 \else: 0 \fi:
+ {#3}
+ }
+ }
+\cs_new:Npn \__fp_tan_series_aux_o:Nnwww #1 #2 #3; #4,#5;
+ {
+ \__fp_fixed_mul_sub_back:wwwn {0000}{0000}{1527}{3493}{0856}{7059};
+ #3; {0000}{0159}{6080}{0274}{5257}{6472};
+ \__fp_fixed_mul_sub_back:wwwn #3; {0002}{4571}{2320}{0157}{2558}{8481};
+ \__fp_fixed_mul_sub_back:wwwn #3; {0115}{5830}{7533}{5397}{3168}{2147};
+ \__fp_fixed_mul_sub_back:wwwn #3; {1929}{8245}{6140}{3508}{7719}{2982};
+ \__fp_fixed_mul_sub_back:wwwn #3;{10000}{0000}{0000}{0000}{0000}{0000};
+ { \__fp_ep_mul:wwwwn 0, } #4,#5;
+ {
+ \__fp_fixed_mul_sub_back:wwwn {0000}{0007}{0258}{0681}{9408}{4706};
+ #3;{0000}{2343}{7175}{1399}{6151}{7670};
+ \__fp_fixed_mul_sub_back:wwwn #3;{0019}{2638}{4588}{9232}{8861}{3691};
+ \__fp_fixed_mul_sub_back:wwwn #3;{0536}{6357}{0691}{4344}{6852}{4252};
+ \__fp_fixed_mul_sub_back:wwwn #3;{5263}{1578}{9473}{6842}{1052}{6315};
+ \__fp_fixed_mul_sub_back:wwwn#3;{10000}{0000}{0000}{0000}{0000}{0000};
+ {
+ \reverse_if:N \if_int_odd:w
+ \__fp_int_eval:w (#2 - 1) / 2 \__fp_int_eval_end:
+ \exp_after:wN \__fp_reverse_args:Nww
+ \fi:
+ \__fp_ep_div:wwwwn 0,
+ }
+ }
+ {
+ \exp_after:wN \__fp_sanitize:Nw
+ \exp_after:wN #1
+ \int_value:w \__fp_int_eval:w \__fp_ep_to_float_o:wwN
+ }
+ #1
+ }
+\cs_new:Npn \__fp_atan_o:Nw #1
+ {
+ \__fp_parse_function_one_two:nnw
+ { #1 { atan } { atand } }
+ { \__fp_atan_default:w \__fp_atanii_o:Nww #1 }
+ }
+\cs_new:Npn \__fp_acot_o:Nw #1
+ {
+ \__fp_parse_function_one_two:nnw
+ { #1 { acot } { acotd } }
+ { \__fp_atan_default:w \__fp_acotii_o:Nww #1 }
+ }
+\cs_new:Npe \__fp_atan_default:w #1#2#3 @ { #1 #2 #3 \c_one_fp @ }
+\cs_new:Npn \__fp_atanii_o:Nww
+ #1 \s__fp \__fp_chk:w #2#3#4; \s__fp \__fp_chk:w #5 #6 @
+ {
+ \if_meaning:w 3 #2 \__fp_case_return_i_o:ww \fi:
+ \if_meaning:w 3 #5 \__fp_case_return_ii_o:ww \fi:
+ \if_case:w
+ \if_meaning:w #2 #5
+ \if_meaning:w 1 #2 10 \else: 0 \fi:
+ \else:
+ \if_int_compare:w #2 > #5 \exp_stop_f: 1 \else: 2 \fi:
+ \fi:
+ \exp_stop_f:
+ \__fp_case_return:nw { \__fp_atan_inf_o:NNNw #1 #3 2 }
+ \or: \__fp_case_return:nw { \__fp_atan_inf_o:NNNw #1 #3 4 }
+ \or: \__fp_case_return:nw { \__fp_atan_inf_o:NNNw #1 #3 0 }
+ \fi:
+ \__fp_atan_normal_o:NNnwNnw #1
+ \s__fp \__fp_chk:w #2#3#4;
+ \s__fp \__fp_chk:w #5 #6
+ }
+\cs_new:Npn \__fp_acotii_o:Nww #1#2; #3;
+ { \__fp_atanii_o:Nww #1#3; #2; }
+\cs_new:Npn \__fp_atan_inf_o:NNNw #1#2#3 \s__fp \__fp_chk:w #4#5#6;
+ {
+ \exp_after:wN \__fp_atan_combine_o:NwwwwwN
+ \exp_after:wN #2
+ \int_value:w \__fp_int_eval:w
+ \if_meaning:w 2 #5 7 - \fi: #3 \exp_after:wN ;
+ \c__fp_one_fixed_tl
+ {0000}{0000}{0000}{0000}{0000}{0000};
+ 0,{0000}{0000}{0000}{0000}{0000}{0000}; #1
+ }
+\cs_new_protected:Npn \__fp_atan_normal_o:NNnwNnw
+ #1 \s__fp \__fp_chk:w 1#2#3#4; \s__fp \__fp_chk:w 1#5#6#7;
+ {
+ \__fp_atan_test_o:NwwNwwN
+ #2 #3, #4{0000}{0000};
+ #5 #6, #7{0000}{0000}; #1
+ }
+\cs_new:Npn \__fp_atan_test_o:NwwNwwN #1#2,#3; #4#5,#6;
+ {
+ \exp_after:wN \__fp_atan_combine_o:NwwwwwN
+ \exp_after:wN #1
+ \int_value:w \__fp_int_eval:w
+ \if_meaning:w 2 #4
+ 7 - \__fp_int_eval:w
+ \fi:
+ \if_int_compare:w
+ \__fp_ep_compare:wwww #2,#3; #5,#6; > \c_zero_int
+ 3 -
+ \exp_after:wN \__fp_reverse_args:Nww
+ \fi:
+ \__fp_atan_div:wnwwnw #2,#3; #5,#6;
+ }
+\cs_new:Npn \__fp_atan_div:wnwwnw #1,#2#3; #4,#5#6;
+ {
+ \if_int_compare:w
+ \__fp_int_eval:w 41421 * #5 < #2 000
+ \if_case:w \__fp_int_eval:w #4 - #1 \__fp_int_eval_end:
+ 00 \or: 0 \fi:
+ \exp_stop_f:
+ \exp_after:wN \__fp_atan_near:wwwn
+ \fi:
+ 0
+ \__fp_ep_div:wwwwn #1,{#2}#3; #4,{#5}#6;
+ \__fp_atan_auxi:ww
+ }
+\cs_new:Npn \__fp_atan_near:wwwn
+ 0 \__fp_ep_div:wwwwn #1,#2; #3,
+ {
+ 1
+ \__fp_ep_to_fixed:wwn #1 - #3, #2;
+ \__fp_atan_near_aux:wwn
+ }
+\cs_new:Npn \__fp_atan_near_aux:wwn #1; #2;
+ {
+ \__fp_fixed_add:wwn #1; #2;
+ { \__fp_fixed_sub:wwn #2; #1; { \__fp_ep_div:wwwwn 0, } 0, }
+ }
+\cs_new:Npn \__fp_atan_auxi:ww #1,#2;
+ { \__fp_ep_to_fixed:wwn #1,#2; \__fp_atan_auxii:w #1,#2; }
+\cs_new:Npn \__fp_atan_auxii:w #1;
+ {
+ \__fp_fixed_mul:wwn #1; #1;
+ {
+ \__fp_atan_Taylor_loop:www 39 ;
+ {0000}{0000}{0000}{0000}{0000}{0000} ;
+ }
+ ! #1;
+ }
+\cs_new:Npn \__fp_atan_Taylor_loop:www #1; #2; #3;
+ {
+ \if_int_compare:w #1 = - \c_one_int
+ \__fp_atan_Taylor_break:w
+ \fi:
+ \exp_after:wN \__fp_fixed_div_int:wwN \c__fp_one_fixed_tl #1;
+ \__fp_rrot:www \__fp_fixed_mul_sub_back:wwwn #2; #3;
+ {
+ \exp_after:wN \__fp_atan_Taylor_loop:www
+ \int_value:w \__fp_int_eval:w #1 - 2 ;
+ }
+ #3;
+ }
+\cs_new:Npn \__fp_atan_Taylor_break:w
+ \fi: #1 \__fp_fixed_mul_sub_back:wwwn #2; #3 !
+ { \fi: ; #2 ; }
+\cs_new:Npn \__fp_atan_combine_o:NwwwwwN #1 #2; #3; #4; #5,#6; #7
+ {
+ \exp_after:wN \__fp_sanitize:Nw
+ \exp_after:wN #1
+ \int_value:w \__fp_int_eval:w
+ \if_meaning:w 0 #2
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ { #5 \__fp_fixed_mul:wwn #3; #6; }
+ {
+ \__fp_fixed_mul:wwn #3; #4;
+ {
+ \exp_after:wN \__fp_atan_combine_aux:ww
+ \int_value:w \__fp_int_eval:w #2 / 2 ; #2;
+ }
+ }
+ { #7 \__fp_fixed_to_float_o:wN \__fp_fixed_to_float_rad_o:wN }
+ #1
+ }
+\cs_new:Npn \__fp_atan_combine_aux:ww #1; #2;
+ {
+ \__fp_fixed_mul_short:wwn
+ {7853}{9816}{3397}{4483}{0961}{5661};
+ {#1}{0000}{0000};
+ {
+ \if_int_odd:w #2 \exp_stop_f:
+ \exp_after:wN \__fp_fixed_sub:wwn
+ \else:
+ \exp_after:wN \__fp_fixed_add:wwn
+ \fi:
+ }
+ }
+\cs_new:Npn \__fp_asin_o:w #1 \s__fp \__fp_chk:w #2#3; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \__fp_case_return_same_o:w
+ \or:
+ \__fp_case_use:nw
+ { \__fp_asin_normal_o:NfwNnnnnw #1 { #1 { asin } { asind } } }
+ \or:
+ \__fp_case_use:nw
+ { \__fp_invalid_operation_o:fw { #1 { asin } { asind } } }
+ \else:
+ \__fp_case_return_same_o:w
+ \fi:
+ \s__fp \__fp_chk:w #2 #3;
+ }
+\cs_new:Npn \__fp_acos_o:w #1 \s__fp \__fp_chk:w #2#3; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \__fp_case_use:nw { \__fp_atan_inf_o:NNNw #1 0 4 }
+ \or:
+ \__fp_case_use:nw
+ {
+ \__fp_asin_normal_o:NfwNnnnnw #1 { #1 { acos } { acosd } }
+ \__fp_reverse_args:Nww
+ }
+ \or:
+ \__fp_case_use:nw
+ { \__fp_invalid_operation_o:fw { #1 { acos } { acosd } } }
+ \else:
+ \__fp_case_return_same_o:w
+ \fi:
+ \s__fp \__fp_chk:w #2 #3;
+ }
+\cs_new:Npn \__fp_asin_normal_o:NfwNnnnnw
+ #1#2#3 \s__fp \__fp_chk:w 1#4#5#6#7#8#9;
+ {
+ \if_int_compare:w #5 < \c_one_int
+ \exp_after:wN \__fp_use_none_until_s:w
+ \fi:
+ \if_int_compare:w \__fp_int_eval:w #5 + #6#7 + #8#9 = 1000 0001 ~
+ \exp_after:wN \__fp_use_none_until_s:w
+ \fi:
+ \__fp_use_i:ww
+ \__fp_invalid_operation_o:fw {#2}
+ \s__fp \__fp_chk:w 1#4{#5}{#6}{#7}{#8}{#9};
+ \__fp_asin_auxi_o:NnNww
+ #1 {#3} #4 #5,{#6}{#7}{#8}{#9}{0000}{0000};
+ }
+\cs_new:Npn \__fp_asin_auxi_o:NnNww #1#2#3#4,#5;
+ {
+ \__fp_ep_to_fixed:wwn #4,#5;
+ \__fp_asin_isqrt:wn
+ \__fp_ep_mul:wwwwn #4,#5;
+ \__fp_ep_to_ep:wwN
+ \__fp_fixed_continue:wn
+ { #2 \__fp_atan_test_o:NwwNwwN #3 }
+ 0 1,{1000}{0000}{0000}{0000}{0000}{0000}; #1
+ }
+\cs_new:Npn \__fp_asin_isqrt:wn #1;
+ {
+ \exp_after:wN \__fp_fixed_sub:wwn \c__fp_one_fixed_tl #1;
+ {
+ \__fp_fixed_add_one:wN #1;
+ \__fp_fixed_continue:wn { \__fp_ep_mul:wwwwn 0, } 0,
+ }
+ \__fp_ep_isqrt:wwn
+ }
+\cs_new:Npn \__fp_acsc_o:w #1 \s__fp \__fp_chk:w #2#3#4; @
+ {
+ \if_case:w \if_meaning:w 2 #2 #3 \fi: #2 \exp_stop_f:
+ \__fp_case_use:nw
+ { \__fp_invalid_operation_o:fw { #1 { acsc } { acscd } } }
+ \or: \__fp_case_use:nw
+ { \__fp_acsc_normal_o:NfwNnw #1 { #1 { acsc } { acscd } } }
+ \or: \__fp_case_return_o:Nw \c_zero_fp
+ \or: \__fp_case_return_same_o:w
+ \else: \__fp_case_return_o:Nw \c_minus_zero_fp
+ \fi:
+ \s__fp \__fp_chk:w #2 #3 #4;
+ }
+\cs_new:Npn \__fp_asec_o:w #1 \s__fp \__fp_chk:w #2#3; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \__fp_case_use:nw
+ { \__fp_invalid_operation_o:fw { #1 { asec } { asecd } } }
+ \or:
+ \__fp_case_use:nw
+ {
+ \__fp_acsc_normal_o:NfwNnw #1 { #1 { asec } { asecd } }
+ \__fp_reverse_args:Nww
+ }
+ \or: \__fp_case_use:nw { \__fp_atan_inf_o:NNNw #1 0 4 }
+ \else: \__fp_case_return_same_o:w
+ \fi:
+ \s__fp \__fp_chk:w #2 #3;
+ }
+\cs_new:Npn \__fp_acsc_normal_o:NfwNnw #1#2#3 \s__fp \__fp_chk:w 1#4#5#6;
+ {
+ \int_compare:nNnTF {#5} < 1
+ {
+ \__fp_invalid_operation_o:fw {#2}
+ \s__fp \__fp_chk:w 1#4{#5}#6;
+ }
+ {
+ \__fp_ep_div:wwwwn
+ 1,{1000}{0000}{0000}{0000}{0000}{0000};
+ #5,#6{0000}{0000};
+ { \__fp_asin_auxi_o:NnNww #1 {#3} #4 }
+ }
+ }
+%% File: l3fp-convert.dtx
+\cs_new:Npn \__fp_tuple_convert:Nw #1 \s__fp_tuple \__fp_tuple_chk:w #2 ;
+ {
+ \int_case:nnF { \__fp_array_count:n {#2} }
+ {
+ { 0 } { ( ) }
+ { 1 } { \__fp_tuple_convert_end:w @ { #1 #2 , } }
+ }
+ {
+ \__fp_tuple_convert_loop:nNw { } #1
+ #2 { ? \__fp_tuple_convert_end:w } ;
+ @ { \use_none:nn }
+ }
+ }
+\cs_new:Npn \__fp_tuple_convert_loop:nNw #1#2#3#4; #5 @ #6
+ {
+ \use_none:n #3
+ \exp_args:Nf \__fp_tuple_convert_loop:nNw { #2 #3#4 ; } #2 #5
+ @ { #6 , ~ #1 }
+ }
+\cs_new:Npn \__fp_tuple_convert_end:w #1 @ #2
+ { \exp_after:wN ( \exp:w \exp_end_continue_f:w #2 ) }
+\cs_new:Npn \__fp_trim_zeros:w #1 ;
+ {
+ \__fp_trim_zeros_loop:w #1
+ ; \__fp_trim_zeros_loop:w 0; \__fp_trim_zeros_dot:w .; \s__fp_stop
+ }
+\cs_new:Npn \__fp_trim_zeros_loop:w #1 0; #2 { #2 #1 ; #2 }
+\cs_new:Npn \__fp_trim_zeros_dot:w #1 .; { \__fp_trim_zeros_end:w #1 ; }
+\cs_new:Npn \__fp_trim_zeros_end:w #1 ; #2 \s__fp_stop { #1 }
+\cs_new:Npn \fp_to_scientific:N #1
+ { \exp_after:wN \__fp_to_scientific_dispatch:w #1 }
+\cs_generate_variant:Nn \fp_to_scientific:N { c }
+\cs_new:Npn \fp_to_scientific:n
+ {
+ \exp_after:wN \__fp_to_scientific_dispatch:w
+ \exp:w \exp_end_continue_f:w \__fp_parse:n
+ }
+\cs_new:Npn \__fp_to_scientific_dispatch:w #1
+ {
+ \__fp_change_func_type:NNN
+ #1 \__fp_to_scientific:w \__fp_to_scientific_recover:w
+ #1
+ }
+\cs_new:Npn \__fp_to_scientific_recover:w #1 #2 ;
+ {
+ \__fp_error:nffn { unknown-type } { \tl_to_str:n { #2 ; } } { } { }
+ nan
+ }
+\cs_new:Npn \__fp_tuple_to_scientific:w
+ { \__fp_tuple_convert:Nw \__fp_to_scientific_dispatch:w }
+\cs_new:Npn \__fp_to_scientific:w \s__fp \__fp_chk:w #1#2
+ {
+ \if_meaning:w 2 #2 \exp_after:wN - \exp:w \exp_end_continue_f:w \fi:
+ \if_case:w #1 \exp_stop_f:
+ \__fp_case_return:nw { 0.000000000000000e0 }
+ \or: \exp_after:wN \__fp_to_scientific_normal:wnnnnn
+ \or:
+ \__fp_case_use:nw
+ {
+ \__fp_invalid_operation:nnw
+ { \fp_to_scientific:N \c__fp_overflowing_fp }
+ { fp_to_scientific }
+ }
+ \or:
+ \__fp_case_use:nw
+ {
+ \__fp_invalid_operation:nnw
+ { \fp_to_scientific:N \c_zero_fp }
+ { fp_to_scientific }
+ }
+ \fi:
+ \s__fp \__fp_chk:w #1 #2
+ }
+\cs_new:Npn \__fp_to_scientific_normal:wnnnnn
+ \s__fp \__fp_chk:w 1 #1 #2 #3#4#5#6 ;
+ {
+ \exp_after:wN \__fp_to_scientific_normal:wNw
+ \exp_after:wN e
+ \int_value:w \__fp_int_eval:w #2 - 1
+ ; #3 #4 #5 #6 ;
+ }
+\cs_new:Npn \__fp_to_scientific_normal:wNw #1 ; #2#3;
+ { #2.#3 #1 }
+\cs_new:Npn \fp_to_decimal:N #1
+ { \exp_after:wN \__fp_to_decimal_dispatch:w #1 }
+\cs_generate_variant:Nn \fp_to_decimal:N { c }
+\cs_new:Npn \fp_to_decimal:n
+ {
+ \exp_after:wN \__fp_to_decimal_dispatch:w
+ \exp:w \exp_end_continue_f:w \__fp_parse:n
+ }
+\cs_new:Npn \__fp_to_decimal_dispatch:w #1
+ {
+ \__fp_change_func_type:NNN
+ #1 \__fp_to_decimal:w \__fp_to_decimal_recover:w
+ #1
+ }
+\cs_new:Npn \__fp_to_decimal_recover:w #1 #2 ;
+ {
+ \__fp_error:nffn { unknown-type } { \tl_to_str:n { #2 ; } } { } { }
+ nan
+ }
+\cs_new:Npn \__fp_tuple_to_decimal:w
+ { \__fp_tuple_convert:Nw \__fp_to_decimal_dispatch:w }
+\cs_new:Npn \__fp_to_decimal:w \s__fp \__fp_chk:w #1#2
+ {
+ \if_meaning:w 2 #2 \exp_after:wN - \exp:w \exp_end_continue_f:w \fi:
+ \if_case:w #1 \exp_stop_f:
+ \__fp_case_return:nw { 0 }
+ \or: \exp_after:wN \__fp_to_decimal_normal:wnnnnn
+ \or:
+ \__fp_case_use:nw
+ {
+ \__fp_invalid_operation:nnw
+ { \fp_to_decimal:N \c__fp_overflowing_fp }
+ { fp_to_decimal }
+ }
+ \or:
+ \__fp_case_use:nw
+ {
+ \__fp_invalid_operation:nnw
+ { 0 }
+ { fp_to_decimal }
+ }
+ \fi:
+ \s__fp \__fp_chk:w #1 #2
+ }
+\cs_new:Npn \__fp_to_decimal_normal:wnnnnn
+ \s__fp \__fp_chk:w 1 #1 #2 #3#4#5#6 ;
+ {
+ \int_compare:nNnTF {#2} > 0
+ {
+ \int_compare:nNnTF {#2} < \c__fp_prec_int
+ {
+ \__fp_decimate:nNnnnn { \c__fp_prec_int - #2 }
+ \__fp_to_decimal_large:Nnnw
+ }
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__fp_to_decimal_huge:wnnnn
+ \prg_replicate:nn { #2 - \c__fp_prec_int } { 0 } ;
+ }
+ {#3} {#4} {#5} {#6}
+ }
+ {
+ \exp_after:wN \__fp_trim_zeros:w
+ \exp_after:wN 0
+ \exp_after:wN .
+ \exp:w \exp_end_continue_f:w \prg_replicate:nn { - #2 } { 0 }
+ #3#4#5#6 ;
+ }
+ }
+\cs_new:Npn \__fp_to_decimal_large:Nnnw #1#2#3#4;
+ {
+ \exp_after:wN \__fp_trim_zeros:w \int_value:w
+ \if_int_compare:w #2 > \c_zero_int
+ #2
+ \fi:
+ \exp_stop_f:
+ #3.#4 ;
+ }
+\cs_new:Npn \__fp_to_decimal_huge:wnnnn #1; #2#3#4#5 { #2#3#4#5 #1 }
+\cs_new:Npn \fp_to_tl:N #1 { \exp_after:wN \__fp_to_tl_dispatch:w #1 }
+\cs_generate_variant:Nn \fp_to_tl:N { c }
+\cs_new:Npn \fp_to_tl:n
+ {
+ \exp_after:wN \__fp_to_tl_dispatch:w
+ \exp:w \exp_end_continue_f:w \__fp_parse:n
+ }
+\cs_new:Npn \__fp_to_tl_dispatch:w #1
+ { \__fp_change_func_type:NNN #1 \__fp_to_tl:w \__fp_to_tl_recover:w #1 }
+\cs_new:Npn \__fp_to_tl_recover:w #1 #2 ;
+ {
+ \__fp_error:nffn { unknown-type } { \tl_to_str:n { #2 ; } } { } { }
+ nan
+ }
+\cs_new:Npn \__fp_tuple_to_tl:w
+ { \__fp_tuple_convert:Nw \__fp_to_tl_dispatch:w }
+\cs_new:Npn \__fp_to_tl:w \s__fp \__fp_chk:w #1#2
+ {
+ \if_meaning:w 2 #2 \exp_after:wN - \exp:w \exp_end_continue_f:w \fi:
+ \if_case:w #1 \exp_stop_f:
+ \__fp_case_return:nw { 0 }
+ \or: \exp_after:wN \__fp_to_tl_normal:nnnnn
+ \or: \__fp_case_return:nw { inf }
+ \else: \__fp_case_return:nw { nan }
+ \fi:
+ }
+\cs_new:Npn \__fp_to_tl_normal:nnnnn #1
+ {
+ \int_compare:nTF
+ { -2 <= #1 <= \c__fp_prec_int }
+ { \__fp_to_decimal_normal:wnnnnn }
+ { \__fp_to_tl_scientific:wnnnnn }
+ \s__fp \__fp_chk:w 1 0 {#1}
+ }
+\cs_new:Npn \__fp_to_tl_scientific:wnnnnn
+ \s__fp \__fp_chk:w 1 #1 #2 #3#4#5#6 ;
+ {
+ \exp_after:wN \__fp_to_tl_scientific:wNw
+ \exp_after:wN e
+ \int_value:w \__fp_int_eval:w #2 - 1
+ ; #3 #4 #5 #6 ;
+ }
+\cs_new:Npn \__fp_to_tl_scientific:wNw #1 ; #2#3;
+ { \__fp_trim_zeros:w #2.#3 ; #1 }
+\cs_new:Npn \fp_to_dim:N #1
+ { \exp_after:wN \__fp_to_dim_dispatch:w #1 }
+\cs_generate_variant:Nn \fp_to_dim:N { c }
+\cs_new:Npn \fp_to_dim:n
+ {
+ \exp_after:wN \__fp_to_dim_dispatch:w
+ \exp:w \exp_end_continue_f:w \__fp_parse:n
+ }
+\cs_new:Npn \__fp_to_dim_dispatch:w #1#2 ;
+ {
+ \__fp_change_func_type:NNN #1 \__fp_to_dim:w \__fp_to_dim_recover:w
+ #1 #2 ;
+ }
+\cs_new:Npn \__fp_to_dim_recover:w #1
+ { \__fp_invalid_operation:nnw { 0pt } { fp_to_dim } }
+\cs_new:Npn \__fp_to_dim:w #1 ; { \__fp_to_decimal:w #1 ; pt }
+\cs_new:Npn \fp_to_int:N #1 { \exp_after:wN \__fp_to_int_dispatch:w #1 }
+\cs_generate_variant:Nn \fp_to_int:N { c }
+\cs_new:Npn \fp_to_int:n
+ {
+ \exp_after:wN \__fp_to_int_dispatch:w
+ \exp:w \exp_end_continue_f:w \__fp_parse:n
+ }
+\cs_new:Npn \__fp_to_int_dispatch:w #1#2 ;
+ {
+ \__fp_change_func_type:NNN #1 \__fp_to_int:w \__fp_to_int_recover:w
+ #1 #2 ;
+ }
+\cs_new:Npn \__fp_to_int_recover:w #1
+ { \__fp_invalid_operation:nnw { 0 } { fp_to_int } }
+\cs_new:Npn \__fp_to_int:w #1;
+ {
+ \exp_after:wN \__fp_to_decimal:w \exp:w \exp_end_continue_f:w
+ \__fp_round:Nwn \__fp_round_to_nearest:NNN #1; { 0 }
+ }
+\cs_new:Npn \dim_to_fp:n #1
+ {
+ \exp_after:wN \__fp_from_dim_test:ww
+ \exp_after:wN 0
+ \exp_after:wN ,
+ \int_value:w \tex_glueexpr:D #1 ;
+ }
+\cs_new:Npn \__fp_from_dim_test:ww #1, #2
+ {
+ \if_meaning:w 0 #2
+ \__fp_case_return:nw { \exp_after:wN \c_zero_fp }
+ \else:
+ \exp_after:wN \__fp_from_dim:wNw
+ \int_value:w \__fp_int_eval:w #1 - 4
+ \if_meaning:w - #2
+ \exp_after:wN , \exp_after:wN 2 \int_value:w
+ \else:
+ \exp_after:wN , \exp_after:wN 0 \int_value:w #2
+ \fi:
+ \fi:
+ }
+\cs_new:Npn \__fp_from_dim:wNw #1,#2#3;
+ {
+ \__fp_pack_twice_four:wNNNNNNNN \__fp_from_dim:wNNnnnnnn ;
+ #3 000 0000 00 {10}987654321; #2 {#1}
+ }
+\cs_new:Npn \__fp_from_dim:wNNnnnnnn #1; #2#3#4#5#6#7#8#9
+ { \__fp_from_dim:wnnnnwNn #1 {#2#300} {0000} ; }
+\cs_new:Npn \__fp_from_dim:wnnnnwNn #1; #2#3#4#5#6; #7#8
+ {
+ \__fp_mul_npos_o:Nww #7
+ \s__fp \__fp_chk:w 1 #7 {#5} #1 ;
+ \s__fp \__fp_chk:w 1 0 {#8} {1525} {8789} {0625} {0000} ;
+ \prg_do_nothing:
+ }
+\cs_new_eq:NN \fp_use:N \fp_to_decimal:N
+\cs_generate_variant:Nn \fp_use:N { c }
+\cs_new_eq:NN \fp_eval:n \fp_to_decimal:n
+\cs_new:Npn \fp_sign:n #1
+ { \fp_to_decimal:n { sign \__fp_parse:n {#1} } }
+\cs_new:Npn \fp_abs:n #1
+ { \fp_to_decimal:n { abs \__fp_parse:n {#1} } }
+\cs_new:Npn \fp_max:nn #1#2
+ { \fp_to_decimal:n { max ( \__fp_parse:n {#1} , \__fp_parse:n {#2} ) } }
+\cs_new:Npn \fp_min:nn #1#2
+ { \fp_to_decimal:n { min ( \__fp_parse:n {#1} , \__fp_parse:n {#2} ) } }
+\cs_new:Npn \__fp_array_to_clist:n #1
+ {
+ \tl_if_empty:nF {#1}
+ {
+ \exp_last_unbraced:Ne \use_ii:nn
+ {
+ \__fp_array_to_clist_loop:Nw #1 { ? \prg_break: } ;
+ \prg_break_point:
+ }
+ }
+ }
+\cs_new:Npn \__fp_array_to_clist_loop:Nw #1#2;
+ {
+ \use_none:n #1
+ , ~
+ \exp_not:f { \__fp_to_tl_dispatch:w #1 #2 ; }
+ \__fp_array_to_clist_loop:Nw
+ }
+%% File: l3fp-random.dtx
+\cs_new:Npn \__fp_parse_word_rand:N
+ { \__fp_parse_function:NNN \__fp_rand_o:Nw ? }
+\cs_new:Npn \__fp_parse_word_randint:N
+ { \__fp_parse_function:NNN \__fp_randint_o:Nw ? }
+\int_const:Nn \c__kernel_randint_max_int { 131071 }
+\cs_new:Npn \__kernel_randint:n #1
+ {
+ (#1 * \tex_uniformdeviate:D 16384
+ + \tex_uniformdeviate:D #1 + 8192 ) / 16384
+ }
+\cs_new:Npn \__fp_rand_myriads:n #1
+ { \__fp_rand_myriads_loop:w #1 \prg_break: X \prg_break_point: ; }
+\cs_new:Npn \__fp_rand_myriads_loop:w #1 X
+ {
+ #1
+ \exp_after:wN \__fp_rand_myriads_get:w
+ \int_value:w \__fp_int_eval:w 9999 +
+ \__kernel_randint:n { 10000 }
+ \__fp_rand_myriads_loop:w
+ }
+\cs_new:Npn \__fp_rand_myriads_get:w 1 #1 ; { ; {#1} }
+\cs_new:Npn \__fp_rand_o:Nw ? #1 @
+ {
+ \tl_if_empty:nTF {#1}
+ {
+ \exp_after:wN \__fp_rand_o:w
+ \exp:w \exp_end_continue_f:w
+ \__fp_rand_myriads:n { XXXX } { 0000 } { 0000 } ; 0
+ }
+ {
+ \msg_expandable_error:nnnnn
+ { fp } { num-args } { rand() } { 0 } { 0 }
+ \exp_after:wN \c_nan_fp
+ }
+ }
+\cs_new:Npn \__fp_rand_o:w ;
+ {
+ \exp_after:wN \__fp_sanitize:Nw
+ \exp_after:wN 0
+ \int_value:w \__fp_int_eval:w \c_zero_int
+ \__fp_fixed_to_float_o:wN
+ }
+\cs_new:Npn \__fp_randint_o:Nw ?
+ {
+ \__fp_parse_function_one_two:nnw
+ { randint }
+ { \__fp_randint_default:w \__fp_randint_o:w }
+ }
+\cs_new:Npn \__fp_randint_default:w #1 { \exp_after:wN #1 \c_one_fp }
+\cs_new:Npn \__fp_randint_badarg:w \s__fp \__fp_chk:w #1#2#3;
+ {
+ \__fp_int:wTF \s__fp \__fp_chk:w #1#2#3;
+ {
+ \if_meaning:w 1 #1
+ \if_int_compare:w
+ \__fp_use_i_until_s:nw #3 ; > \c__fp_prec_int
+ \c_one_int
+ \fi:
+ \fi:
+ }
+ { \c_one_int }
+ }
+\cs_new:Npn \__fp_randint_o:w #1; #2; @
+ {
+ \if_case:w
+ \__fp_randint_badarg:w #1;
+ \__fp_randint_badarg:w #2;
+ \if:w 1 \__fp_compare_back:ww #2; #1; \c_one_int \fi:
+ \c_zero_int
+ \__fp_randint_auxi_o:ww #1; #2;
+ \or:
+ \__fp_invalid_operation_tl_o:ff
+ { randint } { \__fp_array_to_clist:n { #1; #2; } }
+ \exp:w
+ \fi:
+ \exp_after:wN \exp_end:
+ }
+\cs_new:Npn \__fp_randint_auxi_o:ww #1 ; #2 ; #3 \exp_end:
+ {
+ \fi:
+ \__fp_randint_auxii:wn #2 ;
+ { \__fp_randint_auxii:wn #1 ; \__fp_randint_auxiii_o:ww }
+ }
+\cs_new:Npn \__fp_randint_auxii:wn \s__fp \__fp_chk:w #1#2#3#4 ;
+ {
+ \if_meaning:w 0 #1
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ { \exp_after:wN \__fp_fixed_continue:wn \c__fp_one_fixed_tl }
+ {
+ \exp_after:wN \__fp_ep_to_fixed:wwn
+ \int_value:w \__fp_int_eval:w
+ #3 - \c__fp_prec_int , #4 {0000} {0000} ;
+ {
+ \if_meaning:w 0 #2
+ \exp_after:wN \use_i:nnnn
+ \exp_after:wN \__fp_fixed_add_one:wN
+ \fi:
+ \exp_after:wN \__fp_fixed_sub:wwn \c__fp_one_fixed_tl
+ }
+ \__fp_fixed_continue:wn
+ }
+ }
+\cs_new:Npn \__fp_randint_auxiii_o:ww #1 ; #2 ;
+ {
+ \__fp_fixed_add:wwn #2 ;
+ {0000} {0000} {0000} {0001} {0000} {0000} ;
+ \__fp_fixed_sub:wwn #1 ;
+ {
+ \exp_after:wN \use_i:nn
+ \exp_after:wN \__fp_fixed_mul_add:wwwn
+ \exp:w \exp_end_continue_f:w \__fp_rand_myriads:n { XXXXXX } ;
+ }
+ #1 ;
+ \__fp_randint_auxiv_o:ww
+ #2 ;
+ \__fp_randint_auxv_o:w #1 ; @
+ }
+\cs_new:Npn \__fp_randint_auxiv_o:ww #1#2#3#4#5 ; #6#7#8#9
+ {
+ \if_int_compare:w
+ \if_int_compare:w #1#2 > #6#7 \exp_stop_f: 1 \else:
+ \if_int_compare:w #1#2 < #6#7 \exp_stop_f: - \fi: \fi:
+ #3#4 > #8#9 \exp_stop_f:
+ \__fp_use_i_until_s:nw
+ \fi:
+ \__fp_randint_auxv_o:w {#1}{#2}{#3}{#4}#5
+ }
+\cs_new:Npn \__fp_randint_auxv_o:w #1#2#3#4#5 ; #6 @
+ {
+ \exp_after:wN \__fp_sanitize:Nw
+ \int_value:w
+ \if_int_compare:w #1 < 10000 \exp_stop_f:
+ 2
+ \else:
+ 0
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__fp_reverse_args:Nww
+ \fi:
+ \exp_after:wN \__fp_fixed_sub:wwn \c__fp_one_fixed_tl
+ {#1} {#2} {#3} {#4} {0000} {0000} ;
+ {
+ \exp_after:wN \exp_stop_f:
+ \int_value:w \__fp_int_eval:w \c__fp_prec_int
+ \__fp_fixed_to_float_o:wN
+ }
+ 0
+ \exp:w \exp_after:wN \exp_end:
+ }
+\cs_new:Npn \int_rand:nn #1#2
+ {
+ \int_eval:n
+ {
+ \exp_after:wN \__fp_randint:ww
+ \int_value:w \int_eval:n {#1} \exp_after:wN ;
+ \int_value:w \int_eval:n {#2} ;
+ }
+ }
+\cs_new:Npn \__fp_randint:ww #1; #2;
+ {
+ \if_int_compare:w #1 > #2 \exp_stop_f:
+ \msg_expandable_error:nnnn
+ { kernel } { randint-backward-range } {#1} {#2}
+ \__fp_randint:ww #2; #1;
+ \else:
+ \if_int_compare:w \__fp_int_eval:w #2
+ \if_int_compare:w #1 > \c_zero_int
+ - #1 < \__fp_int_eval:w
+ \else:
+ < \__fp_int_eval:w #1 +
+ \fi:
+ \c__kernel_randint_max_int
+ \__fp_int_eval_end:
+ \__kernel_randint:n
+ { \__fp_int_eval:w #2 - #1 + 1 \__fp_int_eval_end: }
+ - 1 + #1
+ \else:
+ \__kernel_randint:nn {#1} {#2}
+ \fi:
+ \fi:
+ }
+\cs_new:Npn \__kernel_randint:nn #1#2
+ {
+ #1
+ \exp_after:wN \__fp_randint_wide_aux:w
+ \int_value:w
+ \exp_after:wN \__fp_randint_split_o:Nw
+ \tex_uniformdeviate:D 268435456 ;
+ \int_value:w
+ \exp_after:wN \__fp_randint_split_o:Nw
+ \tex_uniformdeviate:D 268435456 ;
+ \int_value:w
+ \exp_after:wN \__fp_randint_split_o:Nw
+ \int_value:w \__fp_int_eval:w 131072 +
+ \exp_after:wN \__fp_randint_split_o:Nw
+ \int_value:w
+ \__kernel_int_add:nnn {#2} { -#1 } { -\c_max_int } ;
+ .
+ }
+\cs_new:Npn \__fp_randint_split_o:Nw #1#2 ;
+ {
+ \if_meaning:w 0 #1
+ 0 \exp_after:wN ; \int_value:w 0
+ \else:
+ \exp_after:wN \__fp_randint_split_aux:w
+ \int_value:w \__fp_int_eval:w (#1#2 - 8192) / 16384 ;
+ + #1#2
+ \fi:
+ \exp_after:wN ;
+ }
+\cs_new:Npn \__fp_randint_split_aux:w #1 ;
+ {
+ #1 \exp_after:wN ;
+ \int_value:w \__fp_int_eval:w - #1 * 16384
+ }
+\cs_new:Npn \__fp_randint_wide_aux:w #1;#2; #3;#4; #5;#6;#7; .
+ {
+ \exp_after:wN \__fp_randint_wide_auxii:w
+ \int_value:w \__fp_int_eval:w #5 * #3 + #6 * #1 +
+ (#5 * #4 + #6 * #3 + #7 * #1 +
+ (#5 * #2 + #7 * #3 +
+ (16384 * #6 + #7) * (16384 * #4 + #2) / 268435456) / 16384
+ ) / 16384 \exp_after:wN ;
+ \int_value:w \__fp_int_eval:w (#5 + #6) * 16384 + #7 ;
+ #1 ; #5 ;
+ }
+\cs_new:Npn \__fp_randint_wide_auxii:w #1; #2; #3; #4;
+ {
+ \if_int_odd:w 0
+ \if_int_compare:w #1 = #2 \else: \exp_stop_f: \fi:
+ \if_int_compare:w #4 = \c_zero_int 1 \fi:
+ \if_int_compare:w #3 = 16383 ~ 1 \fi:
+ \exp_stop_f:
+ \exp_after:wN \prg_break:
+ \fi:
+ \if_int_compare:w #4 < 8 \exp_stop_f:
+ + #4 * #3 * 16384
+ \else:
+ + 8 * #3 * 16384 + (#4 - 8) * #3 * 16384
+ \fi:
+ + #1
+ \prg_break_point:
+ }
+\cs_new:Npn \int_rand:n #1
+ {
+ \int_eval:n
+ { \exp_args:Nf \__fp_randint:n { \int_eval:n {#1} } }
+ }
+\cs_new:Npn \__fp_randint:n #1
+ {
+ \if_int_compare:w #1 < \c_one_int
+ \msg_expandable_error:nnnn
+ { kernel } { randint-backward-range } { 1 } {#1}
+ \__fp_randint:ww #1; 1;
+ \else:
+ \if_int_compare:w #1 > \c__kernel_randint_max_int
+ \__kernel_randint:nn { 1 } {#1}
+ \else:
+ \__kernel_randint:n {#1}
+ \fi:
+ \fi:
+ }
+%% File l3fp-types.dtx (C) Copyright 2012-2015,2017,2018,2020,2021,2023 The LaTeX Project
+\cs_new:Npe \__fp_types_cs_to_op:N #1
+ {
+ \exp_not:N \exp_after:wN \exp_not:N \__fp_types_cs_to_op_auxi:wwwn
+ \exp_not:N \token_to_str:N #1 \s__fp_mark
+ \exp_not:N \__fp_use_i_delimit_by_s_stop:nw
+ \tl_to_str:n { __fp_ _o:w } \s__fp_mark
+ { \exp_not:N \__fp_use_i_delimit_by_s_stop:nw ? }
+ \s__fp_stop
+ }
+\use:e
+ {
+ \cs_new:Npn \exp_not:N \__fp_types_cs_to_op_auxi:wwwn
+ #1 \tl_to_str:n { __fp_ } #2
+ \tl_to_str:n { _o:w } #3 \s__fp_mark #4 { #4 {#2} }
+ }
+\cs_new:Npn \__fp_types_unary:NNw #1
+ {
+ \exp_args:Nf \__fp_types_unary_auxi:nNw
+ { \__fp_types_cs_to_op:N #1 }
+ }
+\cs_new:Npn \__fp_types_unary_auxi:nNw #1#2#3
+ {
+ \exp_after:wN \__fp_types_unary_auxii:NnNw
+ \cs:w __fp_#1 \__fp_type_from_scan:N #3 _o:w \cs_end:
+ {#1}
+ #2#3
+ }
+\cs_new:Npn \__fp_types_unary_auxii:NnNw #1#2#3
+ {
+ \token_if_eq_meaning:NNTF \scan_stop: #1
+ { \__fp_invalid_operation_o:nw {#2} }
+ { #1 #3 }
+ }
+\cs_new:Npn \__fp_types_binary:Nww #1
+ {
+ \exp_last_unbraced:Nf \__fp_types_binary_auxi:Nww
+ { \__fp_types_cs_to_op:N #1 }
+ }
+\cs_new:Npn \__fp_types_binary_auxi:Nww #1#2#3; #4#5; @
+ {
+ \exp_after:wN \__fp_types_binary_auxii:NNww
+ \cs:w
+ __fp
+ \__fp_type_from_scan:N #2
+ _#1
+ \__fp_type_from_scan:N #4
+ _o:ww
+ \cs_end:
+ #1 #2#3; #4#5;
+ }
+\cs_new:Npn \__fp_types_binary_auxii:NNww #1#2
+ {
+ \token_if_eq_meaning:NNTF \scan_stop: #1
+ { \__fp_invalid_operation_o:Nww #2 }
+ {#1}
+ }
+%% File l3fp-symbolic.dtx (C) Copyright 2012-2015,2017,2018,2020,2021,2023 The LaTeX Project
+\fp_new:N \l__fp_symbolic_fp
+\scan_new:N \s__fp_symbolic
+\cs_new_protected:Npn \__fp_symbolic_chk:w #1,#2#3;
+ {
+ \msg_error:nne { fp } { misused-fp }
+ {
+ \__fp_to_tl_dispatch:w
+ \s__fp_symbolic \__fp_symbolic_chk:w #1,{#2};
+ }
+ }
+\cs_new:Npn \__fp_if_has_symbolic:nTF #1
+ {
+ \__fp_if_has_symbolic_aux:w
+ #1 \s__fp_mark \use_i:nn
+ \s__fp_symbolic \s__fp_mark \use_ii:nn
+ \s__fp_stop
+ }
+\cs_new:Npn \__fp_if_has_symbolic_aux:w
+ #1 \s__fp_symbolic #2 \s__fp_mark #3#4 \s__fp_stop { #3 }
+\cs_new:Npn \__fp_exp_after_symbolic_f:nw
+ #1 \s__fp_symbolic \__fp_symbolic_chk:w #2, #3#4;
+ {
+ \exp_after:wN \__fp_exp_after_symbolic_aux:w
+ \exp:w
+ \__fp_exp_after_symbolic_loop:N #2
+ { , \exp:w \use_none:nn }
+ \exp_after:wN \exp_end: \exp_after:wN
+ {
+ \exp:w \exp_end_continue_f:w
+ \__fp_exp_after_array_f:w #3 \s__fp_expr_stop
+ \exp_after:wN
+ }
+ \exp_after:wN ;
+ \exp:w \exp_end_continue_f:w #1
+ }
+\cs_new:Npn \__fp_exp_after_symbolic_aux:w #1, #2;
+ {
+ \__fp_if_has_symbolic:nTF {#2}
+ { \s__fp_symbolic \__fp_symbolic_chk:w #1, {#2} ; }
+ { #1 #2 @ \prg_do_nothing: }
+ }
+\cs_new:Npn \__fp_exp_after_symbolic_loop:N #1
+ {
+ \exp_after:wN \exp_end:
+ \exp_after:wN #1
+ \exp:w
+ \__fp_exp_after_symbolic_loop:N
+ }
+\cs_new:Npn \__fp_symbolic_binary_o:Nww #1 #2; #3;
+ {
+ \__fp_exp_after_symbolic_f:nw { \exp_after:wN \exp_stop_f: }
+ \s__fp_symbolic \__fp_symbolic_chk:w
+ \__fp_types_binary:Nww #1 , { #2; #3; } ;
+ }
+\cs_set_protected:Npn \__fp_tmp:w #1#2
+ {
+ \cs_new:cpn
+ { __fp_symbolic_#2_symbolic_o:ww }
+ { \__fp_symbolic_binary_o:Nww #1 }
+ \cs_new_eq:cc
+ { __fp_symbolic_#2 _o:ww }
+ { __fp_symbolic_#2_symbolic_o:ww }
+ \cs_new_eq:cc
+ { __fp _#2_symbolic_o:ww }
+ { __fp_symbolic_#2_symbolic_o:ww }
+ }
+\tl_map_inline:nn { + - * / ^ & | }
+ { \exp_args:Nc \__fp_tmp:w { __fp_#1_o:ww } {#1} }
+\cs_new:Npn \__fp_symbolic_unary_o:NNw #1#2#3; @
+ {
+ \__fp_exp_after_symbolic_f:nw { \exp_after:wN \exp_stop_f: }
+ \s__fp_symbolic \__fp_symbolic_chk:w
+ \__fp_types_unary:NNw #1#2 , { #3; } ;
+ }
+\tl_map_inline:nn
+ {
+ {acos} {acsc} {asec} {asin} {cos} {cot} {csc} {exp} {ln}
+ {not} {sec} {set_sign} {sin} {sqrt} {tan}
+ }
+ {
+ \cs_new:cpe { __fp_symbolic_#1_o:w }
+ {
+ \exp_not:N \__fp_symbolic_unary_o:NNw
+ \exp_not:c { __fp_#1_o:w }
+ }
+ }
+\cs_set_protected:Npn \__fp_tmp:w #1#2#3
+ {
+ \cs_new:cpn { __fp_symbolic_to_#1:w }
+ {
+ \exp_after:wN \__fp_symbolic_convert:wnnN
+ \exp:w \exp_end_continue_f:w
+ \__fp_exp_after_symbolic_f:nw { { #2 } { fp_to_#1 } #3 }
+ }
+ }
+\__fp_tmp:w { decimal } { 0 } \__fp_to_decimal_dispatch:w
+\__fp_tmp:w { int } { 0 } \__fp_to_int_dispatch:w
+\__fp_tmp:w { scientific } { nan } \__fp_to_scientific_dispatch:w
+\cs_new:Npn \__fp_symbolic_convert:wnnN #1#2; #3#4#5
+ {
+ \str_if_eq:nnTF {#1} { \s__fp_symbolic }
+ { \__fp_invalid_operation:nnw {#3} {#4} #1#2; }
+ { #5 #1#2; }
+ }
+\cs_new:Npn \__fp_symbolic_cs_arg_to_fn:NN #1
+ {
+ \exp_args:Nf \__fp_symbolic_op_arg_to_fn:nN
+ { \__fp_types_cs_to_op:N #1 }
+ }
+\cs_new:Npn \__fp_symbolic_op_arg_to_fn:nN #1#2
+ {
+ \str_case:nnF { #1 #2 }
+ {
+ { not ? } { ! }
+ { set_sign 0 } { abs }
+ { set_sign 2 } { - }
+ }
+ {
+ \token_if_eq_meaning:NNTF #2 \use_ii:nn
+ { #1 d } {#1}
+ }
+ }
+\cs_new:Npn \__fp_symbolic_to_tl:w
+ \s__fp_symbolic \__fp_symbolic_chk:w #1#2, #3#4;
+ {
+ \str_case:nnTF {#1}
+ {
+ { \__fp_types_unary:NNw } { \__fp_symbolic_unary_to_tl:NNw }
+ { \__fp_types_binary:Nww } { \__fp_symbolic_binary_to_tl:Nww }
+ { \__fp_function_o:w } { \__fp_symbolic_function_to_tl:Nw }
+ }
+ { #2, #3 @ }
+ { \tl_to_str:n {#2} }
+ }
+\cs_new:Npn \__fp_symbolic_unary_to_tl:NNw #1#2 , #3 @
+ {
+ \use:e
+ {
+ \__fp_symbolic_cs_arg_to_fn:NN #1#2
+ ( \__fp_to_tl_dispatch:w #3 )
+ }
+ }
+\cs_new:Npn \__fp_symbolic_binary_to_tl:Nww #1, #2; #3; @
+ {
+ \use:e
+ {
+ ( \__fp_to_tl_dispatch:w #2; )
+ \__fp_types_cs_to_op:N #1
+ ( \__fp_to_tl_dispatch:w #3; )
+ }
+ }
+\cs_new:Npn \__fp_symbolic_function_to_tl:Nw #1, #2@
+ {
+ \use:e
+ {
+ \__fp_types_cs_to_op:N #1
+ ( \__fp_array_to_clist:n {#2} )
+ }
+ }
+\prg_new_protected_conditional:Npnn
+ \__fp_id_if_invalid:n #1 { T , F , TF }
+ {
+ \tl_if_empty:nTF {#1}
+ { \prg_return_true: }
+ {
+ \tl_if_in:nnTF { #1 } { ~ }
+ { \prg_return_true: }
+ {
+ \__fp_id_if_invalid_aux:N #1
+ { ? \prg_break:n \prg_return_false: }
+ \prg_break_point:
+ }
+ }
+ }
+\cs_new:Npn \__fp_id_if_invalid_aux:N #1
+ {
+ \use_none:n #1
+ \int_compare:nF { `a <= `#1 <= `z }
+ {
+ \int_compare:nF { `A <= `#1 <= `Z }
+ { \prg_break:n \prg_return_true: }
+ }
+ \__fp_id_if_invalid_aux:N
+ }
+\cs_new:Npn \__fp_variable_o:w #1 @ #2
+ {
+ \fp_if_exist:cTF { l__fp_variable_#1_fp }
+ {
+ \exp_last_unbraced:Nf \__fp_exp_after_array_f:w
+ { \use:c { l__fp_variable_#1_fp } } \s__fp_expr_stop
+ \exp_after:wN \exp_stop_f: #2
+ }
+ {
+ \token_if_eq_meaning:NNTF #2 \prg_do_nothing:
+ {
+ \s__fp_symbolic \__fp_symbolic_chk:w
+ \__fp_variable_o:w #1 , { } ;
+ }
+ {
+ \exp_after:wN \s__fp_symbolic
+ \exp_after:wN \__fp_symbolic_chk:w
+ \exp_after:wN \__fp_variable_o:w
+ \exp:w
+ \__fp_exp_after_symbolic_loop:N #1
+ { , \exp:w \use_none:nn }
+ \exp_after:wN \exp_end:
+ \exp_after:wN { \exp_after:wN } \exp_after:wN ;
+ #2
+ }
+ }
+ }
+\cs_new_protected:Npn \__fp_variable_set_parsing:Nn #1#2
+ {
+ \cs_set:Npn \__fp_tmp:w
+ {
+ \__fp_exp_after_symbolic_f:nw { \__fp_parse_infix:NN }
+ \s__fp_symbolic \__fp_symbolic_chk:w
+ \__fp_variable_o:w #2 , { } ;
+ }
+ \exp_args:NNc \__fp_variable_set_parsing_aux:NNn #1
+ { __fp_parse_word_#2:N } {#2}
+ }
+\cs_new_protected:Npn \__fp_variable_set_parsing_aux:NNn #1#2#3
+ {
+ \cs_if_eq:NNF #2 \__fp_tmp:w
+ {
+ \cs_if_exist:NTF #2
+ {
+ \msg_warning:nnnn
+ { fp } { id-used-elsewhere } {#3} { variable }
+ #1 #2 \__fp_tmp:w
+ }
+ {
+ \cs_new_eq:NN #2 \scan_stop: % to declare the function
+ #1 #2 \__fp_tmp:w
+ }
+ }
+ }
+\cs_new_protected:Npn \fp_clear_variable:n #1
+ {
+ \exp_args:No \__fp_clear_variable:n { \tl_to_str:n {#1} }
+ }
+\cs_new_protected:Npn \__fp_clear_variable:n #1
+ {
+ \__fp_id_if_invalid:nTF {#1}
+ { \msg_error:nnn { fp } { id-invalid } {#1} }
+ { \__fp_clear_variable_aux:n {#1} }
+ }
+\cs_new_protected:Npn \__fp_clear_variable_aux:n #1
+ {
+ \cs_set_eq:cN { l__fp_variable_#1_fp } \tex_undefined:D
+ \__fp_variable_set_parsing:Nn \cs_set_eq:NN {#1}
+ }
+\cs_new_protected:Npn \fp_new_variable:n #1
+ {
+ \exp_args:No \__fp_new_variable:n { \tl_to_str:n {#1} }
+ }
+\cs_new_protected:Npn \__fp_new_variable:n #1
+ {
+ \__fp_id_if_invalid:nTF {#1}
+ { \msg_error:nnn { fp } { id-invalid } {#1} }
+ {
+ \cs_if_exist:cT { __fp_parse_word_#1:N }
+ {
+ \msg_error:nnn
+ { fp } { id-already-defined } {#1}
+ \cs_undefine:c { __fp_parse_word_#1:N }
+ \cs_set_eq:cN { l__fp_variable_#1_fp } \tex_undefined:D
+ }
+ \__fp_variable_set_parsing:Nn \cs_gset_eq:NN {#1}
+ }
+ }
+\flag_new:N \l__fp_symbolic_flag
+\cs_new_protected:Npn \fp_set_variable:nn #1
+ {
+ \exp_args:No \__fp_set_variable:nn { \tl_to_str:n {#1} }
+ }
+\cs_new_protected:Npn \__fp_set_variable:nn #1#2
+ {
+ \__fp_id_if_invalid:nTF {#1}
+ { \msg_error:nnn { fp } { id-invalid } {#1} }
+ {
+ \__fp_variable_set_parsing:Nn \cs_set_eq:NN {#1}
+ \fp_set:Nn \l__fp_symbolic_fp {#2}
+ \cs_set_nopar:cpn { l__fp_variable_#1_fp }
+ { \flag_ensure_raised:N \l__fp_symbolic_flag \c_nan_fp }
+ \flag_clear:N \l__fp_symbolic_flag
+ \fp_set:cn { l__fp_variable_#1_fp } { \l__fp_symbolic_fp }
+ \flag_if_raised:NT \l__fp_symbolic_flag
+ {
+ \msg_error:nneee { fp } { id-loop }
+ { #1 }
+ { \tl_to_str:n {#2} }
+ { \fp_to_tl:N \l__fp_symbolic_fp }
+ }
+ }
+ }
+\msg_new:nnnn { fp } { id-invalid }
+ { Floating~point~identifier~'#1'~invalid. }
+ {
+ LaTeX~has~been~asked~to~create~a~new~floating~point~identifier~'#1'~
+ but~this~may~only~contain~ASCII~letters.
+ }
+\msg_new:nnnn { fp } { id-already-defined }
+ { Floating~point~identifier~'#1'~already~defined. }
+ {
+ LaTeX~has~been~asked~to~create~a~new~floating~point~identifier~'#1'~
+ but~this~name~has~already~been~used~elsewhere.
+ }
+\msg_new:nnnn { fp } { id-used-elsewhere }
+ { Floating~point~identifier~'#1'~already~used~for~something~else. }
+ {
+ LaTeX~has~been~asked~to~create~a~new~floating~point~identifier~'#1'~
+ but~this~name~is~used,~and~is~not~a~user-defined~#2.
+ }
+\msg_new:nnnn { fp } { id-loop }
+ { Variable~'#1'~used~in~the~definition~of~'#1'. }
+ {
+ LaTeX~has~been~asked~to~set~the~floating~point~identifier~'#1'~
+ to~the~expression~'#2'.~Evaluating~this~expression~yields~'#3',~
+ which~contains~'#1'~itself.
+ }
+%% File l3fp-functions.dtx (C) Copyright 2012-2018,2020,2021,2023 The LaTeX Project
+\cs_new_protected:Npn \fp_new_function:n #1
+ { \exp_args:No \__fp_new_function:n { \tl_to_str:n {#1} } }
+\cs_new_protected:Npn \__fp_new_function:n #1
+ {
+ \__fp_id_if_invalid:nTF {#1}
+ { \msg_error:nnn { fp } { id-invalid } {#1} }
+ {
+ \cs_if_exist:cT { __fp_parse_word_#1:N }
+ {
+ \msg_error:nnn
+ { fp } { id-already-defined } {#1}
+ \cs_undefine:c { __fp_parse_word_#1:N }
+ \cs_undefine:c { __fp_#1_o:w }
+ }
+ \__fp_function_set_parsing:Nn \cs_gset_eq:NN {#1}
+ }
+ }
+\cs_new_protected:Npn \__fp_function_set_parsing:Nn #1#2
+ {
+ \exp_args:NNc \__fp_function_set_parsing_aux:NNn #1
+ { __fp_parse_word_#2:N } {#2}
+ }
+\cs_new_protected:Npn \__fp_function_set_parsing_aux:NNn #1#2#3
+ {
+ \cs_set:Npe \__fp_tmp:w
+ {
+ \exp_not:N \__fp_parse_function:NNN
+ \exp_not:N \__fp_function_o:w
+ \exp_not:c { __fp_#3_o:w }
+ }
+ \cs_if_eq:NNF #2 \__fp_tmp:w
+ {
+ \cs_if_exist:NTF #2
+ {
+ \msg_warning:nnnn
+ { fp } { id-used-elsewhere } {#3} { function }
+ #1 #2 \__fp_tmp:w
+ }
+ {
+ \cs_new_eq:NN #2 \scan_stop: % to declare the function
+ #1 #2 \__fp_tmp:w
+ }
+ }
+ }
+\cs_new:Npn \__fp_function_o:w #1#2 @
+ {
+ \cs_if_exist:NTF #1
+ { #1 #2 @ }
+ {
+ \exp_after:wN \s__fp_symbolic
+ \exp_after:wN \__fp_symbolic_chk:w
+ \exp_after:wN \__fp_function_o:w
+ \exp_after:wN #1
+ \exp_after:wN ,
+ \exp_after:wN {
+ \exp:w \exp_end_continue_f:w
+ \__fp_exp_after_array_f:w #2 \s__fp_expr_stop
+ \exp_after:wN
+ }
+ \exp_after:wN ;
+ }
+ }
+\int_new:N \l__fp_function_arg_int
+\cs_new_protected:Npn \fp_set_function:nnn #1
+ {
+ \exp_args:NNo \__fp_set_function:Nnnn \cs_set_eq:cN
+ { \tl_to_str:n {#1} }
+ }
+\cs_new_protected:Npn \__fp_set_function:Nnnn #1#2#3#4
+ {
+ \__fp_id_if_invalid:nTF {#2}
+ { \msg_error:nnn { fp } { id-invalid } {#2} }
+ {
+ \cs_if_exist:cF { __fp_parse_word_#2:N }
+ { \__fp_function_set_parsing:Nn \cs_set_eq:NN {#2} }
+ \group_begin:
+ \int_zero:N \l__fp_function_arg_int
+ \exp_args:No \clist_map_inline:nn { \tl_to_str:n {#3} }
+ {
+ \int_incr:N \l__fp_function_arg_int
+ \exp_args:Ne \__fp_clear_variable_aux:n
+ {
+ \c_underscore_str \tex_romannumeral:D \l__fp_function_arg_int
+ }
+ \fp_clear_variable:n {##1}
+ \cs_set_nopar:cpe { l__fp_variable_##1_fp }
+ {
+ \exp_not:N \s__fp_symbolic
+ \exp_not:N \__fp_symbolic_chk:w
+ \exp_not:N \__fp_function_arg_o:w
+ \int_use:N \l__fp_function_arg_int
+ ########1 , { } ;
+ }
+ }
+ \cs_set:Npn \__fp_function_arg_o:w ##1 @
+ {
+ \exp_after:wN \s__fp_symbolic
+ \exp_after:wN \__fp_symbolic_chk:w
+ \exp_after:wN \__fp_function_arg_o:w
+ \tex_romannumeral:D
+ \__fp_exp_after_symbolic_loop:N ##1
+ { , \tex_romannumeral:D \use_none:nn }
+ \exp_after:wN \c_zero_int
+ \exp_after:wN { \exp_after:wN } \exp_after:wN ;
+ }
+ \fp_set:Nn \l__fp_symbolic_fp {#4}
+ \use:e
+ {
+ \exp_not:n { \cs_gset:Npn \__fp_tmp:w ##1 }
+ { \exp_not:o { \l__fp_symbolic_fp } }
+ }
+ \use:e
+ {
+ \exp_not:n { \cs_gset:Npn \__fp_tmp:w ##1 @ }
+ {
+ \exp_not:N \__fp_exp_after_symbolic_f:nw
+ \exp_not:n { { \exp_after:wN \exp_stop_f: } }
+ \exp_not:o { \__fp_tmp:w { . , {##1} } }
+ }
+ }
+ \group_end:
+ #1 { __fp_#2_o:w } \__fp_tmp:w
+ }
+ }
+\cs_new:Npn \__fp_function_arg_o:w #1. #2
+ {
+ \if_meaning:w @ #2
+ \exp_after:wN \__fp_function_arg_few:w
+ \fi:
+ \if_int_compare:w #1 = \c_one_int
+ \exp_after:wN \__fp_function_arg_get:w
+ \fi:
+ \__fp_use_i_until_s:nw
+ {
+ \exp_after:wN \__fp_function_arg_o:w
+ \int_value:w \int_eval:n { #1 - 1 } .
+ }
+ #2
+ }
+\cs_new:Npn \__fp_function_arg_few:w #1 @ { \exp_after:wN \c_nan_fp }
+\cs_new:Npn \__fp_function_arg_get:w #1#2#3; #4 @
+ {
+ \__fp_exp_after_array_f:w #3; \s__fp_expr_stop
+ \exp_after:wN \exp_stop_f:
+ }
+\cs_new_protected:Npn \fp_clear_function:n #1
+ { \exp_args:No \__fp_clear_function:n { \tl_to_str:n {#1} } }
+\cs_new_protected:Npn \__fp_clear_function:n #1
+ {
+ \__fp_id_if_invalid:nTF {#1}
+ { \msg_error:nnn { fp } { id-invalid } {#1} }
+ {
+ \cs_set_eq:cN { __fp_#1_o:w } \tex_undefine:D
+ \__fp_function_set_parsing:Nn \cs_set_eq:NN {#1}
+ }
+ }
+%% File: l3fparray.dtx
+\int_new:N \g__fp_array_int
+\int_new:N \l__fp_array_loop_int
+\cs_new_protected:Npn \fparray_new:Nn #1#2
+ {
+ \tl_new:N #1
+ \prg_replicate:nn { 3 }
+ {
+ \int_gincr:N \g__fp_array_int
+ \exp_args:NNc \tl_gput_right:Nn #1
+ { g__fp_array_ \__fp_int_to_roman:w \g__fp_array_int _intarray }
+ }
+ \exp_last_unbraced:Nfo \__fp_array_new:nNNNN
+ { \int_eval:n {#2} } #1 #1
+ }
+\cs_generate_variant:Nn \fparray_new:Nn { c }
+\cs_new_protected:Npn \__fp_array_new:nNNNN #1#2#3#4#5
+ {
+ \int_compare:nNnTF {#1} < 0
+ {
+ \msg_error:nnn { kernel } { negative-array-size } {#1}
+ \cs_undefine:N #1
+ \int_gsub:Nn \g__fp_array_int { 3 }
+ }
+ {
+ \intarray_new:Nn #2 {#1}
+ \intarray_new:Nn #3 {#1}
+ \intarray_new:Nn #4 {#1}
+ }
+ }
+\cs_new:Npn \fparray_count:N #1
+ {
+ \exp_after:wN \use_i:nnn
+ \exp_after:wN \intarray_count:N #1
+ }
+\cs_generate_variant:Nn \fparray_count:N { c }
+\cs_new:Npn \__fp_array_bounds:NNnTF #1#2#3#4#5
+ {
+ \if_int_compare:w 1 > #3 \exp_stop_f:
+ \__fp_array_bounds_error:NNn #1 #2 {#3}
+ #5
+ \else:
+ \if_int_compare:w #3 > \fparray_count:N #2 \exp_stop_f:
+ \__fp_array_bounds_error:NNn #1 #2 {#3}
+ #5
+ \else:
+ #4
+ \fi:
+ \fi:
+ }
+\cs_new:Npn \__fp_array_bounds_error:NNn #1#2#3
+ {
+ #1 { kernel } { out-of-bounds }
+ { \token_to_str:N #2 } {#3} { \fparray_count:N #2 }
+ }
+\cs_new_protected:Npn \fparray_gset:Nnn #1#2#3
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__fp_array_gset:NNNNww
+ \exp_after:wN #1
+ \exp_after:wN #1
+ \int_value:w \int_eval:n {#2} \exp_after:wN ;
+ \exp:w \exp_end_continue_f:w \__fp_parse:n {#3}
+ }
+\cs_generate_variant:Nn \fparray_gset:Nnn { c }
+\cs_new_protected:Npn \__fp_array_gset:NNNNww #1#2#3#4#5 ; #6 ;
+ {
+ \__fp_array_bounds:NNnTF \msg_error:nneee #4 {#5}
+ {
+ \exp_after:wN \__fp_change_func_type:NNN
+ \__fp_use_i_until_s:nw #6 ;
+ \__fp_array_gset:w
+ \__fp_array_gset_recover:Nw
+ #6 ; {#5} #1 #2 #3
+ }
+ { }
+ }
+\cs_new_protected:Npn \__fp_array_gset_recover:Nw #1#2 ;
+ {
+ \__fp_error:nffn { unknown-type } { \tl_to_str:n { #2 ; } } { } { }
+ \exp_after:wN #1 \c_nan_fp
+ }
+\cs_new_protected:Npn \__fp_array_gset:w \s__fp \__fp_chk:w #1#2
+ {
+ \if_case:w #1 \exp_stop_f:
+ \__fp_case_return:nw { \__fp_array_gset_special:nnNNN {#2} }
+ \or: \exp_after:wN \__fp_array_gset_normal:w
+ \or: \__fp_case_return:nw { \__fp_array_gset_special:nnNNN { #2 3 } }
+ \or: \__fp_case_return:nw { \__fp_array_gset_special:nnNNN { 1 } }
+ \fi:
+ \s__fp \__fp_chk:w #1 #2
+ }
+\cs_new_protected:Npn \__fp_array_gset_normal:w
+ \s__fp \__fp_chk:w 1 #1 #2 #3#4#5 ; #6#7#8#9
+ {
+ \__kernel_intarray_gset:Nnn #7 {#6} {#2}
+ \__kernel_intarray_gset:Nnn #8 {#6}
+ { \if_meaning:w 2 #1 3 \else: 1 \fi: #3#4 }
+ \__kernel_intarray_gset:Nnn #9 {#6} { 1 \use:nn #5 }
+ }
+\cs_new_protected:Npn \__fp_array_gset_special:nnNNN #1#2#3#4#5
+ {
+ \__kernel_intarray_gset:Nnn #3 {#2} {#1}
+ \__kernel_intarray_gset:Nnn #4 {#2} {0}
+ \__kernel_intarray_gset:Nnn #5 {#2} {0}
+ }
+\cs_new_protected:Npn \fparray_gzero:N #1
+ {
+ \int_zero:N \l__fp_array_loop_int
+ \prg_replicate:nn { \fparray_count:N #1 }
+ {
+ \int_incr:N \l__fp_array_loop_int
+ \exp_after:wN \__fp_array_gset_special:nnNNN
+ \exp_after:wN 0
+ \exp_after:wN \l__fp_array_loop_int
+ #1
+ }
+ }
+\cs_generate_variant:Nn \fparray_gzero:N { c }
+\cs_new:Npn \fparray_item:Nn #1#2
+ {
+ \exp_after:wN \__fp_array_item:NwN
+ \exp_after:wN #1
+ \int_value:w \int_eval:n {#2} ;
+ \__fp_to_decimal:w
+ }
+\cs_generate_variant:Nn \fparray_item:Nn { c }
+\cs_new:Npn \fparray_item_to_tl:Nn #1#2
+ {
+ \exp_after:wN \__fp_array_item:NwN
+ \exp_after:wN #1
+ \int_value:w \int_eval:n {#2} ;
+ \__fp_to_tl:w
+ }
+\cs_generate_variant:Nn \fparray_item_to_tl:Nn { c }
+\cs_new:Npn \__fp_array_item:NwN #1#2 ; #3
+ {
+ \__fp_array_bounds:NNnTF \msg_expandable_error:nnfff #1 {#2}
+ { \exp_after:wN \__fp_array_item:NNNnN #1 {#2} #3 }
+ { \exp_after:wN #3 \c_nan_fp }
+ }
+\cs_new:Npn \__fp_array_item:NNNnN #1#2#3#4
+ {
+ \exp_after:wN \__fp_array_item:N
+ \int_value:w \__kernel_intarray_item:Nn #2 {#4} \exp_after:wN ;
+ \int_value:w \__kernel_intarray_item:Nn #3 {#4} \exp_after:wN ;
+ \int_value:w \__kernel_intarray_item:Nn #1 {#4} ;
+ }
+\cs_new:Npn \__fp_array_item:N #1
+ {
+ \if_meaning:w 0 #1 \exp_after:wN \__fp_array_item_special:w \fi:
+ \__fp_array_item:w #1
+ }
+\cs_new:Npn \__fp_array_item:w #1 #2#3#4#5 #6 ; 1 #7 ;
+ {
+ \exp_after:wN \__fp_array_item_normal:w
+ \int_value:w \if_meaning:w #1 1 0 \else: 2 \fi: \exp_stop_f:
+ #7 ; {#2#3#4#5} {#6} ;
+ }
+\cs_new:Npn \__fp_array_item_special:w #1 ; #2 ; #3 ; #4
+ {
+ \exp_after:wN #4
+ \exp:w \exp_end_continue_f:w
+ \if_case:w #3 \exp_stop_f:
+ \exp_after:wN \c_zero_fp
+ \or: \exp_after:wN \c_nan_fp
+ \or: \exp_after:wN \c_minus_zero_fp
+ \or: \exp_after:wN \c_inf_fp
+ \else: \exp_after:wN \c_minus_inf_fp
+ \fi:
+ }
+\cs_new:Npn \__fp_array_item_normal:w #1 #2#3#4#5 #6 ; #7 ; #8 ; #9
+ { #9 \s__fp \__fp_chk:w 1 #1 {#8} #7 {#2#3#4#5} {#6} ; }
+\prg_new_eq_conditional:NNn \fparray_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \fparray_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+%% File: l3bitset.dtx
+\cs_if_exist:NT \@expl@finalise@setup@@
+ {
+ \tl_gput_right:Nn \@expl@finalise@setup@@
+ { \declare@file@substitution { l3bitset.sty } { null.tex } }
+ }
+\cs_new_protected:Npn \bitset_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_eq:NN #1 \c_zero_str
+ \prop_new:c { g__bitset_ \cs_to_str:N #1 _name_prop }
+ }
+\cs_new_protected:Npn \bitset_new:Nn #1 #2
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_eq:NN #1 \c_zero_str
+ \prop_new:c { g__bitset_ \cs_to_str:N #1 _name_prop }
+ \prop_gset_from_keyval:cn
+ { g__bitset_ \cs_to_str:N #1 _name_prop }
+ {#2}
+ }
+\cs_generate_variant:Nn \bitset_new:N { c }
+\cs_generate_variant:Nn \bitset_new:Nn { c }
+\cs_new_protected:Npn \bitset_addto_named_index:Nn #1#2
+ {
+ \prop_gput_from_keyval:cn
+ { g__bitset_ \cs_to_str:N #1 _name_prop } { #2 }
+ }
+\prg_new_eq_conditional:NNn
+ \bitset_if_exist:N \str_if_exist:N { p , T , F , TF }
+\prg_new_eq_conditional:NNn
+ \bitset_if_exist:c \str_if_exist:c { p , T , F , TF }
+\cs_new_protected:Npn \__bitset_set_true:Nn #1#2
+ { \__bitset_set:NNnN \str_set:Ne #1 {#2} 1 }
+\cs_new_protected:Npn \__bitset_gset_true:Nn #1#2
+ { \__bitset_set:NNnN \str_gset:Ne #1 {#2} 1 }
+\cs_new_protected:Npn \__bitset_set_false:Nn #1#2
+ { \__bitset_set:NNnN \str_set:Ne #1 {#2} 0 }
+\cs_new_protected:Npn \__bitset_gset_false:Nn #1#2
+ { \__bitset_set:NNnN \str_gset:Ne #1 {#2} 0 }
+\cs_new_protected:Npn \__bitset_set:NNnN #1#2#3#4
+ {
+ \int_compare:nNnT {#3} > { 0 }
+ {
+ \int_compare:nNnTF { \str_count:N #2 } < {#3}
+ {
+ #1 #2
+ {
+ #4
+ \prg_replicate:nn { #3 - \str_count:N #2 - 1 } { 0 }
+ #2
+ }
+ }
+ {
+ #1 #2
+ {
+ \str_range:Nnn #2 { 1 } { -1 - (#3) }
+ #4
+ \str_range:Nnn #2 { 1 - (#3) } { -1 }
+ }
+ }
+ }
+ }
+\int_new:N \l__bitset_internal_int
+\prg_new_protected_conditional:Npnn \__bitset_test_digits:n #1 { TF }
+ {
+ \tex_afterassignment:D \__bitset_test_digits:w
+ \l__bitset_internal_int = 0 \tl_trim_spaces_apply:nN {#1} \tl_to_str:n
+ \__bitset_test_digits_end:
+ \use_i:nnn \if_false:
+ \__bitset_test_digits_end:
+ \if_int_compare:w \c_zero_int < \l__bitset_internal_int
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new_eq:NN \__bitset_test_digits_end: \exp_stop_f:
+\cs_new_protected:Npn \__bitset_test_digits:w #1 \__bitset_test_digits_end: { }
+\cs_new_protected:Npn \bitset_set_true:Nn #1#2
+ { \__bitset_set:NNn \__bitset_set_true:Nn #1 {#2} }
+\cs_new_protected:Npn \bitset_gset_true:Nn #1#2
+ { \__bitset_set:NNn \__bitset_gset_true:Nn #1 {#2} }
+\cs_new_protected:Npn \bitset_set_false:Nn #1#2
+ { \__bitset_set:NNn \__bitset_set_false:Nn #1 {#2} }
+\cs_new_protected:Npn \bitset_gset_false:Nn #1#2
+ { \__bitset_set:NNn \__bitset_gset_false:Nn #1 {#2} }
+\cs_new_protected:Npn \__bitset_set:NNn #1#2#3
+ {
+ \prop_if_in:cnTF { g__bitset_ \cs_to_str:N #2 _name_prop } {#3}
+ {
+ #1 #2
+ {
+ \prop_item:cn { g__bitset_ \cs_to_str:N #2 _name_prop } {#3}
+ }
+ }
+ {
+ \__bitset_test_digits:nTF {#3}
+ {
+ #1 #2 {#3}
+ \prop_gput:cnn { g__bitset_ \cs_to_str:N #2 _name_prop } {#3} {#3}
+ }
+ {
+ \msg_warning:nnee { bitset } { unknown-name }
+ { \token_to_str:N #2 }
+ { \tl_to_str:n {#3} }
+ }
+ }
+ }
+\cs_generate_variant:Nn \bitset_set_true:Nn { c }
+\cs_generate_variant:Nn \bitset_gset_true:Nn { c }
+\cs_generate_variant:Nn \bitset_set_false:Nn { c }
+\cs_generate_variant:Nn \bitset_gset_false:Nn { c }
+\cs_new_protected:Npn \bitset_clear:N #1
+ {
+ \str_set_eq:NN #1 \c_zero_str
+ }
+\cs_new_protected:Npn \bitset_gclear:N #1
+ {
+ \str_gset_eq:NN #1 \c_zero_str
+ }
+\cs_generate_variant:Nn \bitset_clear:N { c }
+\cs_generate_variant:Nn \bitset_gclear:N { c }
+\cs_new:Npn \bitset_to_arabic:N #1
+ {
+ \int_compare:nNnTF { \str_count:N #1 } < { 32 }
+ { \exp_args:No \int_from_bin:n {#1} }
+ {
+ \exp_after:wN \__bitset_to_int:nN \exp_after:wN 0
+ #1 \q_recursion_tail \q_recursion_stop
+ }
+ }
+\cs_new:Npn \__bitset_to_int:nN #1#2
+ {
+ \quark_if_recursion_tail_stop_do:Nn #2 {#1}
+ \exp_args:Nf \__bitset_to_int:nN { \fp_eval:n { #1 * 2 + #2 } }
+ }
+\cs_new:Npn \bitset_to_bin:N #1
+ {
+ #1
+ }
+\cs_generate_variant:Nn \bitset_to_arabic:N { c }
+\cs_generate_variant:Nn \bitset_to_bin:N { c }
+\cs_new:Npn \bitset_item:Nn #1#2
+ {
+ \prop_if_in:cnTF { g__bitset_ \cs_to_str:N #1 _name_prop } {#2}
+ {
+ \int_eval:n
+ {
+ \str_item:Nn #1
+ { 0 - ( \prop_item:cn { g__bitset_ \cs_to_str:N #1 _name_prop } {#2} ) }
+ +0
+ }
+ }
+ {
+ 0
+ }
+ }
+\cs_generate_variant:Nn \bitset_item:Nn { c }
+\cs_new_protected:Npn \bitset_show:N { \__bitset_show:NN \msg_show:nneeee }
+\cs_generate_variant:Nn \bitset_show:N { c }
+\cs_new_protected:Npn \bitset_log:N { \__bitset_show:NN \msg_log:nneeee }
+\cs_generate_variant:Nn \bitset_log:N { c }
+\cs_new_protected:Npn \__bitset_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ #1 { bitset } { show }
+ { \token_to_str:N #2 }
+ { \bitset_to_bin:N #2 }
+ { \bitset_to_arabic:N #2 }
+ { }
+ }
+ }
+\cs_new_protected:Npn \bitset_show_named_index:N
+ { \__bitset_show_named_index:NN \msg_show:nneeee }
+\cs_generate_variant:Nn \bitset_show_named_index:N { c }
+\cs_new_protected:Npn \bitset_log_named_index:N
+ { \__bitset_show_named_index:NN \msg_log:nneeee }
+\cs_generate_variant:Nn \bitset_log_named_index:N { c }
+\cs_new_protected:Npn \__bitset_show_named_index:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ #1 { bitset } { show-names }
+ { \token_to_str:N #2 }
+ { \prop_map_function:cN { g__bitset_ \cs_to_str:N #2 _name_prop } \msg_show_item:nn }
+ { } { }
+ }
+ }
+\msg_new:nnn { bitset } { show }
+ {
+ The~bitset~#1~has~the~representation: \\
+ >~binary:~#2 \\
+ >~arabic:~#3 .
+ }
+\msg_new:nnn { bitset } { show-names }
+ {
+ The~bitset~#1~
+ \tl_if_empty:nTF {#2}
+ { knows~no~names~yet \\>~ . }
+ { knows~the~name/index~pairs~(without~outer~braces): #2 . }
+ }
+\msg_new:nnn { bitset } { unknown-name }
+ { The~name~'#2'~is~unknown~for~bitset~\tl_to_str:n {#1} }
+\prop_gput:Nnn \g_msg_module_name_prop { bitset } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { bitset } { }
+%% File: l3cctab.dtx
+\seq_new:N \g__cctab_stack_seq
+\seq_new:N \g__cctab_unused_seq
+\seq_new:N \g__cctab_group_seq
+\int_new:N \g__cctab_allocate_int
+\tl_new:N \l__cctab_internal_a_tl
+\tl_new:N \l__cctab_internal_b_tl
+\prop_new:N \g__cctab_endlinechar_prop
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \cctab_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \__cctab_new:N #1
+ }
+ \cs_new_protected:Npn \__cctab_new:N #1
+ {
+ \newcatcodetable #1
+ \tex_initcatcodetable:D #1
+ }
+ }
+ {
+ \cs_new_protected:Npn \__cctab_new:N #1
+ {
+ \debug_suspend:
+ \intarray_new:Nn #1 { 257 }
+ \debug_resume:
+ }
+ \cs_new_protected:Npn \__cctab_gstore:Nnn #1#2#3
+ { \intarray_gset:Nnn #1 { #2 + 1 } {#3} }
+ \cs_new_protected:Npn \cctab_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \__cctab_new:N #1
+ \int_step_inline:nn { 256 }
+ { \__kernel_intarray_gset:Nnn #1 {##1} { 12 } }
+ \__kernel_intarray_gset:Nnn #1 { 257 } { 13 }
+ \__cctab_gstore:Nnn #1 { 0 } { 9 }
+ \__cctab_gstore:Nnn #1 { 13 } { 5 }
+ \__cctab_gstore:Nnn #1 { 32 } { 10 }
+ \__cctab_gstore:Nnn #1 { 37 } { 14 }
+ \int_step_inline:nnn { 65 } { 90 }
+ { \__cctab_gstore:Nnn #1 {##1} { 11 } }
+ \__cctab_gstore:Nnn #1 { 92 } { 0 }
+ \int_step_inline:nnn { 97 } { 122 }
+ { \__cctab_gstore:Nnn #1 {##1} { 11 } }
+ \__cctab_gstore:Nnn #1 { 127 } { 15 }
+ }
+ }
+\cs_generate_variant:Nn \cctab_new:N { c }
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \__cctab_gset:n #1
+ { \exp_args:Nf \__cctab_gset_aux:n { \int_eval:n {#1} } }
+ \cs_new_protected:Npn \__cctab_gset_aux:n #1
+ {
+ \tex_savecatcodetable:D #1 \scan_stop:
+ \int_compare:nNnTF { \tex_endlinechar:D } = { 13 }
+ { \prop_gremove:Nn \g__cctab_endlinechar_prop {#1} }
+ {
+ \prop_gput:NnV \g__cctab_endlinechar_prop {#1}
+ \tex_endlinechar:D
+ }
+ }
+ }
+ {
+ \cs_new_protected:Npn \__cctab_gset:n #1
+ {
+ \int_step_inline:nn { 256 }
+ {
+ \__kernel_intarray_gset:Nnn #1 {##1}
+ { \char_value_catcode:n { ##1 - 1 } }
+ }
+ \__kernel_intarray_gset:Nnn #1 { 257 }
+ { \tex_endlinechar:D }
+ }
+ }
+\cs_new_protected:Npn \cctab_gset:Nn #1#2
+ {
+ \__cctab_chk_if_valid:NT #1
+ {
+ \group_begin:
+ \cctab_select:N \c_initex_cctab
+ #2 \scan_stop:
+ \__cctab_gset:n {#1}
+ \group_end:
+ }
+ }
+\cs_generate_variant:Nn \cctab_gset:Nn { c }
+\cs_new_protected:Npn \cctab_gsave_current:N #1
+ {
+ \__cctab_chk_if_valid:NT #1
+ { \__cctab_gset:n {#1} }
+ }
+\cs_generate_variant:Nn \cctab_gsave_current:N { c }
+\sys_if_engine_luatex:T
+ {
+ \__cctab_new:N \g__cctab_internal_cctab
+ \cs_new:Npn \__cctab_internal_cctab_name:
+ {
+ g__cctab_internal
+ \tex_romannumeral:D \tex_currentgrouplevel:D
+ _cctab
+ }
+ }
+\cs_new_protected:Npn \cctab_select:N #1
+ { \__cctab_chk_if_valid:NT #1 { \__cctab_select:N #1 } }
+\cs_generate_variant:Nn \cctab_select:N { c }
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \__cctab_select:N #1
+ {
+ \tex_catcodetable:D #1
+ \prop_get:NVNTF \g__cctab_endlinechar_prop #1 \l__cctab_internal_a_tl
+ { \int_set:Nn \tex_endlinechar:D { \l__cctab_internal_a_tl } }
+ { \int_set:Nn \tex_endlinechar:D { 13 } }
+ \cs_if_exist:cF { \__cctab_internal_cctab_name: }
+ { \exp_args:Nc \__cctab_new:N { \__cctab_internal_cctab_name: } }
+ \exp_args:Nc \tex_savecatcodetable:D { \__cctab_internal_cctab_name: }
+ \exp_args:Nc \tex_catcodetable:D { \__cctab_internal_cctab_name: }
+ }
+ }
+ {
+ \cs_new_protected:Npn \__cctab_select:N #1
+ {
+ \int_step_inline:nn { 256 }
+ {
+ \char_set_catcode:nn { ##1 - 1 }
+ { \__kernel_intarray_item:Nn #1 {##1} }
+ }
+ \int_set:Nn \tex_endlinechar:D
+ { \__kernel_intarray_item:Nn #1 { 257 } }
+ }
+ }
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \__cctab_begin_aux:
+ {
+ \__cctab_new:N \g__cctab_next_cctab
+ \tl_set:NV \l__cctab_internal_a_tl \g__cctab_next_cctab
+ \cs_undefine:N \g__cctab_next_cctab
+ }
+ }
+ {
+ \cs_new_protected:Npn \__cctab_begin_aux:
+ {
+ \int_gincr:N \g__cctab_allocate_int
+ \exp_args:Nc \__cctab_new:N
+ { g__cctab_ \int_use:N \g__cctab_allocate_int _cctab }
+ \exp_args:NNc \tl_set:Nn \l__cctab_internal_a_tl
+ { g__cctab_ \int_use:N \g__cctab_allocate_int _cctab }
+ }
+ }
+\cs_new_protected:Npn \cctab_begin:N #1
+ {
+ \__cctab_chk_if_valid:NT #1
+ {
+ \seq_gpop:NNF \g__cctab_unused_seq \l__cctab_internal_a_tl
+ { \__cctab_begin_aux: }
+ \__cctab_chk_group_begin:e
+ { \__cctab_nesting_number:N \l__cctab_internal_a_tl }
+ \seq_gpush:NV \g__cctab_stack_seq \l__cctab_internal_a_tl
+ \exp_args:NV \__cctab_gset:n \l__cctab_internal_a_tl
+ \__cctab_select:N #1
+ }
+ }
+\cs_generate_variant:Nn \cctab_begin:N { c }
+\cs_new_protected:Npn \cctab_end:
+ {
+ \seq_gpop:NNTF \g__cctab_stack_seq \l__cctab_internal_a_tl
+ {
+ \seq_gpush:NV \g__cctab_unused_seq \l__cctab_internal_a_tl
+ \exp_args:Ne \__cctab_chk_group_end:n
+ { \__cctab_nesting_number:N \l__cctab_internal_a_tl }
+ \__cctab_select:N \l__cctab_internal_a_tl
+ }
+ { \msg_error:nn { cctab } { extra-end } }
+ }
+\cs_new_protected:Npn \__cctab_chk_group_begin:n #1
+ {
+ \seq_gpush:Ne \g__cctab_group_seq
+ { \int_use:N \tex_currentgrouplevel:D }
+ \cs_set_eq:cN { __cctab_group_ #1 _chk: } \prg_do_nothing:
+ }
+\cs_generate_variant:Nn \__cctab_chk_group_begin:n { e }
+\cs_new_protected:Npn \__cctab_chk_group_end:n #1
+ {
+ \seq_gpop:NN \g__cctab_group_seq \l__cctab_internal_b_tl
+ \bool_lazy_and:nnF
+ {
+ \int_compare_p:nNn
+ { \tex_currentgrouplevel:D } = { \l__cctab_internal_b_tl }
+ }
+ { \cs_if_exist_p:c { __cctab_group_ #1 _chk: } }
+ {
+ \msg_error:nne { cctab } { group-mismatch }
+ {
+ \int_sign:n
+ { \tex_currentgrouplevel:D - \l__cctab_internal_b_tl }
+ }
+ }
+ \cs_undefine:c { __cctab_group_ #1 _chk: }
+ }
+\sys_if_engine_luatex:TF
+ { \cs_new:Npn \__cctab_nesting_number:N #1 {#1} }
+ {
+ \cs_new:Npn \__cctab_nesting_number:N #1
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \__cctab_nesting_number:w
+ \exp_after:wN \token_to_str:N #1
+ }
+ \use:e
+ {
+ \cs_new:Npn \exp_not:N \__cctab_nesting_number:w
+ #1 \tl_to_str:n { g__cctab_ } #2 \tl_to_str:n { _cctab } {#2}
+ }
+ }
+\cs_if_exist:NT \hook_gput_code:nnn
+ {
+ \hook_gput_code:nnn { enddocument/end } { cctab }
+ {
+ \seq_if_empty:NF \g__cctab_stack_seq
+ { \msg_error:nn { cctab } { missing-end } }
+ }
+ }
+\cs_new:Npn \cctab_item:Nn #1#2
+ { \exp_args:Nf \__cctab_item:nN { \int_eval:n {#2} } #1 }
+\sys_if_engine_luatex:TF
+ {
+ \cs_new:Npn \__cctab_item:nN #1#2
+ { \lua_now:e { tex.print(-2, tex.getcatcode(\int_use:N #2, #1)) } }
+ }
+ {
+ \cs_new:Npn \__cctab_item:nN #1#2
+ {
+ \int_compare:nNnTF {#1} < { 256 }
+ { \intarray_item:Nn #2 { #1 + 1 } }
+ { \char_value_catcode:n {#1} }
+ }
+ }
+\cs_generate_variant:Nn \cctab_item:Nn { c }
+\prg_new_eq_conditional:NNn \cctab_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \cctab_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+\prg_new_protected_conditional:Npnn \__cctab_chk_if_valid:N #1
+ { TF , T , F }
+ {
+ \cctab_if_exist:NTF #1
+ {
+ \__cctab_chk_if_valid_aux:NTF #1
+ { \prg_return_true: }
+ {
+ \msg_error:nne { cctab } { invalid-cctab }
+ { \token_to_str:N #1 }
+ \prg_return_false:
+ }
+ }
+ {
+ \msg_error:nne { kernel } { command-not-defined }
+ { \token_to_str:N #1 }
+ \prg_return_false:
+ }
+ }
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \__cctab_chk_if_valid_aux:NTF #1
+ {
+ \int_compare:nNnTF {#1-1} < { \e@alloc@ccodetable@count }
+ }
+ \cs_if_exist:NT \c_syst_catcodes_n
+ {
+ \cs_gset_protected:Npn \__cctab_chk_if_valid_aux:NTF #1
+ {
+ \int_compare:nTF { #1 <= \c_syst_catcodes_n }
+ }
+ }
+ }
+ {
+ \cs_new_protected:Npn \__cctab_chk_if_valid_aux:NTF #1
+ {
+ \exp_args:Nf \str_if_in:nnTF
+ { \cs_meaning:N #1 }
+ { select~font~cmr10~at~ }
+ }
+ }
+\cs_new_protected:Npn \cctab_const:Nn #1#2
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \__cctab_new:N #1
+ \group_begin:
+ \cctab_select:N \c_initex_cctab
+ #2 \scan_stop:
+ \__cctab_gset:n {#1}
+ \group_end:
+ }
+\cs_generate_variant:Nn \cctab_const:Nn { c }
+\cctab_new:N \c_initex_cctab
+\cctab_const:Nn \c_other_cctab
+ {
+ \cctab_select:N \c_initex_cctab
+ \int_set:Nn \tex_endlinechar:D { -1 }
+ \int_step_inline:nnn { 0 } { 127 }
+ { \char_set_catcode_other:n {#1} }
+ }
+\cctab_const:Nn \c_str_cctab
+ {
+ \cctab_select:N \c_other_cctab
+ \char_set_catcode_space:n { 32 }
+ }
+\cs_if_exist:NTF \@expl@finalise@setup@@
+ { \tl_gput_right:Nn \@expl@finalise@setup@@ }
+ { \use:n }
+ {
+ \__cctab_new:N \c_code_cctab
+ \group_begin:
+ \int_set:Nn \tex_endlinechar:D { 32 }
+ \char_set_catcode_invalid:n { 0 }
+ \bool_lazy_or:nnTF
+ { \sys_if_engine_xetex_p: } { \sys_if_engine_luatex_p: }
+ { \int_step_function:nN { 31 } \char_set_catcode_invalid:n }
+ { \int_step_function:nN { 31 } \char_set_catcode_active:n }
+ \int_step_function:nnN { 33 } { 64 } \char_set_catcode_other:n
+ \int_step_function:nnN { 65 } { 90 } \char_set_catcode_letter:n
+ \int_step_function:nnN { 91 } { 96 } \char_set_catcode_other:n
+ \int_step_function:nnN { 97 } { 122 } \char_set_catcode_letter:n
+ \char_set_catcode_ignore:n { 9 } % tab
+ \char_set_catcode_other:n { 10 } % lf
+ \char_set_catcode_active:n { 12 } % ff
+ \char_set_catcode_end_line:n { 13 } % cr
+ \char_set_catcode_ignore:n { 32 } % space
+ \char_set_catcode_parameter:n { 35 } % hash
+ \char_set_catcode_math_toggle:n { 36 } % dollar
+ \char_set_catcode_comment:n { 37 } % percent
+ \char_set_catcode_alignment:n { 38 } % ampersand
+ \char_set_catcode_letter:n { 58 } % colon
+ \char_set_catcode_escape:n { 92 } % backslash
+ \char_set_catcode_math_superscript:n { 94 } % circumflex
+ \char_set_catcode_letter:n { 95 } % underscore
+ \char_set_catcode_group_begin:n { 123 } % left brace
+ \char_set_catcode_other:n { 124 } % pipe
+ \char_set_catcode_group_end:n { 125 } % right brace
+ \char_set_catcode_space:n { 126 } % tilde
+ \char_set_catcode_invalid:n { 127 } % ^^?
+ \bool_lazy_or:nnF
+ { \sys_if_engine_xetex_p: } { \sys_if_engine_luatex_p: }
+ { \int_step_function:nnN { 128 } { 255 } \char_set_catcode_active:n }
+ \__cctab_gset:n { \c_code_cctab }
+ \group_end:
+ \cctab_const:Nn \c_document_cctab
+ {
+ \cctab_select:N \c_code_cctab
+ \int_set:Nn \tex_endlinechar:D { 13 }
+ \char_set_catcode_space:n { 9 }
+ \char_set_catcode_space:n { 32 }
+ \char_set_catcode_other:n { 58 }
+ \char_set_catcode_math_subscript:n { 95 }
+ \char_set_catcode_active:n { 126 }
+ }
+ }
+\cctab_new:N \g_tmpa_cctab
+\cctab_new:N \g_tmpb_cctab
+\msg_new:nnnn { cctab } { stack-full }
+ { The~category~code~table~stack~is~exhausted. }
+ {
+ LaTeX~has~been~asked~to~switch~to~a~new~category~code~table,~
+ but~there~is~no~more~space~to~do~this!
+ }
+\msg_new:nnnn { cctab } { extra-end }
+ { Extra~\iow_char:N\\cctab_end:~ignored~\msg_line_context:. }
+ {
+ LaTeX~came~across~a~\iow_char:N\\cctab_end:~without~a~matching~
+ \iow_char:N\\cctab_begin:N.~This~command~will~be~ignored.
+ }
+\msg_new:nnnn { cctab } { missing-end }
+ { Missing~\iow_char:N\\cctab_end:~before~end~of~TeX~run. }
+ {
+ LaTeX~came~across~more~\iow_char:N\\cctab_begin:N~than~
+ \iow_char:N\\cctab_end:.
+ }
+\msg_new:nnnn { cctab } { invalid-cctab }
+ { Invalid~\iow_char:N\\catcode~table. }
+ {
+ You~can~only~switch~to~a~\iow_char:N\\catcode~table~that~is~
+ initialized~using~\iow_char:N\\cctab_new:N~or~
+ \iow_char:N\\cctab_const:Nn.
+ }
+\msg_new:nnnn { cctab } { group-mismatch }
+ {
+ \iow_char:N\\cctab_end:~occurred~in~a~
+ \int_case:nn {#1}
+ {
+ { 0 } { different~group }
+ { 1 } { higher~group~level }
+ { -1 } { lower~group~level }
+ } ~than~
+ the~matching~\iow_char:N\\cctab_begin:N.
+ }
+ {
+ Catcode~tables~and~groups~must~be~properly~nested,~but~
+ you~tried~to~interleave~them.~LaTeX~will~try~to~proceed,~
+ but~results~may~be~unexpected.
+ }
+\prop_gput:Nnn \g_msg_module_name_prop { cctab } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { cctab } { }
+%% File l3sort.dtx
+\seq_new:N \g__sort_internal_seq
+\tl_new:N \g__sort_internal_tl
+\int_new:N \l__sort_length_int
+\int_new:N \l__sort_min_int
+\int_new:N \l__sort_top_int
+\int_new:N \l__sort_max_int
+\int_new:N \l__sort_true_max_int
+\int_new:N \l__sort_block_int
+\int_new:N \l__sort_begin_int
+\int_new:N \l__sort_end_int
+\int_new:N \l__sort_A_int
+\int_new:N \l__sort_B_int
+\int_new:N \l__sort_C_int
+\scan_new:N \s__sort_mark
+\scan_new:N \s__sort_stop
+\cs_new_protected:Npn \__sort_shrink_range:
+ {
+ \int_set:Nn \l__sort_A_int
+ { \l__sort_true_max_int - \l__sort_min_int + 1 }
+ \int_set:Nn \l__sort_block_int { \c_max_register_int / 2 }
+ \__sort_shrink_range_loop:
+ \int_set:Nn \l__sort_max_int
+ {
+ \int_compare:nNnTF
+ { \l__sort_block_int * 3 / 2 } > \l__sort_A_int
+ {
+ \l__sort_min_int
+ + ( \l__sort_A_int - 1 ) / 2
+ + \l__sort_block_int / 4
+ - 1
+ }
+ { \l__sort_true_max_int - \l__sort_block_int / 2 }
+ }
+ }
+\cs_new_protected:Npn \__sort_shrink_range_loop:
+ {
+ \if_int_compare:w \l__sort_A_int < \l__sort_block_int
+ \tex_divide:D \l__sort_block_int 2 \exp_stop_f:
+ \exp_after:wN \__sort_shrink_range_loop:
+ \fi:
+ }
+\cs_new_protected:Npn \__sort_compute_range:
+ {
+ \int_set:Nn \l__sort_min_int { \tex_count:D 15 + 1 }
+ \int_set:Nn \l__sort_true_max_int { \c_max_register_int + 1 }
+ \__sort_shrink_range:
+ \if_meaning:w \loctoks \tex_undefined:D \else:
+ \if_meaning:w \loctoks \scan_stop: \else:
+ \__sort_redefine_compute_range:
+ \__sort_compute_range:
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \__sort_redefine_compute_range:
+ {
+ \cs_if_exist:cTF { ver@elocalloc.sty }
+ {
+ \cs_gset_protected:Npn \__sort_compute_range:
+ {
+ \int_set:Nn \l__sort_min_int { \tex_count:D 265 }
+ \int_set_eq:NN \l__sort_true_max_int \e@alloc@top
+ \__sort_shrink_range:
+ }
+ }
+ {
+ \cs_gset_protected:Npn \__sort_compute_range:
+ {
+ \int_set:Nn \l__sort_min_int { \tex_count:D 265 }
+ \int_set:Nn \l__sort_true_max_int { \tex_count:D 275 }
+ \__sort_shrink_range:
+ }
+ }
+ }
+\cs_if_exist:NT \loctoks { \__sort_redefine_compute_range: }
+\tl_map_inline:nn { \lastallocatedtoks \c_syst_last_allocated_toks }
+ {
+ \cs_if_exist:NT #1
+ {
+ \cs_gset_protected:Npn \__sort_compute_range:
+ {
+ \int_set:Nn \l__sort_min_int { #1 + 1 }
+ \int_set:Nn \l__sort_true_max_int { \c_max_register_int + 1 }
+ \__sort_shrink_range:
+ }
+ }
+ }
+\cs_new_protected:Npn \__sort_main:NNNn #1#2#3#4
+ {
+ \__sort_disable_toksdef:
+ \__sort_compute_range:
+ \int_set_eq:NN \l__sort_top_int \l__sort_min_int
+ #1 #3
+ {
+ \if_int_compare:w \l__sort_top_int = \l__sort_max_int
+ \__sort_too_long_error:NNw #2 #3
+ \fi:
+ \tex_toks:D \l__sort_top_int {##1}
+ \int_incr:N \l__sort_top_int
+ }
+ \int_set:Nn \l__sort_length_int
+ { \l__sort_top_int - \l__sort_min_int }
+ \cs_set:Npn \__sort_compare:nn ##1 ##2 {#4}
+ \int_set:Nn \l__sort_block_int { 1 }
+ \__sort_level:
+ }
+\cs_new_protected:Npn \tl_sort:Nn { \__sort_tl:NNn \tl_set_eq:NN }
+\cs_generate_variant:Nn \tl_sort:Nn { c }
+\cs_new_protected:Npn \tl_gsort:Nn { \__sort_tl:NNn \tl_gset_eq:NN }
+\cs_generate_variant:Nn \tl_gsort:Nn { c }
+\cs_new_protected:Npn \__sort_tl:NNn #1#2#3
+ {
+ \group_begin:
+ \__sort_main:NNNn \tl_map_inline:Nn \tl_map_break:n #2 {#3}
+ \__kernel_tl_gset:Nx \g__sort_internal_tl
+ { \__sort_tl_toks:w \l__sort_min_int ; }
+ \group_end:
+ #1 #2 \g__sort_internal_tl
+ \tl_gclear:N \g__sort_internal_tl
+ \prg_break_point:
+ }
+\cs_new:Npn \__sort_tl_toks:w #1 ;
+ {
+ \if_int_compare:w #1 < \l__sort_top_int
+ { \tex_the:D \tex_toks:D #1 }
+ \exp_after:wN \__sort_tl_toks:w
+ \int_value:w \int_eval:n { #1 + 1 } \exp_after:wN ;
+ \fi:
+ }
+\cs_new_protected:Npn \seq_sort:Nn
+ { \__sort_seq:NNNNn \seq_map_inline:Nn \seq_map_break:n \seq_set_eq:NN }
+\cs_generate_variant:Nn \seq_sort:Nn { c }
+\cs_new_protected:Npn \seq_gsort:Nn
+ { \__sort_seq:NNNNn \seq_map_inline:Nn \seq_map_break:n \seq_gset_eq:NN }
+\cs_generate_variant:Nn \seq_gsort:Nn { c }
+\cs_new_protected:Npn \clist_sort:Nn
+ {
+ \__sort_seq:NNNNn \clist_map_inline:Nn \clist_map_break:n
+ \clist_set_from_seq:NN
+ }
+\cs_generate_variant:Nn \clist_sort:Nn { c }
+\cs_new_protected:Npn \clist_gsort:Nn
+ {
+ \__sort_seq:NNNNn \clist_map_inline:Nn \clist_map_break:n
+ \clist_gset_from_seq:NN
+ }
+\cs_generate_variant:Nn \clist_gsort:Nn { c }
+\cs_new_protected:Npn \__sort_seq:NNNNn #1#2#3#4#5
+ {
+ \group_begin:
+ \__sort_main:NNNn #1 #2 #4 {#5}
+ \seq_gclear:N \g__sort_internal_seq
+ \int_step_inline:nnn
+ \l__sort_min_int { \l__sort_top_int - 1 }
+ {
+ \seq_gput_right:Ne \g__sort_internal_seq
+ { \tex_the:D \tex_toks:D ##1 }
+ }
+ \group_end:
+ #3 #4 \g__sort_internal_seq
+ \seq_gclear:N \g__sort_internal_seq
+ \prg_break_point:
+ }
+\cs_new_protected:Npn \__sort_level:
+ {
+ \if_int_compare:w \l__sort_block_int < \l__sort_length_int
+ \l__sort_end_int \l__sort_min_int
+ \__sort_merge_blocks:
+ \tex_advance:D \l__sort_block_int \l__sort_block_int
+ \exp_after:wN \__sort_level:
+ \fi:
+ }
+\cs_new_protected:Npn \__sort_merge_blocks:
+ {
+ \l__sort_begin_int \l__sort_end_int
+ \tex_advance:D \l__sort_end_int \l__sort_block_int
+ \if_int_compare:w \l__sort_end_int < \l__sort_top_int
+ \l__sort_A_int \l__sort_end_int
+ \tex_advance:D \l__sort_end_int \l__sort_block_int
+ \if_int_compare:w \l__sort_end_int > \l__sort_top_int
+ \l__sort_end_int \l__sort_top_int
+ \fi:
+ \l__sort_B_int \l__sort_A_int
+ \l__sort_C_int \l__sort_top_int
+ \__sort_copy_block:
+ \int_decr:N \l__sort_A_int
+ \int_decr:N \l__sort_B_int
+ \int_decr:N \l__sort_C_int
+ \exp_after:wN \__sort_merge_blocks_aux:
+ \exp_after:wN \__sort_merge_blocks:
+ \fi:
+ }
+\cs_new_protected:Npn \__sort_copy_block:
+ {
+ \tex_toks:D \l__sort_C_int \tex_toks:D \l__sort_B_int
+ \int_incr:N \l__sort_C_int
+ \int_incr:N \l__sort_B_int
+ \if_int_compare:w \l__sort_B_int = \l__sort_end_int
+ \use_i:nn
+ \fi:
+ \__sort_copy_block:
+ }
+\cs_new_protected:Npn \__sort_merge_blocks_aux:
+ {
+ \exp_after:wN \__sort_compare:nn \exp_after:wN
+ { \tex_the:D \tex_toks:D \exp_after:wN \l__sort_A_int \exp_after:wN }
+ \exp_after:wN { \tex_the:D \tex_toks:D \l__sort_C_int }
+ \prg_do_nothing:
+ \__sort_return_mark:w
+ \__sort_return_mark:w
+ \s__sort_mark
+ \__sort_return_none_error:
+ }
+\cs_new_protected:Npn \sort_return_same:
+ #1 \__sort_return_mark:w #2 \s__sort_mark
+ {
+ #1
+ #2
+ \__sort_return_two_error:
+ \__sort_return_mark:w
+ \s__sort_mark
+ \__sort_return_same:w
+ }
+\cs_new_protected:Npn \sort_return_swapped:
+ #1 \__sort_return_mark:w #2 \s__sort_mark
+ {
+ #1
+ #2
+ \__sort_return_two_error:
+ \__sort_return_mark:w
+ \s__sort_mark
+ \__sort_return_swapped:w
+ }
+\cs_new_protected:Npn \__sort_return_mark:w #1 \s__sort_mark { }
+\cs_new_protected:Npn \__sort_return_none_error:
+ {
+ \msg_error:nnee { sort } { return-none }
+ { \tex_the:D \tex_toks:D \l__sort_A_int }
+ { \tex_the:D \tex_toks:D \l__sort_C_int }
+ \__sort_return_same:w \__sort_return_none_error:
+ }
+\cs_new_protected:Npn \__sort_return_two_error:
+ {
+ \msg_error:nnee { sort } { return-two }
+ { \tex_the:D \tex_toks:D \l__sort_A_int }
+ { \tex_the:D \tex_toks:D \l__sort_C_int }
+ }
+\cs_new_protected:Npn \__sort_return_same:w #1 \__sort_return_none_error:
+ {
+ \tex_toks:D \l__sort_B_int \tex_toks:D \l__sort_C_int
+ \int_decr:N \l__sort_B_int
+ \int_decr:N \l__sort_C_int
+ \if_int_compare:w \l__sort_C_int < \l__sort_top_int
+ \use_i:nn
+ \fi:
+ \__sort_merge_blocks_aux:
+ }
+\cs_new_protected:Npn \__sort_return_swapped:w #1 \__sort_return_none_error:
+ {
+ \tex_toks:D \l__sort_B_int \tex_toks:D \l__sort_A_int
+ \int_decr:N \l__sort_B_int
+ \int_decr:N \l__sort_A_int
+ \if_int_compare:w \l__sort_A_int < \l__sort_begin_int
+ \__sort_merge_blocks_end: \use_i:nn
+ \fi:
+ \__sort_merge_blocks_aux:
+ }
+\cs_new_protected:Npn \__sort_merge_blocks_end:
+ {
+ \tex_toks:D \l__sort_B_int \tex_toks:D \l__sort_C_int
+ \int_decr:N \l__sort_B_int
+ \int_decr:N \l__sort_C_int
+ \if_int_compare:w \l__sort_B_int < \l__sort_begin_int
+ \use_i:nn
+ \fi:
+ \__sort_merge_blocks_end:
+ }
+\cs_new:Npn \tl_sort:nN #1#2
+ {
+ \exp_not:f
+ {
+ \tl_if_blank:nF {#1}
+ {
+ \__sort_quick_prepare:Nnnn #2 { } { }
+ #1
+ { \prg_break_point: \__sort_quick_prepare_end:NNNnw }
+ \s__sort_stop
+ }
+ }
+ }
+\cs_new:Npn \__sort_quick_prepare:Nnnn #1#2#3#4
+ {
+ \prg_break: #4 \prg_break_point:
+ \__sort_quick_prepare:Nnnn #1 { #2 #3 } { #1 {#4} }
+ }
+\cs_new:Npn \__sort_quick_prepare_end:NNNnw #1#2#3#4#5 \s__sort_stop
+ {
+ \__sort_quick_split:NnNn #4 \__sort_quick_end:nnTFNn { }
+ \s__sort_mark { \__sort_quick_cleanup:w \exp_stop_f: }
+ \s__sort_mark \s__sort_stop
+ }
+\cs_new:Npn \__sort_quick_cleanup:w #1 \s__sort_mark \s__sort_stop {#1}
+\cs_new:Npn \__sort_quick_split:NnNn #1#2#3#4
+ {
+ #3 {#2} {#4} \__sort_quick_only_ii:NnnnnNn
+ \__sort_quick_only_i:NnnnnNn
+ \__sort_quick_single_end:nnnwnw
+ { #3 {#4} } { } { } {#2}
+ }
+\cs_new:Npn \__sort_quick_only_i:NnnnnNn #1#2#3#4#5#6#7
+ {
+ #6 {#5} {#7} \__sort_quick_split_ii:NnnnnNn
+ \__sort_quick_only_i:NnnnnNn
+ \__sort_quick_only_i_end:nnnwnw
+ { #6 {#7} } { #3 #2 } { } {#5}
+ }
+\cs_new:Npn \__sort_quick_only_ii:NnnnnNn #1#2#3#4#5#6#7
+ {
+ #6 {#5} {#7} \__sort_quick_only_ii:NnnnnNn
+ \__sort_quick_split_i:NnnnnNn
+ \__sort_quick_only_ii_end:nnnwnw
+ { #6 {#7} } { } { #4 #2 } {#5}
+ }
+\cs_new:Npn \__sort_quick_split_i:NnnnnNn #1#2#3#4#5#6#7
+ {
+ #6 {#5} {#7} \__sort_quick_split_ii:NnnnnNn
+ \__sort_quick_split_i:NnnnnNn
+ \__sort_quick_split_end:nnnwnw
+ { #6 {#7} } { #3 #2 } {#4} {#5}
+ }
+\cs_new:Npn \__sort_quick_split_ii:NnnnnNn #1#2#3#4#5#6#7
+ {
+ #6 {#5} {#7} \__sort_quick_split_ii:NnnnnNn
+ \__sort_quick_split_i:NnnnnNn
+ \__sort_quick_split_end:nnnwnw
+ { #6 {#7} } {#3} { #4 #2 } {#5}
+ }
+\cs_new:Npn \__sort_quick_end:nnTFNn #1#2#3#4#5#6 {#5}
+\cs_new:Npn \__sort_quick_single_end:nnnwnw #1#2#3#4 \s__sort_mark #5#6 \s__sort_stop
+ { #5 {#3} #6 \s__sort_stop }
+\cs_new:Npn \__sort_quick_only_i_end:nnnwnw #1#2#3#4 \s__sort_mark #5#6 \s__sort_stop
+ {
+ \__sort_quick_split:NnNn #1
+ \__sort_quick_end:nnTFNn { } \s__sort_mark {#5}
+ {#3}
+ #6 \s__sort_stop
+ }
+\cs_new:Npn \__sort_quick_only_ii_end:nnnwnw #1#2#3#4 \s__sort_mark #5#6 \s__sort_stop
+ {
+ \__sort_quick_split:NnNn #2
+ \__sort_quick_end:nnTFNn { } \s__sort_mark { #5 {#3} }
+ #6 \s__sort_stop
+ }
+\cs_new:Npn \__sort_quick_split_end:nnnwnw #1#2#3#4 \s__sort_mark #5#6 \s__sort_stop
+ {
+ \__sort_quick_split:NnNn #2 \__sort_quick_end:nnTFNn { } \s__sort_mark
+ {
+ \__sort_quick_split:NnNn #1
+ \__sort_quick_end:nnTFNn { } \s__sort_mark {#5}
+ {#3}
+ }
+ #6 \s__sort_stop
+ }
+\cs_new_protected:Npn \__sort_error:
+ {
+ \cs_set_eq:NN \__sort_merge_blocks_aux: \prg_do_nothing:
+ \cs_set_eq:NN \__sort_merge_blocks: \prg_do_nothing:
+ \cs_set_protected:Npn \__sort_level: { \group_end: \prg_break: }
+ }
+\cs_new_protected:Npn \__sort_disable_toksdef:
+ { \cs_set_eq:NN \toksdef \__sort_disabled_toksdef:n }
+\cs_new_protected:Npn \__sort_disabled_toksdef:n #1
+ {
+ \msg_error:nne { sort } { toksdef }
+ { \token_to_str:N #1 }
+ \__sort_error:
+ \tex_toksdef:D #1
+ }
+\msg_new:nnnn { sort } { toksdef }
+ { Allocation~of~\iow_char:N\\toks~registers~impossible~while~sorting. }
+ {
+ The~comparison~code~used~for~sorting~a~list~has~attempted~to~
+ define~#1~as~a~new~\iow_char:N\\toks~register~using~
+ \iow_char:N\\newtoks~
+ or~a~similar~command.~The~list~will~not~be~sorted.
+ }
+\cs_new_protected:Npn \__sort_too_long_error:NNw #1#2 \fi:
+ {
+ \fi:
+ \msg_error:nneee { sort } { too-large }
+ { \token_to_str:N #2 }
+ { \int_eval:n { \l__sort_true_max_int - \l__sort_min_int } }
+ { \int_eval:n { \l__sort_top_int - \l__sort_min_int } }
+ #1 \__sort_error:
+ }
+\msg_new:nnnn { sort } { too-large }
+ { The~list~#1~is~too~long~to~be~sorted~by~TeX. }
+ {
+ TeX~has~#2~toks~registers~still~available:~
+ this~only~allows~to~sort~with~up~to~#3~
+ items.~The~list~will~not~be~sorted.
+ }
+\msg_new:nnnn { sort } { return-none }
+ { The~comparison~code~did~not~return. }
+ {
+ When~sorting~a~list,~the~code~to~compare~items~#1~and~#2~
+ did~not~call~
+ \iow_char:N\\sort_return_same: ~nor~
+ \iow_char:N\\sort_return_swapped: .~
+ Exactly~one~of~these~should~be~called.
+ }
+\msg_new:nnnn { sort } { return-two }
+ { The~comparison~code~returned~multiple~times. }
+ {
+ When~sorting~a~list,~the~code~to~compare~items~#1~and~#2~called~
+ \iow_char:N\\sort_return_same: ~or~
+ \iow_char:N\\sort_return_swapped: ~multiple~times.~
+ Exactly~one~of~these~should~be~called.
+ }
+\prop_gput:Nnn \g_msg_module_name_prop { sort } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { sort } { }
+%% File: l3str-convert.dtx
+\cs_new_protected:Npn \__str_tmp:w { }
+\tl_new:N \l__str_internal_tl
+\tl_new:N \g__str_result_tl
+\int_const:Nn \c__str_replacement_char_int { "FFFD }
+\int_const:Nn \c__str_max_byte_int { 255 }
+\scan_new:N \s__str
+\quark_new:N \q__str_nil
+\prop_new:N \g__str_alias_prop
+\prop_gput:Nnn \g__str_alias_prop { latin1 } { iso88591 }
+\prop_gput:Nnn \g__str_alias_prop { latin2 } { iso88592 }
+\prop_gput:Nnn \g__str_alias_prop { latin3 } { iso88593 }
+\prop_gput:Nnn \g__str_alias_prop { latin4 } { iso88594 }
+\prop_gput:Nnn \g__str_alias_prop { latin5 } { iso88599 }
+\prop_gput:Nnn \g__str_alias_prop { latin6 } { iso885910 }
+\prop_gput:Nnn \g__str_alias_prop { latin7 } { iso885913 }
+\prop_gput:Nnn \g__str_alias_prop { latin8 } { iso885914 }
+\prop_gput:Nnn \g__str_alias_prop { latin9 } { iso885915 }
+\prop_gput:Nnn \g__str_alias_prop { latin10 } { iso885916 }
+\prop_gput:Nnn \g__str_alias_prop { utf16le } { utf16 }
+\prop_gput:Nnn \g__str_alias_prop { utf16be } { utf16 }
+\prop_gput:Nnn \g__str_alias_prop { utf32le } { utf32 }
+\prop_gput:Nnn \g__str_alias_prop { utf32be } { utf32 }
+\prop_gput:Nnn \g__str_alias_prop { hexadecimal } { hex }
+\bool_lazy_any:nTF
+ {
+ \sys_if_engine_luatex_p:
+ \sys_if_engine_xetex_p:
+ }
+ {
+ \prop_gput:Nnn \g__str_alias_prop { default } { }
+ }
+ {
+ \prop_gput:Nnn \g__str_alias_prop { default } { utf8 }
+ }
+\bool_new:N \g__str_error_bool
+\flag_new:N \l__str_byte_flag
+\flag_new:N \l__str_error_flag
+\prg_new_conditional:Npnn \__str_if_contains_char:Nn #1#2 { T , TF }
+ {
+ \exp_after:wN \__str_if_contains_char_aux:nn \exp_after:wN {#1} {#2}
+ { \prg_break:n { ? \fi: } }
+ \prg_break_point:
+ \prg_return_false:
+ }
+\cs_new:Npn \__str_if_contains_char_aux:nn #1#2
+ { \__str_if_contains_char_auxi:nN {#2} #1 }
+\prg_new_conditional:Npnn \__str_if_contains_char:nn #1#2 { TF }
+ {
+ \__str_if_contains_char_auxi:nN {#2} #1 { \prg_break:n { ? \fi: } }
+ \prg_break_point:
+ \prg_return_false:
+ }
+\cs_new:Npn \__str_if_contains_char_auxi:nN #1#2
+ {
+ \if_charcode:w #1 #2
+ \exp_after:wN \__str_if_contains_char_true:
+ \fi:
+ \__str_if_contains_char_auxi:nN {#1}
+ }
+\cs_new:Npn \__str_if_contains_char_true:
+ { \prg_break:n { \prg_return_true: \use_none:n } }
+\prg_new_conditional:Npnn \__str_octal_use:N #1 { TF }
+ {
+ \if_int_compare:w 1 < '1 \token_to_str:N #1 \exp_stop_f:
+ #1 \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_new_conditional:Npnn \__str_hexadecimal_use:N #1 { TF }
+ {
+ \if_int_compare:w 1 < "1 \token_to_str:N #1 \exp_stop_f:
+ #1 \prg_return_true:
+ \else:
+ \if_case:w \int_eval:n { \exp_after:wN ` \token_to_str:N #1 - `a }
+ A
+ \or: B
+ \or: C
+ \or: D
+ \or: E
+ \or: F
+ \else:
+ \prg_return_false:
+ \exp_after:wN \use_none:n
+ \fi:
+ \prg_return_true:
+ \fi:
+ }
+\group_begin:
+ \__kernel_tl_set:Nx \l__str_internal_tl { \tl_to_str:n { 0123456789ABCDEF } }
+ \tl_map_inline:Nn \l__str_internal_tl
+ {
+ \tl_map_inline:Nn \l__str_internal_tl
+ {
+ \tl_const:ce { c__str_byte_ \int_eval:n {"#1##1} _tl }
+ { \char_generate:nn { "#1##1 } { 12 } #1 ##1 }
+ }
+ }
+\group_end:
+\tl_const:cn { c__str_byte_-1_tl } { { } \use_none:n { } }
+\cs_new:Npn \__str_output_byte:n #1
+ { \__str_output_byte:w #1 \__str_output_end: }
+\cs_new:Npn \__str_output_byte:w
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \use_i:nnn
+ \cs:w c__str_byte_ \int_eval:w
+ }
+\cs_new:Npn \__str_output_hexadecimal:n #1
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \use_none:n
+ \cs:w c__str_byte_ \int_eval:n {#1} _tl \cs_end:
+ }
+\cs_new:Npn \__str_output_end:
+ { \scan_stop: _tl \cs_end: }
+\cs_new:Npn \__str_output_byte_pair_be:n #1
+ {
+ \exp_args:Nf \__str_output_byte_pair:nnN
+ { \int_div_truncate:nn { #1 } { "100 } } {#1} \use:nn
+ }
+\cs_new:Npn \__str_output_byte_pair_le:n #1
+ {
+ \exp_args:Nf \__str_output_byte_pair:nnN
+ { \int_div_truncate:nn { #1 } { "100 } } {#1} \use_ii_i:nn
+ }
+\cs_new:Npn \__str_output_byte_pair:nnN #1#2#3
+ {
+ #3
+ { \__str_output_byte:n { #1 } }
+ { \__str_output_byte:n { #2 - #1 * "100 } }
+ }
+\cs_new_protected:Npn \__str_convert_gmap:N #1
+ {
+ \__kernel_tl_gset:Nx \g__str_result_tl
+ {
+ \exp_after:wN \__str_convert_gmap_loop:NN
+ \exp_after:wN #1
+ \g__str_result_tl { ? \prg_break: }
+ \prg_break_point:
+ }
+ }
+\cs_new:Npn \__str_convert_gmap_loop:NN #1#2
+ {
+ \use_none:n #2
+ #1#2
+ \__str_convert_gmap_loop:NN #1
+ }
+\cs_new_protected:Npn \__str_convert_gmap_internal:N #1
+ {
+ \__kernel_tl_gset:Nx \g__str_result_tl
+ {
+ \exp_after:wN \__str_convert_gmap_internal_loop:Nww
+ \exp_after:wN #1
+ \g__str_result_tl \s__str \s__str_stop \prg_break: \s__str
+ \prg_break_point:
+ }
+ }
+\cs_new:Npn \__str_convert_gmap_internal_loop:Nww #1 #2 \s__str #3 \s__str
+ {
+ \__str_use_none_delimit_by_s_stop:w #3 \s__str_stop
+ #1 {#3}
+ \__str_convert_gmap_internal_loop:Nww #1
+ }
+\cs_new_protected:Npn \__str_if_flag_error:Nne #1
+ {
+ \flag_if_raised:NTF #1
+ { \msg_error:nne { str } }
+ { \use_none:nn }
+ }
+\cs_new_protected:Npn \__str_if_flag_no_error:Nne #1#2#3
+ { \flag_if_raised:NT #1 { \bool_gset_true:N \g__str_error_bool } }
+\cs_new:Npn \__str_if_flag_times:NT #1#2
+ { \flag_if_raised:NT #1 { #2~(x \flag_height:N #1 ) } }
+\cs_new_protected:Npn \str_set_convert:Nnnn
+ { \__str_convert:nNNnnn { } \tl_set_eq:NN }
+\cs_new_protected:Npn \str_gset_convert:Nnnn
+ { \__str_convert:nNNnnn { } \tl_gset_eq:NN }
+\prg_new_protected_conditional:Npnn
+ \str_set_convert:Nnnn #1#2#3#4 { T , F , TF }
+ {
+ \bool_gset_false:N \g__str_error_bool
+ \__str_convert:nNNnnn
+ { \cs_set_eq:NN \__str_if_flag_error:Nne \__str_if_flag_no_error:Nne }
+ \tl_set_eq:NN #1 {#2} {#3} {#4}
+ \bool_if:NTF \g__str_error_bool \prg_return_false: \prg_return_true:
+ }
+\prg_new_protected_conditional:Npnn
+ \str_gset_convert:Nnnn #1#2#3#4 { T , F , TF }
+ {
+ \bool_gset_false:N \g__str_error_bool
+ \__str_convert:nNNnnn
+ { \cs_set_eq:NN \__str_if_flag_error:Nne \__str_if_flag_no_error:Nne }
+ \tl_gset_eq:NN #1 {#2} {#3} {#4}
+ \bool_if:NTF \g__str_error_bool \prg_return_false: \prg_return_true:
+ }
+\cs_new_protected:Npn \__str_convert:nNNnnn #1#2#3#4#5#6
+ {
+ \group_begin:
+ #1
+ \__kernel_tl_gset:Nx \g__str_result_tl { \__kernel_str_to_other_fast:n {#4} }
+ \exp_after:wN \__str_convert:wwwnn
+ \tl_to_str:n {#5} /// \s__str_stop
+ { decode } { unescape }
+ \prg_do_nothing:
+ \__str_convert_decode_:
+ \exp_after:wN \__str_convert:wwwnn
+ \tl_to_str:n {#6} /// \s__str_stop
+ { encode } { escape }
+ \use_ii_i:nn
+ \__str_convert_encode_:
+ \__kernel_tl_gset:Nx \g__str_result_tl
+ { \tl_to_str:V \g__str_result_tl }
+ \group_end:
+ #2 #3 \g__str_result_tl
+ }
+\cs_new_protected:Npn \__str_convert:wwwnn
+ #1 / #2 // #3 \s__str_stop #4#5
+ {
+ \__str_convert:nnn {enc} {#4} {#1}
+ \__str_convert:nnn {esc} {#5} {#2}
+ \exp_args:Ncc \__str_convert:NNnNN
+ { __str_convert_#4_#1: } { __str_convert_#5_#2: } {#2}
+ }
+\cs_new_protected:Npn \__str_convert:NNnNN #1#2#3#4#5
+ {
+ \if_meaning:w #1 #5
+ \tl_if_empty:nF {#3}
+ { \msg_error:nne { str } { native-escaping } {#3} }
+ #1
+ \else:
+ #4 #2 #1
+ \fi:
+ }
+\cs_new_protected:Npn \__str_convert:nnn #1#2#3
+ {
+ \cs_if_exist:cF { __str_convert_#2_#3: }
+ {
+ \exp_args:Ne \__str_convert:nnnn
+ { \__str_convert_lowercase_alphanum:n {#3} }
+ {#1} {#2} {#3}
+ }
+ }
+\cs_new_protected:Npn \__str_convert:nnnn #1#2#3#4
+ {
+ \cs_if_exist:cF { __str_convert_#3_#1: }
+ {
+ \prop_get:NnNF \g__str_alias_prop {#1} \l__str_internal_tl
+ { \tl_set:Nn \l__str_internal_tl {#1} }
+ \cs_if_exist:cF { __str_convert_#3_ \l__str_internal_tl : }
+ {
+ \file_if_exist:nTF { l3str-#2- \l__str_internal_tl .def }
+ {
+ \group_begin:
+ \cctab_select:N \c_code_cctab
+ \file_input:n { l3str-#2- \l__str_internal_tl .def }
+ \group_end:
+ }
+ {
+ \tl_clear:N \l__str_internal_tl
+ \msg_error:nnee { str } { unknown-#2 } {#4} {#1}
+ }
+ }
+ \cs_if_exist:cF { __str_convert_#3_#1: }
+ {
+ \cs_gset_eq:cc { __str_convert_#3_#1: }
+ { __str_convert_#3_ \l__str_internal_tl : }
+ }
+ }
+ \cs_gset_eq:cc { __str_convert_#3_#4: } { __str_convert_#3_#1: }
+ }
+\cs_new:Npn \__str_convert_lowercase_alphanum:n #1
+ {
+ \exp_after:wN \__str_convert_lowercase_alphanum_loop:N
+ \tl_to_str:n {#1} { ? \prg_break: }
+ \prg_break_point:
+ }
+\cs_new:Npn \__str_convert_lowercase_alphanum_loop:N #1
+ {
+ \use_none:n #1
+ \if_int_compare:w `#1 > `Z \exp_stop_f:
+ \if_int_compare:w `#1 > `z \exp_stop_f: \else:
+ \if_int_compare:w `#1 < `a \exp_stop_f: \else:
+ #1
+ \fi:
+ \fi:
+ \else:
+ \if_int_compare:w `#1 < `A \exp_stop_f:
+ \if_int_compare:w 1 < 1#1 \exp_stop_f:
+ #1
+ \fi:
+ \else:
+ \__str_output_byte:n { `#1 + `a - `A }
+ \fi:
+ \fi:
+ \__str_convert_lowercase_alphanum_loop:N
+ }
+\bool_lazy_any:nTF
+ {
+ \sys_if_engine_luatex_p:
+ \sys_if_engine_xetex_p:
+ }
+ {
+ \cs_new:Npn \__str_filter_bytes:n #1
+ {
+ \__str_filter_bytes_aux:N #1
+ { ? \prg_break: }
+ \prg_break_point:
+ }
+ \cs_new:Npn \__str_filter_bytes_aux:N #1
+ {
+ \use_none:n #1
+ \if_int_compare:w `#1 < 256 \exp_stop_f:
+ #1
+ \else:
+ \flag_raise:N \l__str_byte_flag
+ \fi:
+ \__str_filter_bytes_aux:N
+ }
+ }
+ { \cs_new_eq:NN \__str_filter_bytes:n \use:n }
+\bool_lazy_any:nTF
+ {
+ \sys_if_engine_luatex_p:
+ \sys_if_engine_xetex_p:
+ }
+ {
+ \cs_new_protected:Npn \__str_convert_unescape_:
+ {
+ \flag_clear:N \l__str_byte_flag
+ \__kernel_tl_gset:Nx \g__str_result_tl
+ { \exp_args:No \__str_filter_bytes:n \g__str_result_tl }
+ \__str_if_flag_error:Nne \l__str_byte_flag { non-byte } { bytes }
+ }
+ }
+ { \cs_new_protected:Npn \__str_convert_unescape_: { } }
+\cs_new_eq:NN \__str_convert_unescape_bytes: \__str_convert_unescape_:
+\cs_new_protected:Npn \__str_convert_escape_: { }
+\cs_new_eq:NN \__str_convert_escape_bytes: \__str_convert_escape_:
+\cs_new_protected:Npn \__str_convert_decode_:
+ { \__str_convert_gmap:N \__str_decode_native_char:N }
+\cs_new:Npn \__str_decode_native_char:N #1
+ { #1 \s__str \int_value:w `#1 \s__str }
+\bool_lazy_any:nTF
+ {
+ \sys_if_engine_luatex_p:
+ \sys_if_engine_xetex_p:
+ }
+ {
+ \cs_new_protected:Npn \__str_convert_encode_:
+ { \__str_convert_gmap_internal:N \__str_encode_native_char:n }
+ \cs_new:Npn \__str_encode_native_char:n #1
+ { \char_generate:nn {#1} {12} }
+ }
+ {
+ \cs_new_protected:Npn \__str_convert_encode_:
+ {
+ \flag_clear:N \l__str_error_flag
+ \__str_convert_gmap_internal:N \__str_encode_native_char:n
+ \__str_if_flag_error:Nne \l__str_error_flag
+ { native-overflow } { }
+ }
+ \cs_new:Npn \__str_encode_native_char:n #1
+ {
+ \if_int_compare:w #1 > \c__str_max_byte_int
+ \flag_raise:N \l__str_error_flag
+ ?
+ \else:
+ \char_generate:nn {#1} {12}
+ \fi:
+ }
+ \msg_new:nnnn { str } { native-overflow }
+ { Character~code~too~large~for~this~engine. }
+ {
+ This~engine~only~support~8-bit~characters:~
+ valid~character~codes~are~in~the~range~[0,255].~
+ To~manipulate~arbitrary~Unicode,~use~LuaTeX~or~XeTeX.
+ }
+ }
+\cs_new_protected:Npn \__str_convert_decode_clist:
+ {
+ \clist_gset:No \g__str_result_tl \g__str_result_tl
+ \__kernel_tl_gset:Nx \g__str_result_tl
+ {
+ \exp_args:No \clist_map_function:nN
+ \g__str_result_tl \__str_decode_clist_char:n
+ }
+ }
+\cs_new:Npn \__str_decode_clist_char:n #1
+ { #1 \s__str \int_eval:n {#1} \s__str }
+\cs_new_protected:Npn \__str_convert_encode_clist:
+ {
+ \__str_convert_gmap_internal:N \__str_encode_clist_char:n
+ \__kernel_tl_gset:Nx \g__str_result_tl { \tl_tail:N \g__str_result_tl }
+ }
+\cs_new:Npn \__str_encode_clist_char:n #1 { , #1 }
+\cs_new_protected:Npn \__str_declare_eight_bit_encoding:nnnn #1
+ {
+ \tl_set:Nn \l__str_internal_tl {#1}
+ \cs_new_protected:cpn { __str_convert_decode_#1: }
+ { \__str_convert_decode_eight_bit:n {#1} }
+ \cs_new_protected:cpn { __str_convert_encode_#1: }
+ { \__str_convert_encode_eight_bit:n {#1} }
+ \exp_args:Ncc \__str_declare_eight_bit_aux:NNnnn
+ { g__str_decode_#1_intarray } { g__str_encode_#1_intarray }
+ }
+\cs_new_protected:Npn \__str_declare_eight_bit_aux:NNnnn #1#2#3#4#5
+ {
+ \intarray_new:Nn #1 { 256 }
+ \int_step_inline:nnn { 0 } { 255 }
+ { \intarray_gset:Nnn #1 { 1 + ##1 } {##1} }
+ \__str_declare_eight_bit_loop:Nnn #1
+ #4 { \s__str_stop \prg_break: } { }
+ \prg_break_point:
+ \__str_declare_eight_bit_loop:Nn #1
+ #5 { \s__str_stop \prg_break: }
+ \prg_break_point:
+ \intarray_new:Nn #2 {#3}
+ \int_step_inline:nnn { 0 } { 255 }
+ {
+ \int_compare:nNnF { \intarray_item:Nn #1 { 1 + ##1 } } = { -1 }
+ {
+ \intarray_gset:Nnn #2
+ {
+ 1 +
+ \int_mod:nn { \intarray_item:Nn #1 { 1 + ##1 } }
+ { \intarray_count:N #2 }
+ }
+ {##1}
+ }
+ }
+ }
+\cs_new_protected:Npn \__str_declare_eight_bit_loop:Nnn #1#2#3
+ {
+ \__str_use_none_delimit_by_s_stop:w #2 \s__str_stop
+ \intarray_gset:Nnn #1 { 1 + "#2 } { "#3 }
+ \__str_declare_eight_bit_loop:Nnn #1
+ }
+\cs_new_protected:Npn \__str_declare_eight_bit_loop:Nn #1#2
+ {
+ \__str_use_none_delimit_by_s_stop:w #2 \s__str_stop
+ \intarray_gset:Nnn #1 { 1 + "#2 } { -1 }
+ \__str_declare_eight_bit_loop:Nn #1
+ }
+\cs_new_protected:Npn \__str_convert_decode_eight_bit:n #1
+ {
+ \cs_set:Npe \__str_tmp:w
+ {
+ \exp_not:N \__str_decode_eight_bit_aux:Nn
+ \exp_not:c { g__str_decode_#1_intarray }
+ }
+ \flag_clear:N \l__str_error_flag
+ \__str_convert_gmap:N \__str_tmp:w
+ \__str_if_flag_error:Nne \l__str_error_flag { decode-8-bit } {#1}
+ }
+\cs_new:Npn \__str_decode_eight_bit_aux:Nn #1#2
+ {
+ #2 \s__str
+ \exp_args:Nf \__str_decode_eight_bit_aux:n
+ { \intarray_item:Nn #1 { 1 + `#2 } }
+ \s__str
+ }
+\cs_new:Npn \__str_decode_eight_bit_aux:n #1
+ {
+ \if_int_compare:w #1 < \c_zero_int
+ \flag_raise:N \l__str_error_flag
+ \int_value:w \c__str_replacement_char_int
+ \else:
+ #1
+ \fi:
+ }
+\int_new:N \l__str_modulo_int
+\cs_new_protected:Npn \__str_convert_encode_eight_bit:n #1
+ {
+ \cs_set:Npe \__str_tmp:w
+ {
+ \exp_not:N \__str_encode_eight_bit_aux:NNn
+ \exp_not:c { g__str_encode_#1_intarray }
+ \exp_not:c { g__str_decode_#1_intarray }
+ }
+ \flag_clear:N \l__str_error_flag
+ \__str_convert_gmap_internal:N \__str_tmp:w
+ \__str_if_flag_error:Nne \l__str_error_flag { encode-8-bit } {#1}
+ }
+\cs_new:Npn \__str_encode_eight_bit_aux:NNn #1#2#3
+ {
+ \exp_args:Nf \__str_encode_eight_bit_aux:nnN
+ {
+ \intarray_item:Nn #1
+ { 1 + \int_mod:nn {#3} { \intarray_count:N #1 } }
+ }
+ {#3}
+ #2
+ }
+\cs_new:Npn \__str_encode_eight_bit_aux:nnN #1#2#3
+ {
+ \int_compare:nNnTF { \intarray_item:Nn #3 { 1 + #1 } } = {#2}
+ { \__str_output_byte:n {#1} }
+ { \flag_raise:N \l__str_error_flag }
+ }
+\msg_new:nnn { str } { unknown-esc }
+ { Escaping~scheme~'#1'~(filtered:~'#2')~unknown. }
+\msg_new:nnn { str } { unknown-enc }
+ { Encoding~scheme~'#1'~(filtered:~'#2')~unknown. }
+\msg_new:nnnn { str } { native-escaping }
+ { The~'native'~encoding~scheme~does~not~support~any~escaping. }
+ {
+ Since~native~strings~do~not~consist~in~bytes,~
+ none~of~the~escaping~methods~make~sense.~
+ The~specified~escaping,~'#1',~will~be~ignored.
+ }
+\msg_new:nnn { str } { file-not-found }
+ { File~'l3str-#1.def'~not~found. }
+\bool_lazy_any:nT
+ {
+ \sys_if_engine_luatex_p:
+ \sys_if_engine_xetex_p:
+ }
+ {
+ \msg_new:nnnn { str } { non-byte }
+ { String~invalid~in~escaping~'#1':~it~may~only~contain~bytes. }
+ {
+ Some~characters~in~the~string~you~asked~to~convert~are~not~
+ 8-bit~characters.~Perhaps~the~string~is~a~'native'~Unicode~string?~
+ If~it~is,~try~using\\
+ \\
+ \iow_indent:n
+ {
+ \iow_char:N\\str_set_convert:Nnnn \\
+ \ \ <str~var>~\{~<string>~\}~\{~native~\}~\{~<target~encoding>~\}
+ }
+ }
+ }
+\msg_new:nnnn { str } { decode-8-bit }
+ { Invalid~string~in~encoding~'#1'. }
+ {
+ LaTeX~came~across~a~byte~which~is~not~defined~to~represent~
+ any~character~in~the~encoding~'#1'.
+ }
+\msg_new:nnnn { str } { encode-8-bit }
+ { Unicode~string~cannot~be~converted~to~encoding~'#1'. }
+ {
+ The~encoding~'#1'~only~contains~a~subset~of~all~Unicode~characters.~
+ LaTeX~was~asked~to~convert~a~string~to~that~encoding,~but~that~
+ string~contains~a~character~that~'#1'~does~not~support.
+ }
+\cs_new_protected:Npn \__str_convert_unescape_hex:
+ {
+ \group_begin:
+ \flag_clear:N \l__str_error_flag
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \__kernel_tl_gset:Nx \g__str_result_tl
+ {
+ \__str_output_byte:w "
+ \exp_last_unbraced:Nf \__str_unescape_hex_auxi:N
+ { \tl_to_str:N \g__str_result_tl }
+ 0 { ? 0 - 1 \prg_break: }
+ \prg_break_point:
+ \__str_output_end:
+ }
+ \__str_if_flag_error:Nne \l__str_error_flag { unescape-hex } { }
+ \group_end:
+ }
+\cs_new:Npn \__str_unescape_hex_auxi:N #1
+ {
+ \use_none:n #1
+ \__str_hexadecimal_use:NTF #1
+ { \__str_unescape_hex_auxii:N }
+ {
+ \flag_raise:N \l__str_error_flag
+ \__str_unescape_hex_auxi:N
+ }
+ }
+\cs_new:Npn \__str_unescape_hex_auxii:N #1
+ {
+ \use_none:n #1
+ \__str_hexadecimal_use:NTF #1
+ {
+ \__str_output_end:
+ \__str_output_byte:w " \__str_unescape_hex_auxi:N
+ }
+ {
+ \flag_raise:N \l__str_error_flag
+ \__str_unescape_hex_auxii:N
+ }
+ }
+\msg_new:nnnn { str } { unescape-hex }
+ { String~invalid~in~escaping~'hex':~only~hexadecimal~digits~allowed. }
+ {
+ Some~characters~in~the~string~you~asked~to~convert~are~not~
+ hexadecimal~digits~(0-9,~A-F,~a-f)~nor~spaces.
+ }
+\cs_set_protected:Npn \__str_tmp:w #1#2#3
+ {
+ \cs_new_protected:cpn { __str_convert_unescape_#2: }
+ {
+ \group_begin:
+ \flag_clear:N \l__str_byte_flag
+ \flag_clear:N \l__str_error_flag
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \__kernel_tl_gset:Nx \g__str_result_tl
+ {
+ \exp_after:wN #3 \g__str_result_tl
+ #1 ? { ? \prg_break: }
+ \prg_break_point:
+ }
+ \__str_if_flag_error:Nne \l__str_byte_flag { non-byte } { #2 }
+ \__str_if_flag_error:Nne \l__str_error_flag { unescape-#2 } { }
+ \group_end:
+ }
+ \cs_new:Npn #3 ##1#1##2##3
+ {
+ \__str_filter_bytes:n {##1}
+ \use_none:n ##3
+ \__str_output_byte:w "
+ \__str_hexadecimal_use:NTF ##2
+ {
+ \__str_hexadecimal_use:NTF ##3
+ { }
+ {
+ \flag_raise:N \l__str_error_flag
+ * 0 + `#1 \use_i:nn
+ }
+ }
+ {
+ \flag_raise:N \l__str_error_flag
+ 0 + `#1 \use_i:nn
+ }
+ \__str_output_end:
+ \use_i:nnn #3 ##2##3
+ }
+ \msg_new:nnnn { str } { unescape-#2 }
+ { String~invalid~in~escaping~'#2'. }
+ {
+ LaTeX~came~across~the~escape~character~'#1'~not~followed~by~
+ two~hexadecimal~digits.~This~is~invalid~in~the~escaping~'#2'.
+ }
+ }
+\exp_after:wN \__str_tmp:w \c_hash_str { name }
+ \__str_unescape_name_loop:wNN
+\exp_after:wN \__str_tmp:w \c_percent_str { url }
+ \__str_unescape_url_loop:wNN
+\group_begin:
+ \char_set_catcode_other:N \^^J
+ \char_set_catcode_other:N \^^M
+ \cs_set_protected:Npn \__str_tmp:w #1
+ {
+ \cs_new_protected:Npn \__str_convert_unescape_string:
+ {
+ \group_begin:
+ \flag_clear:N \l__str_byte_flag
+ \flag_clear:N \l__str_error_flag
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \__kernel_tl_gset:Nx \g__str_result_tl
+ {
+ \exp_after:wN \__str_unescape_string_newlines:wN
+ \g__str_result_tl \prg_break: ^^M ?
+ \prg_break_point:
+ }
+ \__kernel_tl_gset:Nx \g__str_result_tl
+ {
+ \exp_after:wN \__str_unescape_string_loop:wNNN
+ \g__str_result_tl #1 ?? { ? \prg_break: }
+ \prg_break_point:
+ }
+ \__str_if_flag_error:Nne \l__str_byte_flag { non-byte } { string }
+ \__str_if_flag_error:Nne \l__str_error_flag { unescape-string } { }
+ \group_end:
+ }
+ }
+ \exp_args:No \__str_tmp:w { \c_backslash_str }
+ \exp_last_unbraced:NNNNo
+ \cs_new:Npn \__str_unescape_string_loop:wNNN #1 \c_backslash_str #2#3#4
+ {
+ \__str_filter_bytes:n {#1}
+ \use_none:n #4
+ \__str_output_byte:w '
+ \__str_octal_use:NTF #2
+ {
+ \__str_octal_use:NTF #3
+ {
+ \__str_octal_use:NTF #4
+ {
+ \if_int_compare:w #2 > 3 \exp_stop_f:
+ - 256
+ \fi:
+ \__str_unescape_string_repeat:NNNNNN
+ }
+ { \__str_unescape_string_repeat:NNNNNN ? }
+ }
+ { \__str_unescape_string_repeat:NNNNNN ?? }
+ }
+ {
+ \str_case_e:nnF {#2}
+ {
+ { \c_backslash_str } { 134 }
+ { ( } { 50 }
+ { ) } { 51 }
+ { r } { 15 }
+ { f } { 14 }
+ { n } { 12 }
+ { t } { 11 }
+ { b } { 10 }
+ { ^^J } { 0 - 1 }
+ }
+ {
+ \flag_raise:N \l__str_error_flag
+ 0 - 1 \use_i:nn
+ }
+ }
+ \__str_output_end:
+ \use_i:nn \__str_unescape_string_loop:wNNN #2#3#4
+ }
+ \cs_new:Npn \__str_unescape_string_repeat:NNNNNN #1#2#3#4#5#6
+ { \__str_output_end: \__str_unescape_string_loop:wNNN }
+ \cs_new:Npn \__str_unescape_string_newlines:wN #1 ^^M #2
+ {
+ #1
+ \if_charcode:w ^^J #2 \else: ^^J \fi:
+ \__str_unescape_string_newlines:wN #2
+ }
+ \msg_new:nnnn { str } { unescape-string }
+ { String~invalid~in~escaping~'string'. }
+ {
+ LaTeX~came~across~an~escape~character~'\c_backslash_str'~
+ not~followed~by~any~of:~'n',~'r',~'t',~'b',~'f',~'(',~')',~
+ '\c_backslash_str',~one~to~three~octal~digits,~or~the~end~
+ of~a~line.
+ }
+\group_end:
+\cs_new_protected:Npn \__str_convert_escape_hex:
+ { \__str_convert_gmap:N \__str_escape_hex_char:N }
+\cs_new:Npn \__str_escape_hex_char:N #1
+ { \__str_output_hexadecimal:n { `#1 } }
+\str_const:Nn \c__str_escape_name_not_str { ! " $ & ' } %$
+\str_const:Nn \c__str_escape_name_str { {}/<>[] }
+\cs_new_protected:Npn \__str_convert_escape_name:
+ { \__str_convert_gmap:N \__str_escape_name_char:n }
+\cs_new:Npn \__str_escape_name_char:n #1
+ {
+ \__str_if_escape_name:nTF {#1} {#1}
+ { \c_hash_str \__str_output_hexadecimal:n {`#1} }
+ }
+\prg_new_conditional:Npnn \__str_if_escape_name:n #1 { TF }
+ {
+ \if_int_compare:w `#1 < "2A \exp_stop_f:
+ \__str_if_contains_char:NnTF \c__str_escape_name_not_str {#1}
+ \prg_return_true: \prg_return_false:
+ \else:
+ \if_int_compare:w `#1 > "7E \exp_stop_f:
+ \prg_return_false:
+ \else:
+ \__str_if_contains_char:NnTF \c__str_escape_name_str {#1}
+ \prg_return_false: \prg_return_true:
+ \fi:
+ \fi:
+ }
+\str_const:Ne \c__str_escape_string_str
+ { \c_backslash_str ( ) }
+\cs_new_protected:Npn \__str_convert_escape_string:
+ { \__str_convert_gmap:N \__str_escape_string_char:N }
+\cs_new:Npn \__str_escape_string_char:N #1
+ {
+ \__str_if_escape_string:NTF #1
+ {
+ \__str_if_contains_char:NnT
+ \c__str_escape_string_str {#1}
+ { \c_backslash_str }
+ #1
+ }
+ {
+ \c_backslash_str
+ \int_div_truncate:nn {`#1} {64}
+ \int_mod:nn { \int_div_truncate:nn {`#1} { 8 } } { 8 }
+ \int_mod:nn {`#1} { 8 }
+ }
+ }
+\prg_new_conditional:Npnn \__str_if_escape_string:N #1 { TF }
+ {
+ \if_int_compare:w `#1 < "27 \exp_stop_f:
+ \prg_return_false:
+ \else:
+ \if_int_compare:w `#1 > "7A \exp_stop_f:
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \__str_convert_escape_url:
+ { \__str_convert_gmap:N \__str_escape_url_char:n }
+\cs_new:Npn \__str_escape_url_char:n #1
+ {
+ \__str_if_escape_url:nTF {#1} {#1}
+ { \c_percent_str \__str_output_hexadecimal:n { `#1 } }
+ }
+\prg_new_conditional:Npnn \__str_if_escape_url:n #1 { TF }
+ {
+ \if_int_compare:w `#1 < "30 \exp_stop_f:
+ \__str_if_contains_char:nnTF { "-. } {#1}
+ \prg_return_true: \prg_return_false:
+ \else:
+ \if_int_compare:w `#1 > "7E \exp_stop_f:
+ \prg_return_false:
+ \else:
+ \__str_if_contains_char:nnTF { : ; = ? @ [ ] } {#1}
+ \prg_return_false: \prg_return_true:
+ \fi:
+ \fi:
+ }
+\cs_new_protected:cpn { __str_convert_encode_utf8: }
+ { \__str_convert_gmap_internal:N \__str_encode_utf_viii_char:n }
+\cs_new:Npn \__str_encode_utf_viii_char:n #1
+ {
+ \__str_encode_utf_viii_loop:wwnnw #1 ; - 1 + 0 * ;
+ { 128 } { 0 }
+ { 32 } { 192 }
+ { 16 } { 224 }
+ { 8 } { 240 }
+ \s__str_stop
+ }
+\cs_new:Npn \__str_encode_utf_viii_loop:wwnnw #1; #2; #3#4 #5 \s__str_stop
+ {
+ \if_int_compare:w #1 < #3 \exp_stop_f:
+ \__str_output_byte:n { #1 + #4 }
+ \exp_after:wN \__str_use_none_delimit_by_s_stop:w
+ \fi:
+ \exp_after:wN \__str_encode_utf_viii_loop:wwnnw
+ \int_value:w \int_div_truncate:nn {#1} {64} ; #1 ;
+ #5 \s__str_stop
+ \__str_output_byte:n { #2 - 64 * ( #1 - 2 ) }
+ }
+\flag_clear_new:N \l__str_missing_flag
+\flag_clear_new:N \l__str_extra_flag
+\flag_clear_new:N \l__str_overlong_flag
+\flag_clear_new:N \l__str_overflow_flag
+\msg_new:nnnn { str } { utf8-decode }
+ {
+ Invalid~UTF-8~string:
+ \exp_last_unbraced:Nf \use_none:n
+ {
+ \__str_if_flag_times:NT \l__str_missing_flag { ,~missing~continuation~byte }
+ \__str_if_flag_times:NT \l__str_extra_flag { ,~extra~continuation~byte }
+ \__str_if_flag_times:NT \l__str_overlong_flag { ,~overlong~form }
+ \__str_if_flag_times:NT \l__str_overflow_flag { ,~code~point~too~large }
+ }
+ .
+ }
+ {
+ In~the~UTF-8~encoding,~each~Unicode~character~consists~in~
+ 1~to~4~bytes,~with~the~following~bit~pattern: \\
+ \iow_indent:n
+ {
+ Code~point~\ \ \ \ <~128:~0xxxxxxx \\
+ Code~point~\ \ \ <~2048:~110xxxxx~10xxxxxx \\
+ Code~point~\ \ <~65536:~1110xxxx~10xxxxxx~10xxxxxx \\
+ Code~point~ <~1114112:~11110xxx~10xxxxxx~10xxxxxx~10xxxxxx \\
+ }
+ Bytes~of~the~form~10xxxxxx~are~called~continuation~bytes.
+ \flag_if_raised:NT \l__str_missing_flag
+ {
+ \\\\
+ A~leading~byte~(in~the~range~[192,255])~was~not~followed~by~
+ the~appropriate~number~of~continuation~bytes.
+ }
+ \flag_if_raised:NT \l__str_extra_flag
+ {
+ \\\\
+ LaTeX~came~across~a~continuation~byte~when~it~was~not~expected.
+ }
+ \flag_if_raised:NT \l__str_overlong_flag
+ {
+ \\\\
+ Every~Unicode~code~point~must~be~expressed~in~the~shortest~
+ possible~form.~For~instance,~'0xC0'~'0x83'~is~not~a~valid~
+ representation~for~the~code~point~3.
+ }
+ \flag_if_raised:NT \l__str_overflow_flag
+ {
+ \\\\
+ Unicode~limits~code~points~to~the~range~[0,1114111].
+ }
+ }
+\prop_gput:Nnn \g_msg_module_name_prop { str } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { str } { }
+\cs_new_protected:cpn { __str_convert_decode_utf8: }
+ {
+ \flag_clear:N \l__str_error_flag
+ \flag_clear:N \l__str_missing_flag
+ \flag_clear:N \l__str_extra_flag
+ \flag_clear:N \l__str_overlong_flag
+ \flag_clear:N \l__str_overflow_flag
+ \__kernel_tl_gset:Nx \g__str_result_tl
+ {
+ \exp_after:wN \__str_decode_utf_viii_start:N \g__str_result_tl
+ { \prg_break: \__str_decode_utf_viii_end: }
+ \prg_break_point:
+ }
+ \__str_if_flag_error:Nne \l__str_error_flag { utf8-decode } { }
+ }
+\cs_new:Npn \__str_decode_utf_viii_start:N #1
+ {
+ #1
+ \if_int_compare:w `#1 < "C0 \exp_stop_f:
+ \s__str
+ \if_int_compare:w `#1 < "80 \exp_stop_f:
+ \int_value:w `#1
+ \else:
+ \flag_raise:N \l__str_extra_flag
+ \flag_raise:N \l__str_error_flag
+ \int_use:N \c__str_replacement_char_int
+ \fi:
+ \else:
+ \exp_after:wN \__str_decode_utf_viii_continuation:wwN
+ \int_value:w \int_eval:n { `#1 - "C0 } \exp_after:wN
+ \fi:
+ \s__str
+ \__str_use_none_delimit_by_s_stop:w {"80} {"800} {"10000} {"110000} \s__str_stop
+ \__str_decode_utf_viii_start:N
+ }
+\cs_new:Npn \__str_decode_utf_viii_continuation:wwN
+ #1 \s__str #2 \__str_decode_utf_viii_start:N #3
+ {
+ \use_none:n #3
+ \if_int_compare:w `#3 <
+ \if_int_compare:w `#3 < "80 \exp_stop_f: - \fi:
+ "C0 \exp_stop_f:
+ #3
+ \exp_after:wN \__str_decode_utf_viii_aux:wNnnwN
+ \int_value:w \int_eval:n { #1 * "40 + `#3 - "80 } \exp_after:wN
+ \else:
+ \s__str
+ \flag_raise:N \l__str_missing_flag
+ \flag_raise:N \l__str_error_flag
+ \int_use:N \c__str_replacement_char_int
+ \fi:
+ \s__str
+ #2
+ \__str_decode_utf_viii_start:N #3
+ }
+\cs_new:Npn \__str_decode_utf_viii_aux:wNnnwN
+ #1 \s__str #2#3#4 #5 \__str_decode_utf_viii_start:N #6
+ {
+ \if_int_compare:w #1 < #4 \exp_stop_f:
+ \s__str
+ \if_int_compare:w #1 < #3 \exp_stop_f:
+ \flag_raise:N \l__str_overlong_flag
+ \flag_raise:N \l__str_error_flag
+ \int_use:N \c__str_replacement_char_int
+ \else:
+ #1
+ \fi:
+ \else:
+ \if_meaning:w \s__str_stop #5
+ \__str_decode_utf_viii_overflow:w #1
+ \fi:
+ \exp_after:wN \__str_decode_utf_viii_continuation:wwN
+ \int_value:w \int_eval:n { #1 - #4 } \exp_after:wN
+ \fi:
+ \s__str
+ #2 {#4} #5
+ \__str_decode_utf_viii_start:N
+ }
+\cs_new:Npn \__str_decode_utf_viii_overflow:w #1 \fi: #2 \fi:
+ {
+ \fi: \fi:
+ \flag_raise:N \l__str_overflow_flag
+ \flag_raise:N \l__str_error_flag
+ \int_use:N \c__str_replacement_char_int
+ }
+\cs_new:Npn \__str_decode_utf_viii_end:
+ {
+ \s__str
+ \flag_raise:N \l__str_missing_flag
+ \flag_raise:N \l__str_error_flag
+ \int_use:N \c__str_replacement_char_int \s__str
+ \prg_break:
+ }
+\group_begin:
+ \char_set_catcode_other:N \^^fe
+ \char_set_catcode_other:N \^^ff
+ \cs_new_protected:cpn { __str_convert_encode_utf16: }
+ {
+ \__str_encode_utf_xvi_aux:N \__str_output_byte_pair_be:n
+ \tl_gput_left:Ne \g__str_result_tl { ^^fe ^^ff }
+ }
+ \cs_new_protected:cpn { __str_convert_encode_utf16be: }
+ { \__str_encode_utf_xvi_aux:N \__str_output_byte_pair_be:n }
+ \cs_new_protected:cpn { __str_convert_encode_utf16le: }
+ { \__str_encode_utf_xvi_aux:N \__str_output_byte_pair_le:n }
+ \cs_new_protected:Npn \__str_encode_utf_xvi_aux:N #1
+ {
+ \flag_clear:N \l__str_error_flag
+ \cs_set_eq:NN \__str_tmp:w #1
+ \__str_convert_gmap_internal:N \__str_encode_utf_xvi_char:n
+ \__str_if_flag_error:Nne \l__str_error_flag { utf16-encode } { }
+ }
+ \cs_new:Npn \__str_encode_utf_xvi_char:n #1
+ {
+ \if_int_compare:w #1 < "D800 \exp_stop_f:
+ \__str_tmp:w {#1}
+ \else:
+ \if_int_compare:w #1 < "10000 \exp_stop_f:
+ \if_int_compare:w #1 < "E000 \exp_stop_f:
+ \flag_raise:N \l__str_error_flag
+ \__str_tmp:w { \c__str_replacement_char_int }
+ \else:
+ \__str_tmp:w {#1}
+ \fi:
+ \else:
+ \exp_args:Nf \__str_tmp:w { \int_div_truncate:nn {#1} {"400} + "D7C0 }
+ \exp_args:Nf \__str_tmp:w { \int_mod:nn {#1} {"400} + "DC00 }
+ \fi:
+ \fi:
+ }
+ \flag_clear_new:N \l__str_missing_flag
+ \flag_clear_new:N \l__str_extra_flag
+ \flag_clear_new:N \l__str_end_flag
+ \msg_new:nnnn { str } { utf16-encode }
+ { Unicode~string~cannot~be~expressed~in~UTF-16:~surrogate. }
+ {
+ Surrogate~code~points~(in~the~range~[U+D800,~U+DFFF])~
+ can~be~expressed~in~the~UTF-8~and~UTF-32~encodings,~
+ but~not~in~the~UTF-16~encoding.
+ }
+ \msg_new:nnnn { str } { utf16-decode }
+ {
+ Invalid~UTF-16~string:
+ \exp_last_unbraced:Nf \use_none:n
+ {
+ \__str_if_flag_times:NT \l__str_missing_flag { ,~missing~trail~surrogate }
+ \__str_if_flag_times:NT \l__str_extra_flag { ,~extra~trail~surrogate }
+ \__str_if_flag_times:NT \l__str_end_flag { ,~odd~number~of~bytes }
+ }
+ .
+ }
+ {
+ In~the~UTF-16~encoding,~each~Unicode~character~is~encoded~as~
+ 2~or~4~bytes: \\
+ \iow_indent:n
+ {
+ Code~point~in~[U+0000,~U+D7FF]:~two~bytes \\
+ Code~point~in~[U+D800,~U+DFFF]:~illegal \\
+ Code~point~in~[U+E000,~U+FFFF]:~two~bytes \\
+ Code~point~in~[U+10000,~U+10FFFF]:~
+ a~lead~surrogate~and~a~trail~surrogate \\
+ }
+ Lead~surrogates~are~pairs~of~bytes~in~the~range~[0xD800,~0xDBFF],~
+ and~trail~surrogates~are~in~the~range~[0xDC00,~0xDFFF].
+ \flag_if_raised:NT \l__str_missing_flag
+ {
+ \\\\
+ A~lead~surrogate~was~not~followed~by~a~trail~surrogate.
+ }
+ \flag_if_raised:NT \l__str_extra_flag
+ {
+ \\\\
+ LaTeX~came~across~a~trail~surrogate~when~it~was~not~expected.
+ }
+ \flag_if_raised:NT \l__str_end_flag
+ {
+ \\\\
+ The~string~contained~an~odd~number~of~bytes.~This~is~invalid:~
+ the~basic~code~unit~for~UTF-16~is~16~bits~(2~bytes).
+ }
+ }
+ \cs_new_protected:cpn { __str_convert_decode_utf16be: }
+ { \__str_decode_utf_xvi:Nw 1 \g__str_result_tl \s__str_stop }
+ \cs_new_protected:cpn { __str_convert_decode_utf16le: }
+ { \__str_decode_utf_xvi:Nw 2 \g__str_result_tl \s__str_stop }
+ \cs_new_protected:cpn { __str_convert_decode_utf16: }
+ {
+ \exp_after:wN \__str_decode_utf_xvi_bom:NN
+ \g__str_result_tl \s__str_stop \s__str_stop \s__str_stop
+ }
+ \cs_new_protected:Npn \__str_decode_utf_xvi_bom:NN #1#2
+ {
+ \str_if_eq:nnTF { #1#2 } { ^^ff ^^fe }
+ { \__str_decode_utf_xvi:Nw 2 }
+ {
+ \str_if_eq:nnTF { #1#2 } { ^^fe ^^ff }
+ { \__str_decode_utf_xvi:Nw 1 }
+ { \__str_decode_utf_xvi:Nw 1 #1#2 }
+ }
+ }
+ \cs_new_protected:Npn \__str_decode_utf_xvi:Nw #1#2 \s__str_stop
+ {
+ \flag_clear:N \l__str_error_flag
+ \flag_clear:N \l__str_missing_flag
+ \flag_clear:N \l__str_extra_flag
+ \flag_clear:N \l__str_end_flag
+ \cs_set:Npn \__str_tmp:w ##1 ##2 { ` ## #1 }
+ \__kernel_tl_gset:Nx \g__str_result_tl
+ {
+ \exp_after:wN \__str_decode_utf_xvi_pair:NN
+ #2 \q__str_nil \q__str_nil
+ \prg_break_point:
+ }
+ \__str_if_flag_error:Nne \l__str_error_flag { utf16-decode } { }
+ }
+ \cs_new:Npn \__str_decode_utf_xvi_pair:NN #1#2
+ {
+ \if_meaning:w \q__str_nil #2
+ \__str_decode_utf_xvi_pair_end:Nw #1
+ \fi:
+ \if_case:w
+ \int_eval:n { ( \__str_tmp:w #1#2 - "D6 ) / 4 } \scan_stop:
+ \or: \exp_after:wN \__str_decode_utf_xvi_quad:NNwNN
+ \or: \exp_after:wN \__str_decode_utf_xvi_extra:NNw
+ \fi:
+ #1#2 \s__str
+ \int_eval:n { "100 * \__str_tmp:w #1#2 + \__str_tmp:w #2#1 } \s__str
+ \__str_decode_utf_xvi_pair:NN
+ }
+ \cs_new:Npn \__str_decode_utf_xvi_quad:NNwNN
+ #1#2 #3 \__str_decode_utf_xvi_pair:NN #4#5
+ {
+ \if_meaning:w \q__str_nil #5
+ \__str_decode_utf_xvi_error:nNN { missing } #1#2
+ \__str_decode_utf_xvi_pair_end:Nw #4
+ \fi:
+ \if_int_compare:w
+ \if_int_compare:w \__str_tmp:w #4#5 < "DC \exp_stop_f:
+ 0 = 1
+ \else:
+ \__str_tmp:w #4#5 < "E0
+ \fi:
+ \exp_stop_f:
+ #1 #2 #4 #5 \s__str
+ \int_eval:n
+ {
+ ( "100 * \__str_tmp:w #1#2 + \__str_tmp:w #2#1 - "D7F7 ) * "400
+ + "100 * \__str_tmp:w #4#5 + \__str_tmp:w #5#4
+ }
+ \s__str
+ \exp_after:wN \use_i:nnn
+ \else:
+ \__str_decode_utf_xvi_error:nNN { missing } #1#2
+ \fi:
+ \__str_decode_utf_xvi_pair:NN #4#5
+ }
+ \cs_new:Npn \__str_decode_utf_xvi_pair_end:Nw #1 \fi:
+ {
+ \fi:
+ \if_meaning:w \q__str_nil #1
+ \else:
+ \__str_decode_utf_xvi_error:nNN { end } #1 \prg_do_nothing:
+ \fi:
+ \prg_break:
+ }
+ \cs_new:Npn \__str_decode_utf_xvi_extra:NNw #1#2 \s__str #3 \s__str
+ { \__str_decode_utf_xvi_error:nNN { extra } #1#2 }
+ \cs_new:Npn \__str_decode_utf_xvi_error:nNN #1#2#3
+ {
+ \flag_raise:N \l__str_error_flag
+ \flag_raise:c { l__str_#1_flag }
+ #2 #3 \s__str
+ \int_use:N \c__str_replacement_char_int \s__str
+ }
+\group_end:
+\group_begin:
+ \char_set_catcode_other:N \^^00
+ \char_set_catcode_other:N \^^fe
+ \char_set_catcode_other:N \^^ff
+ \cs_new_protected:cpn { __str_convert_encode_utf32: }
+ {
+ \__str_convert_gmap_internal:N \__str_encode_utf_xxxii_be:n
+ \tl_gput_left:Ne \g__str_result_tl { ^^00 ^^00 ^^fe ^^ff }
+ }
+ \cs_new_protected:cpn { __str_convert_encode_utf32be: }
+ { \__str_convert_gmap_internal:N \__str_encode_utf_xxxii_be:n }
+ \cs_new_protected:cpn { __str_convert_encode_utf32le: }
+ { \__str_convert_gmap_internal:N \__str_encode_utf_xxxii_le:n }
+ \cs_new:Npn \__str_encode_utf_xxxii_be:n #1
+ {
+ \exp_args:Nf \__str_encode_utf_xxxii_be_aux:nn
+ { \int_div_truncate:nn {#1} { "100 } } {#1}
+ }
+ \cs_new:Npn \__str_encode_utf_xxxii_be_aux:nn #1#2
+ {
+ ^^00
+ \__str_output_byte_pair_be:n {#1}
+ \__str_output_byte:n { #2 - #1 * "100 }
+ }
+ \cs_new:Npn \__str_encode_utf_xxxii_le:n #1
+ {
+ \exp_args:Nf \__str_encode_utf_xxxii_le_aux:nn
+ { \int_div_truncate:nn {#1} { "100 } } {#1}
+ }
+ \cs_new:Npn \__str_encode_utf_xxxii_le_aux:nn #1#2
+ {
+ \__str_output_byte:n { #2 - #1 * "100 }
+ \__str_output_byte_pair_le:n {#1}
+ ^^00
+ }
+ \flag_clear_new:N \l__str_overflow_flag
+ \flag_clear_new:N \l__str_end_flag
+ \msg_new:nnnn { str } { utf32-decode }
+ {
+ Invalid~UTF-32~string:
+ \exp_last_unbraced:Nf \use_none:n
+ {
+ \__str_if_flag_times:NT \l__str_overflow_flag { ,~code~point~too~large }
+ \__str_if_flag_times:NT \l__str_end_flag { ,~truncated~string }
+ }
+ .
+ }
+ {
+ In~the~UTF-32~encoding,~every~Unicode~character~
+ (in~the~range~[U+0000,~U+10FFFF])~is~encoded~as~4~bytes.
+ \flag_if_raised:NT \l__str_overflow_flag
+ {
+ \\\\
+ LaTeX~came~across~a~code~point~larger~than~1114111,~
+ the~maximum~code~point~defined~by~Unicode.~
+ Perhaps~the~string~was~not~encoded~in~the~UTF-32~encoding?
+ }
+ \flag_if_raised:NT \l__str_end_flag
+ {
+ \\\\
+ The~length~of~the~string~is~not~a~multiple~of~4.~
+ Perhaps~the~string~was~truncated?
+ }
+ }
+ \cs_new_protected:cpn { __str_convert_decode_utf32be: }
+ { \__str_decode_utf_xxxii:Nw 1 \g__str_result_tl \s__str_stop }
+ \cs_new_protected:cpn { __str_convert_decode_utf32le: }
+ { \__str_decode_utf_xxxii:Nw 2 \g__str_result_tl \s__str_stop }
+ \cs_new_protected:cpn { __str_convert_decode_utf32: }
+ {
+ \exp_after:wN \__str_decode_utf_xxxii_bom:NNNN \g__str_result_tl
+ \s__str_stop \s__str_stop \s__str_stop \s__str_stop \s__str_stop
+ }
+ \cs_new_protected:Npn \__str_decode_utf_xxxii_bom:NNNN #1#2#3#4
+ {
+ \str_if_eq:nnTF { #1#2#3#4 } { ^^ff ^^fe ^^00 ^^00 }
+ { \__str_decode_utf_xxxii:Nw 2 }
+ {
+ \str_if_eq:nnTF { #1#2#3#4 } { ^^00 ^^00 ^^fe ^^ff }
+ { \__str_decode_utf_xxxii:Nw 1 }
+ { \__str_decode_utf_xxxii:Nw 1 #1#2#3#4 }
+ }
+ }
+ \cs_new_protected:Npn \__str_decode_utf_xxxii:Nw #1#2 \s__str_stop
+ {
+ \flag_clear:N \l__str_overflow_flag
+ \flag_clear:N \l__str_end_flag
+ \flag_clear:N \l__str_error_flag
+ \cs_set:Npn \__str_tmp:w ##1 ##2 { ` ## #1 }
+ \__kernel_tl_gset:Nx \g__str_result_tl
+ {
+ \exp_after:wN \__str_decode_utf_xxxii_loop:NNNN
+ #2 \s__str_stop \s__str_stop \s__str_stop \s__str_stop
+ \prg_break_point:
+ }
+ \__str_if_flag_error:Nne \l__str_error_flag { utf32-decode } { }
+ }
+ \cs_new:Npn \__str_decode_utf_xxxii_loop:NNNN #1#2#3#4
+ {
+ \if_meaning:w \s__str_stop #4
+ \exp_after:wN \__str_decode_utf_xxxii_end:w
+ \fi:
+ #1#2#3#4 \s__str
+ \if_int_compare:w \__str_tmp:w #1#4 > \c_zero_int
+ \flag_raise:N \l__str_overflow_flag
+ \flag_raise:N \l__str_error_flag
+ \int_use:N \c__str_replacement_char_int
+ \else:
+ \if_int_compare:w \__str_tmp:w #2#3 > 16 \exp_stop_f:
+ \flag_raise:N \l__str_overflow_flag
+ \flag_raise:N \l__str_error_flag
+ \int_use:N \c__str_replacement_char_int
+ \else:
+ \int_eval:n
+ { \__str_tmp:w #2#3*"10000 + \__str_tmp:w #3#2*"100 + \__str_tmp:w #4#1 }
+ \fi:
+ \fi:
+ \s__str
+ \__str_decode_utf_xxxii_loop:NNNN
+ }
+ \cs_new:Npn \__str_decode_utf_xxxii_end:w #1 \s__str_stop
+ {
+ \tl_if_empty:nF {#1}
+ {
+ \flag_raise:N \l__str_end_flag
+ \flag_raise:N \l__str_error_flag
+ #1 \s__str
+ \int_use:N \c__str_replacement_char_int \s__str
+ }
+ \prg_break:
+ }
+\group_end:
+\cs_new:Npn \str_convert_pdfname:n #1
+ {
+ \exp_args:Ne \tl_to_str:n
+ { \str_map_function:nN {#1} \__str_convert_pdfname:n }
+ }
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_new:Npn \__str_convert_pdfname:n #1
+ {
+ \int_compare:nNnTF { `#1 } > { "7F }
+ { \__str_convert_pdfname_bytes:n {#1} }
+ { \__str_escape_name_char:n {#1} }
+ }
+ \cs_new:Npn \__str_convert_pdfname_bytes:n #1
+ {
+ \exp_args:Ne \__str_convert_pdfname_bytes_aux:n
+ { \__kernel_codepoint_to_bytes:n {`#1} }
+ }
+ \cs_new:Npn \__str_convert_pdfname_bytes_aux:n #1
+ { \__str_convert_pdfname_bytes_aux:nnnn #1 }
+ \cs_new:Npe \__str_convert_pdfname_bytes_aux:nnnn #1#2#3#4
+ {
+ \c_hash_str \exp_not:N \__str_output_hexadecimal:n {#1}
+ \c_hash_str \exp_not:N \__str_output_hexadecimal:n {#2}
+ \exp_not:N \tl_if_blank:nF {#3}
+ {
+ \c_hash_str \exp_not:N \__str_output_hexadecimal:n {#3}
+ \exp_not:N \tl_if_blank:nF {#4}
+ {
+ \c_hash_str \exp_not:N \__str_output_hexadecimal:n {#4}
+ }
+ }
+ }
+ }
+ { \cs_new_eq:NN \__str_convert_pdfname:n \__str_escape_name_char:n }
+%% File: l3tl-analysis.dtx
+\scan_new:N \s__tl
+\cs_new_eq:NN \l__tl_analysis_token ?
+\cs_new_eq:NN \l__tl_analysis_char_token ?
+\tl_new:N \l__tl_peek_code_tl
+\group_begin:
+\char_set_active_eq:NN \ \scan_stop:
+\tl_const:Ne \c__tl_peek_catcodes_tl
+ {
+ \char_generate:nn { 32 } { 3 } 3
+ \char_generate:nn { 32 } { 4 } 4
+ \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:
+\int_new:N \l__tl_analysis_normal_int
+\int_new:N \l__tl_analysis_index_int
+\int_new:N \l__tl_analysis_nesting_int
+\int_new:N \l__tl_analysis_type_int
+\tl_new:N \g__tl_analysis_result_tl
+\cs_new:Npn \__tl_analysis_extract_charcode:
+ {
+ \exp_after:wN \__tl_analysis_extract_charcode_aux:w
+ \token_to_meaning:N \l__tl_analysis_token
+ }
+\cs_new:Npn \__tl_analysis_extract_charcode_aux:w #1 ~ #2 ~ { ` }
+\cs_new:Npn \__tl_analysis_cs_space_count:NN #1 #2
+ {
+ \exp_after:wN #1
+ \int_value:w \int_eval:w 0
+ \exp_after:wN \__tl_analysis_cs_space_count:w
+ \token_to_str:N #2
+ \fi: \__tl_analysis_cs_space_count_end:w ; ~ !
+ }
+\cs_new:Npn \__tl_analysis_cs_space_count:w #1 ~
+ {
+ \if_false: #1 #1 \fi:
+ + 1
+ \__tl_analysis_cs_space_count:w
+ }
+\cs_new:Npn \__tl_analysis_cs_space_count_end:w ; #1 \fi: #2 !
+ { \exp_after:wN ; \int_value:w \str_count_ignore_spaces:n {#1} ; }
+\cs_new_protected:Npn \__tl_analysis:n #1
+ {
+ \group_begin:
+ \group_align_safe_begin:
+ \__tl_analysis_a:n {#1}
+ \__tl_analysis_b:n {#1}
+ \group_align_safe_end:
+ \group_end:
+ }
+\group_begin:
+ \char_set_catcode_active:N \^^@
+ \cs_new_protected:Npn \__tl_analysis_disable:n #1
+ {
+ \tex_lccode:D 0 = #1 \exp_stop_f:
+ \tex_lowercase:D { \tex_let:D ^^@ } \tex_undefined:D
+ }
+ \bool_lazy_or:nnT
+ { \sys_if_engine_ptex_p: }
+ { \sys_if_engine_uptex_p: }
+ {
+ \cs_gset_protected:Npn \__tl_analysis_disable:n #1
+ {
+ \if_int_compare:w 256 > #1 \exp_stop_f:
+ \tex_lccode:D 0 = #1 \exp_stop_f:
+ \tex_lowercase:D { \tex_let:D ^^@ } \tex_undefined:D
+ \fi:
+ }
+ }
+\group_end:
+\group_begin:
+ \char_set_catcode_active:N \^^@
+ \cs_new_protected:Npn \__tl_analysis_disable_char:N #1
+ {
+ \tex_lccode:D `#1 = 32 \exp_stop_f:
+ \tex_lowercase:D { \if_meaning:w #1 } \tex_undefined:D
+ \tex_let:D #1 \tex_undefined:D
+ \fi:
+ }
+ \bool_lazy_or:nnT
+ { \sys_if_engine_ptex_p: }
+ { \sys_if_engine_uptex_p: }
+ {
+ \cs_gset_protected:Npn \__tl_analysis_disable_char:N #1
+ {
+ \if_int_compare:w 256 > `#1 \exp_stop_f:
+ \tex_lccode:D `#1 = 32 \exp_stop_f:
+ \tex_lowercase:D { \if_meaning:w #1 } \tex_undefined:D
+ \tex_let:D #1 \tex_undefined:D
+ \fi:
+ \fi:
+ }
+ }
+\group_end:
+\cs_new_protected:Npn \__tl_analysis_a:n #1
+ {
+ \__tl_analysis_disable:n { 32 }
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \int_zero:N \l__tl_analysis_normal_int
+ \int_zero:N \l__tl_analysis_index_int
+ \int_zero:N \l__tl_analysis_nesting_int
+ \if_false: { \fi: \__tl_analysis_a_loop:w #1 }
+ \int_decr:N \l__tl_analysis_index_int
+ }
+\cs_new_protected:Npn \__tl_analysis_a_loop:w
+ { \tex_futurelet:D \l__tl_analysis_token \__tl_analysis_a_type:w }
+\cs_new_protected:Npn \__tl_analysis_a_type:w
+ {
+ \l__tl_analysis_type_int =
+ \if_meaning:w \l__tl_analysis_token \c_space_token
+ 0
+ \else:
+ \if_catcode:w \exp_not:N \l__tl_analysis_token \c_group_begin_token
+ 1
+ \else:
+ \if_catcode:w \exp_not:N \l__tl_analysis_token \c_group_end_token
+ - 1
+ \else:
+ 2
+ \fi:
+ \fi:
+ \fi:
+ \exp_stop_f:
+ \if_case:w \l__tl_analysis_type_int
+ \exp_after:wN \__tl_analysis_a_space:w
+ \or: \exp_after:wN \__tl_analysis_a_bgroup:w
+ \or: \exp_after:wN \__tl_analysis_a_safe:N
+ \else: \exp_after:wN \__tl_analysis_a_egroup:w
+ \fi:
+ }
+\cs_new_protected:Npn \__tl_analysis_a_space:w
+ {
+ \tex_afterassignment:D \__tl_analysis_a_space_test:w
+ \exp_after:wN \cs_set_eq:NN
+ \exp_after:wN \l__tl_analysis_char_token
+ \token_to_str:N
+ }
+\cs_new_protected:Npn \__tl_analysis_a_space_test:w
+ {
+ \if_meaning:w \l__tl_analysis_char_token \c_space_token
+ \tex_toks:D \l__tl_analysis_index_int { \exp_not:n { ~ } }
+ \__tl_analysis_a_store:
+ \else:
+ \int_incr:N \l__tl_analysis_normal_int
+ \fi:
+ \__tl_analysis_a_loop:w
+ }
+\group_begin:
+ \char_set_catcode_group_begin:N \^^@ % {
+ \cs_new_protected:Npn \__tl_analysis_a_bgroup:w
+ { \__tl_analysis_a_group:nw { \exp_after:wN ^^@ \if_false: } \fi: } }
+ \char_set_catcode_group_end:N \^^@
+ \cs_new_protected:Npn \__tl_analysis_a_egroup:w
+ { \__tl_analysis_a_group:nw { \if_false: { \fi: ^^@ } } % }
+\group_end:
+\cs_new_protected:Npn \__tl_analysis_a_group:nw #1
+ {
+ \tex_lccode:D 0 = \__tl_analysis_extract_charcode: \scan_stop:
+ \tex_lowercase:D { \tex_toks:D \l__tl_analysis_index_int {#1} }
+ \if_int_compare:w \tex_lccode:D 0 = \tex_escapechar:D
+ \int_set:Nn \tex_escapechar:D { 139 - \tex_escapechar:D }
+ \fi:
+ \__tl_analysis_disable:n { \tex_lccode:D 0 }
+ \tex_futurelet:D \l__tl_analysis_token \__tl_analysis_a_group_aux:w
+ }
+\cs_new_protected:Npn \__tl_analysis_a_group_aux:w
+ {
+ \if_meaning:w \l__tl_analysis_token \tex_undefined:D
+ \exp_after:wN \__tl_analysis_a_safe:N
+ \else:
+ \exp_after:wN \__tl_analysis_a_group_auxii:w
+ \fi:
+ }
+\cs_new_protected:Npn \__tl_analysis_a_group_auxii:w
+ {
+ \tex_afterassignment:D \__tl_analysis_a_group_test:w
+ \exp_after:wN \cs_set_eq:NN
+ \exp_after:wN \l__tl_analysis_char_token
+ \token_to_str:N
+ }
+\cs_new_protected:Npn \__tl_analysis_a_group_test:w
+ {
+ \if_charcode:w \l__tl_analysis_token \l__tl_analysis_char_token
+ \__tl_analysis_a_store:
+ \else:
+ \int_incr:N \l__tl_analysis_normal_int
+ \fi:
+ \__tl_analysis_a_loop:w
+ }
+\cs_new_protected:Npn \__tl_analysis_a_store:
+ {
+ \tex_advance:D \l__tl_analysis_nesting_int \l__tl_analysis_type_int
+ \if_int_compare:w \tex_lccode:D 0 = `\ \exp_stop_f:
+ \tex_advance:D \l__tl_analysis_type_int \l__tl_analysis_type_int
+ \fi:
+ \tex_skip:D \l__tl_analysis_index_int
+ = \l__tl_analysis_normal_int sp
+ plus \l__tl_analysis_type_int sp \scan_stop:
+ \int_incr:N \l__tl_analysis_index_int
+ \int_zero:N \l__tl_analysis_normal_int
+ \if_int_compare:w \l__tl_analysis_nesting_int = - \c_one_int
+ \cs_set_eq:NN \__tl_analysis_a_loop:w \scan_stop:
+ \fi:
+ }
+\cs_new_protected:Npn \__tl_analysis_a_safe:N #1
+ {
+ \if_charcode:w
+ \scan_stop:
+ \exp_after:wN \use_none:n \token_to_str:N #1 \prg_do_nothing:
+ \scan_stop:
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ {
+ \__tl_analysis_disable_char:N #1
+ \int_incr:N \l__tl_analysis_normal_int
+ }
+ { \__tl_analysis_cs_space_count:NN \__tl_analysis_a_cs:ww #1 }
+ \__tl_analysis_a_loop:w
+ }
+\cs_new_protected:Npn \__tl_analysis_a_cs:ww #1; #2;
+ {
+ \if_int_compare:w #1 > \c_zero_int
+ \tex_skip:D \l__tl_analysis_index_int
+ = \int_eval:n { \l__tl_analysis_normal_int + 1 } sp \exp_stop_f:
+ \tex_advance:D \l__tl_analysis_index_int #1 \exp_stop_f:
+ \else:
+ \tex_advance:D
+ \fi:
+ \l__tl_analysis_normal_int #2 \exp_stop_f:
+ }
+\cs_new_protected:Npn \__tl_analysis_b:n #1
+ {
+ \__kernel_tl_gset:Nx \g__tl_analysis_result_tl
+ {
+ \__tl_analysis_b_loop:w 0; #1
+ \prg_break_point:
+ }
+ }
+\cs_new:Npn \__tl_analysis_b_loop:w #1;
+ {
+ \exp_after:wN \__tl_analysis_b_normals:ww
+ \int_value:w \tex_skip:D #1 ; #1 ;
+ }
+\cs_new:Npn \__tl_analysis_b_normals:ww #1;
+ {
+ \if_int_compare:w #1 = \c_zero_int
+ \__tl_analysis_b_special:w
+ \fi:
+ \__tl_analysis_b_normal:wwN #1;
+ }
+\cs_new:Npn \__tl_analysis_b_normal:wwN #1; #2; #3
+ {
+ \exp_not:n { \exp_not:n { #3 } } \s__tl
+ \if_charcode:w
+ \scan_stop:
+ \exp_after:wN \use_none:n \token_to_str:N #3 \prg_do_nothing:
+ \scan_stop:
+ \exp_after:wN \__tl_analysis_b_char:Nn
+ \exp_after:wN \__tl_analysis_b_char_aux:nww
+ \else:
+ \exp_after:wN \__tl_analysis_b_cs:Nww
+ \fi:
+ #3 #1; #2;
+ }
+\cs_new:Npe \__tl_analysis_b_char:Nn #1#2
+ {
+ \exp_not:N \if_meaning:w #2 \exp_not:N \tex_undefined:D
+ \token_to_str:N D \exp_not:N \else:
+ \exp_not:N \if_catcode:w #2 \c_catcode_other_token
+ \token_to_str:N C \exp_not:N \else:
+ \exp_not:N \if_catcode:w #2 \c_catcode_letter_token
+ \token_to_str:N B \exp_not:N \else:
+ \exp_not:N \if_catcode:w #2 \c_math_toggle_token 3
+ \exp_not:N \else:
+ \exp_not:N \if_catcode:w #2 \c_alignment_token 4
+ \exp_not:N \else:
+ \exp_not:N \if_catcode:w #2 \c_math_superscript_token 7
+ \exp_not:N \else:
+ \exp_not:N \if_catcode:w #2 \c_math_subscript_token 8
+ \exp_not:N \else:
+ \exp_not:N \if_catcode:w #2 \c_space_token
+ \token_to_str:N A \exp_not:N \else:
+ 6
+ \exp_not:n { \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: }
+ #1 {#2}
+ }
+\cs_new:Npn \__tl_analysis_b_char_aux:nww #1
+ {
+ \int_value:w `#1 \s__tl
+ \exp_after:wN \__tl_analysis_b_normals:ww
+ \int_value:w \int_eval:w - 1 +
+ }
+\cs_new:Npn \__tl_analysis_b_cs:Nww #1
+ {
+ 0 -1 \s__tl
+ \__tl_analysis_cs_space_count:NN \__tl_analysis_b_cs_test:ww #1
+ }
+\cs_new:Npn \__tl_analysis_b_cs_test:ww #1 ; #2 ; #3 ; #4 ;
+ {
+ \exp_after:wN \__tl_analysis_b_normals:ww
+ \int_value:w \int_eval:w
+ \if_int_compare:w #1 = \c_zero_int
+ #3
+ \else:
+ \tex_skip:D \int_eval:n { #4 + #1 } \exp_stop_f:
+ \fi:
+ - #2
+ \exp_after:wN ;
+ \int_value:w \int_eval:n { #4 + #1 } ;
+ }
+\group_begin:
+ \char_set_catcode_other:N A
+ \cs_new:Npn \__tl_analysis_b_special:w
+ \fi: \__tl_analysis_b_normal:wwN 0 ; #1 ;
+ {
+ \fi:
+ \if_int_compare:w #1 = \l__tl_analysis_index_int
+ \exp_after:wN \prg_break:
+ \fi:
+ \tex_the:D \tex_toks:D #1 \s__tl
+ \if_case:w \tex_gluestretch:D \tex_skip:D #1 \exp_stop_f:
+ \token_to_str:N A
+ \or: 1
+ \or: 1
+ \else: 2
+ \fi:
+ \if_int_odd:w \tex_gluestretch:D \tex_skip:D #1 \exp_stop_f:
+ \exp_after:wN \__tl_analysis_b_special_char:wN \int_value:w
+ \else:
+ \exp_after:wN \__tl_analysis_b_special_space:w \int_value:w
+ \fi:
+ \int_eval:n { 1 + #1 } \exp_after:wN ;
+ \token_to_str:N
+ }
+\group_end:
+\cs_new:Npn \__tl_analysis_b_special_char:wN #1 ; #2
+ {
+ \int_value:w `#2 \s__tl
+ \__tl_analysis_b_loop:w #1 ;
+ }
+\cs_new:Npn \__tl_analysis_b_special_space:w #1 ; ~
+ {
+ 32 \s__tl
+ \__tl_analysis_b_loop:w #1 ;
+ }
+\cs_new_protected:Npn \tl_analysis_map_inline:Nn #1
+ { \exp_args:No \tl_analysis_map_inline:nn #1 }
+\cs_new_protected:Npn \tl_analysis_map_inline:nn #1
+ {
+ \__tl_analysis:n {#1}
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:Nc \__tl_analysis_map:Nn
+ { __tl_analysis_map_inline_ \int_use:N \g__kernel_prg_map_int :wNw }
+ }
+\cs_new_protected:Npn \__tl_analysis_map:Nn #1#2
+ {
+ \cs_gset_protected:Npn #1 ##1##2##3 {#2}
+ \exp_after:wN \__tl_analysis_map:NwNw \exp_after:wN #1
+ \g__tl_analysis_result_tl
+ \s__tl { ? \tl_map_break: } \s__tl
+ \prg_break_point:Nn \tl_map_break:
+ { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+\cs_new_protected:Npn \__tl_analysis_map:NwNw #1 #2 \s__tl #3 #4 \s__tl
+ {
+ \use_none:n #3
+ #1 {#2} {#4} {#3}
+ \__tl_analysis_map:NwNw #1
+ }
+\cs_new_protected:Npn \tl_analysis_show:N
+ { \__tl_analysis_show:NNN \msg_show:nneeee \tl_show:N }
+\cs_new_protected:Npn \tl_analysis_log:N
+ { \__tl_analysis_show:NNN \msg_log:nneeee \tl_log:N }
+\cs_new_protected:Npn \__tl_analysis_show:NNN #1#2#3
+ {
+ \tl_if_exist:NTF #3
+ {
+ \exp_args:No \__tl_analysis:n {#3}
+ #1 { tl } { show-analysis }
+ { \token_to_str:N #3 } { \__tl_analysis_show: } { } { }
+ }
+ { #2 #3 }
+ }
+\cs_new_protected:Npn \tl_analysis_show:n
+ { \__tl_analysis_show:Nn \msg_show:nneeee }
+\cs_new_protected:Npn \tl_analysis_log:n
+ { \__tl_analysis_show:Nn \msg_log:nneeee }
+\cs_new_protected:Npn \__tl_analysis_show:Nn #1#2
+ {
+ \__tl_analysis:n {#2}
+ #1 { tl } { show-analysis } { } { \__tl_analysis_show: } { } { }
+ }
+\cs_new:Npn \__tl_analysis_show:
+ {
+ \exp_after:wN \__tl_analysis_show_loop:wNw \g__tl_analysis_result_tl
+ \s__tl { ? \prg_break: } \s__tl
+ \prg_break_point:
+ }
+\cs_new:Npn \__tl_analysis_show_loop:wNw #1 \s__tl #2 #3 \s__tl
+ {
+ \use_none:n #2
+ \iow_newline: > \use:nn { ~ } { ~ }
+ \if_int_compare:w "#2 = \c_zero_int
+ \exp_after:wN \__tl_analysis_show_cs:n
+ \else:
+ \if_int_compare:w "#2 = 13 \exp_stop_f:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__tl_analysis_show_active:n
+ \else:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__tl_analysis_show_normal:n
+ \fi:
+ \fi:
+ {#1}
+ \__tl_analysis_show_loop:wNw
+ }
+\cs_new:Npn \__tl_analysis_show_normal:n #1
+ {
+ \exp_after:wN \token_to_str:N #1 ~
+ ( \exp_after:wN \token_to_meaning:N #1 )
+ }
+\cs_new:Npn \__tl_analysis_show_value:N #1
+ {
+ \token_if_expandable:NF #1
+ {
+ \token_if_chardef:NTF #1 \prg_break: { }
+ \token_if_mathchardef:NTF #1 \prg_break: { }
+ \token_if_dim_register:NTF #1 \prg_break: { }
+ \token_if_int_register:NTF #1 \prg_break: { }
+ \token_if_skip_register:NTF #1 \prg_break: { }
+ \token_if_toks_register:NTF #1 \prg_break: { }
+ \use_none:nnn
+ \prg_break_point:
+ \use:n { \exp_after:wN = \tex_the:D #1 }
+ }
+ }
+\cs_new:Npn \__tl_analysis_show_cs:n #1
+ { \exp_args:No \__tl_analysis_show_long:nn {#1} { control~sequence= } }
+\cs_new:Npn \__tl_analysis_show_active:n #1
+ { \exp_args:No \__tl_analysis_show_long:nn {#1} { active~character= } }
+\cs_new:Npn \__tl_analysis_show_long:nn #1
+ {
+ \__tl_analysis_show_long_aux:oofn
+ { \token_to_str:N #1 }
+ { \token_to_meaning:N #1 }
+ { \__tl_analysis_show_value:N #1 }
+ }
+\cs_new:Npn \__tl_analysis_show_long_aux:nnnn #1#2#3#4
+ {
+ \int_compare:nNnTF
+ { \str_count:n { #1 ~ ( #4 #2 #3 ) } }
+ > { \l_iow_line_count_int - 3 }
+ {
+ \str_range:nnn { #1 ~ ( #4 #2 #3 ) } { 1 }
+ {
+ \l_iow_line_count_int - 3
+ - \str_count:N \c__tl_analysis_show_etc_str
+ }
+ \c__tl_analysis_show_etc_str
+ }
+ { #1 ~ ( #4 #2 #3 ) }
+ }
+\cs_generate_variant:Nn \__tl_analysis_show_long_aux:nnnn { oof }
+\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: }
+\int_new:N \l__tl_peek_charcode_int
+\cs_new:Npn \__tl_analysis_char_arg:Nw
+ {
+ \if_meaning:w \l__tl_analysis_token \c_space_token
+ \exp_after:wN \__tl_analysis_char_arg_aux:Nw
+ \fi:
+ }
+\cs_new:Npn \__tl_analysis_char_arg_aux:Nw #1 ~ { #1 { ~ } }
+\cs_new_protected:Npn \peek_analysis_map_inline:n #1
+ {
+ \group_align_safe_begin:
+ \int_gincr:N \g__kernel_prg_map_int
+ \cs_set_protected:cpn
+ { __tl_analysis_map_ \int_use:N \g__kernel_prg_map_int :nnN }
+ ##1##2##3
+ {
+ \group_end:
+ #1
+ \__tl_peek_analysis_loop:NNn
+ \prg_break_point:Nn \peek_analysis_map_break:
+ {
+ \int_gdecr:N \g__kernel_prg_map_int
+ \group_align_safe_end:
+ }
+ }
+ \__tl_peek_analysis_loop:NNn ? ? ?
+ }
+\cs_new_protected:Npn \__tl_peek_analysis_loop:NNn #1#2#3
+ {
+ \group_begin:
+ \tl_set:Ne \l__tl_peek_code_tl
+ {
+ \exp_not:c
+ { __tl_analysis_map_ \int_use:N \g__kernel_prg_map_int :nnN }
+ }
+ \int_set:Nn \tex_escapechar:D { `\\ }
+ \peek_after:Nw \__tl_peek_analysis_test:
+ }
+\cs_new_protected:Npn \__tl_peek_analysis_test:
+ {
+ \if_case:w
+ \if_catcode:w \exp_not:N \l_peek_token { \c_max_int \fi:
+ \if_catcode:w \exp_not:N \l_peek_token } \c_max_int \fi:
+ \if_meaning:w \l_peek_token \c_space_token \c_max_int \fi:
+ \exp_after:wN \if_meaning:w \exp_not:N \l_peek_token \l_peek_token
+ \c_one_int
+ \fi:
+ \c_zero_int
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \__tl_peek_analysis_exp:N
+ \exp_after:wN \exp_not:N
+ \or:
+ \exp_after:wN \__tl_peek_analysis_nonexp:N
+ \else:
+ \exp_after:wN \__tl_peek_analysis_special:
+ \fi:
+ }
+\cs_new_protected:Npn \__tl_peek_analysis_exp:N #1
+ {
+ \cs_set_nopar:Npe \l__tl_peek_code_tl
+ {
+ \tex_let:D \exp_not:N #1 \scan_stop:
+ \exp_not:o \l__tl_peek_code_tl
+ {
+ \exp_not:n { \__kernel_exp_not:w \exp_after:wN }
+ { \exp_not:N \exp_not:N \exp_not:N #1 }
+ }
+ \exp_after:wN \__tl_peek_analysis_exp_aux:Nw
+ \token_to_str:N #1 \prg_do_nothing: \s__tl
+ }
+ \l__tl_peek_code_tl
+ }
+\cs_new:Npe \__tl_peek_analysis_exp_aux:Nw #1#2 \s__tl
+ {
+ \exp_not:N \if:w \scan_stop: #2 \scan_stop:
+ { \exp_not:N \int_value:w `#1 } \token_to_str:N D
+ \exp_not:N \else:
+ { -1 } 0
+ \exp_not:N \fi:
+ }
+\cs_new_protected:Npn \__tl_peek_analysis_nonexp:N #1
+ {
+ \if_charcode:w
+ \scan_stop:
+ \exp_after:wN \use_none:n \token_to_str:N #1 \prg_do_nothing:
+ \scan_stop:
+ \exp_after:wN \__tl_peek_analysis_char:N
+ \else:
+ \exp_after:wN \__tl_peek_analysis_cs:N
+ \fi:
+ #1
+ }
+\cs_new_protected:Npn \__tl_peek_analysis_cs:N #1
+ { \l__tl_peek_code_tl { \exp_not:n {#1} } { -1 } 0 }
+\group_begin:
+\char_set_active_eq:NN \ \scan_stop:
+\cs_new_protected:Npe \__tl_peek_analysis_char:N #1
+ {
+ \cs_set_eq:NN
+ \char_generate:nn { 32 } { 13 }
+ \exp_not:N \tex_undefined:D
+ \tex_lccode:D `#1 = 32 \exp_stop_f:
+ \tex_lowercase:D
+ {
+ \tl_put_right:Ne \exp_not:N \l__tl_peek_code_tl
+ { \exp_not:n { \__tl_analysis_b_char:Nn \use_none:n } {#1} }
+ }
+ \exp_not:n
+ {
+ \exp_after:wN \__tl_peek_analysis_char:w
+ \int_value:w
+ }
+ `#1
+ \exp_not:n { \exp_after:wN \s__tl \l__tl_peek_code_tl }
+ #1
+ }
+\group_end:
+\cs_new_protected:Npn \__tl_peek_analysis_char:w #1 \s__tl #2#3#4
+ {
+ \if_charcode:w 6 #3
+ \else:
+ \if_charcode:w D #3
+ \else:
+ \exp_args:NNNo
+ \fi:
+ \fi:
+ #2 { \exp_not:n {#4} } {#1} #3
+ }
+\cs_new_protected:Npn \__tl_peek_analysis_special:
+ {
+ \tex_let:D \l__tl_analysis_token = ~ \l_peek_token
+ \int_set:Nn \l__tl_peek_charcode_int
+ { \__tl_analysis_extract_charcode: }
+ \if_int_compare:w \l__tl_peek_charcode_int = \tex_escapechar:D
+ \int_set:Nn \tex_escapechar:D { `\/ }
+ \fi:
+ \char_set_active_eq:nN { \l__tl_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__tl_analysis_token
+ \__tl_peek_analysis_retest:
+ }
+\cs_new_protected:Npn \__tl_peek_analysis_retest:
+ {
+ \if_meaning:w \l__tl_analysis_token \scan_stop:
+ \exp_after:wN \__tl_peek_analysis_nonexp:N
+ \else:
+ \exp_after:wN \__tl_peek_analysis_str:
+ \fi:
+ }
+\cs_new_protected:Npn \__tl_peek_analysis_str:
+ {
+ \exp_after:wN \tex_futurelet:D
+ \exp_after:wN \l__tl_analysis_token
+ \exp_after:wN \__tl_peek_analysis_str:w
+ \token_to_str:N
+ }
+\cs_new_protected:Npn \__tl_peek_analysis_str:w
+ { \__tl_analysis_char_arg:Nw \__tl_peek_analysis_str:n }
+\cs_new_protected:Npn \__tl_peek_analysis_str:n #1
+ {
+ \int_case:nnF { `#1 }
+ {
+ { \l__tl_peek_charcode_int }
+ { \__tl_peek_analysis_explicit:n {#1} }
+ { \tex_escapechar:D } { \__tl_peek_analysis_escape: }
+ }
+ { \__tl_peek_analysis_active_str:n {#1} }
+ }
+\cs_new_protected:Npn \__tl_peek_analysis_active_str:n #1
+ {
+ \tl_put_right:Ne \l__tl_peek_code_tl
+ {
+ { \char_generate:nn { `#1 } { 13 } }
+ { \int_value:w `#1 }
+ \token_to_str:N D
+ }
+ \l__tl_peek_code_tl
+ }
+\cs_new_protected:Npn \__tl_peek_analysis_explicit:n #1
+ {
+ \tl_put_right:Ne \l__tl_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__tl_peek_code_tl
+ }
+\cs_new_protected:Npn \__tl_peek_analysis_escape:
+ {
+ \tl_clear:N \l__tl_internal_a_tl
+ \tex_futurelet:D \l__tl_analysis_token
+ \__tl_peek_analysis_collect:w
+ }
+\cs_new_protected:Npn \__tl_peek_analysis_collect:w
+ { \__tl_analysis_char_arg:Nw \__tl_peek_analysis_collect:n }
+\cs_new_protected:Npn \__tl_peek_analysis_collect:n #1
+ {
+ \tl_put_right:Nn \l__tl_internal_a_tl {#1}
+ \__tl_peek_analysis_collect_loop:
+ }
+\cs_new_protected:Npn \__tl_peek_analysis_collect_loop:
+ {
+ \exp_after:wN \if_meaning:w
+ \cs:w
+ \if_cs_exist:w \l__tl_internal_a_tl \cs_end:
+ \l__tl_internal_a_tl
+ \else:
+ c_one % anything short
+ \fi:
+ \cs_end:
+ \l_peek_token
+ \__tl_peek_analysis_collect_end:NNNN
+ \fi:
+ \tex_futurelet:D \l__tl_analysis_token
+ \__tl_peek_analysis_collect:w
+ }
+\cs_new_protected:Npn \__tl_peek_analysis_collect_end:NNNN #1#2#3#4
+ {
+ #1
+ \tl_put_right:Ne \l__tl_peek_code_tl
+ {
+ { \exp_not:N \exp_not:n { \exp_not:c { \l__tl_internal_a_tl } } }
+ { -1 }
+ 0
+ }
+ \l__tl_peek_code_tl
+ }
+\tl_const:Ne \c__tl_analysis_show_etc_str % (
+ { \token_to_str:N \ETC.) }
+\msg_new:nnn { tl } { show-analysis }
+ {
+ The~token~list~ \tl_if_empty:nF {#1} { #1 ~ }
+ \tl_if_empty:nTF {#2}
+ { is~empty }
+ { contains~the~tokens: #2 }
+ }
+%% File: l3regex.dtx
+\cs_new_eq:NN \__regex_int_eval:w \tex_numexpr:D
+\cs_new_protected:Npn \__regex_standard_escapechar:
+ { \int_set:Nn \tex_escapechar:D { `\\ } }
+\cs_new:Npn \__regex_toks_use:w { \tex_the:D \tex_toks:D }
+\cs_new_protected:Npn \__regex_toks_clear:N #1
+ { \tex_toks:D #1 = { } }
+\cs_new_eq:NN \__regex_toks_set:Nn \tex_toks:D
+\cs_new_protected:Npn \__regex_toks_set:No #1
+ { \tex_toks:D #1 = \exp_after:wN }
+\cs_new_protected:Npn \__regex_toks_memcpy:NNn #1#2#3
+ {
+ \prg_replicate:nn {#3}
+ {
+ \tex_toks:D #1 = \tex_toks:D #2
+ \int_incr:N #1
+ \int_incr:N #2
+ }
+ }
+\cs_if_exist:NTF \tex_etokspre:D
+ { \cs_new_eq:NN \__regex_toks_put_left:Ne \tex_etokspre:D }
+ {
+ \cs_new_protected:Npn \__regex_toks_put_left:Ne #1#2
+ { \tex_toks:D #1 = \tex_expanded:D {{ #2 \tex_the:D \tex_toks:D #1 }} }
+ }
+\cs_if_exist:NTF \tex_etoksapp:D
+ { \cs_new_eq:NN \__regex_toks_put_right:Ne \tex_etoksapp:D }
+ {
+ \cs_new_protected:Npn \__regex_toks_put_right:Ne #1#2
+ { \tex_toks:D #1 = \tex_expanded:D {{ \tex_the:D \tex_toks:D #1 #2 }} }
+ }
+\cs_if_exist:NTF \tex_toksapp:D
+ { \cs_new_eq:NN \__regex_toks_put_right:Nn \tex_toksapp:D }
+ {
+ \cs_new_protected:Npn \__regex_toks_put_right:Nn #1#2
+ { \tex_toks:D #1 = \exp_after:wN { \tex_the:D \tex_toks:D #1 #2 } }
+ }
+\cs_new:Npn \__regex_curr_cs_to_str:
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \cs_to_str:N
+ \l__regex_curr_token_tl
+ }
+\cs_new:Npn \__regex_intarray_item:NnF #1#2
+ { \exp_args:No \__regex_intarray_item_aux:nNF { \tex_the:D \__regex_int_eval:w #2 } #1 }
+\cs_new:Npn \__regex_intarray_item_aux:nNF #1#2
+ {
+ \if_int_compare:w #1 > \c_zero_int
+ \exp_after:wN \use_ii:nnn
+ \fi:
+ \use_ii:nn { \__kernel_intarray_item:Nn #2 {#1} }
+ }
+\cs_new:Npn \__regex_maplike_break:
+ { \prg_map_break:Nn \__regex_maplike_break: { } }
+\cs_new:Npn \__regex_tl_odd_items:n #1 { \__regex_tl_even_items:n { ? #1 } }
+\cs_new:Npn \__regex_tl_even_items:n #1
+ {
+ \__regex_tl_even_items_loop:nn #1 \q__regex_nil \q__regex_nil
+ \prg_break_point:
+ }
+\cs_new:Npn \__regex_tl_even_items_loop:nn #1#2
+ {
+ \__regex_use_none_delimit_by_q_nil:w #2 \prg_break: \q__regex_nil
+ { \exp_not:n {#2} }
+ \__regex_tl_even_items_loop:nn
+ }
+\cs_new:Npn \__regex_tmp:w { }
+\tl_new:N \l__regex_internal_a_tl
+\tl_new:N \l__regex_internal_b_tl
+\int_new:N \l__regex_internal_a_int
+\int_new:N \l__regex_internal_b_int
+\int_new:N \l__regex_internal_c_int
+\bool_new:N \l__regex_internal_bool
+\seq_new:N \l__regex_internal_seq
+\tl_new:N \g__regex_internal_tl
+\tl_new:N \l__regex_build_tl
+\tl_const:Nn \c__regex_no_match_regex
+ {
+ \__regex_branch:n
+ { \__regex_class:NnnnN \c_true_bool { } { 1 } { 0 } \c_true_bool }
+ }
+\int_new:N \l__regex_balance_int
+\int_const:Nn \c__regex_ascii_min_int { 0 }
+\int_const:Nn \c__regex_ascii_max_control_int { 31 }
+\int_const:Nn \c__regex_ascii_max_int { 127 }
+\int_const:Nn \c__regex_ascii_lower_int { `a - `A }
+\quark_new:N \q__regex_recursion_stop
+\quark_new:N \q__regex_nil
+\cs_new:Npn \__regex_use_none_delimit_by_q_recursion_stop:w
+ #1 \q__regex_recursion_stop { }
+\cs_new:Npn \__regex_use_i_delimit_by_q_recursion_stop:nw
+ #1 #2 \q__regex_recursion_stop {#1}
+\cs_new:Npn \__regex_use_none_delimit_by_q_nil:w #1 \q__regex_nil { }
+\__kernel_quark_new_conditional:Nn \__regex_quark_if_nil:N { F }
+\cs_new_protected:Npn \__regex_break_true:w
+ #1 \__regex_break_point:TF #2 #3 {#2}
+\cs_new_protected:Npn \__regex_break_point:TF #1 #2 { #2 }
+\cs_new_protected:Npn \__regex_item_reverse:n #1
+ {
+ #1
+ \__regex_break_point:TF { } \__regex_break_true:w
+ }
+\cs_new_protected:Npn \__regex_item_caseful_equal:n #1
+ {
+ \if_int_compare:w #1 = \l__regex_curr_char_int
+ \exp_after:wN \__regex_break_true:w
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_item_caseful_range:nn #1 #2
+ {
+ \reverse_if:N \if_int_compare:w #1 > \l__regex_curr_char_int
+ \reverse_if:N \if_int_compare:w #2 < \l__regex_curr_char_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \__regex_break_true:w
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_item_caseless_equal:n #1
+ {
+ \if_int_compare:w #1 = \l__regex_curr_char_int
+ \exp_after:wN \__regex_break_true:w
+ \fi:
+ \__regex_maybe_compute_ccc:
+ \if_int_compare:w #1 = \l__regex_case_changed_char_int
+ \exp_after:wN \__regex_break_true:w
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_item_caseless_range:nn #1 #2
+ {
+ \reverse_if:N \if_int_compare:w #1 > \l__regex_curr_char_int
+ \reverse_if:N \if_int_compare:w #2 < \l__regex_curr_char_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \__regex_break_true:w
+ \fi:
+ \fi:
+ \__regex_maybe_compute_ccc:
+ \reverse_if:N \if_int_compare:w #1 > \l__regex_case_changed_char_int
+ \reverse_if:N \if_int_compare:w #2 < \l__regex_case_changed_char_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \__regex_break_true:w
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_compute_case_changed_char:
+ {
+ \int_set_eq:NN \l__regex_case_changed_char_int \l__regex_curr_char_int
+ \if_int_compare:w \l__regex_curr_char_int > `Z \exp_stop_f:
+ \if_int_compare:w \l__regex_curr_char_int > `z \exp_stop_f: \else:
+ \if_int_compare:w \l__regex_curr_char_int < `a \exp_stop_f: \else:
+ \int_sub:Nn \l__regex_case_changed_char_int \c__regex_ascii_lower_int
+ \fi:
+ \fi:
+ \else:
+ \if_int_compare:w \l__regex_curr_char_int < `A \exp_stop_f: \else:
+ \int_add:Nn \l__regex_case_changed_char_int \c__regex_ascii_lower_int
+ \fi:
+ \fi:
+ \cs_set_eq:NN \__regex_maybe_compute_ccc: \prg_do_nothing:
+ }
+\cs_new_eq:NN \__regex_maybe_compute_ccc: \__regex_compute_case_changed_char:
+\cs_new_eq:NN \__regex_item_equal:n ?
+\cs_new_eq:NN \__regex_item_range:nn ?
+\cs_new_protected:Npn \__regex_item_catcode:
+ {
+ "
+ \if_case:w \l__regex_curr_catcode_int
+ 1 \or: 4 \or: 10 \or: 40
+ \or: 100 \or: \or: 1000 \or: 4000
+ \or: 10000 \or: \or: 100000 \or: 400000
+ \or: 1000000 \or: 4000000 \else: 1*0
+ \fi:
+ }
+\prg_new_protected_conditional:Npnn \__regex_item_catcode:n #1 { T }
+ {
+ \if_int_odd:w \__regex_int_eval:w #1 / \__regex_item_catcode: \scan_stop:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_item_catcode_reverse:nT #1#2
+ { \__regex_item_catcode:nT {#1} { \__regex_item_reverse:n {#2} } }
+\cs_new_protected:Npn \__regex_item_exact:nn #1#2
+ {
+ \if_int_compare:w #1 = \l__regex_curr_catcode_int
+ \if_int_compare:w #2 = \l__regex_curr_char_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \__regex_break_true:w
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_item_exact_cs:n #1
+ {
+ \int_compare:nNnTF \l__regex_curr_catcode_int = \c_zero_int
+ {
+ \__kernel_tl_set:Nx \l__regex_internal_a_tl
+ { \scan_stop: \__regex_curr_cs_to_str: \scan_stop: }
+ \tl_if_in:noTF { \scan_stop: #1 \scan_stop: }
+ \l__regex_internal_a_tl
+ { \__regex_break_true:w } { }
+ }
+ { }
+ }
+\cs_new_protected:Npn \__regex_item_cs:n #1
+ {
+ \int_compare:nNnT \l__regex_curr_catcode_int = \c_zero_int
+ {
+ \group_begin:
+ \__regex_single_match:
+ \__regex_disable_submatches:
+ \__regex_build_for_cs:n {#1}
+ \bool_set_eq:NN \l__regex_saved_success_bool
+ \g__regex_success_bool
+ \exp_args:Ne \__regex_match_cs:n { \__regex_curr_cs_to_str: }
+ \if_meaning:w \c_true_bool \g__regex_success_bool
+ \group_insert_after:N \__regex_break_true:w
+ \fi:
+ \bool_gset_eq:NN \g__regex_success_bool
+ \l__regex_saved_success_bool
+ \group_end:
+ }
+ }
+\cs_new_protected:Npn \__regex_prop_d:
+ { \__regex_item_caseful_range:nn { `0 } { `9 } }
+\cs_new_protected:Npn \__regex_prop_h:
+ {
+ \__regex_item_caseful_equal:n { `\ }
+ \__regex_item_caseful_equal:n { `\^^I }
+ }
+\cs_new_protected:Npn \__regex_prop_s:
+ {
+ \__regex_item_caseful_equal:n { `\ }
+ \__regex_item_caseful_equal:n { `\^^I }
+ \__regex_item_caseful_equal:n { `\^^J }
+ \__regex_item_caseful_equal:n { `\^^L }
+ \__regex_item_caseful_equal:n { `\^^M }
+ }
+\cs_new_protected:Npn \__regex_prop_v:
+ { \__regex_item_caseful_range:nn { `\^^J } { `\^^M } } % lf, vtab, ff, cr
+\cs_new_protected:Npn \__regex_prop_w:
+ {
+ \__regex_item_caseful_range:nn { `a } { `z }
+ \__regex_item_caseful_range:nn { `A } { `Z }
+ \__regex_item_caseful_range:nn { `0 } { `9 }
+ \__regex_item_caseful_equal:n { `_ }
+ }
+\cs_new_protected:Npn \__regex_prop_N:
+ {
+ \__regex_item_reverse:n
+ { \__regex_item_caseful_equal:n { `\^^J } }
+ }
+\cs_new_protected:Npn \__regex_posix_alnum:
+ { \__regex_posix_alpha: \__regex_posix_digit: }
+\cs_new_protected:Npn \__regex_posix_alpha:
+ { \__regex_posix_lower: \__regex_posix_upper: }
+\cs_new_protected:Npn \__regex_posix_ascii:
+ {
+ \__regex_item_caseful_range:nn
+ \c__regex_ascii_min_int
+ \c__regex_ascii_max_int
+ }
+\cs_new_eq:NN \__regex_posix_blank: \__regex_prop_h:
+\cs_new_protected:Npn \__regex_posix_cntrl:
+ {
+ \__regex_item_caseful_range:nn
+ \c__regex_ascii_min_int
+ \c__regex_ascii_max_control_int
+ \__regex_item_caseful_equal:n \c__regex_ascii_max_int
+ }
+\cs_new_eq:NN \__regex_posix_digit: \__regex_prop_d:
+\cs_new_protected:Npn \__regex_posix_graph:
+ { \__regex_item_caseful_range:nn { `! } { `\~ } }
+\cs_new_protected:Npn \__regex_posix_lower:
+ { \__regex_item_caseful_range:nn { `a } { `z } }
+\cs_new_protected:Npn \__regex_posix_print:
+ { \__regex_item_caseful_range:nn { `\ } { `\~ } }
+\cs_new_protected:Npn \__regex_posix_punct:
+ {
+ \__regex_item_caseful_range:nn { `! } { `/ }
+ \__regex_item_caseful_range:nn { `: } { `@ }
+ \__regex_item_caseful_range:nn { `[ } { `` }
+ \__regex_item_caseful_range:nn { `\{ } { `\~ }
+ }
+\cs_new_protected:Npn \__regex_posix_space:
+ {
+ \__regex_item_caseful_equal:n { `\ }
+ \__regex_item_caseful_range:nn { `\^^I } { `\^^M }
+ }
+\cs_new_protected:Npn \__regex_posix_upper:
+ { \__regex_item_caseful_range:nn { `A } { `Z } }
+\cs_new_eq:NN \__regex_posix_word: \__regex_prop_w:
+\cs_new_protected:Npn \__regex_posix_xdigit:
+ {
+ \__regex_posix_digit:
+ \__regex_item_caseful_range:nn { `A } { `F }
+ \__regex_item_caseful_range:nn { `a } { `f }
+ }
+\cs_new_protected:Npn \__regex_escape_use:nnnn #1#2#3#4
+ {
+ \group_begin:
+ \tl_clear:N \l__regex_internal_a_tl
+ \cs_set:Npn \__regex_escape_unescaped:N ##1 { #1 }
+ \cs_set:Npn \__regex_escape_escaped:N ##1 { #2 }
+ \cs_set:Npn \__regex_escape_raw:N ##1 { #3 }
+ \__regex_standard_escapechar:
+ \__kernel_tl_gset:Nx \g__regex_internal_tl
+ { \__kernel_str_to_other_fast:n {#4} }
+ \tl_put_right:Ne \l__regex_internal_a_tl
+ {
+ \exp_after:wN \__regex_escape_loop:N \g__regex_internal_tl
+ \scan_stop: \prg_break_point:
+ }
+ \exp_after:wN
+ \group_end:
+ \l__regex_internal_a_tl
+ }
+\cs_new:Npn \__regex_escape_loop:N #1
+ {
+ \cs_if_exist_use:cF { __regex_escape_\token_to_str:N #1:w }
+ { \__regex_escape_unescaped:N #1 }
+ \__regex_escape_loop:N
+ }
+\cs_new:cpn { __regex_escape_ \c_backslash_str :w }
+ \__regex_escape_loop:N #1
+ {
+ \cs_if_exist_use:cF { __regex_escape_/\token_to_str:N #1:w }
+ { \__regex_escape_escaped:N #1 }
+ \__regex_escape_loop:N
+ }
+\cs_new_eq:NN \__regex_escape_unescaped:N ?
+\cs_new_eq:NN \__regex_escape_escaped:N ?
+\cs_new_eq:NN \__regex_escape_raw:N ?
+\cs_new_eq:cN { __regex_escape_ \iow_char:N\\scan_stop: :w } \prg_break:
+\cs_new:cpn { __regex_escape_/ \iow_char:N\\scan_stop: :w }
+ {
+ \msg_expandable_error:nn { regex } { trailing-backslash }
+ \prg_break:
+ }
+\cs_new:cpn { __regex_escape_~:w } { }
+\cs_new:cpe { __regex_escape_/a:w }
+ { \exp_not:N \__regex_escape_raw:N \iow_char:N \^^G }
+\cs_new:cpe { __regex_escape_/t:w }
+ { \exp_not:N \__regex_escape_raw:N \iow_char:N \^^I }
+\cs_new:cpe { __regex_escape_/n:w }
+ { \exp_not:N \__regex_escape_raw:N \iow_char:N \^^J }
+\cs_new:cpe { __regex_escape_/f:w }
+ { \exp_not:N \__regex_escape_raw:N \iow_char:N \^^L }
+\cs_new:cpe { __regex_escape_/r:w }
+ { \exp_not:N \__regex_escape_raw:N \iow_char:N \^^M }
+\cs_new:cpe { __regex_escape_/e:w }
+ { \exp_not:N \__regex_escape_raw:N \iow_char:N \^^[ }
+\cs_new:cpn { __regex_escape_/x:w } \__regex_escape_loop:N
+ {
+ \exp_after:wN \__regex_escape_x_end:w
+ \int_value:w "0 \__regex_escape_x_test:N
+ }
+\cs_new:Npn \__regex_escape_x_end:w #1 ;
+ {
+ \int_compare:nNnTF {#1} > \c_max_char_int
+ {
+ \msg_expandable_error:nnff { regex } { x-overflow }
+ {#1} { \int_to_Hex:n {#1} }
+ }
+ {
+ \exp_last_unbraced:Nf \__regex_escape_raw:N
+ { \char_generate:nn {#1} { 12 } }
+ }
+ }
+\cs_new:Npn \__regex_escape_x_test:N #1
+ {
+ \if_meaning:w \scan_stop: #1
+ \exp_after:wN \use_i:nnn \exp_after:wN ;
+ \fi:
+ \use:n
+ {
+ \if_charcode:w \c_space_token #1
+ \exp_after:wN \__regex_escape_x_test:N
+ \else:
+ \exp_after:wN \__regex_escape_x_testii:N
+ \exp_after:wN #1
+ \fi:
+ }
+ }
+\cs_new:Npn \__regex_escape_x_testii:N #1
+ {
+ \if_charcode:w \c_left_brace_str #1
+ \exp_after:wN \__regex_escape_x_loop:N
+ \else:
+ \__regex_hexadecimal_use:NTF #1
+ { \exp_after:wN \__regex_escape_x:N }
+ { ; \exp_after:wN \__regex_escape_loop:N \exp_after:wN #1 }
+ \fi:
+ }
+\cs_new:Npn \__regex_escape_x:N #1
+ {
+ \if_meaning:w \scan_stop: #1
+ \exp_after:wN \use_i:nnn \exp_after:wN ;
+ \fi:
+ \use:n
+ {
+ \__regex_hexadecimal_use:NTF #1
+ { ; \__regex_escape_loop:N }
+ { ; \__regex_escape_loop:N #1 }
+ }
+ }
+\cs_new:Npn \__regex_escape_x_loop:N #1
+ {
+ \if_meaning:w \scan_stop: #1
+ \exp_after:wN \use_ii:nnn
+ \fi:
+ \use_ii:nn
+ { ; \__regex_escape_x_loop_error:n { } {#1} }
+ {
+ \__regex_hexadecimal_use:NTF #1
+ { \__regex_escape_x_loop:N }
+ {
+ \token_if_eq_charcode:NNTF \c_space_token #1
+ { \__regex_escape_x_loop:N }
+ {
+ ;
+ \exp_after:wN
+ \token_if_eq_charcode:NNTF \c_right_brace_str #1
+ { \__regex_escape_loop:N }
+ { \__regex_escape_x_loop_error:n {#1} }
+ }
+ }
+ }
+ }
+\cs_new:Npn \__regex_escape_x_loop_error:n #1
+ {
+ \msg_expandable_error:nnn { regex } { x-missing-rbrace } {#1}
+ \__regex_escape_loop:N #1
+ }
+\cs_new:Npn \__regex_hexadecimal_use:NTF #1
+ {
+ \if_int_compare:w \c_one_int < "1 \token_to_str:N #1 \exp_stop_f:
+ #1
+ \else:
+ \if_case:w
+ \__regex_int_eval:w \exp_after:wN ` \token_to_str:N #1 - `a \scan_stop:
+ A
+ \or: B
+ \or: C
+ \or: D
+ \or: E
+ \or: F
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_iii:nnn
+ \fi:
+ \fi:
+ \use_i:nn
+ }
+\prg_new_conditional:Npnn \__regex_char_if_special:N #1 { TF }
+ {
+ \if:w
+ T
+ \if_int_compare:w `#1 > `Z \exp_stop_f:
+ \if_int_compare:w `#1 > `z \exp_stop_f:
+ \if_int_compare:w `#1 < \c__regex_ascii_max_int
+ \else: F \fi:
+ \else:
+ \if_int_compare:w `#1 < `a \exp_stop_f:
+ \else: F \fi:
+ \fi:
+ \else:
+ \if_int_compare:w `#1 > `9 \exp_stop_f:
+ \if_int_compare:w `#1 < `A \exp_stop_f:
+ \else: F \fi:
+ \else:
+ \if_int_compare:w `#1 < `0 \exp_stop_f:
+ \if_int_compare:w `#1 < `\ \exp_stop_f:
+ F \fi:
+ \else: F \fi:
+ \fi:
+ \fi:
+ T
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_new_conditional:Npnn \__regex_char_if_alphanumeric:N #1 { TF }
+ {
+ \if:w
+ T
+ \if_int_compare:w `#1 > `Z \exp_stop_f:
+ \if_int_compare:w `#1 > `z \exp_stop_f:
+ F
+ \else:
+ \if_int_compare:w `#1 < `a \exp_stop_f:
+ F \fi:
+ \fi:
+ \else:
+ \if_int_compare:w `#1 > `9 \exp_stop_f:
+ \if_int_compare:w `#1 < `A \exp_stop_f:
+ F \fi:
+ \else:
+ \if_int_compare:w `#1 < `0 \exp_stop_f:
+ F \fi:
+ \fi:
+ \fi:
+ T
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\int_new:N \l__regex_group_level_int
+\int_new:N \l__regex_mode_int
+\int_const:Nn \c__regex_cs_in_class_mode_int { -6 }
+\int_const:Nn \c__regex_cs_mode_int { -2 }
+\int_const:Nn \c__regex_outer_mode_int { 0 }
+\int_const:Nn \c__regex_catcode_mode_int { 2 }
+\int_const:Nn \c__regex_class_mode_int { 3 }
+\int_const:Nn \c__regex_catcode_in_class_mode_int { 6 }
+\int_new:N \l__regex_catcodes_int
+\int_new:N \l__regex_default_catcodes_int
+\bool_new:N \l__regex_catcodes_bool
+\int_const:Nn \c__regex_catcode_C_int { "1 }
+\int_const:Nn \c__regex_catcode_B_int { "4 }
+\int_const:Nn \c__regex_catcode_E_int { "10 }
+\int_const:Nn \c__regex_catcode_M_int { "40 }
+\int_const:Nn \c__regex_catcode_T_int { "100 }
+\int_const:Nn \c__regex_catcode_P_int { "1000 }
+\int_const:Nn \c__regex_catcode_U_int { "4000 }
+\int_const:Nn \c__regex_catcode_D_int { "10000 }
+\int_const:Nn \c__regex_catcode_S_int { "100000 }
+\int_const:Nn \c__regex_catcode_L_int { "400000 }
+\int_const:Nn \c__regex_catcode_O_int { "1000000 }
+\int_const:Nn \c__regex_catcode_A_int { "4000000 }
+\int_const:Nn \c__regex_all_catcodes_int { "5515155 }
+\cs_new_eq:NN \l__regex_internal_regex \c__regex_no_match_regex
+\seq_new:N \l__regex_show_prefix_seq
+\int_new:N \l__regex_show_lines_int
+\cs_new:Npn \__regex_two_if_eq:NNNNTF #1#2#3#4
+ {
+ \if_meaning:w #1 #3
+ \if:w #2 #4
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_ii:nnn
+ \fi:
+ \fi:
+ \use_ii:nn
+ }
+\cs_new_protected:Npn \__regex_get_digits:NTFw #1#2#3#4#5
+ {
+ \__regex_if_raw_digit:NNTF #4 #5
+ { #1 = #5 \__regex_get_digits_loop:nw {#2} }
+ { #3 #4 #5 }
+ }
+\cs_new:Npn \__regex_get_digits_loop:nw #1#2#3
+ {
+ \__regex_if_raw_digit:NNTF #2 #3
+ { #3 \__regex_get_digits_loop:nw {#1} }
+ { \scan_stop: #1 #2 #3 }
+ }
+\cs_new:Npn \__regex_if_raw_digit:NNTF #1#2
+ {
+ \if_meaning:w \__regex_compile_raw:N #1
+ \if_int_compare:w \c_one_int < 1 #2 \exp_stop_f:
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_ii:nnn
+ \fi:
+ \fi:
+ \use_ii:nn
+ }
+\prg_new_conditional:Npnn \__regex_if_in_class: { TF }
+ {
+ \if_int_odd:w \l__regex_mode_int
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \__regex_if_in_cs:TF
+ {
+ \if_int_odd:w \l__regex_mode_int
+ \else:
+ \if_int_compare:w \l__regex_mode_int < \c__regex_outer_mode_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_ii:nnn
+ \fi:
+ \fi:
+ \use_ii:nn
+ }
+\cs_new:Npn \__regex_if_in_class_or_catcode:TF
+ {
+ \if_int_odd:w \l__regex_mode_int
+ \else:
+ \if_int_compare:w \l__regex_mode_int > \c__regex_outer_mode_int
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_iii:nnn
+ \fi:
+ \fi:
+ \use_i:nn
+ }
+\prg_new_conditional:Npnn \__regex_if_within_catcode: { TF }
+ {
+ \if_int_compare:w \l__regex_mode_int > \c__regex_outer_mode_int
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_chk_c_allowed:T
+ {
+ \if_int_compare:w \l__regex_mode_int = \c__regex_outer_mode_int
+ \else:
+ \if_int_compare:w \l__regex_mode_int = \c__regex_class_mode_int
+ \else:
+ \msg_error:nn { regex } { c-bad-mode }
+ \exp_after:wN \use_i:nnn
+ \fi:
+ \fi:
+ \use:n
+ }
+\cs_new_protected:Npn \__regex_mode_quit_c:
+ {
+ \if_int_compare:w \l__regex_mode_int = \c__regex_catcode_mode_int
+ \int_set_eq:NN \l__regex_mode_int \c__regex_outer_mode_int
+ \else:
+ \if_int_compare:w \l__regex_mode_int =
+ \c__regex_catcode_in_class_mode_int
+ \int_set_eq:NN \l__regex_mode_int \c__regex_class_mode_int
+ \fi:
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_compile:w
+ {
+ \group_begin:
+ \tl_build_begin:N \l__regex_build_tl
+ \int_zero:N \l__regex_group_level_int
+ \int_set_eq:NN \l__regex_default_catcodes_int
+ \c__regex_all_catcodes_int
+ \int_set_eq:NN \l__regex_catcodes_int \l__regex_default_catcodes_int
+ \cs_set:Npn \__regex_item_equal:n { \__regex_item_caseful_equal:n }
+ \cs_set:Npn \__regex_item_range:nn { \__regex_item_caseful_range:nn }
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \__regex_branch:n { \if_false: } \fi: }
+ }
+\cs_new_protected:Npn \__regex_compile_end:
+ {
+ \__regex_if_in_class:TF
+ {
+ \msg_error:nn { regex } { missing-rbrack }
+ \use:c { __regex_compile_]: }
+ \prg_do_nothing: \prg_do_nothing:
+ }
+ { }
+ \if_int_compare:w \l__regex_group_level_int > \c_zero_int
+ \msg_error:nne { regex } { missing-rparen }
+ { \int_use:N \l__regex_group_level_int }
+ \prg_replicate:nn
+ \l__regex_group_level_int
+ {
+ \tl_build_put_right:Nn \l__regex_build_tl
+ {
+ \if_false: { \fi: }
+ \if_false: { \fi: } { 1 } { 0 } \c_true_bool
+ }
+ \tl_build_end:N \l__regex_build_tl
+ \exp_args:NNNo
+ \group_end:
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \l__regex_build_tl }
+ }
+ \fi:
+ \tl_build_put_right:Nn \l__regex_build_tl { \if_false: { \fi: } }
+ \tl_build_end:N \l__regex_build_tl
+ \exp_args:NNNe
+ \group_end:
+ \tl_set:Nn \l__regex_internal_regex { \l__regex_build_tl }
+ }
+\cs_new_protected:Npn \__regex_compile:n #1
+ {
+ \__regex_compile:w
+ \__regex_standard_escapechar:
+ \int_set_eq:NN \l__regex_mode_int \c__regex_outer_mode_int
+ \__regex_escape_use:nnnn
+ {
+ \__regex_char_if_special:NTF ##1
+ \__regex_compile_special:N \__regex_compile_raw:N ##1
+ }
+ {
+ \__regex_char_if_alphanumeric:NTF ##1
+ \__regex_compile_escaped:N \__regex_compile_raw:N ##1
+ }
+ { \__regex_compile_raw:N ##1 }
+ { #1 }
+ \prg_do_nothing: \prg_do_nothing:
+ \prg_do_nothing: \prg_do_nothing:
+ \int_compare:nNnT \l__regex_mode_int = \c__regex_catcode_mode_int
+ { \msg_error:nn { regex } { c-trailing } }
+ \int_compare:nNnT \l__regex_mode_int < \c__regex_outer_mode_int
+ {
+ \msg_error:nn { regex } { c-missing-rbrace }
+ \__regex_compile_end_cs:
+ \prg_do_nothing: \prg_do_nothing:
+ \prg_do_nothing: \prg_do_nothing:
+ }
+ \__regex_compile_end:
+ }
+\cs_new_protected:Npn \__regex_compile_use:n #1
+ {
+ \tl_if_single_token:nT {#1}
+ {
+ \exp_after:wN \__regex_compile_use_aux:w
+ \token_to_meaning:N #1 ~ \q__regex_nil
+ }
+ \__regex_compile:n {#1} \l__regex_internal_regex
+ }
+\cs_new_protected:Npn \__regex_compile_use_aux:w #1 ~ #2 \q__regex_nil
+ {
+ \str_if_eq:nnT { #1 ~ } { macro:->\__regex_branch:n }
+ { \use_ii:nnn }
+ }
+\cs_new_protected:Npn \__regex_compile_special:N #1
+ {
+ \cs_if_exist_use:cF { __regex_compile_#1: }
+ { \__regex_compile_raw:N #1 }
+ }
+\cs_new_protected:Npn \__regex_compile_escaped:N #1
+ {
+ \cs_if_exist_use:cF { __regex_compile_/#1: }
+ { \__regex_compile_raw:N #1 }
+ }
+\cs_new_protected:Npn \__regex_compile_one:n #1
+ {
+ \__regex_mode_quit_c:
+ \__regex_if_in_class:TF { }
+ {
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \__regex_class:NnnnN \c_true_bool { \if_false: } \fi: }
+ }
+ \tl_build_put_right:Ne \l__regex_build_tl
+ {
+ \if_int_compare:w \l__regex_catcodes_int <
+ \c__regex_all_catcodes_int
+ \__regex_item_catcode:nT { \int_use:N \l__regex_catcodes_int }
+ { \exp_not:N \exp_not:n {#1} }
+ \else:
+ \exp_not:N \exp_not:n {#1}
+ \fi:
+ }
+ \int_set_eq:NN \l__regex_catcodes_int \l__regex_default_catcodes_int
+ \__regex_if_in_class:TF { } { \__regex_compile_quantifier:w }
+ }
+\cs_new_protected:Npn \__regex_compile_abort_tokens:n #1
+ {
+ \use:e
+ {
+ \exp_args:No \tl_map_function:nN { \tl_to_str:n {#1} }
+ \__regex_compile_raw:N
+ }
+ }
+\cs_generate_variant:Nn \__regex_compile_abort_tokens:n { e }
+\cs_new_protected:Npn \__regex_compile_if_quantifier:TFw #1#2#3#4
+ {
+ \token_if_eq_meaning:NNTF #3 \__regex_compile_special:N
+ { \cs_if_exist:cTF { __regex_compile_quantifier_#4:w } }
+ { \use_ii:nn }
+ {#1} {#2} #3 #4
+ }
+\cs_new_protected:Npn \__regex_compile_quantifier:w #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \__regex_compile_special:N
+ {
+ \cs_if_exist_use:cF { __regex_compile_quantifier_#2:w }
+ { \__regex_compile_quantifier_none: #1 #2 }
+ }
+ { \__regex_compile_quantifier_none: #1 #2 }
+ }
+\cs_new_protected:Npn \__regex_compile_quantifier_none:
+ {
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \if_false: { \fi: } { 1 } { 0 } \c_false_bool }
+ }
+\cs_new_protected:Npn \__regex_compile_quantifier_abort:eNN #1#2#3
+ {
+ \__regex_compile_quantifier_none:
+ \msg_warning:nnee { regex } { invalid-quantifier } {#1} {#3}
+ \__regex_compile_abort_tokens:e {#1}
+ #2 #3
+ }
+\cs_new_protected:Npn \__regex_compile_quantifier_lazyness:nnNN #1#2#3#4
+ {
+ \__regex_two_if_eq:NNNNTF #3 #4 \__regex_compile_special:N ?
+ {
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \if_false: { \fi: } { #1 } { #2 } \c_true_bool }
+ }
+ {
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \if_false: { \fi: } { #1 } { #2 } \c_false_bool }
+ #3 #4
+ }
+ }
+\cs_new_protected:cpn { __regex_compile_quantifier_?:w }
+ { \__regex_compile_quantifier_lazyness:nnNN { 0 } { 1 } }
+\cs_new_protected:cpn { __regex_compile_quantifier_*:w }
+ { \__regex_compile_quantifier_lazyness:nnNN { 0 } { -1 } }
+\cs_new_protected:cpn { __regex_compile_quantifier_+:w }
+ { \__regex_compile_quantifier_lazyness:nnNN { 1 } { -1 } }
+\cs_new_protected:cpn { __regex_compile_quantifier_ \c_left_brace_str :w }
+ {
+ \__regex_get_digits:NTFw \l__regex_internal_a_int
+ { \__regex_compile_quantifier_braced_auxi:w }
+ { \__regex_compile_quantifier_abort:eNN { \c_left_brace_str } }
+ }
+\cs_new_protected:Npn \__regex_compile_quantifier_braced_auxi:w #1#2
+ {
+ \str_case_e:nnF { #1 #2 }
+ {
+ { \__regex_compile_special:N \c_right_brace_str }
+ {
+ \exp_args:No \__regex_compile_quantifier_lazyness:nnNN
+ { \int_use:N \l__regex_internal_a_int } 0
+ }
+ { \__regex_compile_special:N , }
+ {
+ \__regex_get_digits:NTFw \l__regex_internal_b_int
+ { \__regex_compile_quantifier_braced_auxiii:w }
+ { \__regex_compile_quantifier_braced_auxii:w }
+ }
+ }
+ {
+ \__regex_compile_quantifier_abort:eNN
+ { \c_left_brace_str \int_use:N \l__regex_internal_a_int }
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \__regex_compile_quantifier_braced_auxii:w #1#2
+ {
+ \__regex_two_if_eq:NNNNTF #1 #2 \__regex_compile_special:N \c_right_brace_str
+ {
+ \exp_args:No \__regex_compile_quantifier_lazyness:nnNN
+ { \int_use:N \l__regex_internal_a_int } { -1 }
+ }
+ {
+ \__regex_compile_quantifier_abort:eNN
+ { \c_left_brace_str \int_use:N \l__regex_internal_a_int , }
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \__regex_compile_quantifier_braced_auxiii:w #1#2
+ {
+ \__regex_two_if_eq:NNNNTF #1 #2 \__regex_compile_special:N \c_right_brace_str
+ {
+ \if_int_compare:w \l__regex_internal_a_int >
+ \l__regex_internal_b_int
+ \msg_error:nnee { regex } { backwards-quantifier }
+ { \int_use:N \l__regex_internal_a_int }
+ { \int_use:N \l__regex_internal_b_int }
+ \int_zero:N \l__regex_internal_b_int
+ \else:
+ \int_sub:Nn \l__regex_internal_b_int \l__regex_internal_a_int
+ \fi:
+ \exp_args:Noo \__regex_compile_quantifier_lazyness:nnNN
+ { \int_use:N \l__regex_internal_a_int }
+ { \int_use:N \l__regex_internal_b_int }
+ }
+ {
+ \__regex_compile_quantifier_abort:eNN
+ {
+ \c_left_brace_str
+ \int_use:N \l__regex_internal_a_int ,
+ \int_use:N \l__regex_internal_b_int
+ }
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \__regex_compile_raw_error:N #1
+ {
+ \msg_error:nne { regex } { bad-escape } {#1}
+ \__regex_compile_raw:N #1
+ }
+\cs_new_protected:Npn \__regex_compile_raw:N #1#2#3
+ {
+ \__regex_if_in_class:TF
+ {
+ \__regex_two_if_eq:NNNNTF #2 #3 \__regex_compile_special:N -
+ { \__regex_compile_range:Nw #1 }
+ {
+ \__regex_compile_one:n
+ { \__regex_item_equal:n { \int_value:w `#1 } }
+ #2 #3
+ }
+ }
+ {
+ \__regex_compile_one:n
+ { \__regex_item_equal:n { \int_value:w `#1 } }
+ #2 #3
+ }
+ }
+\cs_new_protected:Npn \__regex_if_end_range:NNTF #1#2
+ {
+ \if_meaning:w \__regex_compile_raw:N #1
+ \else:
+ \if_meaning:w \__regex_compile_special:N #1
+ \if_charcode:w ] #2
+ \use_i:nn
+ \fi:
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_iii:nnn
+ \fi:
+ \fi:
+ \use_i:nn
+ }
+\cs_new_protected:Npn \__regex_compile_range:Nw #1#2#3
+ {
+ \__regex_if_end_range:NNTF #2 #3
+ {
+ \if_int_compare:w `#1 > `#3 \exp_stop_f:
+ \msg_error:nnee { regex } { range-backwards } {#1} {#3}
+ \else:
+ \tl_build_put_right:Ne \l__regex_build_tl
+ {
+ \if_int_compare:w `#1 = `#3 \exp_stop_f:
+ \__regex_item_equal:n
+ \else:
+ \__regex_item_range:nn { \int_value:w `#1 }
+ \fi:
+ { \int_value:w `#3 }
+ }
+ \fi:
+ }
+ {
+ \msg_warning:nnee { regex } { range-missing-end }
+ {#1} { \c_backslash_str #3 }
+ \tl_build_put_right:Ne \l__regex_build_tl
+ {
+ \__regex_item_equal:n { \int_value:w `#1 \exp_stop_f: }
+ \__regex_item_equal:n { \int_value:w `- \exp_stop_f: }
+ }
+ #2#3
+ }
+ }
+\cs_new_protected:cpe { __regex_compile_.: }
+ {
+ \exp_not:N \__regex_if_in_class:TF
+ { \__regex_compile_raw:N . }
+ { \__regex_compile_one:n \exp_not:c { __regex_prop_.: } }
+ }
+\cs_new_protected:cpn { __regex_prop_.: }
+ {
+ \if_int_compare:w \l__regex_curr_char_int > - 2 \exp_stop_f:
+ \exp_after:wN \__regex_break_true:w
+ \fi:
+ }
+\cs_set_protected:Npn \__regex_tmp:w #1#2
+ {
+ \cs_new_protected:cpe { __regex_compile_/#1: }
+ { \__regex_compile_one:n \exp_not:c { __regex_prop_#1: } }
+ \cs_new_protected:cpe { __regex_compile_/#2: }
+ {
+ \__regex_compile_one:n
+ { \__regex_item_reverse:n { \exp_not:c { __regex_prop_#1: } } }
+ }
+ }
+\__regex_tmp:w d D
+\__regex_tmp:w h H
+\__regex_tmp:w s S
+\__regex_tmp:w v V
+\__regex_tmp:w w W
+\cs_new_protected:cpn { __regex_compile_/N: }
+ { \__regex_compile_one:n \__regex_prop_N: }
+\cs_new_protected:Npn \__regex_compile_anchor_letter:NNN #1#2#3
+ {
+ \__regex_if_in_class_or_catcode:TF { \__regex_compile_raw_error:N #1 }
+ {
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \__regex_assertion:Nn #2 {#3} }
+ }
+ }
+\cs_new_protected:cpn { __regex_compile_/A: }
+ { \__regex_compile_anchor_letter:NNN A \c_true_bool \__regex_A_test: }
+\cs_new_protected:cpn { __regex_compile_/G: }
+ { \__regex_compile_anchor_letter:NNN G \c_true_bool \__regex_G_test: }
+\cs_new_protected:cpn { __regex_compile_/Z: }
+ { \__regex_compile_anchor_letter:NNN Z \c_true_bool \__regex_Z_test: }
+\cs_new_protected:cpn { __regex_compile_/z: }
+ { \__regex_compile_anchor_letter:NNN z \c_true_bool \__regex_Z_test: }
+\cs_new_protected:cpn { __regex_compile_/b: }
+ { \__regex_compile_anchor_letter:NNN b \c_true_bool \__regex_b_test: }
+\cs_new_protected:cpn { __regex_compile_/B: }
+ { \__regex_compile_anchor_letter:NNN B \c_false_bool \__regex_b_test: }
+\cs_set_protected:Npn \__regex_tmp:w #1#2
+ {
+ \cs_new_protected:cpn { __regex_compile_#1: }
+ {
+ \__regex_if_in_class_or_catcode:TF { \__regex_compile_raw:N #1 }
+ {
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \__regex_assertion:Nn \c_true_bool {#2} }
+ }
+ }
+ }
+\exp_args:Ne \__regex_tmp:w { \iow_char:N \^ } { \__regex_A_test: }
+\exp_args:Ne \__regex_tmp:w { \iow_char:N \$ } { \__regex_Z_test: }
+\cs_new_protected:cpn { __regex_compile_]: }
+ {
+ \__regex_if_in_class:TF
+ {
+ \if_int_compare:w \l__regex_mode_int >
+ \c__regex_catcode_in_class_mode_int
+ \tl_build_put_right:Nn \l__regex_build_tl { \if_false: { \fi: } }
+ \fi:
+ \tex_advance:D \l__regex_mode_int - 15 \exp_stop_f:
+ \tex_divide:D \l__regex_mode_int 13 \exp_stop_f:
+ \if_int_odd:w \l__regex_mode_int \else:
+ \exp_after:wN \__regex_compile_quantifier:w
+ \fi:
+ }
+ { \__regex_compile_raw:N ] }
+ }
+\cs_new_protected:cpn { __regex_compile_[: }
+ {
+ \__regex_if_in_class:TF
+ { \__regex_compile_class_posix_test:w }
+ {
+ \__regex_if_within_catcode:TF
+ {
+ \exp_after:wN \__regex_compile_class_catcode:w
+ \int_use:N \l__regex_catcodes_int ;
+ }
+ { \__regex_compile_class_normal:w }
+ }
+ }
+\cs_new_protected:Npn \__regex_compile_class_normal:w
+ {
+ \__regex_compile_class:TFNN
+ { \__regex_class:NnnnN \c_true_bool }
+ { \__regex_class:NnnnN \c_false_bool }
+ }
+\cs_new_protected:Npn \__regex_compile_class_catcode:w #1;
+ {
+ \if_int_compare:w \l__regex_mode_int = \c__regex_catcode_mode_int
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \__regex_class:NnnnN \c_true_bool { \if_false: } \fi: }
+ \fi:
+ \int_set_eq:NN \l__regex_catcodes_int \l__regex_default_catcodes_int
+ \__regex_compile_class:TFNN
+ { \__regex_item_catcode:nT {#1} }
+ { \__regex_item_catcode_reverse:nT {#1} }
+ }
+\cs_new_protected:Npn \__regex_compile_class:TFNN #1#2#3#4
+ {
+ \l__regex_mode_int = \int_value:w \l__regex_mode_int 3 \exp_stop_f:
+ \__regex_two_if_eq:NNNNTF #3 #4 \__regex_compile_special:N ^
+ {
+ \tl_build_put_right:Nn \l__regex_build_tl { #2 { \if_false: } \fi: }
+ \__regex_compile_class:NN
+ }
+ {
+ \tl_build_put_right:Nn \l__regex_build_tl { #1 { \if_false: } \fi: }
+ \__regex_compile_class:NN #3 #4
+ }
+ }
+\cs_new_protected:Npn \__regex_compile_class:NN #1#2
+ {
+ \token_if_eq_charcode:NNTF #2 ]
+ { \__regex_compile_raw:N #2 }
+ { #1 #2 }
+ }
+\cs_new_protected:Npn \__regex_compile_class_posix_test:w #1#2
+ {
+ \token_if_eq_meaning:NNT \__regex_compile_special:N #1
+ {
+ \str_case:nn { #2 }
+ {
+ : { \__regex_compile_class_posix:NNNNw }
+ = {
+ \msg_warning:nne { regex }
+ { posix-unsupported } { = }
+ }
+ . {
+ \msg_warning:nne { regex }
+ { posix-unsupported } { . }
+ }
+ }
+ }
+ \__regex_compile_raw:N [ #1 #2
+ }
+\cs_new_protected:Npn \__regex_compile_class_posix:NNNNw #1#2#3#4#5#6
+ {
+ \__regex_two_if_eq:NNNNTF #5 #6 \__regex_compile_special:N ^
+ {
+ \bool_set_false:N \l__regex_internal_bool
+ \__kernel_tl_set:Nx \l__regex_internal_a_tl { \if_false: } \fi:
+ \__regex_compile_class_posix_loop:w
+ }
+ {
+ \bool_set_true:N \l__regex_internal_bool
+ \__kernel_tl_set:Nx \l__regex_internal_a_tl { \if_false: } \fi:
+ \__regex_compile_class_posix_loop:w #5 #6
+ }
+ }
+\cs_new:Npn \__regex_compile_class_posix_loop:w #1#2
+ {
+ \token_if_eq_meaning:NNTF \__regex_compile_raw:N #1
+ { #2 \__regex_compile_class_posix_loop:w }
+ { \if_false: { \fi: } \__regex_compile_class_posix_end:w #1 #2 }
+ }
+\cs_new_protected:Npn \__regex_compile_class_posix_end:w #1#2#3#4
+ {
+ \__regex_two_if_eq:NNNNTF #1 #2 \__regex_compile_special:N :
+ { \__regex_two_if_eq:NNNNTF #3 #4 \__regex_compile_special:N ] }
+ { \use_ii:nn }
+ {
+ \cs_if_exist:cTF { __regex_posix_ \l__regex_internal_a_tl : }
+ {
+ \__regex_compile_one:n
+ {
+ \bool_if:NTF \l__regex_internal_bool \use:n \__regex_item_reverse:n
+ { \exp_not:c { __regex_posix_ \l__regex_internal_a_tl : } }
+ }
+ }
+ {
+ \msg_warning:nne { regex } { posix-unknown }
+ { \l__regex_internal_a_tl }
+ \__regex_compile_abort_tokens:e
+ {
+ [: \bool_if:NF \l__regex_internal_bool { ^ }
+ \l__regex_internal_a_tl :]
+ }
+ }
+ }
+ {
+ \msg_error:nnee { regex } { posix-missing-close }
+ { [: \l__regex_internal_a_tl } { #2 #4 }
+ \__regex_compile_abort_tokens:e { [: \l__regex_internal_a_tl }
+ #1 #2 #3 #4
+ }
+ }
+\cs_new_protected:Npn \__regex_compile_group_begin:N #1
+ {
+ \tl_build_put_right:Nn \l__regex_build_tl { #1 { \if_false: } \fi: }
+ \__regex_mode_quit_c:
+ \group_begin:
+ \tl_build_begin:N \l__regex_build_tl
+ \int_set_eq:NN \l__regex_default_catcodes_int \l__regex_catcodes_int
+ \int_incr:N \l__regex_group_level_int
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \__regex_branch:n { \if_false: } \fi: }
+ }
+\cs_new_protected:Npn \__regex_compile_group_end:
+ {
+ \if_int_compare:w \l__regex_group_level_int > \c_zero_int
+ \tl_build_put_right:Nn \l__regex_build_tl { \if_false: { \fi: } }
+ \tl_build_end:N \l__regex_build_tl
+ \exp_args:NNNe
+ \group_end:
+ \tl_build_put_right:Nn \l__regex_build_tl { \l__regex_build_tl }
+ \int_set_eq:NN \l__regex_catcodes_int \l__regex_default_catcodes_int
+ \exp_after:wN \__regex_compile_quantifier:w
+ \else:
+ \msg_warning:nn { regex } { extra-rparen }
+ \exp_after:wN \__regex_compile_raw:N \exp_after:wN )
+ \fi:
+ }
+\cs_new_protected:cpn { __regex_compile_(: }
+ {
+ \__regex_if_in_class:TF { \__regex_compile_raw:N ( }
+ {
+ \if_int_compare:w \l__regex_mode_int =
+ \c__regex_catcode_in_class_mode_int
+ \msg_error:nn { regex } { c-lparen-in-class }
+ \exp_after:wN \__regex_compile_raw:N \exp_after:wN (
+ \else:
+ \exp_after:wN \__regex_compile_lparen:w
+ \fi:
+ }
+ }
+\cs_new_protected:Npn \__regex_compile_lparen:w #1#2#3#4
+ {
+ \__regex_two_if_eq:NNNNTF #1 #2 \__regex_compile_special:N ?
+ {
+ \cs_if_exist_use:cF
+ { __regex_compile_special_group_\token_to_str:N #4 :w }
+ {
+ \msg_warning:nne { regex } { special-group-unknown }
+ { (? #4 }
+ \__regex_compile_group_begin:N \__regex_group:nnnN
+ \__regex_compile_raw:N ? #3 #4
+ }
+ }
+ {
+ \__regex_compile_group_begin:N \__regex_group:nnnN
+ #1 #2 #3 #4
+ }
+ }
+\cs_new_protected:cpn { __regex_compile_|: }
+ {
+ \__regex_if_in_class:TF { \__regex_compile_raw:N | }
+ {
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \if_false: { \fi: } \__regex_branch:n { \if_false: } \fi: }
+ }
+ }
+\cs_new_protected:cpn { __regex_compile_): }
+ {
+ \__regex_if_in_class:TF { \__regex_compile_raw:N ) }
+ { \__regex_compile_group_end: }
+ }
+\cs_new_protected:cpn { __regex_compile_special_group_::w }
+ { \__regex_compile_group_begin:N \__regex_group_no_capture:nnnN }
+\cs_new_protected:cpn { __regex_compile_special_group_|:w }
+ { \__regex_compile_group_begin:N \__regex_group_resetting:nnnN }
+\cs_new_protected:Npn \__regex_compile_special_group_i:w #1#2
+ {
+ \__regex_two_if_eq:NNNNTF #1 #2 \__regex_compile_special:N )
+ {
+ \cs_set:Npn \__regex_item_equal:n
+ { \__regex_item_caseless_equal:n }
+ \cs_set:Npn \__regex_item_range:nn
+ { \__regex_item_caseless_range:nn }
+ }
+ {
+ \msg_warning:nne { regex } { unknown-option } { (?i #2 }
+ \__regex_compile_raw:N (
+ \__regex_compile_raw:N ?
+ \__regex_compile_raw:N i
+ #1 #2
+ }
+ }
+\cs_new_protected:cpn { __regex_compile_special_group_-:w } #1#2#3#4
+ {
+ \__regex_two_if_eq:NNNNTF #1 #2 \__regex_compile_raw:N i
+ { \__regex_two_if_eq:NNNNTF #3 #4 \__regex_compile_special:N ) }
+ { \use_ii:nn }
+ {
+ \cs_set:Npn \__regex_item_equal:n
+ { \__regex_item_caseful_equal:n }
+ \cs_set:Npn \__regex_item_range:nn
+ { \__regex_item_caseful_range:nn }
+ }
+ {
+ \msg_warning:nne { regex } { unknown-option } { (?-#2#4 }
+ \__regex_compile_raw:N (
+ \__regex_compile_raw:N ?
+ \__regex_compile_raw:N -
+ #1 #2 #3 #4
+ }
+ }
+\cs_new_protected:cpn { __regex_compile_/c: }
+ { \__regex_chk_c_allowed:T { \__regex_compile_c_test:NN } }
+\cs_new_protected:Npn \__regex_compile_c_test:NN #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \__regex_compile_raw:N
+ {
+ \int_if_exist:cTF { c__regex_catcode_#2_int }
+ {
+ \int_set_eq:Nc \l__regex_catcodes_int
+ { c__regex_catcode_#2_int }
+ \l__regex_mode_int
+ = \if_case:w \l__regex_mode_int
+ \c__regex_catcode_mode_int
+ \else:
+ \c__regex_catcode_in_class_mode_int
+ \fi:
+ \token_if_eq_charcode:NNT C #2 { \__regex_compile_c_C:NN }
+ }
+ }
+ { \cs_if_exist_use:cF { __regex_compile_c_#2:w } }
+ {
+ \msg_error:nne { regex } { c-missing-category } {#2}
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \__regex_compile_c_C:NN #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \__regex_compile_special:N
+ {
+ \token_if_eq_charcode:NNTF #2 .
+ { \use_none:n }
+ { \token_if_eq_charcode:NNF #2 ( } % )
+ }
+ { \use:n }
+ { \msg_error:nnn { regex } { c-C-invalid } {#2} }
+ #1 #2
+ }
+\cs_new_protected:cpn { __regex_compile_c_[:w } #1#2
+ {
+ \l__regex_mode_int
+ = \if_case:w \l__regex_mode_int
+ \c__regex_catcode_mode_int
+ \else:
+ \c__regex_catcode_in_class_mode_int
+ \fi:
+ \int_zero:N \l__regex_catcodes_int
+ \__regex_two_if_eq:NNNNTF #1 #2 \__regex_compile_special:N ^
+ {
+ \bool_set_false:N \l__regex_catcodes_bool
+ \__regex_compile_c_lbrack_loop:NN
+ }
+ {
+ \bool_set_true:N \l__regex_catcodes_bool
+ \__regex_compile_c_lbrack_loop:NN
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \__regex_compile_c_lbrack_loop:NN #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \__regex_compile_raw:N
+ {
+ \int_if_exist:cTF { c__regex_catcode_#2_int }
+ {
+ \exp_args:Nc \__regex_compile_c_lbrack_add:N
+ { c__regex_catcode_#2_int }
+ \__regex_compile_c_lbrack_loop:NN
+ }
+ }
+ {
+ \token_if_eq_charcode:NNTF #2 ]
+ { \__regex_compile_c_lbrack_end: }
+ }
+ {
+ \msg_error:nne { regex } { c-missing-rbrack } {#2}
+ \__regex_compile_c_lbrack_end:
+ #1 #2
+ }
+ }
+\cs_new_protected:Npn \__regex_compile_c_lbrack_add:N #1
+ {
+ \if_int_odd:w \__regex_int_eval:w \l__regex_catcodes_int / #1 \scan_stop:
+ \else:
+ \int_add:Nn \l__regex_catcodes_int {#1}
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_compile_c_lbrack_end:
+ {
+ \if_meaning:w \c_false_bool \l__regex_catcodes_bool
+ \int_set:Nn \l__regex_catcodes_int
+ { \c__regex_all_catcodes_int - \l__regex_catcodes_int }
+ \fi:
+ }
+\cs_new_protected:cpn { __regex_compile_c_ \c_left_brace_str :w }
+ {
+ \__regex_compile:w
+ \__regex_disable_submatches:
+ \l__regex_mode_int
+ = \if_case:w \l__regex_mode_int
+ \c__regex_cs_mode_int
+ \else:
+ \c__regex_cs_in_class_mode_int
+ \fi:
+ }
+\cs_new_protected:cpn { __regex_compile_ \c_left_brace_str : }
+ {
+ \__regex_if_in_cs:TF
+ { \msg_error:nnn { regex } { cu-lbrace } { c } }
+ { \exp_after:wN \__regex_compile_raw:N \c_left_brace_str }
+ }
+\flag_new:N \l__regex_cs_flag
+\cs_new_protected:cpn { __regex_compile_ \c_right_brace_str : }
+ {
+ \__regex_if_in_cs:TF
+ { \__regex_compile_end_cs: }
+ { \exp_after:wN \__regex_compile_raw:N \c_right_brace_str }
+ }
+\cs_new_protected:Npn \__regex_compile_end_cs:
+ {
+ \__regex_compile_end:
+ \flag_clear:N \l__regex_cs_flag
+ \__kernel_tl_set:Nx \l__regex_internal_a_tl
+ {
+ \exp_after:wN \__regex_compile_cs_aux:Nn \l__regex_internal_regex
+ \q__regex_nil \q__regex_nil \q__regex_recursion_stop
+ }
+ \exp_args:Ne \__regex_compile_one:n
+ {
+ \flag_if_raised:NTF \l__regex_cs_flag
+ { \__regex_item_cs:n { \exp_not:o \l__regex_internal_regex } }
+ {
+ \__regex_item_exact_cs:n
+ { \tl_tail:N \l__regex_internal_a_tl }
+ }
+ }
+ }
+\cs_new:Npn \__regex_compile_cs_aux:Nn #1#2
+ {
+ \cs_if_eq:NNTF #1 \__regex_branch:n
+ {
+ \scan_stop:
+ \__regex_compile_cs_aux:NNnnnN #2
+ \q__regex_nil \q__regex_nil \q__regex_nil
+ \q__regex_nil \q__regex_nil \q__regex_nil \q__regex_recursion_stop
+ \__regex_compile_cs_aux:Nn
+ }
+ {
+ \__regex_quark_if_nil:NF #1 { \flag_ensure_raised:N \l__regex_cs_flag }
+ \__regex_use_none_delimit_by_q_recursion_stop:w
+ }
+ }
+\cs_new:Npn \__regex_compile_cs_aux:NNnnnN #1#2#3#4#5#6
+ {
+ \bool_lazy_all:nTF
+ {
+ { \cs_if_eq_p:NN #1 \__regex_class:NnnnN }
+ {#2}
+ { \tl_if_head_eq_meaning_p:nN {#3} \__regex_item_caseful_equal:n }
+ { \int_compare_p:nNn { \tl_count:n {#3} } = { 2 } }
+ { \int_compare_p:nNn {#5} = \c_zero_int }
+ }
+ {
+ \prg_replicate:nn {#4}
+ { \char_generate:nn { \use_ii:nn #3 } {12} }
+ \__regex_compile_cs_aux:NNnnnN
+ }
+ {
+ \__regex_quark_if_nil:NF #1
+ {
+ \flag_ensure_raised:N \l__regex_cs_flag
+ \__regex_use_i_delimit_by_q_recursion_stop:nw
+ }
+ \__regex_use_none_delimit_by_q_recursion_stop:w
+ }
+ }
+\cs_new_protected:cpn { __regex_compile_/u: } #1#2
+ {
+ \__regex_if_in_class_or_catcode:TF
+ { \__regex_compile_raw_error:N u #1 #2 }
+ {
+ \__regex_two_if_eq:NNNNTF #1 #2 \__regex_compile_raw:N r
+ { \__regex_compile_u_brace:NNN \__regex_compile_ur_end: }
+ { \__regex_compile_u_brace:NNN \__regex_compile_u_end: #1 #2 }
+ }
+ }
+\cs_new:Npn \__regex_compile_u_brace:NNN #1#2#3
+ {
+ \__regex_two_if_eq:NNNNTF #2 #3 \__regex_compile_special:N \c_left_brace_str
+ {
+ \tl_set:Nn \l__regex_internal_b_tl {#1}
+ \__kernel_tl_set:Nx \l__regex_internal_a_tl { \if_false: } \fi:
+ \__regex_compile_u_loop:NN
+ }
+ {
+ \msg_error:nn { regex } { u-missing-lbrace }
+ \token_if_eq_meaning:NNTF #1 \__regex_compile_ur_end:
+ { \__regex_compile_raw:N u \__regex_compile_raw:N r }
+ { \__regex_compile_raw:N u }
+ #2 #3
+ }
+ }
+\cs_new:Npn \__regex_compile_u_loop:NN #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \__regex_compile_raw:N
+ { #2 \__regex_compile_u_loop:NN }
+ {
+ \token_if_eq_meaning:NNTF #1 \__regex_compile_special:N
+ {
+ \exp_after:wN \token_if_eq_charcode:NNTF \c_right_brace_str #2
+ { \if_false: { \fi: } \l__regex_internal_b_tl }
+ {
+ \if_charcode:w \c_left_brace_str #2
+ \msg_expandable_error:nnn { regex } { cu-lbrace } { u }
+ \else:
+ #2
+ \fi:
+ \__regex_compile_u_loop:NN
+ }
+ }
+ {
+ \if_false: { \fi: }
+ \msg_error:nne { regex } { u-missing-rbrace } {#2}
+ \l__regex_internal_b_tl
+ #1 #2
+ }
+ }
+ }
+\cs_new_protected:Npn \__regex_compile_ur_end:
+ {
+ \group_begin:
+ \cs_set:Npn \__regex_group:nnnN { \__regex_group_no_capture:nnnN }
+ \cs_set:Npn \__regex_group_resetting:nnnN { \__regex_group_no_capture:nnnN }
+ \exp_args:NNe
+ \group_end:
+ \__regex_compile_ur:n { \use:c { \l__regex_internal_a_tl } }
+ }
+\cs_new_protected:Npn \__regex_compile_ur:n #1
+ {
+ \tl_if_empty:oTF { \__regex_compile_ur_aux:w #1 {} ? ? \q__regex_nil }
+ { \__regex_compile_if_quantifier:TFw }
+ { \use_i:nn }
+ {
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \__regex_group_no_capture:nnnN { \if_false: } \fi: #1 }
+ \__regex_compile_quantifier:w
+ }
+ { \tl_build_put_right:Nn \l__regex_build_tl { \use_ii:nn #1 } }
+ }
+\cs_new:Npn \__regex_compile_ur_aux:w \__regex_branch:n #1#2#3 \q__regex_nil {#2}
+\cs_new_protected:Npn \__regex_compile_u_end:
+ {
+ \__regex_compile_if_quantifier:TFw
+ {
+ \tl_build_put_right:Nn \l__regex_build_tl
+ {
+ \__regex_group_no_capture:nnnN { \if_false: } \fi:
+ \__regex_branch:n { \if_false: } \fi:
+ }
+ \__regex_compile_u_payload:
+ \tl_build_put_right:Nn \l__regex_build_tl { \if_false: { \fi: } }
+ \__regex_compile_quantifier:w
+ }
+ { \__regex_compile_u_payload: }
+ }
+\cs_new_protected:Npn \__regex_compile_u_payload:
+ {
+ \tl_set:Nv \l__regex_internal_a_tl { \l__regex_internal_a_tl }
+ \if_int_compare:w \l__regex_mode_int = \c__regex_outer_mode_int
+ \__regex_compile_u_not_cs:
+ \else:
+ \__regex_compile_u_in_cs:
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_compile_u_in_cs:
+ {
+ \__kernel_tl_gset:Nx \g__regex_internal_tl
+ {
+ \exp_args:No \__kernel_str_to_other_fast:n
+ { \l__regex_internal_a_tl }
+ }
+ \tl_build_put_right:Ne \l__regex_build_tl
+ {
+ \tl_map_function:NN \g__regex_internal_tl
+ \__regex_compile_u_in_cs_aux:n
+ }
+ }
+\cs_new:Npn \__regex_compile_u_in_cs_aux:n #1
+ {
+ \__regex_class:NnnnN \c_true_bool
+ { \__regex_item_caseful_equal:n { \int_value:w `#1 } }
+ { 1 } { 0 } \c_false_bool
+ }
+\cs_new_protected:Npn \__regex_compile_u_not_cs:
+ {
+ \tl_analysis_map_inline:Nn \l__regex_internal_a_tl
+ {
+ \tl_build_put_right:Ne \l__regex_build_tl
+ {
+ \__regex_class:NnnnN \c_true_bool
+ {
+ \if_int_compare:w "##3 = \c_zero_int
+ \__regex_item_exact_cs:n
+ { \exp_after:wN \cs_to_str:N ##1 }
+ \else:
+ \__regex_item_exact:nn { \int_value:w "##3 } { ##2 }
+ \fi:
+ }
+ { 1 } { 0 } \c_false_bool
+ }
+ }
+ }
+\cs_new_protected:cpn { __regex_compile_/K: }
+ {
+ \int_compare:nNnTF \l__regex_mode_int = \c__regex_outer_mode_int
+ { \tl_build_put_right:Nn \l__regex_build_tl { \__regex_command_K: } }
+ { \__regex_compile_raw_error:N K }
+ }
+\cs_new:Npn \__regex_clean_bool:n #1
+ {
+ \tl_if_single:nTF {#1}
+ { \bool_if:NTF #1 \c_true_bool \c_false_bool }
+ { \c_true_bool }
+ }
+\cs_new:Npn \__regex_clean_int:n #1
+ {
+ \tl_if_head_eq_meaning:nNTF {#1} -
+ { - \exp_args:No \__regex_clean_int:n { \use_none:n #1 } }
+ { \int_eval:n { 0 \str_map_function:nN {#1} \__regex_clean_int_aux:N } }
+ }
+\cs_new:Npn \__regex_clean_int_aux:N #1
+ {
+ \if_int_compare:w \c_one_int < 1 #1 ~
+ #1
+ \else:
+ \str_map_break:n
+ \fi:
+ }
+\cs_new:Npn \__regex_clean_regex:n #1
+ {
+ \__regex_clean_regex_loop:w #1
+ \__regex_branch:n { \q_recursion_tail } \q_recursion_stop
+ }
+\cs_new:Npn \__regex_clean_regex_loop:w #1 \__regex_branch:n #2
+ {
+ \quark_if_recursion_tail_stop:n {#2}
+ \__regex_branch:n { \__regex_clean_branch:n {#2} }
+ \__regex_clean_regex_loop:w
+ }
+\cs_new:Npn \__regex_clean_branch:n #1
+ {
+ \__regex_clean_branch_loop:n #1
+ ? ? ? ? ? ? \prg_break_point:
+ }
+\cs_new:Npn \__regex_clean_branch_loop:n #1
+ {
+ \tl_if_single:nF {#1} \prg_break:
+ \token_case_meaning:NnF #1
+ {
+ \__regex_command_K: { #1 \__regex_clean_branch_loop:n }
+ \__regex_assertion:Nn { #1 \__regex_clean_assertion:Nn }
+ \__regex_class:NnnnN { #1 \__regex_clean_class:NnnnN }
+ \__regex_group:nnnN { #1 \__regex_clean_group:nnnN }
+ \__regex_group_no_capture:nnnN { #1 \__regex_clean_group:nnnN }
+ \__regex_group_resetting:nnnN { #1 \__regex_clean_group:nnnN }
+ }
+ \prg_break:
+ }
+\cs_new:Npn \__regex_clean_assertion:Nn #1#2
+ {
+ \__regex_clean_bool:n {#1}
+ \tl_if_single:nF {#2} { { \__regex_A_test: } \prg_break: }
+ \token_case_meaning:NnTF #2
+ {
+ \__regex_A_test: { }
+ \__regex_G_test: { }
+ \__regex_Z_test: { }
+ \__regex_b_test: { }
+ }
+ { {#2} }
+ { { \__regex_A_test: } \prg_break: }
+ \__regex_clean_branch_loop:n
+ }
+\cs_new:Npn \__regex_clean_class:NnnnN #1#2#3#4#5
+ {
+ \__regex_clean_bool:n {#1}
+ { \__regex_clean_class:n {#2} }
+ { \int_max:nn \c_zero_int { \__regex_clean_int:n {#3} } }
+ { \int_max:nn { -\c_one_int } { \__regex_clean_int:n {#4} } }
+ \__regex_clean_bool:n {#5}
+ \__regex_clean_branch_loop:n
+ }
+\cs_new:Npn \__regex_clean_group:nnnN #1#2#3#4
+ {
+ { \__regex_clean_regex:n {#1} }
+ { \int_max:nn \c_zero_int { \__regex_clean_int:n {#2} } }
+ { \int_max:nn { -\c_one_int } { \__regex_clean_int:n {#3} } }
+ \__regex_clean_bool:n {#4}
+ \__regex_clean_branch_loop:n
+ }
+\cs_new:Npn \__regex_clean_class:n #1
+ { \__regex_clean_class_loop:nnn #1 ????? \prg_break_point: }
+\cs_new:Npn \__regex_clean_class_loop:nnn #1#2#3
+ {
+ \tl_if_single:nF {#1} \prg_break:
+ \token_case_meaning:NnTF #1
+ {
+ \__regex_item_cs:n { #1 { \__regex_clean_regex:n {#2} } }
+ \__regex_item_exact_cs:n { #1 { \__regex_clean_exact_cs:n {#2} } }
+ \__regex_item_caseful_equal:n { #1 { \__regex_clean_int:n {#2} } }
+ \__regex_item_caseless_equal:n { #1 { \__regex_clean_int:n {#2} } }
+ \__regex_item_reverse:n { #1 { \__regex_clean_class:n {#2} } }
+ }
+ { \__regex_clean_class_loop:nnn {#3} }
+ {
+ \token_case_meaning:NnTF #1
+ {
+ \__regex_item_caseful_range:nn { }
+ \__regex_item_caseless_range:nn { }
+ \__regex_item_exact:nn { }
+ }
+ {
+ #1 { \__regex_clean_int:n {#2} } { \__regex_clean_int:n {#3} }
+ \__regex_clean_class_loop:nnn
+ }
+ {
+ \token_case_meaning:NnTF #1
+ {
+ \__regex_item_catcode:nT { }
+ \__regex_item_catcode_reverse:nT { }
+ }
+ {
+ #1 { \__regex_clean_int:n {#2} } { \__regex_clean_class:n {#3} }
+ \__regex_clean_class_loop:nnn
+ }
+ {
+ \exp_args:Ne \str_case:nnTF
+ {
+ \exp_args:Ne \str_range:nnn
+ { \cs_to_str:N #1 } \c_one_int { 13 }
+ }
+ {
+ { __regex_prop_ } { }
+ { __regex_posix } { }
+ }
+ {
+ #1
+ \__regex_clean_class_loop:nnn {#2} {#3}
+ }
+ \prg_break:
+ }
+ }
+ }
+ }
+\cs_new:Npn \__regex_clean_exact_cs:n #1
+ {
+ \exp_last_unbraced:Nf \use_none:n
+ {
+ \__regex_clean_exact_cs:w #1
+ \scan_stop: \q_recursion_tail \scan_stop:
+ \q_recursion_stop
+ }
+ }
+\cs_new:Npn \__regex_clean_exact_cs:w #1 \scan_stop:
+ {
+ \quark_if_recursion_tail_stop:n {#1}
+ \scan_stop: \tl_to_str:n {#1}
+ \__regex_clean_exact_cs:w
+ }
+\cs_new_protected:Npn \__regex_show:N #1
+ {
+ \group_begin:
+ \tl_build_begin:N \l__regex_build_tl
+ \cs_set_protected:Npn \__regex_branch:n
+ {
+ \seq_pop_right:NN \l__regex_show_prefix_seq
+ \l__regex_internal_a_tl
+ \__regex_show_one:n { +-branch }
+ \seq_put_right:No \l__regex_show_prefix_seq
+ \l__regex_internal_a_tl
+ \use:n
+ }
+ \cs_set_protected:Npn \__regex_group:nnnN
+ { \__regex_show_group_aux:nnnnN { } }
+ \cs_set_protected:Npn \__regex_group_no_capture:nnnN
+ { \__regex_show_group_aux:nnnnN { ~(no~capture) } }
+ \cs_set_protected:Npn \__regex_group_resetting:nnnN
+ { \__regex_show_group_aux:nnnnN { ~(resetting) } }
+ \cs_set_eq:NN \__regex_class:NnnnN \__regex_show_class:NnnnN
+ \cs_set_protected:Npn \__regex_command_K:
+ { \__regex_show_one:n { reset~match~start~(\iow_char:N\\K) } }
+ \cs_set_protected:Npn \__regex_assertion:Nn ##1##2
+ {
+ \__regex_show_one:n
+ { \bool_if:NF ##1 { negative~ } assertion:~##2 }
+ }
+ \cs_set:Npn \__regex_b_test: { word~boundary }
+ \cs_set:Npn \__regex_Z_test: { anchor~at~end~(\iow_char:N\\Z) }
+ \cs_set:Npn \__regex_A_test: { anchor~at~start~(\iow_char:N\\A) }
+ \cs_set:Npn \__regex_G_test: { anchor~at~start~of~match~(\iow_char:N\\G) }
+ \cs_set_protected:Npn \__regex_item_caseful_equal:n ##1
+ { \__regex_show_one:n { char~code~\__regex_show_char:n{##1} } }
+ \cs_set_protected:Npn \__regex_item_caseful_range:nn ##1##2
+ {
+ \__regex_show_one:n
+ { range~[\__regex_show_char:n{##1}, \__regex_show_char:n{##2}] }
+ }
+ \cs_set_protected:Npn \__regex_item_caseless_equal:n ##1
+ { \__regex_show_one:n { char~code~\__regex_show_char:n{##1}~(caseless) } }
+ \cs_set_protected:Npn \__regex_item_caseless_range:nn ##1##2
+ {
+ \__regex_show_one:n
+ { Range~[\__regex_show_char:n{##1}, \__regex_show_char:n{##2}]~(caseless) }
+ }
+ \cs_set_protected:Npn \__regex_item_catcode:nT
+ { \__regex_show_item_catcode:NnT \c_true_bool }
+ \cs_set_protected:Npn \__regex_item_catcode_reverse:nT
+ { \__regex_show_item_catcode:NnT \c_false_bool }
+ \cs_set_protected:Npn \__regex_item_reverse:n
+ { \__regex_show_scope:nn { Reversed~match } }
+ \cs_set_protected:Npn \__regex_item_exact:nn ##1##2
+ { \__regex_show_one:n { char~\__regex_show_char:n{##2},~catcode~##1 } }
+ \cs_set_eq:NN \__regex_item_exact_cs:n \__regex_show_item_exact_cs:n
+ \cs_set_protected:Npn \__regex_item_cs:n
+ { \__regex_show_scope:nn { control~sequence } }
+ \cs_set:cpn { __regex_prop_.: } { \__regex_show_one:n { any~token } }
+ \seq_clear:N \l__regex_show_prefix_seq
+ \__regex_show_push:n { ~ }
+ \cs_if_exist_use:N #1
+ \tl_build_end:N \l__regex_build_tl
+ \exp_args:NNNo
+ \group_end:
+ \tl_set:Nn \l__regex_internal_a_tl { \l__regex_build_tl }
+ }
+\cs_new:Npn \__regex_show_char:n #1
+ {
+ \int_eval:n {#1}
+ \int_compare:nT { 32 <= #1 <= 126 }
+ { ~ ( \char_generate:nn {#1} {12} ) }
+ }
+\cs_new_protected:Npn \__regex_show_one:n #1
+ {
+ \int_incr:N \l__regex_show_lines_int
+ \tl_build_put_right:Ne \l__regex_build_tl
+ {
+ \exp_not:N \iow_newline:
+ \seq_map_function:NN \l__regex_show_prefix_seq \use:n
+ #1
+ }
+ }
+\cs_new_protected:Npn \__regex_show_push:n #1
+ { \seq_put_right:Ne \l__regex_show_prefix_seq { #1 ~ } }
+\cs_new_protected:Npn \__regex_show_pop:
+ { \seq_pop_right:NN \l__regex_show_prefix_seq \l__regex_internal_a_tl }
+\cs_new_protected:Npn \__regex_show_scope:nn #1#2
+ {
+ \__regex_show_one:n {#1}
+ \__regex_show_push:n { ~ }
+ #2
+ \__regex_show_pop:
+ }
+\cs_new_protected:Npn \__regex_show_group_aux:nnnnN #1#2#3#4#5
+ {
+ \__regex_show_one:n { ,-group~begin #1 }
+ \__regex_show_push:n { | }
+ \use_ii:nn #2
+ \__regex_show_pop:
+ \__regex_show_one:n
+ { `-group~end \__regex_msg_repeated:nnN {#3} {#4} #5 }
+ }
+\cs_new:Npn \__regex_show_class:NnnnN #1#2#3#4#5
+ {
+ \group_begin:
+ \tl_build_begin:N \l__regex_build_tl
+ \int_zero:N \l__regex_show_lines_int
+ \__regex_show_push:n {~}
+ #2
+ \int_compare:nTF { \l__regex_show_lines_int = \c_zero_int }
+ {
+ \group_end:
+ \__regex_show_one:n { \bool_if:NTF #1 { Fail } { Pass } }
+ }
+ {
+ \bool_if:nTF
+ { #1 && \int_compare_p:n { \l__regex_show_lines_int = \c_one_int } }
+ {
+ \group_end:
+ #2
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \__regex_msg_repeated:nnN {#3} {#4} #5 }
+ }
+ {
+ \tl_build_end:N \l__regex_build_tl
+ \exp_args:NNNo
+ \group_end:
+ \tl_set:Nn \l__regex_internal_a_tl \l__regex_build_tl
+ \__regex_show_one:n
+ {
+ \bool_if:NTF #1 { Match } { Don't~match }
+ \__regex_msg_repeated:nnN {#3} {#4} #5
+ }
+ \tl_build_put_right:Ne \l__regex_build_tl
+ { \exp_not:o \l__regex_internal_a_tl }
+ }
+ }
+ }
+\cs_new_protected:Npn \__regex_show_item_catcode:NnT #1#2
+ {
+ \seq_set_split:Nnn \l__regex_internal_seq { } { CBEMTPUDSLOA }
+ \seq_set_filter:NNn \l__regex_internal_seq \l__regex_internal_seq
+ { \int_if_odd_p:n { #2 / \int_use:c { c__regex_catcode_##1_int } } }
+ \__regex_show_scope:nn
+ {
+ categories~
+ \seq_map_function:NN \l__regex_internal_seq \use:n
+ , ~
+ \bool_if:NF #1 { negative~ } class
+ }
+ }
+\cs_new_protected:Npn \__regex_show_item_exact_cs:n #1
+ {
+ \seq_set_split:Nnn \l__regex_internal_seq { \scan_stop: } {#1}
+ \seq_set_map_e:NNn \l__regex_internal_seq
+ \l__regex_internal_seq { \iow_char:N\\##1 }
+ \__regex_show_one:n
+ { control~sequence~ \seq_use:Nn \l__regex_internal_seq { ~or~ } }
+ }
+\int_new:N \l__regex_min_state_int
+\int_set:Nn \l__regex_min_state_int { 1 }
+\int_new:N \l__regex_max_state_int
+\int_new:N \l__regex_left_state_int
+\int_new:N \l__regex_right_state_int
+\seq_new:N \l__regex_left_state_seq
+\seq_new:N \l__regex_right_state_seq
+\int_new:N \l__regex_capturing_group_int
+\cs_new_protected:Npn \__regex_build:n
+ { \__regex_build_aux:Nn \c_true_bool }
+\cs_new_protected:Npn \__regex_build:N
+ { \__regex_build_aux:NN \c_true_bool }
+\cs_new_protected:Npn \__regex_build_aux:Nn #1#2
+ {
+ \__regex_compile:n {#2}
+ \__regex_build_aux:NN #1 \l__regex_internal_regex
+ }
+\cs_new_protected:Npn \__regex_build_aux:NN #1#2
+ {
+ \__regex_standard_escapechar:
+ \int_zero:N \l__regex_capturing_group_int
+ \int_set_eq:NN \l__regex_max_state_int \l__regex_min_state_int
+ \__regex_build_new_state:
+ \__regex_build_new_state:
+ \__regex_toks_put_right:Nn \l__regex_left_state_int
+ { \__regex_action_start_wildcard:N #1 }
+ \__regex_group:nnnN {#2} { 1 } { 0 } \c_false_bool
+ \__regex_toks_put_right:Nn \l__regex_right_state_int
+ { \__regex_action_success: }
+ }
+\int_new:N \g__regex_case_int
+\int_new:N \l__regex_case_max_group_int
+\cs_new_protected:Npn \__regex_case_build:n #1
+ {
+ \__regex_case_build_aux:Nn \c_true_bool {#1}
+ \int_gzero:N \g__regex_case_int
+ }
+\cs_generate_variant:Nn \__regex_case_build:n { e }
+\cs_new_protected:Npn \__regex_case_build_aux:Nn #1#2
+ {
+ \__regex_standard_escapechar:
+ \int_set_eq:NN \l__regex_max_state_int \l__regex_min_state_int
+ \__regex_build_new_state:
+ \__regex_build_new_state:
+ \__regex_toks_put_right:Nn \l__regex_left_state_int
+ { \__regex_action_start_wildcard:N #1 }
+ %
+ \__regex_build_new_state:
+ \__regex_toks_put_left:Ne \l__regex_left_state_int
+ { \__regex_action_submatch:nN \c_zero_int < }
+ \__regex_push_lr_states:
+ \int_zero:N \l__regex_case_max_group_int
+ \int_gzero:N \g__regex_case_int
+ \tl_map_inline:nn {#2}
+ {
+ \int_gincr:N \g__regex_case_int
+ \__regex_case_build_loop:n {##1}
+ }
+ \int_set_eq:NN \l__regex_capturing_group_int \l__regex_case_max_group_int
+ \__regex_pop_lr_states:
+ }
+\cs_new_protected:Npn \__regex_case_build_loop:n #1
+ {
+ \int_set_eq:NN \l__regex_capturing_group_int \c_one_int
+ \__regex_compile_use:n {#1}
+ \int_set:Nn \l__regex_case_max_group_int
+ { \int_max:nn \l__regex_case_max_group_int \l__regex_capturing_group_int }
+ \seq_pop:NN \l__regex_right_state_seq \l__regex_internal_a_tl
+ \int_set:Nn \l__regex_right_state_int \l__regex_internal_a_tl
+ \__regex_toks_put_left:Ne \l__regex_right_state_int
+ {
+ \__regex_action_submatch:nN \c_zero_int >
+ \int_gset:Nn \g__regex_case_int
+ { \int_use:N \g__regex_case_int }
+ \__regex_action_success:
+ }
+ \__regex_toks_clear:N \l__regex_max_state_int
+ \seq_push:No \l__regex_right_state_seq
+ { \int_use:N \l__regex_max_state_int }
+ \int_incr:N \l__regex_max_state_int
+ }
+\cs_new_protected:Npn \__regex_build_for_cs:n #1
+ {
+ \int_set_eq:NN \l__regex_min_state_int \l__regex_max_state_int
+ \__regex_build_new_state:
+ \__regex_build_new_state:
+ \__regex_push_lr_states:
+ #1
+ \__regex_pop_lr_states:
+ \__regex_toks_put_right:Nn \l__regex_right_state_int
+ {
+ \if_int_compare:w -2 = \l__regex_curr_char_int
+ \exp_after:wN \__regex_action_success:
+ \fi:
+ }
+ }
+\cs_new_protected:Npn \__regex_push_lr_states:
+ {
+ \seq_push:No \l__regex_left_state_seq
+ { \int_use:N \l__regex_left_state_int }
+ \seq_push:No \l__regex_right_state_seq
+ { \int_use:N \l__regex_right_state_int }
+ }
+\cs_new_protected:Npn \__regex_pop_lr_states:
+ {
+ \seq_pop:NN \l__regex_left_state_seq \l__regex_internal_a_tl
+ \int_set:Nn \l__regex_left_state_int \l__regex_internal_a_tl
+ \seq_pop:NN \l__regex_right_state_seq \l__regex_internal_a_tl
+ \int_set:Nn \l__regex_right_state_int \l__regex_internal_a_tl
+ }
+\cs_new_protected:Npn \__regex_build_transition_left:NNN #1#2#3
+ { \__regex_toks_put_left:Ne #2 { #1 { \tex_the:D \__regex_int_eval:w #3 - #2 } } }
+\cs_new_protected:Npn \__regex_build_transition_right:nNn #1#2#3
+ { \__regex_toks_put_right:Ne #2 { #1 { \tex_the:D \__regex_int_eval:w #3 - #2 } } }
+\cs_new_protected:Npn \__regex_build_new_state:
+ {
+ \__regex_toks_clear:N \l__regex_max_state_int
+ \int_set_eq:NN \l__regex_left_state_int \l__regex_right_state_int
+ \int_set_eq:NN \l__regex_right_state_int \l__regex_max_state_int
+ \int_incr:N \l__regex_max_state_int
+ }
+\cs_new_protected:Npn \__regex_build_transitions_lazyness:NNNNN #1#2#3#4#5
+ {
+ \__regex_build_new_state:
+ \__regex_toks_put_right:Ne \l__regex_left_state_int
+ {
+ \if_meaning:w \c_true_bool #1
+ #2 { \tex_the:D \__regex_int_eval:w #3 - \l__regex_left_state_int }
+ #4 { \tex_the:D \__regex_int_eval:w #5 - \l__regex_left_state_int }
+ \else:
+ #4 { \tex_the:D \__regex_int_eval:w #5 - \l__regex_left_state_int }
+ #2 { \tex_the:D \__regex_int_eval:w #3 - \l__regex_left_state_int }
+ \fi:
+ }
+ }
+\cs_new_protected:Npn \__regex_class:NnnnN #1#2#3#4#5
+ {
+ \cs_set:Npe \__regex_tests_action_cost:n ##1
+ {
+ \exp_not:n { \exp_not:n {#2} }
+ \bool_if:NTF #1
+ { \__regex_break_point:TF { \__regex_action_cost:n {##1} } { } }
+ { \__regex_break_point:TF { } { \__regex_action_cost:n {##1} } }
+ }
+ \if_case:w - #4 \exp_stop_f:
+ \__regex_class_repeat:n {#3}
+ \or: \__regex_class_repeat:nN {#3} #5
+ \else: \__regex_class_repeat:nnN {#3} {#4} #5
+ \fi:
+ }
+\cs_new:Npn \__regex_tests_action_cost:n { \__regex_action_cost:n }
+\cs_new_protected:Npn \__regex_class_repeat:n #1
+ {
+ \prg_replicate:nn {#1}
+ {
+ \__regex_build_new_state:
+ \__regex_build_transition_right:nNn \__regex_tests_action_cost:n
+ \l__regex_left_state_int \l__regex_right_state_int
+ }
+ }
+\cs_new_protected:Npn \__regex_class_repeat:nN #1#2
+ {
+ \if_int_compare:w #1 = \c_zero_int
+ \__regex_build_transitions_lazyness:NNNNN #2
+ \__regex_action_free:n \l__regex_right_state_int
+ \__regex_tests_action_cost:n \l__regex_left_state_int
+ \else:
+ \__regex_class_repeat:n {#1}
+ \int_set_eq:NN \l__regex_internal_a_int \l__regex_left_state_int
+ \__regex_build_transitions_lazyness:NNNNN #2
+ \__regex_action_free:n \l__regex_right_state_int
+ \__regex_action_free:n \l__regex_internal_a_int
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_class_repeat:nnN #1#2#3
+ {
+ \__regex_class_repeat:n {#1}
+ \int_set:Nn \l__regex_internal_a_int
+ { \l__regex_max_state_int + #2 - \c_one_int }
+ \prg_replicate:nn { #2 }
+ {
+ \__regex_build_transitions_lazyness:NNNNN #3
+ \__regex_action_free:n \l__regex_internal_a_int
+ \__regex_tests_action_cost:n \l__regex_right_state_int
+ }
+ }
+\cs_new_protected:Npn \__regex_group_aux:nnnnN #1#2#3#4#5
+ {
+ \if_int_compare:w #3 = \c_zero_int
+ \__regex_build_new_state:
+ \__regex_build_transition_right:nNn \__regex_action_free_group:n
+ \l__regex_left_state_int \l__regex_right_state_int
+ \fi:
+ \__regex_build_new_state:
+ \__regex_push_lr_states:
+ #2
+ \__regex_pop_lr_states:
+ \if_case:w - #4 \exp_stop_f:
+ \__regex_group_repeat:nn {#1} {#3}
+ \or: \__regex_group_repeat:nnN {#1} {#3} #5
+ \else: \__regex_group_repeat:nnnN {#1} {#3} {#4} #5
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_group:nnnN #1
+ {
+ \exp_args:No \__regex_group_aux:nnnnN
+ { \int_use:N \l__regex_capturing_group_int }
+ {
+ \int_incr:N \l__regex_capturing_group_int
+ #1
+ }
+ }
+\cs_new_protected:Npn \__regex_group_no_capture:nnnN
+ { \__regex_group_aux:nnnnN { -1 } }
+\cs_new_protected:Npn \__regex_group_resetting:nnnN #1
+ {
+ \__regex_group_aux:nnnnN { -1 }
+ {
+ \exp_args:Noo \__regex_group_resetting_loop:nnNn
+ { \int_use:N \l__regex_capturing_group_int }
+ { \int_use:N \l__regex_capturing_group_int }
+ #1
+ { ?? \prg_break:n } { }
+ \prg_break_point:
+ }
+ }
+\cs_new_protected:Npn \__regex_group_resetting_loop:nnNn #1#2#3#4
+ {
+ \use_none:nn #3 { \int_set:Nn \l__regex_capturing_group_int {#1} }
+ \int_set:Nn \l__regex_capturing_group_int {#2}
+ #3 {#4}
+ \exp_args:Ne \__regex_group_resetting_loop:nnNn
+ { \int_max:nn {#1} \l__regex_capturing_group_int }
+ {#2}
+ }
+\cs_new_protected:Npn \__regex_branch:n #1
+ {
+ \__regex_build_new_state:
+ \seq_get:NN \l__regex_left_state_seq \l__regex_internal_a_tl
+ \int_set:Nn \l__regex_left_state_int \l__regex_internal_a_tl
+ \__regex_build_transition_right:nNn \__regex_action_free:n
+ \l__regex_left_state_int \l__regex_right_state_int
+ #1
+ \seq_get:NN \l__regex_right_state_seq \l__regex_internal_a_tl
+ \__regex_build_transition_right:nNn \__regex_action_free:n
+ \l__regex_right_state_int \l__regex_internal_a_tl
+ }
+\cs_new_protected:Npn \__regex_group_repeat:nn #1#2
+ {
+ \if_int_compare:w #2 = \c_zero_int
+ \int_set:Nn \l__regex_max_state_int
+ { \l__regex_left_state_int - \c_one_int }
+ \__regex_build_new_state:
+ \else:
+ \__regex_group_repeat_aux:n {#2}
+ \__regex_group_submatches:nNN {#1}
+ \l__regex_internal_a_int \l__regex_right_state_int
+ \__regex_build_new_state:
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_group_submatches:nNN #1#2#3
+ {
+ \if_int_compare:w #1 > - \c_one_int
+ \__regex_toks_put_left:Ne #2 { \__regex_action_submatch:nN {#1} < }
+ \__regex_toks_put_left:Ne #3 { \__regex_action_submatch:nN {#1} > }
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_group_repeat_aux:n #1
+ {
+ \__regex_build_transition_right:nNn \__regex_action_free:n
+ \l__regex_right_state_int \l__regex_max_state_int
+ \int_set_eq:NN \l__regex_internal_a_int \l__regex_left_state_int
+ \int_set_eq:NN \l__regex_internal_b_int \l__regex_max_state_int
+ \if_int_compare:w \__regex_int_eval:w #1 > \c_one_int
+ \int_set:Nn \l__regex_internal_c_int
+ {
+ ( #1 - \c_one_int )
+ * ( \l__regex_internal_b_int - \l__regex_internal_a_int )
+ }
+ \int_add:Nn \l__regex_right_state_int \l__regex_internal_c_int
+ \int_add:Nn \l__regex_max_state_int \l__regex_internal_c_int
+ \__regex_toks_memcpy:NNn
+ \l__regex_internal_b_int
+ \l__regex_internal_a_int
+ \l__regex_internal_c_int
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_group_repeat:nnN #1#2#3
+ {
+ \if_int_compare:w #2 = \c_zero_int
+ \__regex_group_submatches:nNN {#1}
+ \l__regex_left_state_int \l__regex_right_state_int
+ \int_set:Nn \l__regex_internal_a_int
+ { \l__regex_left_state_int - \c_one_int }
+ \__regex_build_transition_right:nNn \__regex_action_free:n
+ \l__regex_right_state_int \l__regex_internal_a_int
+ \__regex_build_new_state:
+ \if_meaning:w \c_true_bool #3
+ \__regex_build_transition_left:NNN \__regex_action_free:n
+ \l__regex_internal_a_int \l__regex_right_state_int
+ \else:
+ \__regex_build_transition_right:nNn \__regex_action_free:n
+ \l__regex_internal_a_int \l__regex_right_state_int
+ \fi:
+ \else:
+ \__regex_group_repeat_aux:n {#2}
+ \__regex_group_submatches:nNN {#1}
+ \l__regex_internal_a_int \l__regex_right_state_int
+ \if_meaning:w \c_true_bool #3
+ \__regex_build_transition_right:nNn \__regex_action_free_group:n
+ \l__regex_right_state_int \l__regex_internal_a_int
+ \else:
+ \__regex_build_transition_left:NNN \__regex_action_free_group:n
+ \l__regex_right_state_int \l__regex_internal_a_int
+ \fi:
+ \__regex_build_new_state:
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_group_repeat:nnnN #1#2#3#4
+ {
+ \__regex_group_submatches:nNN {#1}
+ \l__regex_left_state_int \l__regex_right_state_int
+ \__regex_group_repeat_aux:n { #2 + #3 }
+ \if_meaning:w \c_true_bool #4
+ \int_set_eq:NN \l__regex_left_state_int \l__regex_max_state_int
+ \prg_replicate:nn { #3 }
+ {
+ \int_sub:Nn \l__regex_left_state_int
+ { \l__regex_internal_b_int - \l__regex_internal_a_int }
+ \__regex_build_transition_left:NNN \__regex_action_free:n
+ \l__regex_left_state_int \l__regex_max_state_int
+ }
+ \else:
+ \prg_replicate:nn { #3 - \c_one_int }
+ {
+ \int_sub:Nn \l__regex_right_state_int
+ { \l__regex_internal_b_int - \l__regex_internal_a_int }
+ \__regex_build_transition_right:nNn \__regex_action_free:n
+ \l__regex_right_state_int \l__regex_max_state_int
+ }
+ \if_int_compare:w #2 = \c_zero_int
+ \int_set:Nn \l__regex_right_state_int
+ { \l__regex_left_state_int - \c_one_int }
+ \else:
+ \int_sub:Nn \l__regex_right_state_int
+ { \l__regex_internal_b_int - \l__regex_internal_a_int }
+ \fi:
+ \__regex_build_transition_right:nNn \__regex_action_free:n
+ \l__regex_right_state_int \l__regex_max_state_int
+ \fi:
+ \__regex_build_new_state:
+ }
+\cs_new_protected:Npn \__regex_assertion:Nn #1#2
+ {
+ \__regex_build_new_state:
+ \__regex_toks_put_right:Ne \l__regex_left_state_int
+ {
+ \exp_not:n {#2}
+ \__regex_break_point:TF
+ \bool_if:NF #1 { { } }
+ {
+ \__regex_action_free:n
+ {
+ \tex_the:D \__regex_int_eval:w
+ \l__regex_right_state_int - \l__regex_left_state_int
+ }
+ }
+ \bool_if:NT #1 { { } }
+ }
+ }
+\cs_new_protected:Npn \__regex_b_test:
+ {
+ \group_begin:
+ \int_set_eq:NN \l__regex_curr_char_int \l__regex_last_char_int
+ \__regex_prop_w:
+ \__regex_break_point:TF
+ { \group_end: \__regex_item_reverse:n { \__regex_prop_w: } }
+ { \group_end: \__regex_prop_w: }
+ }
+\cs_new_protected:Npn \__regex_Z_test:
+ {
+ \if_int_compare:w -2 = \l__regex_curr_char_int
+ \exp_after:wN \__regex_break_true:w
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_A_test:
+ {
+ \if_int_compare:w -2 = \l__regex_last_char_int
+ \exp_after:wN \__regex_break_true:w
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_G_test:
+ {
+ \if_int_compare:w \l__regex_curr_pos_int = \l__regex_start_pos_int
+ \exp_after:wN \__regex_break_true:w
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_command_K:
+ {
+ \__regex_build_new_state:
+ \__regex_toks_put_right:Ne \l__regex_left_state_int
+ {
+ \__regex_action_submatch:nN \c_zero_int <
+ \bool_set_true:N \l__regex_fresh_thread_bool
+ \__regex_action_free:n
+ {
+ \tex_the:D \__regex_int_eval:w
+ \l__regex_right_state_int - \l__regex_left_state_int
+ }
+ \bool_set_false:N \l__regex_fresh_thread_bool
+ }
+ }
+\int_new:N \l__regex_min_pos_int
+\int_new:N \l__regex_max_pos_int
+\int_new:N \l__regex_curr_pos_int
+\int_new:N \l__regex_start_pos_int
+\int_new:N \l__regex_success_pos_int
+\int_new:N \l__regex_curr_char_int
+\int_new:N \l__regex_curr_catcode_int
+\tl_new:N \l__regex_curr_token_tl
+\int_new:N \l__regex_last_char_int
+\int_new:N \l__regex_last_char_success_int
+\int_new:N \l__regex_case_changed_char_int
+\int_new:N \l__regex_curr_state_int
+\tl_new:N \l__regex_curr_submatches_tl
+\tl_new:N \l__regex_success_submatches_tl
+\int_new:N \l__regex_step_int
+\int_new:N \l__regex_min_thread_int
+\int_new:N \l__regex_max_thread_int
+\intarray_new:Nn \g__regex_state_active_intarray { 65536 }
+\intarray_new:Nn \g__regex_thread_info_intarray { 65536 }
+\tl_new:N \l__regex_matched_analysis_tl
+\tl_new:N \l__regex_curr_analysis_tl
+\tl_new:N \l__regex_every_match_tl
+\bool_new:N \l__regex_fresh_thread_bool
+\bool_new:N \l__regex_empty_success_bool
+\cs_new_eq:NN \__regex_if_two_empty_matches:F \use:n
+\bool_new:N \g__regex_success_bool
+\bool_new:N \l__regex_saved_success_bool
+\bool_new:N \l__regex_match_success_bool
+\cs_new_protected:Npn \__regex_match:n #1
+ {
+ \__regex_match_init:
+ \__regex_match_once_init:
+ \tl_analysis_map_inline:nn {#1}
+ { \__regex_match_one_token:nnN {##1} {##2} ##3 }
+ \__regex_match_one_token:nnN { } { -2 } F
+ \prg_break_point:Nn \__regex_maplike_break: { }
+ }
+\cs_new_protected:Npn \__regex_match_cs:n #1
+ {
+ \int_set_eq:NN \l__regex_min_thread_int \l__regex_max_thread_int
+ \__regex_match_init:
+ \__regex_match_once_init:
+ \str_map_inline:nn {#1}
+ {
+ \tl_if_blank:nTF {##1}
+ { \__regex_match_one_token:nnN {##1} {`##1} A }
+ { \__regex_match_one_token:nnN {##1} {`##1} C }
+ }
+ \__regex_match_one_token:nnN { } { -2 } F
+ \prg_break_point:Nn \__regex_maplike_break: { }
+ }
+\cs_new_protected:Npn \__regex_match_init:
+ {
+ \bool_gset_false:N \g__regex_success_bool
+ \int_step_inline:nnn
+ \l__regex_min_state_int { \l__regex_max_state_int - \c_one_int }
+ {
+ \__kernel_intarray_gset:Nnn
+ \g__regex_state_active_intarray {##1} \c_one_int
+ }
+ \int_zero:N \l__regex_step_int
+ \int_set:Nn \l__regex_min_pos_int { 2 }
+ \int_set_eq:NN \l__regex_success_pos_int \l__regex_min_pos_int
+ \int_set:Nn \l__regex_last_char_success_int { -2 }
+ \tl_build_begin:N \l__regex_matched_analysis_tl
+ \tl_clear:N \l__regex_curr_analysis_tl
+ \int_set_eq:NN \l__regex_min_submatch_int \c_one_int
+ \int_set_eq:NN \l__regex_submatch_int \l__regex_min_submatch_int
+ \bool_set_false:N \l__regex_empty_success_bool
+ }
+\cs_new_protected:Npn \__regex_match_once_init:
+ {
+ \if_meaning:w \c_true_bool \l__regex_empty_success_bool
+ \cs_set:Npn \__regex_if_two_empty_matches:F
+ {
+ \int_compare:nNnF
+ \l__regex_start_pos_int = \l__regex_curr_pos_int
+ }
+ \else:
+ \cs_set_eq:NN \__regex_if_two_empty_matches:F \use:n
+ \fi:
+ \int_set_eq:NN \l__regex_start_pos_int \l__regex_success_pos_int
+ \bool_set_false:N \l__regex_match_success_bool
+ \tl_set:Ne \l__regex_curr_submatches_tl
+ { \prg_replicate:nn { 2 * \l__regex_capturing_group_int } { 0 , } }
+ \int_set_eq:NN \l__regex_max_thread_int \l__regex_min_thread_int
+ \__regex_store_state:n { \l__regex_min_state_int }
+ \int_set:Nn \l__regex_curr_pos_int { \l__regex_start_pos_int - \c_one_int }
+ \int_set_eq:NN \l__regex_curr_char_int \l__regex_last_char_success_int
+ \tl_build_get_intermediate:NN \l__regex_matched_analysis_tl \l__regex_internal_a_tl
+ \exp_args:NNf \__regex_match_once_init_aux:
+ \tl_map_inline:nn
+ { \exp_after:wN \l__regex_internal_a_tl \l__regex_curr_analysis_tl }
+ { \__regex_match_one_token:nnN ##1 }
+ \prg_break_point:Nn \__regex_maplike_break: { }
+ }
+\cs_new_protected:Npn \__regex_match_once_init_aux:
+ {
+ \tl_build_begin:N \l__regex_matched_analysis_tl
+ \tl_clear:N \l__regex_curr_analysis_tl
+ }
+\cs_new_protected:Npn \__regex_single_match:
+ {
+ \tl_set:Nn \l__regex_every_match_tl
+ {
+ \bool_gset_eq:NN
+ \g__regex_success_bool
+ \l__regex_match_success_bool
+ \__regex_maplike_break:
+ }
+ }
+\cs_new_protected:Npn \__regex_multi_match:n #1
+ {
+ \tl_set:Nn \l__regex_every_match_tl
+ {
+ \if_meaning:w \c_false_bool \l__regex_match_success_bool
+ \exp_after:wN \__regex_maplike_break:
+ \fi:
+ \bool_gset_true:N \g__regex_success_bool
+ #1
+ \__regex_match_once_init:
+ }
+ }
+\cs_new_protected:Npn \__regex_match_one_token:nnN #1#2#3
+ {
+ \int_add:Nn \l__regex_step_int { 2 }
+ \int_incr:N \l__regex_curr_pos_int
+ \int_set_eq:NN \l__regex_last_char_int \l__regex_curr_char_int
+ \cs_set_eq:NN \__regex_maybe_compute_ccc: \__regex_compute_case_changed_char:
+ \tl_set:Nn \l__regex_curr_token_tl {#1}
+ \int_set:Nn \l__regex_curr_char_int {#2}
+ \int_set:Nn \l__regex_curr_catcode_int { "#3 }
+ \tl_build_put_right:Ne \l__regex_matched_analysis_tl
+ { \exp_not:o \l__regex_curr_analysis_tl }
+ \tl_set:Nn \l__regex_curr_analysis_tl { { {#1} {#2} #3 } }
+ \use:e
+ {
+ \int_set_eq:NN \l__regex_max_thread_int \l__regex_min_thread_int
+ \int_step_function:nnN
+ \l__regex_min_thread_int
+ { \l__regex_max_thread_int - \c_one_int }
+ \__regex_match_one_active:n
+ }
+ \prg_break_point:
+ \bool_set_false:N \l__regex_fresh_thread_bool
+ \if_int_compare:w \l__regex_max_thread_int > \l__regex_min_thread_int
+ \if_int_compare:w -2 < \l__regex_curr_char_int
+ \exp_after:wN \use_i:nn
+ \fi:
+ \fi:
+ \l__regex_every_match_tl
+ }
+\cs_new:Npn \__regex_match_one_active:n #1
+ {
+ \__regex_use_state_and_submatches:w
+ \__kernel_intarray_range_to_clist:Nnn
+ \g__regex_thread_info_intarray
+ { \c_one_int + #1 * (\l__regex_capturing_group_int * 2 + \c_one_int) }
+ { (\c_one_int + #1) * (\l__regex_capturing_group_int * 2 + \c_one_int) }
+ ;
+ }
+\cs_new_protected:Npn \__regex_use_state:
+ {
+ \__kernel_intarray_gset:Nnn \g__regex_state_active_intarray
+ \l__regex_curr_state_int \l__regex_step_int
+ \__regex_toks_use:w \l__regex_curr_state_int
+ \__kernel_intarray_gset:Nnn \g__regex_state_active_intarray
+ \l__regex_curr_state_int
+ { \__regex_int_eval:w \l__regex_step_int + \c_one_int \scan_stop: }
+ }
+\cs_new_protected:Npn \__regex_use_state_and_submatches:w #1 , #2 ;
+ {
+ \int_set:Nn \l__regex_curr_state_int {#1}
+ \if_int_compare:w
+ \__kernel_intarray_item:Nn \g__regex_state_active_intarray
+ \l__regex_curr_state_int
+ < \l__regex_step_int
+ \tl_set:Nn \l__regex_curr_submatches_tl { #2 , }
+ \exp_after:wN \__regex_use_state:
+ \fi:
+ \scan_stop:
+ }
+\cs_new_protected:Npn \__regex_action_start_wildcard:N #1
+ {
+ \bool_set_true:N \l__regex_fresh_thread_bool
+ \__regex_action_free:n {1}
+ \bool_set_false:N \l__regex_fresh_thread_bool
+ \bool_if:NT #1 { \__regex_action_cost:n {0} }
+ }
+\cs_new_protected:Npn \__regex_action_free:n
+ { \__regex_action_free_aux:nn { > \l__regex_step_int \else: } }
+\cs_new_protected:Npn \__regex_action_free_group:n
+ { \__regex_action_free_aux:nn { < \l__regex_step_int } }
+\cs_new_protected:Npn \__regex_action_free_aux:nn #1#2
+ {
+ \use:e
+ {
+ \int_add:Nn \l__regex_curr_state_int {#2}
+ \exp_not:n
+ {
+ \if_int_compare:w
+ \__kernel_intarray_item:Nn \g__regex_state_active_intarray
+ \l__regex_curr_state_int
+ #1
+ \exp_after:wN \__regex_use_state:
+ \fi:
+ }
+ \int_set:Nn \l__regex_curr_state_int
+ { \int_use:N \l__regex_curr_state_int }
+ \tl_set:Nn \exp_not:N \l__regex_curr_submatches_tl
+ { \exp_not:o \l__regex_curr_submatches_tl }
+ }
+ }
+\cs_new_protected:Npn \__regex_action_cost:n #1
+ {
+ \exp_args:No \__regex_store_state:n
+ { \tex_the:D \__regex_int_eval:w \l__regex_curr_state_int + #1 }
+ }
+\cs_new_protected:Npn \__regex_store_state:n #1
+ {
+ \exp_args:No \__regex_store_submatches:nn
+ \l__regex_curr_submatches_tl {#1}
+ \int_incr:N \l__regex_max_thread_int
+ }
+\cs_new_protected:Npn \__regex_store_submatches:nn #1#2
+ {
+ \__kernel_intarray_gset_range_from_clist:Nnn
+ \g__regex_thread_info_intarray
+ {
+ \__regex_int_eval:w
+ \c_one_int + \l__regex_max_thread_int *
+ (\l__regex_capturing_group_int * 2 + \c_one_int)
+ }
+ { #2 , #1 }
+ }
+\cs_new_protected:Npn \__regex_disable_submatches:
+ {
+ \cs_set_protected:Npn \__regex_store_submatches:n ##1 { }
+ \cs_set_protected:Npn \__regex_action_submatch:nN ##1##2 { }
+ }
+\cs_new_protected:Npn \__regex_action_submatch:nN #1#2
+ {
+ \exp_after:wN \__regex_action_submatch_aux:w
+ \l__regex_curr_submatches_tl ; {#1} #2
+ }
+\cs_new_protected:Npn \__regex_action_submatch_aux:w #1 ; #2#3
+ {
+ \tl_set:Ne \l__regex_curr_submatches_tl
+ {
+ \prg_replicate:nn
+ { #2 \if_meaning:w > #3 + \l__regex_capturing_group_int \fi: }
+ { \__regex_action_submatch_auxii:w }
+ \__regex_action_submatch_auxiii:w
+ #1
+ }
+ }
+\cs_new:Npn \__regex_action_submatch_auxii:w
+ #1 \__regex_action_submatch_auxiii:w #2 ,
+ { #2 , #1 \__regex_action_submatch_auxiii:w }
+\cs_new:Npn \__regex_action_submatch_auxiii:w #1 ,
+ { \int_use:N \l__regex_curr_pos_int , }
+\cs_new_protected:Npn \__regex_action_success:
+ {
+ \__regex_if_two_empty_matches:F
+ {
+ \bool_set_true:N \l__regex_match_success_bool
+ \bool_set_eq:NN \l__regex_empty_success_bool
+ \l__regex_fresh_thread_bool
+ \int_set_eq:NN \l__regex_success_pos_int \l__regex_curr_pos_int
+ \int_set_eq:NN \l__regex_last_char_success_int \l__regex_last_char_int
+ \tl_build_begin:N \l__regex_matched_analysis_tl
+ \tl_set_eq:NN \l__regex_success_submatches_tl
+ \l__regex_curr_submatches_tl
+ \prg_break:
+ }
+ }
+\int_new:N \l__regex_replacement_csnames_int
+\tl_new:N \l__regex_replacement_category_tl
+\seq_new:N \l__regex_replacement_category_seq
+\tl_new:N \g__regex_balance_tl
+\cs_new:Npn \__regex_replacement_balance_one_match:n #1
+ { - \__regex_submatch_balance:n {#1} }
+\cs_new:Npn \__regex_replacement_do_one_match:n #1
+ {
+ \__regex_query_range:nn
+ { \__kernel_intarray_item:Nn \g__regex_submatch_prev_intarray {#1} }
+ { \__kernel_intarray_item:Nn \g__regex_submatch_begin_intarray {#1} }
+ }
+\cs_new:Npn \__regex_replacement_exp_not:N #1 { \exp_not:n {#1} }
+\cs_new_eq:NN \__regex_replacement_exp_not:V \exp_not:V
+\cs_new:Npn \__regex_query_range:nn #1#2
+ {
+ \exp_after:wN \__regex_query_range_loop:ww
+ \int_value:w \__regex_int_eval:w #1 \exp_after:wN ;
+ \int_value:w \__regex_int_eval:w #2 ;
+ \prg_break_point:
+ }
+\cs_new:Npn \__regex_query_range_loop:ww #1 ; #2 ;
+ {
+ \if_int_compare:w #1 < #2 \exp_stop_f:
+ \else:
+ \prg_break:n
+ \fi:
+ \__regex_toks_use:w #1 \exp_stop_f:
+ \exp_after:wN \__regex_query_range_loop:ww
+ \int_value:w \__regex_int_eval:w #1 + \c_one_int ; #2 ;
+ }
+\cs_new:Npn \__regex_query_submatch:n #1
+ {
+ \__regex_query_range:nn
+ { \__kernel_intarray_item:Nn \g__regex_submatch_begin_intarray {#1} }
+ { \__kernel_intarray_item:Nn \g__regex_submatch_end_intarray {#1} }
+ }
+\cs_new_protected:Npn \__regex_submatch_balance:n #1
+ {
+ \tex_the:D \__regex_int_eval:w
+ \__regex_intarray_item:NnF \g__regex_balance_intarray
+ {
+ \__kernel_intarray_item:Nn
+ \g__regex_submatch_end_intarray {#1}
+ }
+ \c_zero_int
+ -
+ \__regex_intarray_item:NnF \g__regex_balance_intarray
+ {
+ \__kernel_intarray_item:Nn
+ \g__regex_submatch_begin_intarray {#1}
+ }
+ \c_zero_int
+ \scan_stop:
+ }
+\cs_new_protected:Npn \__regex_replacement:n
+ { \__regex_replacement_apply:Nn \__regex_replacement_set:n }
+\cs_new_protected:Npn \__regex_replacement_apply:Nn #1#2
+ {
+ \group_begin:
+ \tl_build_begin:N \l__regex_build_tl
+ \int_zero:N \l__regex_balance_int
+ \tl_gclear:N \g__regex_balance_tl
+ \__regex_escape_use:nnnn
+ {
+ \if_charcode:w \c_right_brace_str ##1
+ \__regex_replacement_rbrace:N
+ \else:
+ \if_charcode:w \c_left_brace_str ##1
+ \__regex_replacement_lbrace:N
+ \else:
+ \__regex_replacement_normal:n
+ \fi:
+ \fi:
+ ##1
+ }
+ { \__regex_replacement_escaped:N ##1 }
+ { \__regex_replacement_normal:n ##1 }
+ {#2}
+ \prg_do_nothing: \prg_do_nothing:
+ \if_int_compare:w \l__regex_replacement_csnames_int > \c_zero_int
+ \msg_error:nne { regex } { replacement-missing-rbrace }
+ { \int_use:N \l__regex_replacement_csnames_int }
+ \tl_build_put_right:Ne \l__regex_build_tl
+ { \prg_replicate:nn \l__regex_replacement_csnames_int \cs_end: }
+ \fi:
+ \seq_if_empty:NF \l__regex_replacement_category_seq
+ {
+ \msg_error:nne { regex } { replacement-missing-rparen }
+ { \seq_count:N \l__regex_replacement_category_seq }
+ \seq_clear:N \l__regex_replacement_category_seq
+ }
+ \tl_gput_right:Ne \g__regex_balance_tl
+ { + \int_use:N \l__regex_balance_int }
+ \tl_build_end:N \l__regex_build_tl
+ \exp_args:NNo
+ \group_end:
+ #1 \l__regex_build_tl
+ }
+\cs_generate_variant:Nn \__regex_replacement:n { e }
+\cs_new_protected:Npn \__regex_replacement_set:n #1
+ {
+ \cs_set:Npn \__regex_replacement_do_one_match:n ##1
+ {
+ \__regex_query_range:nn
+ {
+ \__kernel_intarray_item:Nn
+ \g__regex_submatch_prev_intarray {##1}
+ }
+ {
+ \__kernel_intarray_item:Nn
+ \g__regex_submatch_begin_intarray {##1}
+ }
+ #1
+ }
+ \exp_args:Nno \use:n
+ { \cs_gset:Npn \__regex_replacement_balance_one_match:n ##1 }
+ {
+ \g__regex_balance_tl
+ - \__regex_submatch_balance:n {##1}
+ }
+ }
+\tl_new:N \g__regex_case_replacement_tl
+\tl_new:N \g__regex_case_balance_tl
+\cs_new_protected:Npn \__regex_case_replacement:n #1
+ {
+ \tl_gset:Nn \g__regex_case_balance_tl
+ {
+ \if_case:w
+ \__kernel_intarray_item:Nn
+ \g__regex_submatch_case_intarray {##1}
+ }
+ \tl_gset_eq:NN \g__regex_case_replacement_tl \g__regex_case_balance_tl
+ \tl_map_tokens:nn {#1}
+ { \__regex_replacement_apply:Nn \__regex_case_replacement_aux:n }
+ \tl_gset:No \g__regex_balance_tl
+ { \g__regex_case_balance_tl \fi: }
+ \exp_args:No \__regex_replacement_set:n
+ { \g__regex_case_replacement_tl \fi: }
+ }
+\cs_generate_variant:Nn \__regex_case_replacement:n { e }
+\cs_new_protected:Npn \__regex_case_replacement_aux:n #1
+ {
+ \tl_gput_right:Nn \g__regex_case_replacement_tl { \or: #1 }
+ \tl_gput_right:No \g__regex_case_balance_tl
+ { \exp_after:wN \or: \g__regex_balance_tl }
+ }
+\cs_new_protected:Npn \__regex_replacement_put:n
+ { \tl_build_put_right:Nn \l__regex_build_tl }
+\cs_new_protected:Npn \__regex_replacement_normal:n #1
+ {
+ \int_compare:nNnTF \l__regex_replacement_csnames_int > \c_zero_int
+ { \exp_args:No \__regex_replacement_put:n { \token_to_str:N #1 } }
+ {
+ \tl_if_empty:NTF \l__regex_replacement_category_tl
+ { \__regex_replacement_normal_aux:N #1 }
+ { % (
+ \token_if_eq_charcode:NNTF #1 )
+ {
+ \seq_pop:NN \l__regex_replacement_category_seq
+ \l__regex_replacement_category_tl
+ }
+ {
+ \use:c { __regex_replacement_c_ \l__regex_replacement_category_tl :w }
+ ? #1
+ }
+ }
+ }
+ }
+\cs_new_protected:Npn \__regex_replacement_normal_aux:N #1
+ {
+ \token_if_eq_charcode:NNTF #1 \c_space_token
+ { \__regex_replacement_c_S:w }
+ {
+ \exp_after:wN \exp_after:wN
+ \if_case:w \tex_catcode:D `#1 \exp_stop_f:
+ \__regex_replacement_c_O:w
+ \or: \__regex_replacement_c_B:w
+ \or: \__regex_replacement_c_E:w
+ \or: \__regex_replacement_c_M:w
+ \or: \__regex_replacement_c_T:w
+ \or: \__regex_replacement_c_O:w
+ \or: \__regex_replacement_c_P:w
+ \or: \__regex_replacement_c_U:w
+ \or: \__regex_replacement_c_D:w
+ \or: \__regex_replacement_c_O:w
+ \or: \__regex_replacement_c_S:w
+ \or: \__regex_replacement_c_L:w
+ \or: \__regex_replacement_c_O:w
+ \or: \__regex_replacement_c_A:w
+ \else: \__regex_replacement_c_O:w
+ \fi:
+ }
+ ? #1
+ }
+\cs_new_protected:Npn \__regex_replacement_escaped:N #1
+ {
+ \cs_if_exist_use:cF { __regex_replacement_#1:w }
+ {
+ \if_int_compare:w \c_one_int < 1#1 \exp_stop_f:
+ \__regex_replacement_put_submatch:n {#1}
+ \else:
+ \__regex_replacement_normal:n {#1}
+ \fi:
+ }
+ }
+\cs_new_protected:Npn \__regex_replacement_put_submatch:n #1
+ {
+ \if_int_compare:w #1 < \l__regex_capturing_group_int
+ \__regex_replacement_put_submatch_aux:n {#1}
+ \else:
+ \msg_expandable_error:nnff { regex } { submatch-too-big }
+ {#1} { \int_eval:n { \l__regex_capturing_group_int - \c_one_int } }
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_replacement_put_submatch_aux:n #1
+ {
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \__regex_query_submatch:n { \__regex_int_eval:w #1 + ##1 \scan_stop: } }
+ \if_int_compare:w \l__regex_replacement_csnames_int = \c_zero_int
+ \tl_gput_right:Nn \g__regex_balance_tl
+ { + \__regex_submatch_balance:n { \__regex_int_eval:w #1 + ##1 \scan_stop: } }
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_replacement_g:w #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \__regex_replacement_lbrace:N
+ { \l__regex_internal_a_int = \__regex_replacement_g_digits:NN }
+ { \__regex_replacement_error:NNN g #1 #2 }
+ }
+\cs_new:Npn \__regex_replacement_g_digits:NN #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \__regex_replacement_normal:n
+ {
+ \if_int_compare:w \c_one_int < 1#2 \exp_stop_f:
+ #2
+ \exp_after:wN \use_i:nnn
+ \exp_after:wN \__regex_replacement_g_digits:NN
+ \else:
+ \exp_stop_f:
+ \exp_after:wN \__regex_replacement_error:NNN
+ \exp_after:wN g
+ \fi:
+ }
+ {
+ \exp_stop_f:
+ \if_meaning:w \__regex_replacement_rbrace:N #1
+ \exp_args:No \__regex_replacement_put_submatch:n
+ { \int_use:N \l__regex_internal_a_int }
+ \exp_after:wN \use_none:nn
+ \else:
+ \exp_after:wN \__regex_replacement_error:NNN
+ \exp_after:wN g
+ \fi:
+ }
+ #1 #2
+ }
+\cs_new_protected:Npn \__regex_replacement_c:w #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \__regex_replacement_normal:n
+ {
+ \cs_if_exist:cTF { __regex_replacement_c_#2:w }
+ { \__regex_replacement_cat:NNN #2 }
+ { \__regex_replacement_error:NNN c #1#2 }
+ }
+ {
+ \token_if_eq_meaning:NNTF #1 \__regex_replacement_lbrace:N
+ { \__regex_replacement_cu_aux:Nw \__regex_replacement_exp_not:N }
+ { \__regex_replacement_error:NNN c #1#2 }
+ }
+ }
+\cs_new_protected:Npn \__regex_replacement_cu_aux:Nw #1
+ {
+ \if_case:w \l__regex_replacement_csnames_int
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \exp_not:n { \exp_after:wN #1 \cs:w } }
+ \else:
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \exp_not:n { \exp_after:wN \tl_to_str:V \cs:w } }
+ \fi:
+ \int_incr:N \l__regex_replacement_csnames_int
+ }
+\cs_new_protected:Npn \__regex_replacement_u:w #1#2
+ {
+ \token_if_eq_meaning:NNTF #1 \__regex_replacement_lbrace:N
+ { \__regex_replacement_cu_aux:Nw \__regex_replacement_exp_not:V }
+ { \__regex_replacement_error:NNN u #1#2 }
+ }
+\cs_new_protected:Npn \__regex_replacement_rbrace:N #1
+ {
+ \if_int_compare:w \l__regex_replacement_csnames_int > \c_zero_int
+ \tl_build_put_right:Nn \l__regex_build_tl { \cs_end: }
+ \int_decr:N \l__regex_replacement_csnames_int
+ \else:
+ \__regex_replacement_normal:n {#1}
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_replacement_lbrace:N #1
+ {
+ \if_int_compare:w \l__regex_replacement_csnames_int > \c_zero_int
+ \msg_error:nnn { regex } { cu-lbrace } { u }
+ \else:
+ \__regex_replacement_normal:n {#1}
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_replacement_cat:NNN #1#2#3
+ {
+ \token_if_eq_meaning:NNTF \prg_do_nothing: #3
+ { \msg_error:nn { regex } { replacement-catcode-end } }
+ {
+ \int_compare:nNnTF \l__regex_replacement_csnames_int > \c_zero_int
+ {
+ \msg_error:nnnn
+ { regex } { replacement-catcode-in-cs } {#1} {#3}
+ #2 #3
+ }
+ {
+ \__regex_two_if_eq:NNNNTF #2 #3 \__regex_replacement_normal:n (
+ {
+ \seq_push:NV \l__regex_replacement_category_seq
+ \l__regex_replacement_category_tl
+ \tl_set:Nn \l__regex_replacement_category_tl {#1}
+ }
+ {
+ \token_if_eq_meaning:NNT #2 \__regex_replacement_escaped:N
+ {
+ \__regex_char_if_alphanumeric:NTF #3
+ {
+ \msg_error:nnnn
+ { regex } { replacement-catcode-escaped }
+ {#1} {#3}
+ }
+ { }
+ }
+ \use:c { __regex_replacement_c_#1:w } #2 #3
+ }
+ }
+ }
+ }
+\group_begin:
+ \cs_new_protected:Npn \__regex_replacement_char:nNN #1#2#3
+ {
+ \tex_lccode:D \c_zero_int = `#3 \scan_stop:
+ \tex_lowercase:D { \__regex_replacement_put:n {#1} }
+ }
+ \char_set_catcode_active:N \^^@
+ \cs_new_protected:Npn \__regex_replacement_c_A:w
+ { \__regex_replacement_char:nNN { \exp_not:n { \exp_not:N ^^@ } } }
+ \char_set_catcode_group_begin:N \^^@
+ \cs_new_protected:Npn \__regex_replacement_c_B:w
+ {
+ \if_int_compare:w \l__regex_replacement_csnames_int = \c_zero_int
+ \int_incr:N \l__regex_balance_int
+ \fi:
+ \__regex_replacement_char:nNN
+ { \exp_not:n { \exp_after:wN ^^@ \if_false: } \fi: } }
+ }
+ \cs_new_protected:Npn \__regex_replacement_c_C:w #1#2
+ {
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \exp_not:N \__regex_replacement_exp_not:N \exp_not:c {#2} }
+ }
+ \char_set_catcode_math_subscript:N \^^@
+ \cs_new_protected:Npn \__regex_replacement_c_D:w
+ { \__regex_replacement_char:nNN { ^^@ } }
+ \char_set_catcode_group_end:N \^^@
+ \cs_new_protected:Npn \__regex_replacement_c_E:w
+ {
+ \if_int_compare:w \l__regex_replacement_csnames_int = \c_zero_int
+ \int_decr:N \l__regex_balance_int
+ \fi:
+ \__regex_replacement_char:nNN
+ { \exp_not:n { \if_false: { \fi: ^^@ } }
+ }
+ \char_set_catcode_letter:N \^^@
+ \cs_new_protected:Npn \__regex_replacement_c_L:w
+ { \__regex_replacement_char:nNN { ^^@ } }
+ \char_set_catcode_math_toggle:N \^^@
+ \cs_new_protected:Npn \__regex_replacement_c_M:w
+ { \__regex_replacement_char:nNN { ^^@ } }
+ \char_set_catcode_other:N \^^@
+ \cs_new_protected:Npn \__regex_replacement_c_O:w
+ { \__regex_replacement_char:nNN { ^^@ } }
+ \char_set_catcode_parameter:N \^^@
+ \cs_new_protected:Npn \__regex_replacement_c_P:w
+ {
+ \__regex_replacement_char:nNN
+ { \exp_not:n { \exp_not:n { ^^@^^@^^@^^@ } } }
+ }
+ \cs_new_protected:Npn \__regex_replacement_c_S:w #1#2
+ {
+ \if_int_compare:w `#2 = \c_zero_int
+ \msg_error:nn { regex } { replacement-null-space }
+ \fi:
+ \tex_lccode:D `\ = `#2 \scan_stop:
+ \tex_lowercase:D { \__regex_replacement_put:n {~} }
+ }
+ \char_set_catcode_alignment:N \^^@
+ \cs_new_protected:Npn \__regex_replacement_c_T:w
+ { \__regex_replacement_char:nNN { ^^@ } }
+ \char_set_catcode_math_superscript:N \^^@
+ \cs_new_protected:Npn \__regex_replacement_c_U:w
+ { \__regex_replacement_char:nNN { ^^@ } }
+\group_end:
+\cs_new_protected:Npn \__regex_replacement_error:NNN #1#2#3
+ {
+ \msg_error:nne { regex } { replacement-#1 } {#3}
+ #2 #3
+ }
+\cs_new_protected:Npn \regex_new:N #1
+ { \cs_new_eq:NN #1 \c__regex_no_match_regex }
+\regex_new:N \l_tmpa_regex
+\regex_new:N \l_tmpb_regex
+\regex_new:N \g_tmpa_regex
+\regex_new:N \g_tmpb_regex
+\cs_new_protected:Npn \regex_set:Nn #1#2
+ {
+ \__regex_compile:n {#2}
+ \tl_set_eq:NN #1 \l__regex_internal_regex
+ }
+\cs_new_protected:Npn \regex_gset:Nn #1#2
+ {
+ \__regex_compile:n {#2}
+ \tl_gset_eq:NN #1 \l__regex_internal_regex
+ }
+\cs_new_protected:Npn \regex_const:Nn #1#2
+ {
+ \__regex_compile:n {#2}
+ \tl_const:Ne #1 { \exp_not:o \l__regex_internal_regex }
+ }
+\cs_new_protected:Npn \regex_show:n { \__regex_show:Nn \msg_show:nneeee }
+\cs_new_protected:Npn \regex_log:n { \__regex_show:Nn \msg_log:nneeee }
+\cs_new_protected:Npn \__regex_show:Nn #1#2
+ {
+ \__regex_compile:n {#2}
+ \__regex_show:N \l__regex_internal_regex
+ #1 { regex } { show }
+ { \tl_to_str:n {#2} } { }
+ { \l__regex_internal_a_tl } { }
+ }
+\cs_new_protected:Npn \regex_show:N { \__regex_show:NN \msg_show:nneeee }
+\cs_new_protected:Npn \regex_log:N { \__regex_show:NN \msg_log:nneeee }
+\cs_new_protected:Npn \__regex_show:NN #1#2
+ {
+ \__kernel_chk_tl_type:NnnT #2 { regex }
+ { \exp_args:No \__regex_clean_regex:n {#2} }
+ {
+ \__regex_show:N #2
+ #1 { regex } { show }
+ { } { \token_to_str:N #2 }
+ { \l__regex_internal_a_tl } { }
+ }
+ }
+\prg_new_protected_conditional:Npnn \regex_match:nn #1#2 { T , F , TF }
+ {
+ \__regex_if_match:nn { \__regex_build:n {#1} } {#2}
+ \__regex_return:
+ }
+\prg_generate_conditional_variant:Nnn \regex_match:nn { nV } { T , F , TF }
+\prg_new_protected_conditional:Npnn \regex_match:Nn #1#2 { T , F , TF }
+ {
+ \__regex_if_match:nn { \__regex_build:N #1 } {#2}
+ \__regex_return:
+ }
+\prg_generate_conditional_variant:Nnn \regex_match:Nn { NV } { T , F , TF }
+\cs_new_protected:Npn \regex_count:nnN #1
+ { \__regex_count:nnN { \__regex_build:n {#1} } }
+\cs_new_protected:Npn \regex_count:NnN #1
+ { \__regex_count:nnN { \__regex_build:N #1 } }
+\cs_generate_variant:Nn \regex_count:nnN { nV }
+\cs_generate_variant:Nn \regex_count:NnN { NV }
+\cs_new_protected:Npn \regex_match_case:nnTF #1#2#3
+ {
+ \__regex_match_case:nnTF {#1} {#2}
+ {
+ \tl_item:nn {#1} { 2 * \g__regex_case_int }
+ #3
+ }
+ }
+\cs_new_protected:Npn \regex_match_case:nn #1#2
+ { \regex_match_case:nnTF {#1} {#2} { } { } }
+\cs_new_protected:Npn \regex_match_case:nnT #1#2#3
+ { \regex_match_case:nnTF {#1} {#2} {#3} { } }
+\cs_new_protected:Npn \regex_match_case:nnF #1#2
+ { \regex_match_case:nnTF {#1} {#2} { } }
+\cs_set_protected:Npn \__regex_tmp:w #1#2#3
+ {
+ \cs_new_protected:Npn #2 ##1 { #1 { \__regex_build:n {##1} } }
+ \cs_new_protected:Npn #3 ##1 { #1 { \__regex_build:N ##1 } }
+ \prg_new_protected_conditional:Npnn #2 ##1##2##3 { T , F , TF }
+ { #1 { \__regex_build:n {##1} } {##2} ##3 \__regex_return: }
+ \prg_new_protected_conditional:Npnn #3 ##1##2##3 { T , F , TF }
+ { #1 { \__regex_build:N ##1 } {##2} ##3 \__regex_return: }
+ \cs_generate_variant:Nn #2 { nV }
+ \prg_generate_conditional_variant:Nnn #2 { nV } { T , F , TF }
+ \cs_generate_variant:Nn #3 { NV }
+ \prg_generate_conditional_variant:Nnn #3 { NV } { T , F , TF }
+ }
+\__regex_tmp:w \__regex_extract_once:nnN
+ \regex_extract_once:nnN \regex_extract_once:NnN
+\__regex_tmp:w \__regex_extract_all:nnN
+ \regex_extract_all:nnN \regex_extract_all:NnN
+\__regex_tmp:w \__regex_replace_once:nnN
+ \regex_replace_once:nnN \regex_replace_once:NnN
+\__regex_tmp:w \__regex_replace_all:nnN
+ \regex_replace_all:nnN \regex_replace_all:NnN
+\__regex_tmp:w \__regex_split:nnN \regex_split:nnN \regex_split:NnN
+\cs_new_protected:Npn \regex_replace_case_once:nNTF #1#2
+ {
+ \int_if_odd:nTF { \tl_count:n {#1} }
+ {
+ \msg_error:nneeee { regex } { case-odd }
+ { \token_to_str:N \regex_replace_case_once:nN(TF) } { code }
+ { \tl_count:n {#1} } { \tl_to_str:n {#1} }
+ \use_ii:nn
+ }
+ {
+ \__regex_replace_once_aux:nnN
+ { \__regex_case_build:e { \__regex_tl_odd_items:n {#1} } }
+ { \__regex_replacement:e { \tl_item:nn {#1} { 2 * \g__regex_case_int } } }
+ #2
+ \bool_if:NTF \g__regex_success_bool
+ }
+ }
+\cs_new_protected:Npn \regex_replace_case_once:nN #1#2
+ { \regex_replace_case_once:nNTF {#1} {#2} { } { } }
+\cs_new_protected:Npn \regex_replace_case_once:nNT #1#2#3
+ { \regex_replace_case_once:nNTF {#1} {#2} {#3} { } }
+\cs_new_protected:Npn \regex_replace_case_once:nNF #1#2
+ { \regex_replace_case_once:nNTF {#1} {#2} { } }
+\cs_new_protected:Npn \regex_replace_case_all:nNTF #1#2
+ {
+ \int_if_odd:nTF { \tl_count:n {#1} }
+ {
+ \msg_error:nneeee { regex } { case-odd }
+ { \token_to_str:N \regex_replace_case_all:nN(TF) } { code }
+ { \tl_count:n {#1} } { \tl_to_str:n {#1} }
+ \use_ii:nn
+ }
+ {
+ \__regex_replace_all_aux:nnN
+ { \__regex_case_build:e { \__regex_tl_odd_items:n {#1} } }
+ { \__regex_case_replacement:e { \__regex_tl_even_items:n {#1} } }
+ #2
+ \bool_if:NTF \g__regex_success_bool
+ }
+ }
+\cs_new_protected:Npn \regex_replace_case_all:nN #1#2
+ { \regex_replace_case_all:nNTF {#1} {#2} { } { } }
+\cs_new_protected:Npn \regex_replace_case_all:nNT #1#2#3
+ { \regex_replace_case_all:nNTF {#1} {#2} {#3} { } }
+\cs_new_protected:Npn \regex_replace_case_all:nNF #1#2
+ { \regex_replace_case_all:nNTF {#1} {#2} { } }
+\int_new:N \l__regex_match_count_int
+\flag_new:N \l__regex_begin_flag
+\flag_new:N \l__regex_end_flag
+\int_new:N \l__regex_min_submatch_int
+\int_new:N \l__regex_submatch_int
+\int_new:N \l__regex_zeroth_submatch_int
+\intarray_new:Nn \g__regex_submatch_prev_intarray { 65536 }
+\intarray_new:Nn \g__regex_submatch_begin_intarray { 65536 }
+\intarray_new:Nn \g__regex_submatch_end_intarray { 65536 }
+\intarray_new:Nn \g__regex_submatch_case_intarray { 65536 }
+\intarray_new:Nn \g__regex_balance_intarray { 65536 }
+\int_new:N \l__regex_added_begin_int
+\int_new:N \l__regex_added_end_int
+\cs_new_protected:Npn \__regex_return:
+ {
+ \if_meaning:w \c_true_bool \g__regex_success_bool
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_query_set:n #1
+ {
+ \int_zero:N \l__regex_balance_int
+ \int_zero:N \l__regex_curr_pos_int
+ \__regex_query_set_aux:nN { } F
+ \tl_analysis_map_inline:nn {#1}
+ { \__regex_query_set_aux:nN {##1} ##3 }
+ \__regex_query_set_aux:nN { } F
+ \int_set_eq:NN \l__regex_max_pos_int \l__regex_curr_pos_int
+ }
+\cs_new_protected:Npn \__regex_query_set_aux:nN #1#2
+ {
+ \int_incr:N \l__regex_curr_pos_int
+ \__regex_toks_set:Nn \l__regex_curr_pos_int {#1}
+ \__kernel_intarray_gset:Nnn \g__regex_balance_intarray
+ \l__regex_curr_pos_int \l__regex_balance_int
+ \if_case:w "#2 \exp_stop_f:
+ \or: \int_incr:N \l__regex_balance_int
+ \or: \int_decr:N \l__regex_balance_int
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_if_match:nn #1#2
+ {
+ \group_begin:
+ \__regex_disable_submatches:
+ \__regex_single_match:
+ #1
+ \__regex_match:n {#2}
+ \group_end:
+ }
+\cs_new_protected:Npn \__regex_match_case:nnTF #1#2
+ {
+ \int_if_odd:nTF { \tl_count:n {#1} }
+ {
+ \msg_error:nneeee { regex } { case-odd }
+ { \token_to_str:N \regex_match_case:nn(TF) } { code }
+ { \tl_count:n {#1} } { \tl_to_str:n {#1} }
+ \use_ii:nn
+ }
+ {
+ \__regex_if_match:nn
+ { \__regex_case_build:e { \__regex_tl_odd_items:n {#1} } }
+ {#2}
+ \bool_if:NTF \g__regex_success_bool
+ }
+ }
+\cs_new:Npn \__regex_match_case_aux:nn #1#2 { \exp_not:n { {#1} } }
+\cs_new_protected:Npn \__regex_count:nnN #1#2#3
+ {
+ \group_begin:
+ \__regex_disable_submatches:
+ \int_zero:N \l__regex_match_count_int
+ \__regex_multi_match:n { \int_incr:N \l__regex_match_count_int }
+ #1
+ \__regex_match:n {#2}
+ \exp_args:NNNo
+ \group_end:
+ \int_set:Nn #3 { \int_use:N \l__regex_match_count_int }
+ }
+\cs_new_protected:Npn \__regex_extract_once:nnN #1#2#3
+ {
+ \group_begin:
+ \__regex_single_match:
+ #1
+ \__regex_match:n {#2}
+ \__regex_extract:
+ \__regex_query_set:n {#2}
+ \__regex_group_end_extract_seq:N #3
+ }
+\cs_new_protected:Npn \__regex_extract_all:nnN #1#2#3
+ {
+ \group_begin:
+ \__regex_multi_match:n { \__regex_extract: }
+ #1
+ \__regex_match:n {#2}
+ \__regex_query_set:n {#2}
+ \__regex_group_end_extract_seq:N #3
+ }
+\cs_new_protected:Npn \__regex_split:nnN #1#2#3
+ {
+ \group_begin:
+ \__regex_multi_match:n
+ {
+ \if_int_compare:w
+ \l__regex_start_pos_int < \l__regex_success_pos_int
+ \__regex_extract:
+ \__kernel_intarray_gset:Nnn \g__regex_submatch_prev_intarray
+ \l__regex_zeroth_submatch_int \c_zero_int
+ \__kernel_intarray_gset:Nnn \g__regex_submatch_end_intarray
+ \l__regex_zeroth_submatch_int
+ {
+ \__kernel_intarray_item:Nn \g__regex_submatch_begin_intarray
+ \l__regex_zeroth_submatch_int
+ }
+ \__kernel_intarray_gset:Nnn \g__regex_submatch_begin_intarray
+ \l__regex_zeroth_submatch_int
+ \l__regex_start_pos_int
+ \fi:
+ }
+ #1
+ \__regex_match:n {#2}
+ \__regex_query_set:n {#2}
+ \__kernel_intarray_gset:Nnn \g__regex_submatch_prev_intarray
+ \l__regex_submatch_int \c_zero_int
+ \__kernel_intarray_gset:Nnn \g__regex_submatch_end_intarray
+ \l__regex_submatch_int
+ \l__regex_max_pos_int
+ \__kernel_intarray_gset:Nnn \g__regex_submatch_begin_intarray
+ \l__regex_submatch_int
+ \l__regex_start_pos_int
+ \int_incr:N \l__regex_submatch_int
+ \if_meaning:w \c_true_bool \l__regex_empty_success_bool
+ \if_int_compare:w \l__regex_start_pos_int = \l__regex_max_pos_int
+ \int_decr:N \l__regex_submatch_int
+ \fi:
+ \fi:
+ \__regex_group_end_extract_seq:N #3
+ }
+\cs_new_protected:Npn \__regex_group_end_extract_seq:N #1
+ {
+ \flag_clear:N \l__regex_begin_flag
+ \flag_clear:N \l__regex_end_flag
+ \cs_set_eq:NN \__regex_tmp:w \scan_stop:
+ \__kernel_tl_gset:Nx \g__regex_internal_tl
+ {
+ \int_step_function:nnN \l__regex_min_submatch_int
+ { \l__regex_submatch_int - \c_one_int } \__regex_extract_seq_aux:n
+ \__regex_tmp:w
+ }
+ \int_set:Nn \l__regex_added_begin_int
+ { \flag_height:N \l__regex_begin_flag }
+ \int_set:Nn \l__regex_added_end_int
+ { \flag_height:N \l__regex_end_flag }
+ \tex_afterassignment:D \__regex_extract_check:w
+ \__kernel_tl_gset:Nx \g__regex_internal_tl
+ { \g__regex_internal_tl \if_false: { \fi: } }
+ \int_compare:nNnT
+ { \l__regex_added_begin_int + \l__regex_added_end_int } > \c_zero_int
+ {
+ \msg_error:nneee { regex } { result-unbalanced }
+ { splitting~or~extracting~submatches }
+ { \int_use:N \l__regex_added_begin_int }
+ { \int_use:N \l__regex_added_end_int }
+ }
+ \group_end:
+ \__regex_extract_seq:N #1
+ }
+\cs_gset_protected:Npn \__regex_extract_seq:N #1
+ {
+ \seq_clear:N #1
+ \cs_set_eq:NN \__regex_tmp:w \__regex_extract_seq_loop:Nw
+ \exp_after:wN \__regex_extract_seq:NNn
+ \exp_after:wN #1
+ \g__regex_internal_tl \use_none:nnn
+ }
+\cs_new_protected:Npn \__regex_extract_seq:NNn #1#2#3
+ { #3 #2 #1 \prg_do_nothing: }
+\cs_new_protected:Npn \__regex_extract_seq_loop:Nw #1#2 \__regex_tmp:w #3
+ {
+ \seq_put_right:No #1 {#2}
+ #3 \__regex_extract_seq_loop:Nw #1 \prg_do_nothing:
+ }
+\cs_new:Npn \__regex_extract_seq_aux:n #1
+ {
+ \__regex_tmp:w { }
+ \exp_after:wN \__regex_extract_seq_aux:ww
+ \int_value:w \__regex_submatch_balance:n {#1} ; #1;
+ }
+\cs_new:Npn \__regex_extract_seq_aux:ww #1; #2;
+ {
+ \if_int_compare:w #1 < \c_zero_int
+ \prg_replicate:nn {-#1}
+ {
+ \flag_raise:N \l__regex_begin_flag
+ \exp_not:n { { \if_false: } \fi: }
+ }
+ \fi:
+ \__regex_query_submatch:n {#2}
+ \if_int_compare:w #1 > \c_zero_int
+ \prg_replicate:nn {#1}
+ {
+ \flag_raise:N \l__regex_end_flag
+ \exp_not:n { \if_false: { \fi: } }
+ }
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_extract_check:w
+ {
+ \exp_after:wN \__regex_extract_check:n
+ \exp_after:wN { \if_false: } \fi:
+ }
+\cs_new_protected:Npn \__regex_extract_check:n #1
+ {
+ \tl_if_empty:nF {#1}
+ {
+ \int_incr:N \l__regex_added_begin_int
+ \int_incr:N \l__regex_added_end_int
+ \tex_afterassignment:D \__regex_extract_check:w
+ \__kernel_tl_gset:Nx \g__regex_internal_tl
+ {
+ \exp_after:wN \__regex_extract_check_loop:w
+ \g__regex_internal_tl
+ \__regex_tmp:w \__regex_extract_check_end:w
+ #1
+ }
+ }
+ }
+\cs_new:Npn \__regex_extract_check_loop:w #1 \__regex_tmp:w #2
+ {
+ #2
+ \exp_not:o {#1}
+ \__regex_tmp:w { }
+ \__regex_extract_check_loop:w \prg_do_nothing:
+ }
+\cs_new:Npn \__regex_extract_check_end:w
+ \exp_not:o #1#2 \__regex_extract_check_loop:w #3 \__regex_tmp:w
+ {
+ { \exp_not:o {#1} }
+ #3
+ \if_false: { \fi: }
+ \__regex_tmp:w
+ }
+\cs_new_protected:Npn \__regex_extract:
+ {
+ \if_meaning:w \c_true_bool \g__regex_success_bool
+ \int_set_eq:NN \l__regex_zeroth_submatch_int \l__regex_submatch_int
+ \prg_replicate:nn \l__regex_capturing_group_int
+ {
+ \__kernel_intarray_gset:Nnn \g__regex_submatch_prev_intarray
+ \l__regex_submatch_int \c_zero_int
+ \__kernel_intarray_gset:Nnn \g__regex_submatch_case_intarray
+ \l__regex_submatch_int \c_zero_int
+ \int_incr:N \l__regex_submatch_int
+ }
+ \__kernel_intarray_gset:Nnn \g__regex_submatch_prev_intarray
+ \l__regex_zeroth_submatch_int \l__regex_start_pos_int
+ \__kernel_intarray_gset:Nnn \g__regex_submatch_case_intarray
+ \l__regex_zeroth_submatch_int \g__regex_case_int
+ \int_zero:N \l__regex_internal_a_int
+ \exp_after:wN \__regex_extract_aux:w \l__regex_success_submatches_tl
+ \prg_break_point: \__regex_use_none_delimit_by_q_recursion_stop:w ,
+ \q__regex_recursion_stop
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_extract_aux:w #1 ,
+ {
+ \prg_break: #1 \prg_break_point:
+ \if_int_compare:w \l__regex_internal_a_int < \l__regex_capturing_group_int
+ \__kernel_intarray_gset:Nnn \g__regex_submatch_begin_intarray
+ { \__regex_int_eval:w \l__regex_zeroth_submatch_int + \l__regex_internal_a_int } {#1}
+ \else:
+ \__kernel_intarray_gset:Nnn \g__regex_submatch_end_intarray
+ {
+ \__regex_int_eval:w
+ \l__regex_zeroth_submatch_int + \l__regex_internal_a_int
+ - \l__regex_capturing_group_int
+ }
+ {#1}
+ \fi:
+ \int_incr:N \l__regex_internal_a_int
+ \__regex_extract_aux:w
+ }
+\cs_new_protected:Npn \__regex_replace_once:nnN #1#2
+ { \__regex_replace_once_aux:nnN {#1} { \__regex_replacement:n {#2} } }
+\cs_new_protected:Npn \__regex_replace_once_aux:nnN #1#2#3
+ {
+ \group_begin:
+ \__regex_single_match:
+ #1
+ \exp_args:No \__regex_match:n {#3}
+ \bool_if:NTF \g__regex_success_bool
+ {
+ \__regex_extract:
+ \exp_args:No \__regex_query_set:n {#3}
+ #2
+ \int_set:Nn \l__regex_balance_int
+ { \__regex_replacement_balance_one_match:n \l__regex_zeroth_submatch_int }
+ \__kernel_tl_set:Nx \l__regex_internal_a_tl
+ {
+ \__regex_replacement_do_one_match:n \l__regex_zeroth_submatch_int
+ \__regex_query_range:nn
+ {
+ \__kernel_intarray_item:Nn \g__regex_submatch_end_intarray
+ \l__regex_zeroth_submatch_int
+ }
+ \l__regex_max_pos_int
+ }
+ \__regex_group_end_replace:N #3
+ }
+ { \group_end: }
+ }
+\cs_new_protected:Npn \__regex_replace_all:nnN #1#2
+ { \__regex_replace_all_aux:nnN {#1} { \__regex_replacement:n {#2} } }
+\cs_new_protected:Npn \__regex_replace_all_aux:nnN #1#2#3
+ {
+ \group_begin:
+ \__regex_multi_match:n { \__regex_extract: }
+ #1
+ \exp_args:No \__regex_match:n {#3}
+ \exp_args:No \__regex_query_set:n {#3}
+ #2
+ \int_set:Nn \l__regex_balance_int
+ {
+ \c_zero_int
+ \int_step_function:nnnN
+ \l__regex_min_submatch_int
+ \l__regex_capturing_group_int
+ { \l__regex_submatch_int - \c_one_int }
+ \__regex_replacement_balance_one_match:n
+ }
+ \__kernel_tl_set:Nx \l__regex_internal_a_tl
+ {
+ \int_step_function:nnnN
+ \l__regex_min_submatch_int
+ \l__regex_capturing_group_int
+ { \l__regex_submatch_int - \c_one_int }
+ \__regex_replacement_do_one_match:n
+ \__regex_query_range:nn
+ \l__regex_start_pos_int \l__regex_max_pos_int
+ }
+ \__regex_group_end_replace:N #3
+ }
+\cs_new_protected:Npn \__regex_group_end_replace:N #1
+ {
+ \int_set:Nn \l__regex_added_begin_int
+ { \int_max:nn { - \l__regex_balance_int } \c_zero_int }
+ \int_set:Nn \l__regex_added_end_int
+ { \int_max:nn \l__regex_balance_int \c_zero_int }
+ \__regex_group_end_replace_try:
+ \int_compare:nNnT { \l__regex_added_begin_int + \l__regex_added_end_int }
+ > \c_zero_int
+ {
+ \msg_error:nneee { regex } { result-unbalanced }
+ { replacing } { \int_use:N \l__regex_added_begin_int }
+ { \int_use:N \l__regex_added_end_int }
+ }
+ \group_end:
+ \tl_set_eq:NN #1 \g__regex_internal_tl
+ }
+\cs_new_protected:Npn \__regex_group_end_replace_try:
+ {
+ \tex_afterassignment:D \__regex_group_end_replace_check:w
+ \__kernel_tl_gset:Nx \g__regex_internal_tl
+ {
+ \prg_replicate:nn \l__regex_added_begin_int { { \if_false: } \fi: }
+ \l__regex_internal_a_tl
+ \prg_replicate:nn \l__regex_added_end_int { \if_false: { \fi: } }
+ \if_false: { \fi: }
+ }
+ }
+\cs_new_protected:Npn \__regex_group_end_replace_check:w
+ {
+ \exp_after:wN \__regex_group_end_replace_check:n
+ \exp_after:wN { \if_false: } \fi:
+ }
+\cs_new_protected:Npn \__regex_group_end_replace_check:n #1
+ {
+ \tl_if_empty:nF {#1}
+ {
+ \int_incr:N \l__regex_added_begin_int
+ \int_incr:N \l__regex_added_end_int
+ \__regex_group_end_replace_try:
+ }
+ }
+\tl_new:N \l__regex_peek_true_tl
+\tl_new:N \l__regex_peek_false_tl
+\tl_new:N \l__regex_replacement_tl
+\tl_new:N \l__regex_input_tl
+\cs_new_eq:NN \__regex_input_item:n ?
+\cs_new_protected:Npn \peek_regex:nTF #1
+ {
+ \__regex_peek:nnTF
+ { \__regex_build_aux:Nn \c_false_bool {#1} }
+ { \__regex_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
+ {
+ \__regex_peek:nnTF
+ { \__regex_build_aux:NN \c_false_bool #1 }
+ { \__regex_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
+ {
+ \__regex_peek:nnTF
+ { \__regex_build_aux:Nn \c_false_bool {#1} }
+ { \__regex_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
+ {
+ \__regex_peek:nnTF
+ { \__regex_build_aux:NN \c_false_bool #1 }
+ { \__regex_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 \__regex_peek:nnTF #1
+ {
+ \__regex_peek_aux:nnTF
+ {
+ \__regex_disable_submatches:
+ #1
+ }
+ }
+\cs_new_protected:Npn \__regex_peek_aux:nnTF #1#2#3#4
+ {
+ \group_begin:
+ \tl_set:Nn \l__regex_peek_true_tl { \group_end: #3 }
+ \tl_set:Nn \l__regex_peek_false_tl { \group_end: #4 }
+ \__regex_single_match:
+ #1
+ \__regex_match_init:
+ \tl_build_begin:N \l__regex_input_tl
+ \__regex_match_once_init:
+ \peek_analysis_map_inline:n
+ {
+ \tl_build_put_right:Nn \l__regex_input_tl
+ { \__regex_input_item:n {##1} }
+ \__regex_match_one_token:nnN {##1} {##2} ##3
+ \use_none:nnn
+ \prg_break_point:Nn \__regex_maplike_break:
+ { \peek_analysis_map_break:n {#2} }
+ }
+ }
+\cs_new_protected:Npn \__regex_peek_end:
+ {
+ \bool_if:NTF \g__regex_success_bool
+ { \__regex_peek_reinsert:N \l__regex_peek_true_tl }
+ { \__regex_peek_reinsert:N \l__regex_peek_false_tl }
+ }
+\cs_new_protected:Npn \__regex_peek_remove_end:n #1
+ {
+ \bool_if:NTF \g__regex_success_bool
+ { \exp_args:NNo \use:nn \l__regex_peek_true_tl {#1} }
+ { \__regex_peek_reinsert:N \l__regex_peek_false_tl }
+ }
+\cs_new_protected:Npn \__regex_peek_reinsert:N #1
+ {
+ \tl_build_end:N \l__regex_input_tl
+ \cs_set_eq:NN \__regex_input_item:n \__regex_reinsert_item:n
+ \exp_after:wN #1 \exp:w \l__regex_input_tl \exp_end:
+ }
+\cs_new_protected:Npn \__regex_reinsert_item:n #1
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \exp_end:
+ \exp_after:wN \exp_after:wN
+ #1
+ \exp:w
+ }
+\cs_new_protected:Npn \peek_regex_replace_once:nnTF #1
+ { \__regex_peek_replace:nnTF { \__regex_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
+ { \__regex_peek_replace:nnTF { \__regex_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 \__regex_peek_replace:nnTF #1#2
+ {
+ \tl_set:Nn \l__regex_replacement_tl {#2}
+ \__regex_peek_aux:nnTF {#1} { \__regex_peek_replace_end: }
+ }
+\cs_new_protected:Npn \__regex_peek_replace_end:
+ {
+ \bool_if:NTF \g__regex_success_bool
+ {
+ \__regex_extract:
+ \__regex_query_set_from_input_tl:
+ \cs_set_eq:NN \__regex_replacement_put:n \__regex_peek_replacement_put:n
+ \cs_set_eq:NN \__regex_replacement_put_submatch_aux:n
+ \__regex_peek_replacement_put_submatch_aux:n
+ \cs_set_eq:NN \__regex_input_item:n \__regex_reinsert_item:n
+ \cs_set_eq:NN \__regex_replacement_exp_not:N \__regex_peek_replacement_token:n
+ \cs_set_eq:NN \__regex_replacement_exp_not:V \__regex_peek_replacement_var:N
+ \exp_args:No \__regex_replacement:n { \l__regex_replacement_tl }
+ \use:e
+ {
+ \exp_not:n { \exp_after:wN \l__regex_peek_true_tl \exp:w }
+ \__regex_replacement_do_one_match:n \l__regex_zeroth_submatch_int
+ \__regex_query_range:nn
+ {
+ \__kernel_intarray_item:Nn \g__regex_submatch_end_intarray
+ \l__regex_zeroth_submatch_int
+ }
+ \l__regex_max_pos_int
+ \exp_end:
+ }
+ }
+ { \__regex_peek_reinsert:N \l__regex_peek_false_tl }
+ }
+\cs_new_protected:Npn \__regex_query_set_from_input_tl:
+ {
+ \tl_build_end:N \l__regex_input_tl
+ \int_zero:N \l__regex_curr_pos_int
+ \cs_set_eq:NN \__regex_input_item:n \__regex_query_set_item:n
+ \__regex_query_set_item:n { }
+ \l__regex_input_tl
+ \__regex_query_set_item:n { }
+ \int_set_eq:NN \l__regex_max_pos_int \l__regex_curr_pos_int
+ }
+\cs_new_protected:Npn \__regex_query_set_item:n #1
+ {
+ \int_incr:N \l__regex_curr_pos_int
+ \__regex_toks_set:Nn \l__regex_curr_pos_int { \__regex_input_item:n {#1} }
+ }
+\cs_new_protected:Npn \__regex_peek_replacement_put:n #1
+ {
+ \if_case:w \l__regex_replacement_csnames_int
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \exp_not:N \__regex_reinsert_item:n {#1} }
+ \else:
+ \tl_build_put_right:Nn \l__regex_build_tl {#1}
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_peek_replacement_token:n #1
+ { \exp_after:wN \exp_end: \exp_after:wN #1 \exp:w }
+\cs_new_protected:Npn \__regex_peek_replacement_put_submatch_aux:n #1
+ {
+ \if_case:w \l__regex_replacement_csnames_int
+ \tl_build_put_right:Nn \l__regex_build_tl
+ { \__regex_query_submatch:n { \__regex_int_eval:w #1 + ##1 \scan_stop: } }
+ \else:
+ \tl_build_put_right:Nn \l__regex_build_tl
+ {
+ \exp:w
+ \__regex_query_submatch:n { \__regex_int_eval:w #1 + ##1 \scan_stop: }
+ \exp_end:
+ }
+ \fi:
+ }
+\cs_new_protected:Npn \__regex_peek_replacement_var:N #1
+ {
+ \exp_after:wN \exp_last_unbraced:NV
+ \exp_after:wN \exp_end:
+ \exp_after:wN #1
+ \exp:w
+ }
+\use:e
+ {
+ \msg_new:nnn { regex } { trailing-backslash }
+ { Trailing~'\iow_char:N\\'~in~regex~or~replacement. }
+ \msg_new:nnn { regex } { x-missing-rbrace }
+ {
+ Missing~brace~'\iow_char:N\}'~in~regex~
+ '...\iow_char:N\\x\iow_char:N\{...##1'.
+ }
+ \msg_new:nnn { regex } { x-overflow }
+ {
+ Character~code~##1~too~large~in~
+ \iow_char:N\\x\iow_char:N\{##2\iow_char:N\}~regex.
+ }
+ }
+\msg_new:nnnn { regex } { invalid-quantifier }
+ { Braced~quantifier~'#1'~may~not~be~followed~by~'#2'. }
+ {
+ The~character~'#2'~is~invalid~in~the~braced~quantifier~'#1'.~
+ The~only~valid~quantifiers~are~'*',~'?',~'+',~'{<int>}',~
+ '{<min>,}'~and~'{<min>,<max>}',~optionally~followed~by~'?'.
+ }
+\msg_new:nnnn { regex } { missing-rbrack }
+ { Missing~right~bracket~inserted~in~regular~expression. }
+ {
+ LaTeX~was~given~a~regular~expression~where~a~character~class~
+ was~started~with~'[',~but~the~matching~']'~is~missing.
+ }
+\msg_new:nnnn { regex } { missing-rparen }
+ {
+ Missing~right~
+ \int_compare:nTF { #1 = 1 } { parenthesis } { parentheses } ~
+ inserted~in~regular~expression.
+ }
+ {
+ LaTeX~was~given~a~regular~expression~with~\int_eval:n {#1} ~
+ more~left~parentheses~than~right~parentheses.
+ }
+\msg_new:nnnn { regex } { extra-rparen }
+ { Extra~right~parenthesis~ignored~in~regular~expression. }
+ {
+ LaTeX~came~across~a~closing~parenthesis~when~no~submatch~group~
+ was~open.~The~parenthesis~will~be~ignored.
+ }
+\msg_new:nnnn { regex } { bad-escape }
+ {
+ Invalid~escape~'\iow_char:N\\#1'~
+ \__regex_if_in_cs:TF { within~a~control~sequence. }
+ {
+ \__regex_if_in_class:TF
+ { in~a~character~class. }
+ { following~a~category~test. }
+ }
+ }
+ {
+ The~escape~sequence~'\iow_char:N\\#1'~may~not~appear~
+ \__regex_if_in_cs:TF
+ {
+ within~a~control~sequence~test~introduced~by~
+ '\iow_char:N\\c\iow_char:N\{'.
+ }
+ {
+ \__regex_if_in_class:TF
+ { within~a~character~class~ }
+ { following~a~category~test~such~as~'\iow_char:N\\cL'~ }
+ because~it~does~not~match~exactly~one~character.
+ }
+ }
+\msg_new:nnnn { regex } { range-missing-end }
+ { Invalid~end-point~for~range~'#1-#2'~in~character~class. }
+ {
+ The~end-point~'#2'~of~the~range~'#1-#2'~may~not~serve~as~an~
+ end-point~for~a~range:~alphanumeric~characters~should~not~be~
+ escaped,~and~non-alphanumeric~characters~should~be~escaped.
+ }
+\msg_new:nnnn { regex } { range-backwards }
+ { Range~'[#1-#2]'~out~of~order~in~character~class. }
+ {
+ In~ranges~of~characters~'[x-y]'~appearing~in~character~classes,~
+ the~first~character~code~must~not~be~larger~than~the~second.~
+ Here,~'#1'~has~character~code~\int_eval:n {`#1},~while~
+ '#2'~has~character~code~\int_eval:n {`#2}.
+ }
+\msg_new:nnnn { regex } { c-bad-mode }
+ { Invalid~nested~'\iow_char:N\\c'~escape~in~regular~expression. }
+ {
+ The~'\iow_char:N\\c'~escape~cannot~be~used~within~
+ a~control~sequence~test~'\iow_char:N\\c{...}'~
+ nor~another~category~test.~
+ To~combine~several~category~tests,~use~'\iow_char:N\\c[...]'.
+ }
+\msg_new:nnnn { regex } { c-C-invalid }
+ { '\iow_char:N\\cC'~should~be~followed~by~'.'~or~'(',~not~'#1'. }
+ {
+ The~'\iow_char:N\\cC'~construction~restricts~the~next~item~to~be~a~
+ control~sequence~or~the~next~group~to~be~made~of~control~sequences.~
+ It~only~makes~sense~to~follow~it~by~'.'~or~by~a~group.
+ }
+\msg_new:nnnn { regex } { cu-lbrace }
+ { Left~braces~must~be~escaped~in~'\iow_char:N\\#1{...}'. }
+ {
+ Constructions~such~as~'\iow_char:N\\#1{...\iow_char:N\{...}'~are~
+ not~allowed~and~should~be~replaced~by~
+ '\iow_char:N\\#1{...\token_to_str:N\{...}'.
+ }
+\msg_new:nnnn { regex } { c-lparen-in-class }
+ { Catcode~test~cannot~apply~to~group~in~character~class }
+ {
+ Construction~such~as~'\iow_char:N\\cL(abc)'~are~not~allowed~inside~a~
+ class~'[...]'~because~classes~do~not~match~multiple~characters~at~once.
+ }
+\msg_new:nnnn { regex } { c-missing-rbrace }
+ { Missing~right~brace~inserted~for~'\iow_char:N\\c'~escape. }
+ {
+ LaTeX~was~given~a~regular~expression~where~a~
+ '\iow_char:N\\c\iow_char:N\{...'~construction~was~not~ended~
+ with~a~closing~brace~'\iow_char:N\}'.
+ }
+\msg_new:nnnn { regex } { c-missing-rbrack }
+ { Missing~right~bracket~inserted~for~'\iow_char:N\\c'~escape. }
+ {
+ A~construction~'\iow_char:N\\c[...'~appears~in~a~
+ regular~expression,~but~the~closing~']'~is~not~present.
+ }
+\msg_new:nnnn { regex } { c-missing-category }
+ { Invalid~character~'#1'~following~'\iow_char:N\\c'~escape. }
+ {
+ In~regular~expressions,~the~'\iow_char:N\\c'~escape~sequence~
+ may~only~be~followed~by~a~left~brace,~a~left~bracket,~or~a~
+ capital~letter~representing~a~character~category,~namely~
+ one~of~'ABCDELMOPSTU'.
+ }
+\msg_new:nnnn { regex } { c-trailing }
+ { Trailing~category~code~escape~'\iow_char:N\\c'... }
+ {
+ A~regular~expression~ends~with~'\iow_char:N\\c'~followed~
+ by~a~letter.~It~will~be~ignored.
+ }
+\msg_new:nnnn { regex } { u-missing-lbrace }
+ { Missing~left~brace~following~'\iow_char:N\\u'~escape. }
+ {
+ The~'\iow_char:N\\u'~escape~sequence~must~be~followed~by~
+ a~brace~group~with~the~name~of~the~variable~to~use.
+ }
+\msg_new:nnnn { regex } { u-missing-rbrace }
+ { Missing~right~brace~inserted~for~'\iow_char:N\\u'~escape. }
+ {
+ LaTeX~
+ \str_if_eq:eeTF { } {#2}
+ { reached~the~end~of~the~string~ }
+ { encountered~an~escaped~alphanumeric~character '\iow_char:N\\#2'~ }
+ when~parsing~the~argument~of~an~
+ '\iow_char:N\\u\iow_char:N\{...\}'~escape.
+ }
+\msg_new:nnnn { regex } { posix-unsupported }
+ { POSIX~collating~element~'[#1 ~ #1]'~not~supported. }
+ {
+ The~'[.foo.]'~and~'[=bar=]'~syntaxes~have~a~special~meaning~
+ in~POSIX~regular~expressions.~This~is~not~supported~by~LaTeX.~
+ Maybe~you~forgot~to~escape~a~left~bracket~in~a~character~class?
+ }
+\msg_new:nnnn { regex } { posix-unknown }
+ { POSIX~class~'[:#1:]'~unknown. }
+ {
+ '[:#1:]'~is~not~among~the~known~POSIX~classes~
+ '[:alnum:]',~'[:alpha:]',~'[:ascii:]',~'[:blank:]',~
+ '[:cntrl:]',~'[:digit:]',~'[:graph:]',~'[:lower:]',~
+ '[:print:]',~'[:punct:]',~'[:space:]',~'[:upper:]',~
+ '[:word:]',~and~'[:xdigit:]'.
+ }
+\msg_new:nnnn { regex } { posix-missing-close }
+ { Missing~closing~':]'~for~POSIX~class. }
+ { The~POSIX~syntax~'#1'~must~be~followed~by~':]',~not~'#2'. }
+\msg_new:nnnn { regex } { result-unbalanced }
+ { Missing~brace~inserted~when~#1. }
+ {
+ LaTeX~was~asked~to~do~some~regular~expression~operation,~
+ and~the~resulting~token~list~would~not~have~the~same~number~
+ of~begin-group~and~end-group~tokens.~Braces~were~inserted:~
+ #2~left,~#3~right.
+ }
+\msg_new:nnnn { regex } { unknown-option }
+ { Unknown~option~'#1'~for~regular~expressions. }
+ {
+ The~only~available~option~is~'case-insensitive',~toggled~by~
+ '(?i)'~and~'(?-i)'.
+ }
+\msg_new:nnnn { regex } { special-group-unknown }
+ { Unknown~special~group~'#1~...'~in~a~regular~expression. }
+ {
+ The~only~valid~constructions~starting~with~'(?'~are~
+ '(?:~...~)',~'(?|~...~)',~'(?i)',~and~'(?-i)'.
+ }
+\msg_new:nnnn { regex } { replacement-c }
+ { Misused~'\iow_char:N\\c'~command~in~a~replacement~text. }
+ {
+ In~a~replacement~text,~the~'\iow_char:N\\c'~escape~sequence~
+ can~be~followed~by~one~of~the~letters~'ABCDELMOPSTU'~
+ or~a~brace~group,~not~by~'#1'.
+ }
+\msg_new:nnnn { regex } { replacement-u }
+ { Misused~'\iow_char:N\\u'~command~in~a~replacement~text. }
+ {
+ In~a~replacement~text,~the~'\iow_char:N\\u'~escape~sequence~
+ must~be~~followed~by~a~brace~group~holding~the~name~of~the~
+ variable~to~use.
+ }
+\msg_new:nnnn { regex } { replacement-g }
+ {
+ Missing~brace~for~the~'\iow_char:N\\g'~construction~
+ in~a~replacement~text.
+ }
+ {
+ In~the~replacement~text~for~a~regular~expression~search,~
+ submatches~are~represented~either~as~'\iow_char:N \\g{dd..d}',~
+ or~'\\d',~where~'d'~are~single~digits.~Here,~a~brace~is~missing.
+ }
+\msg_new:nnnn { regex } { replacement-catcode-end }
+ {
+ Missing~character~for~the~'\iow_char:N\\c<category><character>'~
+ construction~in~a~replacement~text.
+ }
+ {
+ In~a~replacement~text,~the~'\iow_char:N\\c'~escape~sequence~
+ can~be~followed~by~one~of~the~letters~'ABCDELMOPSTU'~representing~
+ the~character~category.~Then,~a~character~must~follow.~LaTeX~
+ reached~the~end~of~the~replacement~when~looking~for~that.
+ }
+\msg_new:nnnn { regex } { replacement-catcode-escaped }
+ {
+ Escaped~letter~or~digit~after~category~code~in~replacement~text.
+ }
+ {
+ In~a~replacement~text,~the~'\iow_char:N\\c'~escape~sequence~
+ can~be~followed~by~one~of~the~letters~'ABCDELMOPSTU'~representing~
+ the~character~category.~Then,~a~character~must~follow,~not~
+ '\iow_char:N\\#2'.
+ }
+\msg_new:nnnn { regex } { replacement-catcode-in-cs }
+ {
+ Category~code~'\iow_char:N\\c#1#3'~ignored~inside~
+ '\iow_char:N\\c\{...\}'~in~a~replacement~text.
+ }
+ {
+ In~a~replacement~text,~the~category~codes~of~the~argument~of~
+ '\iow_char:N\\c\{...\}'~are~ignored~when~building~the~control~
+ sequence~name.
+ }
+\msg_new:nnnn { regex } { replacement-null-space }
+ { TeX~cannot~build~a~space~token~with~character~code~0. }
+ {
+ You~asked~for~a~character~token~with~category~space,~
+ and~character~code~0,~for~instance~through~
+ '\iow_char:N\\cS\iow_char:N\\x00'.~
+ This~specific~case~is~impossible~and~will~be~replaced~
+ by~a~normal~space.
+ }
+\msg_new:nnnn { regex } { replacement-missing-rbrace }
+ { Missing~right~brace~inserted~in~replacement~text. }
+ {
+ There~ \int_compare:nTF { #1 = 1 } { was } { were } ~ #1~
+ missing~right~\int_compare:nTF { #1 = 1 } { brace } { braces } .
+ }
+\msg_new:nnnn { regex } { replacement-missing-rparen }
+ { Missing~right~parenthesis~inserted~in~replacement~text. }
+ {
+ There~ \int_compare:nTF { #1 = 1 } { was } { were } ~ #1~
+ missing~right~
+ \int_compare:nTF { #1 = 1 } { parenthesis } { parentheses } .
+ }
+\msg_new:nnn { regex } { submatch-too-big }
+ { Submatch~#1~used~but~regex~only~has~#2~group(s) }
+\msg_new:nnnn { regex } { backwards-quantifier }
+ { Quantifer~"{#1,#2}"~is~backwards. }
+ { The~values~given~in~a~quantifier~must~be~in~order. }
+\msg_new:nnnn { regex } { case-odd }
+ { #1~with~odd~number~of~items }
+ {
+ There~must~be~a~#2~part~for~each~regex:~
+ found~odd~number~of~items~(#3)~in\\
+ \iow_indent:n {#4}
+ }
+\msg_new:nnn { regex } { show }
+ {
+ >~Compiled~regex~
+ \tl_if_empty:nTF {#1} { variable~ #2 } { {#1} } :
+ #3
+ }
+\prop_gput:Nnn \g_msg_module_name_prop { regex } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { regex } { }
+\cs_new:Npn \__regex_msg_repeated:nnN #1#2#3
+ {
+ \str_if_eq:eeF { #1 #2 } { 1 0 }
+ {
+ , ~ repeated ~
+ \int_case:nnF {#2}
+ {
+ { -1 } { #1~or~more~times,~\bool_if:NTF #3 { lazy } { greedy } }
+ { 0 } { #1~times }
+ }
+ {
+ between~#1~and~\int_eval:n {#1+#2}~times,~
+ \bool_if:NTF #3 { lazy } { greedy }
+ }
+ }
+ }
+\cs_new_protected:Npn \__regex_trace_push:nnN #1#2#3
+ { \__regex_trace:nne {#1} {#2} { entering~ \token_to_str:N #3 } }
+\cs_new_protected:Npn \__regex_trace_pop:nnN #1#2#3
+ { \__regex_trace:nne {#1} {#2} { leaving~ \token_to_str:N #3 } }
+\cs_new_protected:Npn \__regex_trace:nne #1#2#3
+ {
+ \int_compare:nNnF
+ { \int_use:c { g__regex_trace_#1_int } } < {#2}
+ { \iow_term:e { Trace:~#3 } }
+ }
+\int_new:N \g__regex_trace_regex_int
+\cs_new_protected:Npn \__regex_trace_states:n #1
+ {
+ \int_step_inline:nnn
+ \l__regex_min_state_int
+ { \l__regex_max_state_int - \c_one_int }
+ {
+ \__regex_trace:nne { regex } {#1}
+ { \iow_char:N \\toks ##1 = { \__regex_toks_use:w ##1 } }
+ }
+ }
+%% File: l3box.dtx
+\cs_new_eq:NN \__box_dim_eval:w \tex_dimexpr:D
+\cs_new:Npn \__box_dim_eval:n #1
+ { \__box_dim_eval:w #1 \scan_stop: }
+\cs_new_protected:Npn \__kernel_kern:n #1
+ { \tex_kern:D \__box_dim_eval:n {#1} }
+\cs_new_protected:Npn \box_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs:w newbox \cs_end: #1
+ }
+\cs_generate_variant:Nn \box_new:N { c }
+\cs_new_protected:Npn \box_clear:N #1
+ { \box_set_eq:NN #1 \c_empty_box }
+\cs_new_protected:Npn \box_gclear:N #1
+ { \box_gset_eq:NN #1 \c_empty_box }
+\cs_generate_variant:Nn \box_clear:N { c }
+\cs_generate_variant:Nn \box_gclear:N { c }
+\cs_new_protected:Npn \box_clear_new:N #1
+ { \box_if_exist:NTF #1 { \box_clear:N #1 } { \box_new:N #1 } }
+\cs_new_protected:Npn \box_gclear_new:N #1
+ { \box_if_exist:NTF #1 { \box_gclear:N #1 } { \box_new:N #1 } }
+\cs_generate_variant:Nn \box_clear_new:N { c }
+\cs_generate_variant:Nn \box_gclear_new:N { c }
+\cs_new_protected:Npn \box_set_eq:NN #1#2
+ { \tex_setbox:D #1 \tex_copy:D #2 }
+\cs_new_protected:Npn \box_gset_eq:NN #1#2
+ { \tex_global:D \tex_setbox:D #1 \tex_copy:D #2 }
+\cs_generate_variant:Nn \box_set_eq:NN { c , Nc , cc }
+\cs_generate_variant:Nn \box_gset_eq:NN { c , Nc , cc }
+\cs_new_protected:Npn \box_set_eq_drop:NN #1#2
+ { \tex_setbox:D #1 \tex_box:D #2 }
+\cs_new_protected:Npn \box_gset_eq_drop:NN #1#2
+ { \tex_global:D \tex_setbox:D #1 \tex_box:D #2 }
+\cs_generate_variant:Nn \box_set_eq_drop:NN { c , Nc , cc }
+\cs_generate_variant:Nn \box_gset_eq_drop:NN { c , Nc , cc }
+\prg_new_eq_conditional:NNn \box_if_exist:N \cs_if_exist:N
+ { TF , T , F , p }
+\prg_new_eq_conditional:NNn \box_if_exist:c \cs_if_exist:c
+ { TF , T , F , p }
+\cs_new_eq:NN \box_ht:N \tex_ht:D
+\cs_new_eq:NN \box_dp:N \tex_dp:D
+\cs_new_eq:NN \box_wd:N \tex_wd:D
+\cs_generate_variant:Nn \box_ht:N { c }
+\cs_generate_variant:Nn \box_dp:N { c }
+\cs_generate_variant:Nn \box_wd:N { c }
+\cs_new_protected:Npn \box_ht_plus_dp:N #1
+ { \__box_dim_eval:n { \box_ht:N #1 + \box_dp:N #1 } }
+\cs_generate_variant:Nn \box_ht_plus_dp:N { c }
+\cs_new_protected:Npn \box_set_dp:Nn #1#2
+ {
+ \tex_setbox:D #1 = \tex_copy:D #1
+ \box_dp:N #1 \__box_dim_eval:n {#2}
+ }
+\cs_generate_variant:Nn \box_set_dp:Nn { c }
+\cs_new_protected:Npn \box_gset_dp:Nn #1#2
+ { \box_dp:N #1 \__box_dim_eval:n {#2} }
+\cs_generate_variant:Nn \box_gset_dp:Nn { c }
+\cs_new_protected:Npn \box_set_ht:Nn #1#2
+ {
+ \tex_setbox:D #1 = \tex_copy:D #1
+ \box_ht:N #1 \__box_dim_eval:n {#2}
+ }
+\cs_generate_variant:Nn \box_set_ht:Nn { c }
+\cs_new_protected:Npn \box_gset_ht:Nn #1#2
+ { \box_ht:N #1 \__box_dim_eval:n {#2} }
+\cs_generate_variant:Nn \box_gset_ht:Nn { c }
+\cs_new_protected:Npn \box_set_wd:Nn #1#2
+ {
+ \tex_setbox:D #1 = \tex_copy:D #1
+ \box_wd:N #1 \__box_dim_eval:n {#2}
+ }
+\cs_generate_variant:Nn \box_set_wd:Nn { c }
+\cs_new_protected:Npn \box_gset_wd:Nn #1#2
+ { \box_wd:N #1 \__box_dim_eval:n {#2} }
+\cs_generate_variant:Nn \box_gset_wd:Nn { c }
+\cs_new_eq:NN \box_use_drop:N \tex_box:D
+\cs_new_eq:NN \box_use:N \tex_copy:D
+\cs_generate_variant:Nn \box_use_drop:N { c }
+\cs_generate_variant:Nn \box_use:N { c }
+\cs_new_protected:Npn \box_move_left:nn #1#2
+ { \tex_moveleft:D \__box_dim_eval:n {#1} #2 }
+\cs_new_protected:Npn \box_move_right:nn #1#2
+ { \tex_moveright:D \__box_dim_eval:n {#1} #2 }
+\cs_new_protected:Npn \box_move_up:nn #1#2
+ { \tex_raise:D \__box_dim_eval:n {#1} #2 }
+\cs_new_protected:Npn \box_move_down:nn #1#2
+ { \tex_lower:D \__box_dim_eval:n {#1} #2 }
+\cs_new_eq:NN \if_hbox:N \tex_ifhbox:D
+\cs_new_eq:NN \if_vbox:N \tex_ifvbox:D
+\cs_new_eq:NN \if_box_empty:N \tex_ifvoid:D
+\prg_new_conditional:Npnn \box_if_horizontal:N #1 { p , T , F , TF }
+ { \if_hbox:N #1 \prg_return_true: \else: \prg_return_false: \fi: }
+\prg_new_conditional:Npnn \box_if_vertical:N #1 { p , T , F , TF }
+ { \if_vbox:N #1 \prg_return_true: \else: \prg_return_false: \fi: }
+\prg_generate_conditional_variant:Nnn \box_if_horizontal:N
+ { c } { p , T , F , TF }
+\prg_generate_conditional_variant:Nnn \box_if_vertical:N
+ { c } { p , T , F , TF }
+\prg_new_conditional:Npnn \box_if_empty:N #1 { p , T , F , TF }
+ { \if_box_empty:N #1 \prg_return_true: \else: \prg_return_false: \fi: }
+\prg_generate_conditional_variant:Nnn \box_if_empty:N
+ { c } { p , T , F , TF }
+\cs_new_protected:Npn \box_set_to_last:N #1
+ { \tex_setbox:D #1 \tex_lastbox:D }
+\cs_new_protected:Npn \box_gset_to_last:N #1
+ { \tex_global:D \tex_setbox:D #1 \tex_lastbox:D }
+\cs_generate_variant:Nn \box_set_to_last:N { c }
+\cs_generate_variant:Nn \box_gset_to_last:N { c }
+\box_new:N \c_empty_box
+\box_new:N \l_tmpa_box
+\box_new:N \l_tmpb_box
+\box_new:N \g_tmpa_box
+\box_new:N \g_tmpb_box
+\cs_new_protected:Npn \box_show:N #1
+ { \box_show:Nnn #1 \c_max_int \c_max_int }
+\cs_generate_variant:Nn \box_show:N { c }
+\cs_new_protected:Npn \box_show:Nnn #1#2#3
+ { \__box_show:NNff 1 #1 { \int_eval:n {#2} } { \int_eval:n {#3} } }
+\cs_generate_variant:Nn \box_show:Nnn { c }
+\cs_new_protected:Npn \box_log:N #1
+ { \box_log:Nnn #1 \c_max_int \c_max_int }
+\cs_generate_variant:Nn \box_log:N { c }
+\cs_new_protected:Npn \box_log:Nnn
+ { \exp_args:No \__box_log:nNnn { \tex_the:D \tex_interactionmode:D } }
+\cs_new_protected:Npn \__box_log:nNnn #1#2#3#4
+ {
+ \int_gset:Nn \tex_interactionmode:D { 0 }
+ \__box_show:NNff 0 #2 { \int_eval:n {#3} } { \int_eval:n {#4} }
+ \int_gset:Nn \tex_interactionmode:D {#1}
+ }
+\cs_generate_variant:Nn \box_log:Nnn { c }
+\cs_new_protected:Npn \__box_show:NNnn #1#2#3#4
+ {
+ \box_if_exist:NTF #2
+ {
+ \group_begin:
+ \int_set:Nn \tex_showboxbreadth:D {#3}
+ \int_set:Nn \tex_showboxdepth:D {#4}
+ \int_set:Nn \tex_tracingonline:D {#1}
+ \int_set:Nn \tex_errorcontextlines:D { -1 }
+ \tex_showbox:D \use:n {#2}
+ \group_end:
+ }
+ {
+ \msg_error:nne { kernel } { variable-not-defined }
+ { \token_to_str:N #2 }
+ }
+ }
+\cs_generate_variant:Nn \__box_show:NNnn { NNff }
+\cs_new_protected:Npn \hbox:n #1
+ { \tex_hbox:D \scan_stop: { \color_group_begin: #1 \color_group_end: } }
+\cs_new_protected:Npn \hbox_set:Nn #1#2
+ {
+ \tex_setbox:D #1 \tex_hbox:D
+ { \color_group_begin: #2 \color_group_end: }
+ }
+\cs_new_protected:Npn \hbox_gset:Nn #1#2
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_hbox:D
+ { \color_group_begin: #2 \color_group_end: }
+ }
+\cs_generate_variant:Nn \hbox_set:Nn { c }
+\cs_generate_variant:Nn \hbox_gset:Nn { c }
+\cs_new_protected:Npn \hbox_set_to_wd:Nnn #1#2#3
+ {
+ \tex_setbox:D #1 \tex_hbox:D to \__box_dim_eval:n {#2}
+ { \color_group_begin: #3 \color_group_end: }
+ }
+\cs_new_protected:Npn \hbox_gset_to_wd:Nnn #1#2#3
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_hbox:D to \__box_dim_eval:n {#2}
+ { \color_group_begin: #3 \color_group_end: }
+ }
+\cs_generate_variant:Nn \hbox_set_to_wd:Nnn { c }
+\cs_generate_variant:Nn \hbox_gset_to_wd:Nnn { c }
+\cs_new_protected:Npn \hbox_set:Nw #1
+ {
+ \tex_setbox:D #1 \tex_hbox:D
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\cs_new_protected:Npn \hbox_gset:Nw #1
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_hbox:D
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\cs_generate_variant:Nn \hbox_set:Nw { c }
+\cs_generate_variant:Nn \hbox_gset:Nw { c }
+\cs_new_protected:Npn \hbox_set_end:
+ {
+ \color_group_end:
+ \c_group_end_token
+ }
+\cs_new_eq:NN \hbox_gset_end: \hbox_set_end:
+\cs_new_protected:Npn \hbox_set_to_wd:Nnw #1#2
+ {
+ \tex_setbox:D #1 \tex_hbox:D to \__box_dim_eval:n {#2}
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\cs_new_protected:Npn \hbox_gset_to_wd:Nnw #1#2
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_hbox:D to \__box_dim_eval:n {#2}
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\cs_generate_variant:Nn \hbox_set_to_wd:Nnw { c }
+\cs_generate_variant:Nn \hbox_gset_to_wd:Nnw { c }
+\cs_new_protected:Npn \hbox_to_wd:nn #1#2
+ {
+ \tex_hbox:D to \__box_dim_eval:n {#1}
+ { \color_group_begin: #2 \color_group_end: }
+ }
+\cs_new_protected:Npn \hbox_to_zero:n #1
+ {
+ \tex_hbox:D to \c_zero_dim
+ { \color_group_begin: #1 \color_group_end: }
+ }
+\cs_new_protected:Npn \hbox_overlap_center:n #1
+ { \hbox_to_zero:n { \tex_hss:D #1 \tex_hss:D } }
+\cs_new_protected:Npn \hbox_overlap_left:n #1
+ { \hbox_to_zero:n { \tex_hss:D #1 } }
+\cs_new_protected:Npn \hbox_overlap_right:n #1
+ { \hbox_to_zero:n { #1 \tex_hss:D } }
+\cs_new_eq:NN \hbox_unpack:N \tex_unhcopy:D
+\cs_new_eq:NN \hbox_unpack_drop:N \tex_unhbox:D
+\cs_generate_variant:Nn \hbox_unpack:N { c }
+\cs_generate_variant:Nn \hbox_unpack_drop:N { c }
+\cs_new_protected:Npn \vbox:n #1
+ { \tex_vbox:D { \color_group_begin: #1 \par \color_group_end: } }
+\cs_new_protected:Npn \vbox_top:n #1
+ { \tex_vtop:D { \color_group_begin: #1 \par \color_group_end: } }
+\cs_new_protected:Npn \vbox_to_ht:nn #1#2
+ {
+ \tex_vbox:D to \__box_dim_eval:n {#1}
+ { \color_group_begin: #2 \par \color_group_end: }
+ }
+\cs_new_protected:Npn \vbox_to_zero:n #1
+ {
+ \tex_vbox:D to \c_zero_dim
+ { \color_group_begin: #1 \par \color_group_end: }
+ }
+\cs_new_protected:Npn \vbox_set:Nn #1#2
+ {
+ \tex_setbox:D #1 \tex_vbox:D
+ { \color_group_begin: #2 \par \color_group_end: }
+ }
+\cs_new_protected:Npn \vbox_gset:Nn #1#2
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_vbox:D
+ { \color_group_begin: #2 \par \color_group_end: }
+ }
+\cs_generate_variant:Nn \vbox_set:Nn { c }
+\cs_generate_variant:Nn \vbox_gset:Nn { c }
+\cs_new_protected:Npn \vbox_set_top:Nn #1#2
+ {
+ \tex_setbox:D #1 \tex_vtop:D
+ { \color_group_begin: #2 \par \color_group_end: }
+ }
+\cs_new_protected:Npn \vbox_gset_top:Nn #1#2
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_vtop:D
+ { \color_group_begin: #2 \par \color_group_end: }
+ }
+\cs_generate_variant:Nn \vbox_set_top:Nn { c }
+\cs_generate_variant:Nn \vbox_gset_top:Nn { c }
+\cs_new_protected:Npn \vbox_set_to_ht:Nnn #1#2#3
+ {
+ \tex_setbox:D #1 \tex_vbox:D to \__box_dim_eval:n {#2}
+ { \color_group_begin: #3 \par \color_group_end: }
+ }
+\cs_new_protected:Npn \vbox_gset_to_ht:Nnn #1#2#3
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_vbox:D to \__box_dim_eval:n {#2}
+ { \color_group_begin: #3 \par \color_group_end: }
+ }
+\cs_generate_variant:Nn \vbox_set_to_ht:Nnn { c }
+\cs_generate_variant:Nn \vbox_gset_to_ht:Nnn { c }
+\cs_new_protected:Npn \vbox_set:Nw #1
+ {
+ \tex_setbox:D #1 \tex_vbox:D
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\cs_new_protected:Npn \vbox_gset:Nw #1
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_vbox:D
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\cs_generate_variant:Nn \vbox_set:Nw { c }
+\cs_generate_variant:Nn \vbox_gset:Nw { c }
+\cs_new_protected:Npn \vbox_set_end:
+ {
+ \par
+ \color_group_end:
+ \c_group_end_token
+ }
+\cs_new_eq:NN \vbox_gset_end: \vbox_set_end:
+\cs_new_protected:Npn \vbox_set_to_ht:Nnw #1#2
+ {
+ \tex_setbox:D #1 \tex_vbox:D to \__box_dim_eval:n {#2}
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\cs_new_protected:Npn \vbox_gset_to_ht:Nnw #1#2
+ {
+ \tex_global:D \tex_setbox:D #1 \tex_vbox:D to \__box_dim_eval:n {#2}
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\cs_generate_variant:Nn \vbox_set_to_ht:Nnw { c }
+\cs_generate_variant:Nn \vbox_gset_to_ht:Nnw { c }
+\cs_new_eq:NN \vbox_unpack:N \tex_unvcopy:D
+\cs_new_eq:NN \vbox_unpack_drop:N \tex_unvbox:D
+\cs_generate_variant:Nn \vbox_unpack:N { c }
+\cs_generate_variant:Nn \vbox_unpack_drop:N { c }
+\cs_new_protected:Npn \vbox_set_split_to_ht:NNn #1#2#3
+ { \tex_setbox:D #1 \tex_vsplit:D #2 to \__box_dim_eval:n {#3} }
+\cs_generate_variant:Nn \vbox_set_split_to_ht:NNn { c , Nc , cc }
+\cs_new_protected:Npn \vbox_gset_split_to_ht:NNn #1#2#3
+ {
+ \tex_global:D \tex_setbox:D #1
+ \tex_vsplit:D #2 to \__box_dim_eval:n {#3}
+ }
+\cs_generate_variant:Nn \vbox_gset_split_to_ht:NNn { c , Nc , cc }
+\fp_new:N \l__box_angle_fp
+\fp_new:N \l__box_cos_fp
+\fp_new:N \l__box_sin_fp
+\dim_new:N \l__box_top_dim
+\dim_new:N \l__box_bottom_dim
+\dim_new:N \l__box_left_dim
+\dim_new:N \l__box_right_dim
+\dim_new:N \l__box_top_new_dim
+\dim_new:N \l__box_bottom_new_dim
+\dim_new:N \l__box_left_new_dim
+\dim_new:N \l__box_right_new_dim
+\box_new:N \l__box_internal_box
+\cs_new_protected:Npn \box_rotate:Nn #1#2
+ { \__box_rotate:NnN #1 {#2} \hbox_set:Nn }
+\cs_generate_variant:Nn \box_rotate:Nn { c }
+\cs_new_protected:Npn \box_grotate:Nn #1#2
+ { \__box_rotate:NnN #1 {#2} \hbox_gset:Nn }
+\cs_generate_variant:Nn \box_grotate:Nn { c }
+\cs_new_protected:Npn \__box_rotate:NnN #1#2#3
+ {
+ #3 #1
+ {
+ \fp_set:Nn \l__box_angle_fp {#2}
+ \fp_set:Nn \l__box_sin_fp { sind ( \l__box_angle_fp ) }
+ \fp_set:Nn \l__box_cos_fp { cosd ( \l__box_angle_fp ) }
+ \__box_rotate:N #1
+ }
+ }
+\cs_new_protected:Npn \__box_rotate:N #1
+ {
+ \dim_set:Nn \l__box_top_dim { \box_ht:N #1 }
+ \dim_set:Nn \l__box_bottom_dim { -\box_dp:N #1 }
+ \dim_set:Nn \l__box_right_dim { \box_wd:N #1 }
+ \dim_zero:N \l__box_left_dim
+ \fp_compare:nNnTF \l__box_sin_fp > \c_zero_fp
+ {
+ \fp_compare:nNnTF \l__box_cos_fp > \c_zero_fp
+ { \__box_rotate_quadrant_one: }
+ { \__box_rotate_quadrant_two: }
+ }
+ {
+ \fp_compare:nNnTF \l__box_cos_fp < \c_zero_fp
+ { \__box_rotate_quadrant_three: }
+ { \__box_rotate_quadrant_four: }
+ }
+ \hbox_set:Nn \l__box_internal_box { \box_use:N #1 }
+ \hbox_set:Nn \l__box_internal_box
+ {
+ \__kernel_kern:n { -\l__box_left_new_dim }
+ \hbox:n
+ {
+ \__box_backend_rotate:Nn
+ \l__box_internal_box
+ \l__box_angle_fp
+ }
+ }
+ \box_set_ht:Nn \l__box_internal_box { \l__box_top_new_dim }
+ \box_set_dp:Nn \l__box_internal_box { -\l__box_bottom_new_dim }
+ \box_set_wd:Nn \l__box_internal_box
+ { \l__box_right_new_dim - \l__box_left_new_dim }
+ \box_use_drop:N \l__box_internal_box
+ }
+\cs_new_protected:Npn \__box_rotate_xdir:nnN #1#2#3
+ {
+ \dim_set:Nn #3
+ {
+ \fp_to_dim:n
+ {
+ \l__box_cos_fp * \dim_to_fp:n {#1}
+ - \l__box_sin_fp * \dim_to_fp:n {#2}
+ }
+ }
+ }
+\cs_new_protected:Npn \__box_rotate_ydir:nnN #1#2#3
+ {
+ \dim_set:Nn #3
+ {
+ \fp_to_dim:n
+ {
+ \l__box_sin_fp * \dim_to_fp:n {#1}
+ + \l__box_cos_fp * \dim_to_fp:n {#2}
+ }
+ }
+ }
+\cs_new_protected:Npn \__box_rotate_quadrant_one:
+ {
+ \__box_rotate_ydir:nnN \l__box_right_dim \l__box_top_dim
+ \l__box_top_new_dim
+ \__box_rotate_ydir:nnN \l__box_left_dim \l__box_bottom_dim
+ \l__box_bottom_new_dim
+ \__box_rotate_xdir:nnN \l__box_left_dim \l__box_top_dim
+ \l__box_left_new_dim
+ \__box_rotate_xdir:nnN \l__box_right_dim \l__box_bottom_dim
+ \l__box_right_new_dim
+ }
+\cs_new_protected:Npn \__box_rotate_quadrant_two:
+ {
+ \__box_rotate_ydir:nnN \l__box_right_dim \l__box_bottom_dim
+ \l__box_top_new_dim
+ \__box_rotate_ydir:nnN \l__box_left_dim \l__box_top_dim
+ \l__box_bottom_new_dim
+ \__box_rotate_xdir:nnN \l__box_right_dim \l__box_top_dim
+ \l__box_left_new_dim
+ \__box_rotate_xdir:nnN \l__box_left_dim \l__box_bottom_dim
+ \l__box_right_new_dim
+ }
+\cs_new_protected:Npn \__box_rotate_quadrant_three:
+ {
+ \__box_rotate_ydir:nnN \l__box_left_dim \l__box_bottom_dim
+ \l__box_top_new_dim
+ \__box_rotate_ydir:nnN \l__box_right_dim \l__box_top_dim
+ \l__box_bottom_new_dim
+ \__box_rotate_xdir:nnN \l__box_right_dim \l__box_bottom_dim
+ \l__box_left_new_dim
+ \__box_rotate_xdir:nnN \l__box_left_dim \l__box_top_dim
+ \l__box_right_new_dim
+ }
+\cs_new_protected:Npn \__box_rotate_quadrant_four:
+ {
+ \__box_rotate_ydir:nnN \l__box_left_dim \l__box_top_dim
+ \l__box_top_new_dim
+ \__box_rotate_ydir:nnN \l__box_right_dim \l__box_bottom_dim
+ \l__box_bottom_new_dim
+ \__box_rotate_xdir:nnN \l__box_left_dim \l__box_bottom_dim
+ \l__box_left_new_dim
+ \__box_rotate_xdir:nnN \l__box_right_dim \l__box_top_dim
+ \l__box_right_new_dim
+ }
+\fp_new:N \l__box_scale_x_fp
+\fp_new:N \l__box_scale_y_fp
+\cs_new_protected:Npn \box_resize_to_wd_and_ht_plus_dp:Nnn #1#2#3
+ {
+ \__box_resize_to_wd_and_ht_plus_dp:NnnN #1 {#2} {#3}
+ \hbox_set:Nn
+ }
+\cs_generate_variant:Nn \box_resize_to_wd_and_ht_plus_dp:Nnn { c }
+\cs_new_protected:Npn \box_gresize_to_wd_and_ht_plus_dp:Nnn #1#2#3
+ {
+ \__box_resize_to_wd_and_ht_plus_dp:NnnN #1 {#2} {#3}
+ \hbox_gset:Nn
+ }
+\cs_generate_variant:Nn \box_gresize_to_wd_and_ht_plus_dp:Nnn { c }
+\cs_new_protected:Npn \__box_resize_to_wd_and_ht_plus_dp:NnnN #1#2#3#4
+ {
+ #4 #1
+ {
+ \__box_resize_set_corners:N #1
+ \fp_set:Nn \l__box_scale_x_fp
+ { \dim_to_fp:n {#2} / \dim_to_fp:n { \l__box_right_dim } }
+ \fp_set:Nn \l__box_scale_y_fp
+ {
+ \dim_to_fp:n {#3}
+ / \dim_to_fp:n { \l__box_top_dim - \l__box_bottom_dim }
+ }
+ \__box_resize:N #1
+ }
+ }
+\cs_new_protected:Npn \__box_resize_set_corners:N #1
+ {
+ \dim_set:Nn \l__box_top_dim { \box_ht:N #1 }
+ \dim_set:Nn \l__box_bottom_dim { -\box_dp:N #1 }
+ \dim_set:Nn \l__box_right_dim { \box_wd:N #1 }
+ \dim_zero:N \l__box_left_dim
+ }
+\cs_new_protected:Npn \__box_resize:N #1
+ {
+ \__box_resize:NNN \l__box_right_new_dim
+ \l__box_scale_x_fp \l__box_right_dim
+ \__box_resize:NNN \l__box_bottom_new_dim
+ \l__box_scale_y_fp \l__box_bottom_dim
+ \__box_resize:NNN \l__box_top_new_dim
+ \l__box_scale_y_fp \l__box_top_dim
+ \__box_resize_common:N #1
+ }
+\cs_new_protected:Npn \__box_resize:NNN #1#2#3
+ {
+ \dim_set:Nn #1
+ { \fp_to_dim:n { \fp_abs:n { #2 } * \dim_to_fp:n { #3 } } }
+ }
+\cs_new_protected:Npn \box_resize_to_ht:Nn #1#2
+ { \__box_resize_to_ht:NnN #1 {#2} \hbox_set:Nn }
+\cs_generate_variant:Nn \box_resize_to_ht:Nn { c }
+\cs_new_protected:Npn \box_gresize_to_ht:Nn #1#2
+ { \__box_resize_to_ht:NnN #1 {#2} \hbox_gset:Nn }
+\cs_generate_variant:Nn \box_gresize_to_ht:Nn { c }
+\cs_new_protected:Npn \__box_resize_to_ht:NnN #1#2#3
+ {
+ #3 #1
+ {
+ \__box_resize_set_corners:N #1
+ \fp_set:Nn \l__box_scale_y_fp
+ {
+ \dim_to_fp:n {#2}
+ / \dim_to_fp:n { \l__box_top_dim }
+ }
+ \fp_set_eq:NN \l__box_scale_x_fp \l__box_scale_y_fp
+ \__box_resize:N #1
+ }
+ }
+\cs_new_protected:Npn \box_resize_to_ht_plus_dp:Nn #1#2
+ { \__box_resize_to_ht_plus_dp:NnN #1 {#2} \hbox_set:Nn }
+\cs_generate_variant:Nn \box_resize_to_ht_plus_dp:Nn { c }
+\cs_new_protected:Npn \box_gresize_to_ht_plus_dp:Nn #1#2
+ { \__box_resize_to_ht_plus_dp:NnN #1 {#2} \hbox_gset:Nn }
+\cs_generate_variant:Nn \box_gresize_to_ht_plus_dp:Nn { c }
+\cs_new_protected:Npn \__box_resize_to_ht_plus_dp:NnN #1#2#3
+ {
+ #3 #1
+ {
+ \__box_resize_set_corners:N #1
+ \fp_set:Nn \l__box_scale_y_fp
+ {
+ \dim_to_fp:n {#2}
+ / \dim_to_fp:n { \l__box_top_dim - \l__box_bottom_dim }
+ }
+ \fp_set_eq:NN \l__box_scale_x_fp \l__box_scale_y_fp
+ \__box_resize:N #1
+ }
+ }
+\cs_new_protected:Npn \box_resize_to_wd:Nn #1#2
+ { \__box_resize_to_wd:NnN #1 {#2} \hbox_set:Nn }
+\cs_generate_variant:Nn \box_resize_to_wd:Nn { c }
+\cs_new_protected:Npn \box_gresize_to_wd:Nn #1#2
+ { \__box_resize_to_wd:NnN #1 {#2} \hbox_gset:Nn }
+\cs_generate_variant:Nn \box_gresize_to_wd:Nn { c }
+\cs_new_protected:Npn \__box_resize_to_wd:NnN #1#2#3
+ {
+ #3 #1
+ {
+ \__box_resize_set_corners:N #1
+ \fp_set:Nn \l__box_scale_x_fp
+ { \dim_to_fp:n {#2} / \dim_to_fp:n { \l__box_right_dim } }
+ \fp_set_eq:NN \l__box_scale_y_fp \l__box_scale_x_fp
+ \__box_resize:N #1
+ }
+ }
+\cs_new_protected:Npn \box_resize_to_wd_and_ht:Nnn #1#2#3
+ { \__box_resize_to_wd_and_ht:NnnN #1 {#2} {#3} \hbox_set:Nn }
+\cs_generate_variant:Nn \box_resize_to_wd_and_ht:Nnn { c }
+\cs_new_protected:Npn \box_gresize_to_wd_and_ht:Nnn #1#2#3
+ { \__box_resize_to_wd_and_ht:NnnN #1 {#2} {#3} \hbox_gset:Nn }
+\cs_generate_variant:Nn \box_gresize_to_wd_and_ht:Nnn { c }
+\cs_new_protected:Npn \__box_resize_to_wd_and_ht:NnnN #1#2#3#4
+ {
+ #4 #1
+ {
+ \__box_resize_set_corners:N #1
+ \fp_set:Nn \l__box_scale_x_fp
+ { \dim_to_fp:n {#2} / \dim_to_fp:n { \l__box_right_dim } }
+ \fp_set:Nn \l__box_scale_y_fp
+ {
+ \dim_to_fp:n {#3}
+ / \dim_to_fp:n { \l__box_top_dim }
+ }
+ \__box_resize:N #1
+ }
+ }
+\cs_new_protected:Npn \box_scale:Nnn #1#2#3
+ { \__box_scale:NnnN #1 {#2} {#3} \hbox_set:Nn }
+\cs_generate_variant:Nn \box_scale:Nnn { c }
+\cs_new_protected:Npn \box_gscale:Nnn #1#2#3
+ { \__box_scale:NnnN #1 {#2} {#3} \hbox_gset:Nn }
+\cs_generate_variant:Nn \box_gscale:Nnn { c }
+\cs_new_protected:Npn \__box_scale:NnnN #1#2#3#4
+ {
+ #4 #1
+ {
+ \fp_set:Nn \l__box_scale_x_fp {#2}
+ \fp_set:Nn \l__box_scale_y_fp {#3}
+ \__box_scale:N #1
+ }
+ }
+\cs_new_protected:Npn \__box_scale:N #1
+ {
+ \dim_set:Nn \l__box_top_dim { \box_ht:N #1 }
+ \dim_set:Nn \l__box_bottom_dim { -\box_dp:N #1 }
+ \dim_set:Nn \l__box_right_dim { \box_wd:N #1 }
+ \dim_zero:N \l__box_left_dim
+ \dim_set:Nn \l__box_top_new_dim
+ { \fp_abs:n { \l__box_scale_y_fp } \l__box_top_dim }
+ \dim_set:Nn \l__box_bottom_new_dim
+ { \fp_abs:n { \l__box_scale_y_fp } \l__box_bottom_dim }
+ \dim_set:Nn \l__box_right_new_dim
+ { \fp_abs:n { \l__box_scale_x_fp } \l__box_right_dim }
+ \__box_resize_common:N #1
+ }
+\cs_new_protected:Npn \box_autosize_to_wd_and_ht:Nnn #1#2#3
+ { \__box_autosize:NnnnN #1 {#2} {#3} { \box_ht:N #1 } \hbox_set:Nn }
+\cs_generate_variant:Nn \box_autosize_to_wd_and_ht:Nnn { c }
+\cs_new_protected:Npn \box_gautosize_to_wd_and_ht:Nnn #1#2#3
+ { \__box_autosize:NnnnN #1 {#2} {#3} { \box_ht:N #1 } \hbox_gset:Nn }
+\cs_generate_variant:Nn \box_gautosize_to_wd_and_ht:Nnn { c }
+\cs_new_protected:Npn \box_autosize_to_wd_and_ht_plus_dp:Nnn #1#2#3
+ {
+ \__box_autosize:NnnnN #1 {#2} {#3} { \box_ht:N #1 + \box_dp:N #1 }
+ \hbox_set:Nn
+ }
+\cs_generate_variant:Nn \box_autosize_to_wd_and_ht_plus_dp:Nnn { c }
+\cs_new_protected:Npn \box_gautosize_to_wd_and_ht_plus_dp:Nnn #1#2#3
+ {
+ \__box_autosize:NnnnN #1 {#2} {#3} { \box_ht:N #1 + \box_dp:N #1 }
+ \hbox_gset:Nn
+ }
+\cs_generate_variant:Nn \box_gautosize_to_wd_and_ht_plus_dp:Nnn { c }
+\cs_new_protected:Npn \__box_autosize:NnnnN #1#2#3#4#5
+ {
+ #5 #1
+ {
+ \fp_set:Nn \l__box_scale_x_fp { ( \dim_to_fp:n {#2} ) / \box_wd:N #1 }
+ \fp_set:Nn \l__box_scale_y_fp
+ { ( \dim_to_fp:n {#3} ) / ( \dim_to_fp:n {#4} ) }
+ \fp_compare:nNnTF \l__box_scale_x_fp > \l__box_scale_y_fp
+ { \fp_set_eq:NN \l__box_scale_x_fp \l__box_scale_y_fp }
+ { \fp_set_eq:NN \l__box_scale_y_fp \l__box_scale_x_fp }
+ \__box_scale:N #1
+ }
+ }
+\cs_new_protected:Npn \__box_resize_common:N #1
+ {
+ \hbox_set:Nn \l__box_internal_box
+ {
+ \__box_backend_scale:Nnn
+ #1
+ \l__box_scale_x_fp
+ \l__box_scale_y_fp
+ }
+ \fp_compare:nNnTF \l__box_scale_y_fp > \c_zero_fp
+ {
+ \box_set_ht:Nn \l__box_internal_box { \l__box_top_new_dim }
+ \box_set_dp:Nn \l__box_internal_box { -\l__box_bottom_new_dim }
+ }
+ {
+ \box_set_dp:Nn \l__box_internal_box { \l__box_top_new_dim }
+ \box_set_ht:Nn \l__box_internal_box { -\l__box_bottom_new_dim }
+ }
+ \fp_compare:nNnTF \l__box_scale_x_fp < \c_zero_fp
+ {
+ \hbox_to_wd:nn { \l__box_right_new_dim }
+ {
+ \__kernel_kern:n { \l__box_right_new_dim }
+ \box_use_drop:N \l__box_internal_box
+ \tex_hss:D
+ }
+ }
+ {
+ \box_set_wd:Nn \l__box_internal_box { \l__box_right_new_dim }
+ \hbox:n
+ {
+ \__kernel_kern:n { 0pt }
+ \box_use_drop:N \l__box_internal_box
+ \tex_hss:D
+ }
+ }
+ }
+\cs_new_protected:Npn \box_set_clipped:N #1
+ { \hbox_set:Nn #1 { \__box_backend_clip:N #1 } }
+\cs_generate_variant:Nn \box_set_clipped:N { c }
+\cs_new_protected:Npn \box_gset_clipped:N #1
+ { \hbox_gset:Nn #1 { \__box_backend_clip:N #1 } }
+\cs_generate_variant:Nn \box_gset_clipped:N { c }
+\cs_new_protected:Npn \box_set_trim:Nnnnn #1#2#3#4#5
+ { \__box_set_trim:NnnnnN #1 {#2} {#3} {#4} {#5} \box_set_eq:NN }
+\cs_generate_variant:Nn \box_set_trim:Nnnnn { c }
+\cs_new_protected:Npn \box_gset_trim:Nnnnn #1#2#3#4#5
+ { \__box_set_trim:NnnnnN #1 {#2} {#3} {#4} {#5} \box_gset_eq:NN }
+\cs_generate_variant:Nn \box_gset_trim:Nnnnn { c }
+\cs_new_protected:Npn \__box_set_trim:NnnnnN #1#2#3#4#5#6
+ {
+ \hbox_set:Nn \l__box_internal_box
+ {
+ \__kernel_kern:n { -#2 }
+ \box_use:N #1
+ \__kernel_kern:n { -#4 }
+ }
+ \dim_compare:nNnTF { \box_dp:N #1 } > {#3}
+ {
+ \hbox_set:Nn \l__box_internal_box
+ {
+ \box_move_down:nn \c_zero_dim
+ { \box_use_drop:N \l__box_internal_box }
+ }
+ \box_set_dp:Nn \l__box_internal_box { \box_dp:N #1 - (#3) }
+ }
+ {
+ \hbox_set:Nn \l__box_internal_box
+ {
+ \box_move_down:nn { (#3) - \box_dp:N #1 }
+ { \box_use_drop:N \l__box_internal_box }
+ }
+ \box_set_dp:Nn \l__box_internal_box \c_zero_dim
+ }
+ \dim_compare:nNnTF { \box_ht:N \l__box_internal_box } > {#5}
+ {
+ \hbox_set:Nn \l__box_internal_box
+ {
+ \box_move_up:nn \c_zero_dim
+ { \box_use_drop:N \l__box_internal_box }
+ }
+ \box_set_ht:Nn \l__box_internal_box
+ { \box_ht:N \l__box_internal_box - (#5) }
+ }
+ {
+ \hbox_set:Nn \l__box_internal_box
+ {
+ \box_move_up:nn { (#5) - \box_ht:N \l__box_internal_box }
+ { \box_use_drop:N \l__box_internal_box }
+ }
+ \box_set_ht:Nn \l__box_internal_box \c_zero_dim
+ }
+ #6 #1 \l__box_internal_box
+ }
+\cs_new_protected:Npn \box_set_viewport:Nnnnn #1#2#3#4#5
+ { \__box_set_viewport:NnnnnN #1 {#2} {#3} {#4} {#5} \box_set_eq:NN }
+\cs_generate_variant:Nn \box_set_viewport:Nnnnn { c }
+\cs_new_protected:Npn \box_gset_viewport:Nnnnn #1#2#3#4#5
+ { \__box_set_viewport:NnnnnN #1 {#2} {#3} {#4} {#5} \box_gset_eq:NN }
+\cs_generate_variant:Nn \box_gset_viewport:Nnnnn { c }
+\cs_new_protected:Npn \__box_set_viewport:NnnnnN #1#2#3#4#5#6
+ {
+ \hbox_set:Nn \l__box_internal_box
+ {
+ \__kernel_kern:n { -#2 }
+ \box_use:N #1
+ \__kernel_kern:n { #4 - \box_wd:N #1 }
+ }
+ \dim_compare:nNnTF {#3} < \c_zero_dim
+ {
+ \hbox_set:Nn \l__box_internal_box
+ {
+ \box_move_down:nn \c_zero_dim
+ { \box_use_drop:N \l__box_internal_box }
+ }
+ \box_set_dp:Nn \l__box_internal_box { - \__box_dim_eval:n {#3} }
+ }
+ {
+ \hbox_set:Nn \l__box_internal_box
+ { \box_move_down:nn {#3} { \box_use_drop:N \l__box_internal_box } }
+ \box_set_dp:Nn \l__box_internal_box \c_zero_dim
+ }
+ \dim_compare:nNnTF {#5} > \c_zero_dim
+ {
+ \hbox_set:Nn \l__box_internal_box
+ {
+ \box_move_up:nn \c_zero_dim
+ { \box_use_drop:N \l__box_internal_box }
+ }
+ \box_set_ht:Nn \l__box_internal_box
+ {
+ (#5)
+ \dim_compare:nNnT {#3} > \c_zero_dim
+ { - (#3) }
+ }
+ }
+ {
+ \hbox_set:Nn \l__box_internal_box
+ {
+ \box_move_up:nn { - \__box_dim_eval:n {#5} }
+ { \box_use_drop:N \l__box_internal_box }
+ }
+ \box_set_ht:Nn \l__box_internal_box \c_zero_dim
+ }
+ #6 #1 \l__box_internal_box
+ }
+
+%% File: l3color.dtx
+\cs_new_eq:NN \color_group_begin: \group_begin:
+\cs_new_eq:NN \color_group_end: \group_end:
+\cs_new_protected:Npn \color_ensure_current:
+ { \__color_select:N \l__color_current_tl }
+\scan_new:N \s__color_stop
+\cs_new_protected:Npn \__color_select:N #1
+ {
+ \exp_after:wN \__color_select:nn #1
+ \group_insert_after:N \__color_backend_reset:
+ }
+\cs_new_protected:Npn \__color_select_math:N #1
+ { \exp_after:wN \__color_select:nn #1 }
+\cs_new_protected:Npn \__color_select:nn #1#2
+ { \use:c { __color_backend_select_ #1 :n } {#2} }
+\tl_new:N \l__color_current_tl
+\tl_set:Nn \l__color_current_tl { { gray } { 0 } }
+\int_new:N \l__color_internal_int
+\tl_new:N \l__color_internal_tl
+\scan_new:N \s__color_mark
+\bool_new:N \l__color_ignore_error_bool
+\prg_new_conditional:Npnn \color_if_exist:n #1 { p , T, F, TF }
+ {
+ \prop_if_exist:cTF { l__color_named_ #1 _prop }
+ {
+ \prop_if_empty:cTF { l__color_named_ #1 _prop }
+ \prg_return_false:
+ \prg_return_true:
+ }
+ \prg_return_false:
+ }
+\cs_new:Npn \__color_model:N #1 { \exp_after:wN \use_i:nn #1 }
+\cs_new:Npn \__color_values:N #1 { \exp_after:wN \use_ii:nn #1 }
+\cs_new_protected:Npn \__color_extract:nNN #1#2#3
+ {
+ \tl_set_eq:Nc #2 { l__color_named_ #1 _tl }
+ \prop_get:cVN { l__color_named_ #1 _prop } #2 #3
+ }
+\cs_generate_variant:Nn \__color_extract:nNN { V }
+\cs_new_protected:Npn \__color_convert:nnN #1#2#3
+ { \__color_convert:nnVN {#1} {#2} #3 #3 }
+\cs_generate_variant:Nn \__color_convert:nnN { VV }
+\cs_generate_variant:Nn \exp_last_unbraced:Nf { c }
+\cs_new_protected:Npn \__color_convert:nnnN #1#2#3#4
+ {
+ \tl_set:Ne #4
+ {
+ \cs_if_exist_use:cTF { __color_convert_ #1 _ #2 :w }
+ { #3 \s__color_stop }
+ {
+ \cs_if_exist:cTF { __color_convert_ \use:c { c__color_fallback_ #1 _tl } _ #2 :w }
+ {
+ \exp_last_unbraced:cf
+ { __color_convert_ \use:c { c__color_fallback_ #1 _tl } _ #2 :w }
+ { \use:c { __color_convert_ #1 _ \use:c { c__color_fallback_ #1 _tl } :w } #3 \s__color_stop }
+ \s__color_stop
+ }
+ {
+ \exp_last_unbraced:cf
+ { __color_convert_ \use:c { c__color_fallback_ #2 _tl } _ #2 :w }
+ {
+ \cs_if_exist_use:cTF { __color_convert_ #1 _ \use:c { c__color_fallback_ #2 _tl } :w }
+ { #3 \s__color_stop }
+ {
+ \exp_last_unbraced:cf
+ { __color_convert_ \use:c { c__color_fallback_ #1 _tl } _ \use:c { c__color_fallback_ #2 _tl } :w }
+ { \use:c { __color_convert_ #1 _ \use:c { c__color_fallback_ #1 _tl } :w } #3 \s__color_stop }
+ \s__color_stop
+ }
+ }
+ \s__color_stop
+ }
+ }
+ }
+ }
+\cs_generate_variant:Nn \__color_convert:nnnN { nV , nnV }
+\cs_new:Npn \__color_convert_gray_gray:w #1 \s__color_stop
+ { #1 }
+\cs_new:Npn \__color_convert_gray_rgb:w #1 \s__color_stop
+ { #1 ~ #1 ~ #1 }
+\cs_new:Npn \__color_convert_gray_cmyk:w #1 \s__color_stop
+ { 0 ~ 0 ~ 0 ~ \fp_eval:n { 1 - #1 } }
+\cs_new:Npn \__color_convert_rgb_gray:w #1 ~ #2 ~ #3 \s__color_stop
+ { \fp_eval:n { 0.3 * #1 + 0.59 * #2 + 0.11 * #3 } }
+\cs_new:Npn \__color_convert_rgb_rgb:w #1 \s__color_stop
+ { #1 }
+\cs_new:Npn \__color_convert_rgb_cmyk:w #1 ~ #2 ~ #3 \s__color_stop
+ {
+ \exp_args:Neee \__color_convert_rgb_cmyk:nnn
+ { \fp_eval:n { 1 - #1 } }
+ { \fp_eval:n { 1 - #2 } }
+ { \fp_eval:n { 1 - #3 } }
+ }
+\cs_new:Npn \__color_convert_rgb_cmyk:nnn #1#2#3
+ {
+ \exp_args:Ne \__color_convert_rgb_cmyk:nnnn
+ { \fp_eval:n { min( #1, #2 , #3 ) } } {#1} {#2} {#3}
+ }
+\cs_new:Npn \__color_convert_rgb_cmyk:nnnn #1#2#3#4
+ {
+ \fp_eval:n { min ( 1 , max ( 0 , #2 - #1 ) ) } \c_space_tl
+ \fp_eval:n { min ( 1 , max ( 0 , #3 - #1 ) ) } \c_space_tl
+ \fp_eval:n { min ( 1 , max ( 0 , #4 - #1 ) ) } \c_space_tl
+ #1
+ }
+\cs_new:Npn \__color_convert_cmyk_gray:w #1 ~ #2 ~ #3 ~ #4 \s__color_stop
+ { \fp_eval:n { 1 - min ( 1 , 0.3 * #1 + 0.59 * #2 + 0.11 * #3 + #4 ) } }
+\cs_new:Npn \__color_convert_cmyk_rgb:w #1 ~ #2 ~ #3 ~ #4 \s__color_stop
+ {
+ \fp_eval:n { 1 - min ( 1 , #1 + #4 ) } \c_space_tl
+ \fp_eval:n { 1 - min ( 1 , #2 + #4 ) } \c_space_tl
+ \fp_eval:n { 1 - min ( 1 , #3 + #4 ) }
+ }
+\cs_new:Npn \__color_convert_cmyk_cmyk:w #1 \s__color_stop
+ { #1 }
+\tl_new:N \l__color_model_tl
+\tl_new:N \l__color_value_tl
+\tl_new:N \l__color_next_model_tl
+\tl_new:N \l__color_next_value_tl
+\cs_new_protected:Npe \__color_parse:nN #1#2
+ {
+ \tl_set:Ne \exp_not:c { l__color_named_ . _tl }
+ { \exp_not:N \__color_model:N \exp_not:N \l__color_current_tl }
+ \prop_put:NVe \exp_not:c { l__color_named_ . _prop }
+ \exp_not:c { l__color_named_ . _tl }
+ { \exp_not:N \__color_values:N \exp_not:N \l__color_current_tl }
+ \exp_not:N \exp_args:Ne \exp_not:N \__color_parse_aux:nN
+ { \exp_not:N \tl_to_str:n {#1} } #2
+ }
+\cs_new_protected:Npn \__color_parse_aux:nN #1#2
+ {
+ \color_if_exist:nTF {#1}
+ { \__color_parse_set_eq:Nn #2 {#1} }
+ { \__color_parse:Nw #2#1 ! \s__color_stop }
+ \__color_check_model:N #2
+ }
+\cs_new_protected:Npn \__color_parse_set_eq:Nn #1#2
+ {
+ \tl_if_empty:NTF \l_color_fixed_model_tl
+ { \exp_args:Nv \__color_parse_set_eq:nNn { l__color_named_ #2 _tl } }
+ { \exp_args:NV \__color_parse_set_eq:nNn \l_color_fixed_model_tl }
+ #1 {#2}
+ }
+\cs_new_protected:Npn \__color_parse_set_eq:nNn #1#2#3
+ {
+ \prop_get:cnNTF
+ { l__color_named_ #3 _prop } {#1}
+ \l__color_value_tl
+ { \tl_set:Ne #2 { {#1} { \l__color_value_tl } } }
+ {
+ \tl_set_eq:Nc \l__color_model_tl { l__color_named_ #3 _tl }
+ \prop_get:cVN { l__color_named_ #3 _prop } \l__color_model_tl
+ \l__color_value_tl
+ \__color_convert:nnN
+ \l__color_model_tl {#1} \l__color_value_tl
+ \tl_set:Ne #2
+ {
+ {#1}
+ { \l__color_value_tl }
+ }
+ }
+ }
+\cs_new_protected:Npn \__color_parse:Nw #1#2 ! #3 \s__color_stop
+ {
+ \color_if_exist:nTF {#2}
+ {
+ \tl_if_blank:nTF {#3}
+ { \__color_parse_set_eq:Nn #1 {#2} }
+ { \__color_parse_loop_init:Nnn #1 {#2} {#3} }
+ }
+ {
+ \msg_error:nnn { color } { unknown-color } {#2}
+ \tl_set:Nn \l__color_current_tl { { gray } { 0 } }
+ }
+ }
+\cs_new_protected:Npn \__color_parse_loop_init:Nnn #1#2#3
+ {
+ \group_begin:
+ \__color_extract:nNN {#2} \l__color_model_tl \l__color_value_tl
+ \__color_parse_loop:w #3 ! ! ! ! \s__color_stop
+ \tl_set:Ne \l__color_internal_tl
+ { { \l__color_model_tl } { \l__color_value_tl } }
+ \exp_args:NNNV \group_end:
+ \tl_set:Nn #1 \l__color_internal_tl
+ }
+\cs_new_protected:Npn \__color_parse_loop:w #1 ! #2 ! #3 ! #4 ! #5 \s__color_stop
+ {
+ \tl_if_blank:nF {#1}
+ {
+ \bool_lazy_and:nnTF
+ { \fp_compare_p:nNn {#1} > { 0 } }
+ { \fp_compare_p:nNn {#1} < { 100 } }
+ {
+ \use:e
+ {
+ \__color_parse_loop:nn {#1}
+ { \tl_if_blank:nTF {#2} { white } {#2} }
+ }
+ }
+ { \__color_parse_loop_check:nn {#1} {#2} }
+ }
+ \tl_if_blank:nF {#3}
+ { \__color_parse_loop:w #3 ! #4 ! #5 \s__color_stop }
+ \__color_parse_end:
+ }
+\cs_new_protected:Npn \__color_parse_loop_check:nn #1#2
+ {
+ \bool_if:NF \l__color_ignore_error_bool
+ {
+ \bool_lazy_or:nnT
+ { \fp_compare_p:nNn {#1} < { 0 } }
+ { \fp_compare_p:nNn {#1} > { 100 } }
+ { \msg_error:nnnnn { color } { out-of-range } {#1} { 0 } { 100 } }
+ }
+ \fp_compare:nNnF {#1} > \c_zero_fp
+ {
+ \tl_if_blank:nTF {#2}
+ { \__color_extract:nNN { white } }
+ { \__color_extract:nNN {#2} }
+ \l__color_model_tl \l__color_value_tl
+ }
+ }
+\cs_new_protected:Npn \__color_parse_loop:nn #1#2
+ {
+ \color_if_exist:nTF {#2}
+ {
+ \__color_extract:nNN {#2} \l__color_next_model_tl \l__color_next_value_tl
+ \tl_if_eq:NNF \l__color_model_tl \l__color_next_model_tl
+ {
+ \str_if_eq:VnTF \l__color_model_tl { gray }
+ { \__color_parse_gray:n {#2} }
+ { \__color_parse_std:n {#2} }
+ }
+ \tl_set:Ne \l__color_value_tl
+ {
+ \__color_parse_mix:NVVn
+ \l__color_model_tl \l__color_value_tl \l__color_next_value_tl {#1}
+ }
+ }
+ {
+ \msg_error:nnn { color } { unknown-color } {#2}
+ \__color_extract:nNN { black } \l__color_model_tl \l__color_value_tl
+ \__color_parse_break:w
+ }
+ }
+\cs_new_protected:Npn \__color_parse_gray:n #1
+ {
+ \tl_set_eq:NN \l__color_model_tl \l__color_next_model_tl
+ \tl_set:Nn \l__color_next_model_tl { gray }
+ \exp_args:NnV \__color_convert:nnN { gray } \l__color_model_tl
+ \l__color_value_tl
+ \prop_get:cVN { l__color_named_ #1 _prop } \l__color_model_tl
+ \l__color_next_value_tl
+ }
+\cs_new_protected:Npn \__color_parse_std:n #1
+ {
+ \prop_get:cVNF { l__color_named_ #1 _prop }
+ \l__color_model_tl
+ \l__color_next_value_tl
+ {
+ \__color_convert:VVN
+ \l__color_next_model_tl
+ \l__color_model_tl
+ \l__color_next_value_tl
+ }
+ }
+\cs_new_protected:Npn \__color_parse_break:w #1 \__color_parse_end: { }
+\cs_new_protected:Npn \__color_parse_end: { }
+\cs_new:Npn \__color_parse_mix:Nnnn #1#2#3#4
+ {
+ \exp_args:Nf \__color_parse_mix:nNnn
+ { \fp_eval:n { #4 / 100 } }
+ #1 {#2} {#3}
+ }
+\cs_generate_variant:Nn \__color_parse_mix:Nnnn { NVV }
+\cs_new:Npn \__color_parse_mix:nNnn #1#2#3#4
+ {
+ \use:c { __color_parse_mix_ #2 :nw } {#1}
+ #3 \s__color_mark #4 \s__color_stop
+ }
+\cs_new:Npn \__color_parse_mix_gray:nw #1#2 \s__color_mark #3 \s__color_stop
+ { \fp_eval:n { #2 * #1 + #3 * ( 1 - #1 ) } }
+\cs_new:Npn \__color_parse_mix_rgb:nw
+ #1#2 ~ #3 ~ #4 \s__color_mark #5 ~ #6 ~ #7 \s__color_stop
+ {
+ \fp_eval:n { #2 * #1 + #5 * ( 1 - #1 ) } \c_space_tl
+ \fp_eval:n { #3 * #1 + #6 * ( 1 - #1 ) } \c_space_tl
+ \fp_eval:n { #4 * #1 + #7 * ( 1 - #1 ) }
+ }
+\cs_new:Npn \__color_parse_mix_cmyk:nw
+ #1#2 ~ #3 ~ #4 ~ #5 \s__color_mark #6 ~ #7 ~ #8 ~ #9 \s__color_stop
+ {
+ \fp_eval:n { #2 * #1 + #6 * ( 1 - #1 ) } \c_space_tl
+ \fp_eval:n { #3 * #1 + #7 * ( 1 - #1 ) } \c_space_tl
+ \fp_eval:n { #4 * #1 + #8 * ( 1 - #1 ) } \c_space_tl
+ \fp_eval:n { #5 * #1 + #9 * ( 1 - #1 ) }
+ }
+\cs_new:Npn \__color_parse_model_gray:w #1 , #2 \s__color_stop
+ { { gray } { \__color_parse_number:n {#1} } }
+\cs_new:Npn \__color_parse_model_rgb:w #1 , #2 , #3 , #4 \s__color_stop
+ {
+ { rgb }
+ {
+ \__color_parse_number:n {#1} ~
+ \__color_parse_number:n {#2} ~
+ \__color_parse_number:n {#3}
+ }
+ }
+\cs_new:Npn \__color_parse_model_cmyk:w #1 , #2 , #3 , #4 , #5 \s__color_stop
+ {
+ { cmyk }
+ {
+ \__color_parse_number:n {#1} ~
+ \__color_parse_number:n {#2} ~
+ \__color_parse_number:n {#3} ~
+ \__color_parse_number:n {#4}
+ }
+ }
+\cs_new:Npn \__color_parse_number:n #1
+ { \__color_parse_number:w #1 . 0 . \s__color_stop }
+\cs_new:Npn \__color_parse_number:w #1 . #2 . #3 \s__color_stop
+ { \tl_if_blank:nTF {#1} { 0 } {#1} . #2 }
+\cs_new:Npn \__color_parse_model_Gray:w #1 , #2 \s__color_stop
+ { { gray } { \fp_eval:n { #1 / 15 } } }
+\cs_new:Npn \__color_parse_model_hsb:w #1 , #2 , #3 , #4 \s__color_stop
+ { \__color_parse_model_hsb:nnn {#1} {#2} {#3} }
+\cs_new:Npn \__color_parse_model_Hsb:w #1 , #2 , #3 , #4 \s__color_stop
+ {
+ \exp_args:Ne \__color_parse_model_hsb:nnn { \fp_eval:n { #1 / 360 } }
+ {#2} {#3}
+ }
+\cs_new:Npn \__color_parse_model_hsb:nnn #1#2#3
+ {
+ { rgb }
+ {
+ \exp_args:Ne \__color_parse_model_hsb_aux:nnn
+ { \fp_eval:n { 6 * (#1) } } {#2} {#3}
+ }
+ }
+\cs_new:Npn \__color_parse_model_hsb_aux:nnn #1#2#3
+ {
+ \exp_args:Nee \__color_parse_model_hsb_aux:nnnn
+ { \fp_eval:n { floor(#1) } } { \fp_eval:n { #1 - floor(#1) } }
+ {#2} {#3}
+ }
+\cs_new:Npn \__color_parse_model_hsb_aux:nnnn #1#2#3#4
+ {
+ \use:e
+ {
+ \exp_not:N \__color_parse_model_hsb_aux:nnnnn
+ { \__color_parse_number:n {#4} }
+ { \fp_eval:n { round(#4 * (1 - #3) ,5) } }
+ { \fp_eval:n { round(#4 * ( 1 - #3 * #2 ) ,5) } }
+ { \fp_eval:n { round(#4 * ( 1 - #3 * (1 - #2) ) ,5) } }
+ {#1}
+ }
+ }
+\cs_new:Npn \__color_parse_model_hsb_aux:nnnnn #1#2#3#4#5
+ { \use:c { __color_parse_model_hsb_ #5 :nnnn } {#1} {#2} {#3} {#4} }
+\cs_new:cpn { __color_parse_model_hsb_0:nnnn } #1#2#3#4 { #1 ~ #4 ~ #2 }
+\cs_new:cpn { __color_parse_model_hsb_1:nnnn } #1#2#3#4 { #3 ~ #1 ~ #2 }
+\cs_new:cpn { __color_parse_model_hsb_2:nnnn } #1#2#3#4 { #2 ~ #1 ~ #4 }
+\cs_new:cpn { __color_parse_model_hsb_3:nnnn } #1#2#3#4 { #2 ~ #3 ~ #1 }
+\cs_new:cpn { __color_parse_model_hsb_4:nnnn } #1#2#3#4 { #4 ~ #2 ~ #1 }
+\cs_new:cpn { __color_parse_model_hsb_5:nnnn } #1#2#3#4 { #1 ~ #2 ~ #3 }
+\cs_new:cpn { __color_parse_model_hsb_6:nnnn } #1#2#3#4 { #1 ~ #2 ~ #2 }
+\cs_new:Npn \__color_parse_model_HSB:w #1 , #2 , #3 , #4 \s__color_stop
+ {
+ \exp_args:Neee \__color_parse_model_hsb:nnn
+ { \fp_eval:n { round((#1) / 240,5) } }
+ { \fp_eval:n { round((#2) / 240,5) } }
+ { \fp_eval:n { round((#3) / 240,5) } }
+ }
+\cs_new:Npn \__color_parse_model_HTML:w #1 , #2 \s__color_stop
+ { \__color_parse_model_HTML_aux:w #1 0 0 0 0 0 0 \s__color_stop }
+\cs_new:Npn \__color_parse_model_HTML_aux:w #1#2#3#4#5#6#7 \s__color_stop
+ {
+ { rgb }
+ {
+ \fp_eval:n { round(\int_from_hex:n {#1#2} / 255,5) } ~
+ \fp_eval:n { round(\int_from_hex:n {#3#4} / 255,5) } ~
+ \fp_eval:n { round(\int_from_hex:n {#5#6} / 255,5) }
+ }
+ }
+\cs_new:Npn \__color_parse_model_RGB:w #1 , #2 , #3 , #4 \s__color_stop
+ {
+ { rgb }
+ {
+ \fp_eval:n { round((#1) / 255,5) } ~
+ \fp_eval:n { round((#2) / 255,5) } ~
+ \fp_eval:n { round((#3) / 255,5) }
+ }
+ }
+\cs_new:Npn \__color_parse_model_wave:w #1 , #2 \s__color_stop
+ {
+ { rgb }
+ {
+ \fp_compare:nNnTF {#1} < { 420 }
+ { \__color_parse_model_wave_auxi:nn {#1} { 0.3 + 0.7 * (#1 - 380) / 40 }
+ }
+ {
+ \fp_compare:nNnTF {#1} > { 700 }
+ { \__color_parse_model_wave_auxi:nn {#1} { 0.3 + 0.7 * (#1 - 780) / -80 } }
+ { \__color_parse_model_wave_auxi:nn {#1} { 1 } }
+ }
+ }
+ }
+\cs_new:Npn \__color_parse_model_wave_auxi:nn #1#2
+ {
+ \fp_compare:nNnTF {#1} < { 440 }
+ {
+ \__color_parse_model_wave_auxii:nn
+ { 4 + \__color_parse_model_wave_rho:n { (#1 - 440) / -60 } }
+ {#2}
+ }
+ {
+ \fp_compare:nNnTF {#1} < { 490 }
+ {
+ \__color_parse_model_wave_auxii:nn
+ { 4 - \__color_parse_model_wave_rho:n { (#1 - 440) / 50 } }
+ {#2}
+ }
+ {
+ \fp_compare:nNnTF {#1} < { 510 }
+ {
+ \__color_parse_model_wave_auxii:nn
+ { 2 + \__color_parse_model_wave_rho:n { (#1 - 510) / -20 } }
+ {#2}
+ }
+ {
+ \fp_compare:nNnTF {#1} < { 580 }
+ {
+ \__color_parse_model_wave_auxii:nn
+ { 2 - \__color_parse_model_wave_rho:n { (#1 - 510) / 70 } }
+ {#2}
+ }
+ {
+ \fp_compare:nNnTF {#1} < { 645 }
+ {
+ \__color_parse_model_wave_auxii:nn
+ { \__color_parse_model_wave_rho:n { (#1 - 645) / -65 } }
+ {#2}
+ }
+ { \__color_parse_model_wave_auxii:nn { 0 } {#2} }
+ }
+ }
+ }
+ }
+ }
+\cs_new:Npn \__color_parse_model_wave_auxii:nn #1#2
+ {
+ \exp_args:Neee \__color_parse_model_hsb_aux:nnn
+ { \fp_eval:n {#1} }
+ { 1 }
+ { \__color_parse_model_wave_rho:n {#2} }
+ }
+\cs_new:Npn \__color_parse_model_wave_rho:n #1
+ { \fp_eval:n { min(1, max(0,#1) ) } }
+\cs_new:Npn \__color_parse_model_cmy:w #1 , #2 , #3 , #4 \s__color_stop
+ {
+ { cmyk }
+ { \__color_convert_rgb_cmyk:nnn {#1} {#2} {#3} }
+ }
+\cs_new:Npn \__color_parse_model_tHsb:w #1 , #2 , #3 , #4 \s__color_stop
+ {
+ \exp_args:Ne \__color_parse_model_hsb:nnn
+ { \__color_parse_model_tHsb:n {#1} } {#2} {#3}
+ }
+\cs_new:Npn \__color_parse_model_tHsb:n #1
+ {
+ \__color_parse_model_tHsb:nw {#1}
+ 0 , 0 ;
+ 60 , 30 ;
+ 120 , 60 ;
+ 180 , 120 ;
+ 210 , 180 ;
+ 240 , 240 ;
+ 360 , 360 ;
+ \q_recursion_tail , ;
+ \q_recursion_stop
+ }
+\cs_new:Npn \__color_parse_model_tHsb:nw #1 #2 , #3 ; #4 , #5 ;
+ {
+ \quark_if_recursion_tail_stop_do:nn {#4} { 0 }
+ \fp_compare:nNnTF {#1} > {#4}
+ { \__color_parse_model_tHsb:nw {#1} #4 , #5 ; }
+ {
+ \use_i_delimit_by_q_recursion_stop:nw
+ { \fp_eval:n { ((#1 - #2) / (#4 - #2) * (#5 - #3) + #3) / 360 } }
+ }
+ }
+\cs_new:cpn { __color_parse_model_&spot:w } #1 , #2 \s__color_stop
+ { { gray } { #1 } }
+\tl_new:N \l_color_fixed_model_tl
+\cs_new_protected:Npn \__color_check_model:N #1
+ {
+ \tl_if_empty:NF \l_color_fixed_model_tl
+ {
+ \exp_after:wN \__color_check_model:nn #1
+ \tl_if_eq:NNF \l__color_model_tl \l_color_fixed_model_tl
+ {
+ \__color_convert:VVN \l__color_model_tl \l_color_fixed_model_tl
+ \l__color_value_tl
+ }
+ \tl_set:Ne #1
+ { { \l_color_fixed_model_tl } { \l__color_value_tl } }
+ }
+ }
+\cs_new_protected:Npn \__color_check_model:nn #1#2
+ {
+ \tl_set:Nn \l__color_model_tl {#1}
+ \tl_set:Nn \l__color_value_tl {#2}
+ }
+\cs_new_protected:Npe \__color_finalise_current:
+ {
+ \tl_set:Ne \exp_not:c { l__color_named_ . _tl }
+ { \exp_not:N \__color_model:N \exp_not:N \l__color_current_tl }
+ \prop_clear:N \exp_not:c { l__color_named_ . _prop }
+ \prop_put:NVe \exp_not:c { l__color_named_ . _prop }
+ \exp_not:c { l__color_named_ . _tl }
+ { \exp_not:N \__color_values:N \exp_not:N \l__color_current_tl }
+ }
+\cs_new_protected:Npn \color_select:n #1
+ {
+ \__color_parse:nN {#1} \l__color_current_tl
+ \__color_finalise_current:
+ \__color_select:N \l__color_current_tl
+ }
+\cs_new_protected:Npn \color_select:nn #1#2
+ {
+ \__color_select_main:Nw \l__color_current_tl
+ #1 / / \s__color_mark #2 / / \s__color_stop
+ \__color_finalise_current:
+ \__color_select:N \l__color_current_tl
+ }
+\cs_new_protected:Npn \__color_select_main:Nw
+ #1 #2 / #3 / #4 \s__color_mark #5 / #6 / #7 \s__color_stop
+ {
+ \__color_select:nnN {#2} {#5} #1
+ \bool_lazy_or:nnF
+ { \tl_if_empty_p:N \l_color_fixed_model_tl }
+ { \str_if_eq_p:nV {#2} \l_color_fixed_model_tl }
+ { \__color_select_loop:Nw #1 #3 / #4 \s__color_mark #6 / #7 \s__color_stop }
+ }
+\cs_new_protected:Npn \__color_select_loop:Nw
+ #1 #2 / #3 \s__color_mark #4 / #5 \s__color_stop
+ {
+ \str_if_eq:nVTF {#2} \l_color_fixed_model_tl
+ { \__color_select:nnN {#2} {#4} #1 }
+ {
+ \tl_if_blank:nTF {#2}
+ { \exp_after:wN \__color_select_swap:Nnn \exp_after:wN #1 #1 }
+ { \__color_select_loop:Nw #1 #3 \s__color_mark #5 \s__color_stop }
+ }
+ }
+\cs_new_protected:Npn \__color_select:nnN #1#2#3
+ {
+ \cs_if_exist:cTF { __color_parse_model_ #1 :w }
+ {
+ \tl_set:Ne #3
+ { \use:c { __color_parse_model_ #1 :w } #2 , 0 , 0 , 0 , 0 \s__color_stop }
+ }
+ { \msg_error:nnn { color } { unknown-model } {#1} }
+ }
+\cs_new_protected:Npn \__color_select_swap:Nnn #1#2#3
+ {
+ \__color_convert:nVnN {#2} \l_color_fixed_model_tl {#3} \l__color_value_tl
+ \tl_set:Ne #1
+ { { \l_color_fixed_model_tl } { \l__color_value_tl } }
+ }
+\tl_new:N \l_color_math_active_tl
+\tl_set:Nn \l_color_math_active_tl { ' }
+\seq_new:N \g__color_math_seq
+\cs_new_protected:Npn \color_math:nn #1#2
+ {
+ \__color_math:nn {#2}
+ { \__color_parse:nN {#1} \l__color_current_tl }
+ }
+\cs_new_protected:Npn \color_math:nnn #1#2#3
+ {
+ \__color_math:nn {#3}
+ {
+ \__color_select_main:Nw \l__color_current_tl
+ #1 / / \s__color_mark #2 / / \s__color_stop
+ }
+ }
+\cs_new_protected:Npn \__color_math:nn #1#2
+ {
+ \seq_gpush:NV \g__color_math_seq \l__color_current_tl
+ #2
+ \__color_select_math:N \l__color_current_tl
+ #1
+ \__color_math_scan:w
+ }
+\cs_new_protected:Npn \__color_math_scan:w
+ {
+ \peek_remove_filler:n
+ {
+ \group_align_safe_begin:
+ \peek_catcode:NTF \c_alignment_token
+ {
+ \group_align_safe_end:
+ \__color_math_scan_end:
+ }
+ {
+ \group_align_safe_end:
+ \__color_math_scan_auxi:
+ }
+ }
+ }
+\cs_new_protected:Npn \__color_math_scan_auxi:
+ {
+ \token_case_catcode:NnTF \l_peek_token
+ {
+ \c_math_subscript_token { }
+ \c_math_superscript_token { }
+ }
+ { \__color_math_scripts:Nw }
+ {
+ \token_case_meaning:NnTF \l_peek_token
+ {
+ \tex_limits:D { \tex_limits:D }
+ \tex_nolimits:D { \tex_nolimits:D }
+ \tex_displaylimits:D { \tex_displaylimits:D }
+ }
+ { \__color_math_scan:w \use_none:n }
+ { \__color_math_scan_auxii: }
+ }
+ }
+\cs_new_protected:Npn \__color_math_scan_auxii:
+ {
+ \tl_map_inline:Nn \l_color_math_active_tl
+ {
+ \token_if_eq_meaning:NNT \l_peek_token ##1
+ {
+ \tl_map_break:n
+ {
+ \use_i:nn
+ { \__color_math_scan_auxiii:N ##1 }
+ }
+ }
+ \__color_math_scan_end:
+ }
+ }
+\cs_new_protected:Npn \__color_math_scan_auxiii:N #1
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \__color_math_scan:w
+ \char_generate:nn { `#1 } { 13 }
+ }
+\cs_new_protected:Npn \__color_math_scan_end:
+ {
+ \__color_backend_reset:
+ \seq_gpop:NN \g__color_math_seq \l__color_current_tl
+ }
+\cs_new_protected:Npn \__color_math_scripts:Nw #1
+ {
+ #1
+ \c_group_begin_token
+ \c_group_begin_token
+ \seq_get:NN \g__color_math_seq \l__color_current_tl
+ \__color_select:N \l__color_current_tl
+ \group_insert_after:N \c_group_end_token
+ \group_insert_after:N \__color_math_scan:w
+ \peek_remove_filler:n
+ {
+ \peek_catcode_remove:NF \c_group_begin_token
+ { \__color_math_script_aux:N }
+ }
+ }
+\cs_new_protected:Npn \__color_math_script_aux:N #1 { #1 \c_group_end_token }
+\cs_new_protected:Npn \color_fill:n #1
+ {
+ \__color_parse:nN {#1} \l__color_current_tl
+ \exp_after:wN \__color_draw:nnn \l__color_current_tl { fill }
+ }
+\cs_new_protected:Npn \color_stroke:n #1
+ {
+ \__color_parse:nN {#1} \l__color_current_tl
+ \exp_after:wN \__color_draw:nnn \l__color_current_tl { stroke }
+ }
+\cs_new_protected:Npn \color_fill:nn #1#2
+ {
+ \__color_select_main:Nw \l__color_current_tl
+ #1 / / \s__color_mark #2 / / \s__color_stop
+ \exp_after:wN \__color_draw:nnn \l__color_current_tl { fill }
+ }
+\cs_new_protected:Npn \color_stroke:nn #1#2
+ {
+ \__color_select_main:Nw \l__color_current_tl
+ #1 / / \s__color_mark #2 / / \s__color_stop
+ \exp_after:wN \__color_draw:nnn \l__color_current_tl { stroke }
+ }
+\cs_new_protected:Npn \__color_draw:nnn #1#2#3
+ {
+ \use:c { __color_backend_ #3 _ #1 :n } {#2}
+ \exp_args:Nc \group_insert_after:N { __color_backend_ #3 _ reset: }
+ }
+\tl_new:N \l__color_named_tl
+\cs_new_protected:Npn \color_set:nn #1#2
+ {
+ \exp_args:NV \__color_set:nnn
+ \l_color_fixed_model_tl {#1} {#2}
+ }
+\cs_new_protected:Npn \__color_set:nnn #1#2#3
+ {
+ \tl_clear:N \l_color_fixed_model_tl
+ \__color_set:nn {#2} {#3}
+ \tl_set:Nn \l_color_fixed_model_tl {#1}
+ }
+\cs_new_protected:Npn \__color_set:nn #1#2
+ {
+ \str_if_eq:nnF {#1} { . }
+ {
+ \__color_parse:nN {#2} \l__color_named_tl
+ \tl_clear_new:c { l__color_named_ #1 _tl }
+ \tl_set:ce { l__color_named_ #1 _tl }
+ { \__color_model:N \l__color_named_tl }
+ \prop_clear_new:c { l__color_named_ #1 _prop }
+ \prop_put:cve { l__color_named_ #1 _prop } { l__color_named_ #1 _tl }
+ { \__color_values:N \l__color_named_tl }
+ \__color_set:nnw {#1} {#2} #2 ! \s__color_stop
+ }
+ }
+\cs_new_protected:Npn \__color_set:nnw #1#2#3 ! #4 \s__color_stop
+ {
+ \clist_map_inline:nn { cmyk , gray , rgb }
+ {
+ \prop_get:cnNT { l__color_named_ #3 _prop } {##1} \l__color_internal_tl
+ {
+ \prop_if_in:cnF { l__color_named_ #1 _prop } {##1}
+ {
+ \group_begin:
+ \bool_set_true:N \l__color_ignore_error_bool
+ \tl_set:cn { l__color_named_ #3 _tl } {##1}
+ \__color_parse:nN {#2} \l__color_internal_tl
+ \exp_args:NNNV \group_end:
+ \tl_set:Nn \l__color_internal_tl \l__color_internal_tl
+ \prop_put:cee { l__color_named_ #1 _prop }
+ { \__color_model:N \l__color_internal_tl }
+ { \__color_values:N \l__color_internal_tl }
+ }
+ }
+ }
+ }
+\cs_new_protected:Npn \color_set:nnn #1#2#3
+ {
+ \str_if_eq:nnF {#1} { . }
+ {
+ \tl_clear_new:c { l__color_named_ #1 _tl }
+ \prop_clear_new:c { l__color_named_ #1 _prop }
+ \exp_args:Ne \__color_set_aux:nnn { \tl_to_str:n {#2} }
+ {#1} {#3}
+ }
+ }
+\cs_new_protected:Npe \__color_set_aux:nnn #1#2#3
+ {
+ \exp_not:N \__color_set_colon:nnw {#2} {#3}
+ #1 \c_colon_str \c_colon_str \exp_not:N \s__color_stop
+ }
+\use:e
+ {
+ \cs_new_protected:Npn \exp_not:N \__color_set_colon:nnw
+ #1#2 #3 \c_colon_str #4 \c_colon_str
+ #5 \exp_not:N \s__color_stop
+ }
+ {
+ \tl_if_blank:nTF {#4}
+ { \__color_set_loop:nw {#1} #3 }
+ { \__color_set_loop:nw {#1} #4 }
+ / / \s__color_mark #2 / / \s__color_stop
+ }
+\cs_new_protected:Npn \__color_set_loop:nw
+ #1#2 / #3 \s__color_mark #4 / #5 \s__color_stop
+ {
+ \tl_if_blank:nF {#2}
+ {
+ \__color_select:nnN {#2} {#4} \l__color_named_tl
+ \tl_set:Ne \l__color_internal_tl { \__color_model:N \l__color_named_tl }
+ \tl_if_empty:cT { l__color_named_ #1 _tl }
+ { \tl_set_eq:cN { l__color_named_ #1 _tl } \l__color_internal_tl }
+ \prop_put:cVe { l__color_named_ #1 _prop } \l__color_internal_tl
+ { \__color_values:N \l__color_named_tl }
+ \__color_set_loop:nw {#1} #3 \s__color_mark #5 \s__color_stop
+ }
+ }
+\cs_new_protected:Npn \color_set_eq:nn #1#2
+ {
+ \color_if_exist:nTF {#2}
+ {
+ \tl_clear_new:c { l__color_named_ #1 _tl }
+ \prop_clear_new:c { l__color_named_ #1 _prop }
+ \str_if_eq:nnTF {#2} { . }
+ {
+ \tl_set:ce { l__color_named_ #1 _tl }
+ { \__color_model:N \l__color_current_tl }
+ \prop_put:cve { l__color_named_ #1 _prop } { l__color_named_ #1 _tl }
+ { \__color_values:N \l__color_current_tl }
+ }
+ {
+ \tl_set_eq:cc { l__color_named_ #1 _tl } { l__color_named_ #2 _tl }
+ \prop_set_eq:cc { l__color_named_ #1 _prop } { l__color_named_ #2 _prop }
+ }
+ }
+ {
+ \msg_error:nnn { color } { unknown-color } {#2}
+ }
+ }
+\color_set:nnn { black } { gray } { 0 }
+\color_set:nnn { white } { gray } { 1 }
+\color_set:nnn { cyan } { cmyk } { 1 , 0 , 0 , 0 }
+\color_set:nnn { magenta } { cmyk } { 0 , 1 , 0 , 0 }
+\color_set:nnn { yellow } { cmyk } { 0 , 0 , 1 , 0 }
+\color_set:nnn { red } { rgb } { 1 , 0 , 0 }
+\color_set:nnn { green } { rgb } { 0 , 1 , 0 }
+\color_set:nnn { blue } { rgb } { 0 , 0 , 1 }
+\prop_new:c { l__color_named_._prop }
+\tl_new:c { l__color_named_._tl }
+\tl_set:ce { l__color_named_._tl } { \__color_model:N \l__color_current_tl }
+\cs_new_protected:Npn \color_export:nnN #1#2#3
+ {
+ \group_begin:
+ \tl_if_exist:cT { c__color_export_ #2 _tl }
+ { \tl_set_eq:Nc \l_color_fixed_model_tl { c__color_export_ #2 _tl } }
+ \__color_parse:nN {#1} #3
+ \__color_export:nN {#2} #3
+ \exp_args:NNNV \group_end:
+ \tl_set:Nn #3 #3
+ }
+\cs_new_protected:Npn \color_export:nnnN #1#2#3#4
+ {
+ \__color_select_main:Nw #4
+ #1 / / \s__color_mark #2 / / \s__color_stop
+ \__color_export:nN {#3} #4
+ }
+\cs_new_protected:Npn \__color_export:nN #1#2
+ { \exp_after:wN \__color_export:nnnN #2 {#1} #2 }
+\cs_new:Npn \__color_export:nnnN #1#2#3#4
+ {
+ \cs_if_exist_use:cF { __color_export_format_ #3 :nnN }
+ {
+ \msg_error:nnn { color } { unknown-export-format } {#3}
+ \use_none:nnn
+ }
+ {#1} {#2} #4
+ }
+\cs_new_protected:Npn \__color_export_format_backend:nnN #1#2#3
+ { \tl_set:Nn #3 { {#1} {#2} } }
+\cs_new_protected:Npn \__color_export:nnnNN #1#2#3#4#5
+ {
+ \str_if_eq:nnTF {#2} {#1}
+ { #5 #4 #3 \s__color_stop }
+ {
+ \__color_convert:nnnN {#2} {#1} {#3} #4
+ \exp_after:wN #5 \exp_after:wN #4
+ #4 \s__color_stop
+ }
+ }
+\tl_const:cn { c__color_export_comma-sep-cmyk_tl } { cmyk }
+\tl_const:cn { c__color_export_comma-sep-rgb_tl } { rgb }
+\tl_const:Nn \c__color_export_HTML_tl { rgb }
+\tl_const:cn { c__color_export_space-sep-cmyk_tl } { cmyk }
+\tl_const:cn { c__color_export_space-sep-rgb_tl } { rgb }
+\group_begin:
+ \cs_set_protected:Npn \__color_tmp:w #1#2
+ {
+ \cs_new_protected:cpe { __color_export_format_ #1 :nnN } ##1##2##3
+ {
+ \exp_not:N \__color_export:nnnNN {#2} {##1} {##2} ##3
+ \exp_not:c { __color_export_ #1 :Nw }
+ }
+ }
+ \__color_tmp:w { comma-sep-cmyk } { cmyk }
+ \__color_tmp:w { comma-sep-rgb } { rgb }
+ \__color_tmp:w { HTML } { rgb }
+ \__color_tmp:w { space-sep-cmyk } { cmyk }
+ \__color_tmp:w { space-sep-rgb } { rgb }
+
+\group_end:
+\cs_new_protected:cpn { __color_export_comma-sep-cmyk:Nw }
+ #1#2 ~ #3 ~ #4 ~ #5 \s__color_stop
+ { \tl_set:Nn #1 { #2 , #3 , #4 , #5 } }
+\cs_new_protected:cpn { __color_export_space-sep-cmyk:Nw } #1#2 \s__color_stop
+ { \tl_set:Nn #1 {#2} }
+\cs_new_protected:cpn { __color_export_comma-sep-rgb:Nw } #1#2 ~ #3 ~ #4 \s__color_stop
+ { \tl_set:Ne #1 { #2 , #3 , #4 } }
+\cs_new_protected:Npn \__color_export_HTML:Nw #1#2 ~ #3 ~ #4 \s__color_stop
+ {
+ \tl_set:Ne #1
+ {
+ \__color_export_HTML:n {#2}
+ \__color_export_HTML:n {#3}
+ \__color_export_HTML:n {#4}
+ }
+ }
+\cs_new:Npn \__color_export_HTML:n #1
+ {
+ \fp_compare:nNnTF {#1} = { 0 }
+ { 00 }
+ {
+ \fp_compare:nNnT { #1 * 255 } < { 16 } { 0 }
+ \int_to_Hex:n { \fp_to_int:n { #1 * 255 } }
+ }
+ }
+\cs_new_protected:cpn { __color_export_space-sep-rgb:Nw } #1#2 \s__color_stop
+ { \tl_set:Nn #1 {#2} }
+\prop_new:N \l__color_internal_prop
+\int_new:N \g__color_model_int
+\tl_const:Nn \c__color_fallback_cmyk_tl { cmyk }
+\tl_const:Nn \c__color_fallback_gray_tl { gray }
+\tl_const:Nn \c__color_fallback_rgb_tl { rgb }
+\prop_new:N \g__color_colorants_prop
+\prop_gput:Nnn \g__color_colorants_prop { black } { Black }
+\prop_gput:Nnn \g__color_colorants_prop { blue } { Blue }
+\prop_gput:Nnn \g__color_colorants_prop { cyan } { Cyan }
+\prop_gput:Nnn \g__color_colorants_prop { green } { Green }
+\prop_gput:Nnn \g__color_colorants_prop { magenta } { Magenta }
+\prop_gput:Nnn \g__color_colorants_prop { none } { None }
+\prop_gput:Nnn \g__color_colorants_prop { red } { Red }
+\prop_gput:Nnn \g__color_colorants_prop { yellow } { Yellow }
+\tl_const:Nn \c__color_model_whitepoint_CIELAB_a_tl { 1.0985 ~ 1 ~ 0.3558 }
+\tl_const:Nn \c__color_model_whitepoint_CIELAB_b_tl { 0.9807 ~ 1 ~ 1.1822 }
+\tl_const:Nn \c__color_model_whitepoint_CIELAB_e_tl { 1 ~ 1 ~ 1 }
+\tl_const:cn { c__color_model_whitepoint_CIELAB_d50_tl } { 0.9642 ~ 1 ~ 0.8251 }
+\tl_const:cn { c__color_model_whitepoint_CIELAB_d55_tl } { 0.9568 ~ 1 ~ 0.9214 }
+\tl_const:cn { c__color_model_whitepoint_CIELAB_d65_tl } { 0.9504 ~ 1 ~ 1.0888 }
+\tl_const:cn { c__color_model_whitepoint_CIELAB_d75_tl } { 0.9497 ~ 1 ~ 1.2261 }
+\tl_const:Nn \c__color_model_range_CIELAB_tl { 0 ~ 100 ~ -128 ~ 127 ~ -128 ~ 127 }
+\prop_new:N \g__color_alternative_model_prop
+\clist_map_inline:nn { cyan , magenta , yellow , black }
+ { \prop_gput:Nnn \g__color_alternative_model_prop {#1} { cmyk } }
+\clist_map_inline:nn { red , green , blue }
+ { \prop_gput:Nnn \g__color_alternative_model_prop {#1} { rgb } }
+\prop_new:N \g__color_alternative_values_prop
+\prop_gput:Nnn \g__color_alternative_values_prop { cyan } { 1 , 0 , 0 , 0 }
+\prop_gput:Nnn \g__color_alternative_values_prop { magenta } { 0 , 1 , 0 , 0 }
+\prop_gput:Nnn \g__color_alternative_values_prop { yellow } { 0 , 0 , 1 , 0 }
+\prop_gput:Nnn \g__color_alternative_values_prop { black } { 0 , 0 , 0 , 1 }
+\prop_gput:Nnn \g__color_alternative_values_prop { red } { 1 , 0 , 0 }
+\prop_gput:Nnn \g__color_alternative_values_prop { green } { 0 , 1 , 0 }
+\prop_gput:Nnn \g__color_alternative_values_prop { blue } { 0 , 0 , 1 }
+\cs_new_protected:Npn \color_model_new:nnn #1#2#3
+ {
+ \exp_args:Nee \__color_model_new:nnn
+ { \tl_to_str:n {#1} }
+ { \str_casefold:n {#2} } {#3}
+ }
+\cs_new_protected:Npn \__color_model_new:nnn #1#2#3
+ {
+ \cs_if_exist:cTF { __color_parse_model_ #1 :w }
+ {
+ \msg_error:nnn { color } { model-already-defined } {#1}
+ }
+ {
+ \cs_if_exist:cTF { __color_model_ #2 :n }
+ {
+ \prop_set_from_keyval:Nn \l__color_internal_prop {#3}
+ \use:c { __color_model_ #2 :n } {#1}
+ }
+ {
+ \msg_error:nnn { color } { unknown-model-type } {#2}
+ }
+ }
+ }
+\cs_new_protected:Npn \__color_model_init:nnn #1#2#3
+ {
+ \int_gincr:N \g__color_model_int
+ \clist_map_inline:nn { fill , stroke , select }
+ {
+ \cs_new_protected:cpe { __color_backend_ ##1 _ #1 :n } ####1
+ {
+ \exp_not:c { __color_backend_ ##1 _ #2 :nn }
+ { color \int_use:N \g__color_model_int } {####1}
+ }
+ }
+ \cs_new_protected:cpe { __color_model_ #1 _white: }
+ {
+ \prop_put:Nnn \exp_not:N \l__color_named_white_prop {#1}
+ { \exp_not:n {#3} }
+ \exp_not:N \int_compare:nNnF { \tex_currentgrouplevel:D } = 0
+ { \group_insert_after:N \exp_not:c { __color_model_ #1 _ white: } }
+ }
+ \use:c { __color_model_ #1 _white: }
+ }
+\cs_generate_variant:Nn \__color_model_init:nnn { nne }
+\cs_new_protected:Npn \__color_model_separation:n #1
+ {
+ \prop_get:NnNTF \l__color_internal_prop { name }
+ \l__color_internal_tl
+ {
+ \exp_args:NV \__color_model_separation:nn
+ \l__color_internal_tl {#1}
+ }
+ {
+ \msg_error:nnn { color }
+ { separation-requires-name } {#1}
+ }
+ }
+\cs_new_protected:Npn \__color_model_separation:nn #1#2
+ {
+ \prop_get:NnNTF \l__color_internal_prop { alternative-model }
+ \l__color_internal_tl
+ {
+ \exp_args:NV \__color_model_separation:nnn
+ \l__color_internal_tl {#2} {#1}
+ }
+ {
+ \msg_error:nnn { color }
+ { separation-alternative-model } {#2}
+ }
+ }
+\cs_new_protected:Npn \__color_model_separation:nnn #1#2#3
+ {
+ \cs_if_exist:cTF { __color_model_separation_ #1 :nnnnnn }
+ {
+ \prop_get:NnNTF \l__color_internal_prop { alternative-values }
+ \l__color_internal_tl
+ {
+ \exp_after:wN \__color_model_separation:w \l__color_internal_tl
+ , 0 , 0 , 0 , 0 \s__color_stop {#2} {#3} {#1}
+ }
+ {
+ \msg_error:nnn { color }
+ { separation-alternative-values } {#2}
+ }
+ }
+ {
+ \msg_error:nnn { color }
+ { unknown-alternative-model } {#1}
+ }
+ }
+\cs_new_protected:Npn \__color_model_separation:w
+ #1 , #2 , #3 , #4 , #5 \s__color_stop #6#7#8
+ {
+ \__color_model_init:nnn {#6} { separation } { 0 }
+ \cs_new_eq:cN { __color_parse_mix_ #6 :nw } \__color_parse_mix_gray:nw
+ \cs_new:cpn { __color_parse_model_ #6 :w } ##1 , ##2 \s__color_stop
+ { {#6} { \__color_parse_number:n {##1} } }
+ \use:c { __color_model_separation_ #8 :nnnnnn }
+ {#6} {#7} {#1} {#2} {#3} {#4}
+ \prop_gput:Nnn \g__color_alternative_model_prop {#6} {#8}
+ \prop_gput:Nne \g__color_colorants_prop {#6}
+ { \str_convert_pdfname:n {#7} }
+ }
+\cs_new_protected:Npn \__color_model_separation_cmyk:nnnnnn #1#2#3#4#5#6
+ {
+ \tl_const:cn { c__color_fallback_ #1 _tl } { cmyk }
+ \cs_new:cpn { __color_convert_ #1 _cmyk:w } ##1 \s__color_stop
+ {
+ \fp_eval:n {##1 * #3} ~
+ \fp_eval:n {##1 * #4} ~
+ \fp_eval:n {##1 * #5} ~
+ \fp_eval:n {##1 * #6}
+ }
+ \cs_new:cpn { __color_convert_cmyk_ #1 :w } ##1 \s__color_stop { 1 }
+ \prop_gput:Nnn \g__color_alternative_values_prop {#1} { #3 , #4 , #5 , #6 }
+ \__color_backend_separation_init:nnnnn {#2} { /DeviceCMYK } { }
+ { 0 ~ 0 ~ 0 ~ 0 } { #3 ~ #4 ~ #5 ~ #6 }
+ }
+\cs_new_protected:Npn \__color_model_separation_rgb:nnnnnn #1#2#3#4#5#6
+ {
+ \tl_const:cn { c__color_fallback_ #1 _tl } { rgb }
+ \cs_new:cpn { __color_convert_ #1 _rgb:w } ##1 \s__color_stop
+ {
+ \fp_eval:n {##1 * #3} ~
+ \fp_eval:n {##1 * #4} ~
+ \fp_eval:n {##1 * #5}
+ }
+ \cs_new:cpn { __color_convert_rgb_ #1 :w } ##1 \s__color_stop { 1 }
+ \prop_gput:Nnn \g__color_alternative_values_prop {#1} { #3 , #4 , #5 }
+ \__color_backend_separation_init:nnnnn {#2} { /DeviceRGB } { }
+ { 0 ~ 0 ~ 0 } { #3 ~ #4 ~ #5 }
+ }
+\cs_new_protected:Npn \__color_model_separation_gray:nnnnnn #1#2#3#4#5#6
+ {
+ \tl_const:cn { c__color_fallback_ #1 _tl } { gray }
+ \cs_new:cpn { __color_convert_ #1 _gray:w } ##1 \s__color_stop
+ { \fp_eval:n {##1 * #3} }
+ \cs_new:cpn { __color_convert_gray_ #1 :w } ##1 \s__color_stop { 1 }
+ \prop_gput:Nnn \g__color_alternative_values_prop {#1} {#3}
+ \__color_backend_separation_init:nnnnn {#2} { /DeviceGray } { } { 0 } {#3}
+ }
+\cs_new_protected:Npn \__color_model_convert:nnn #1#2#3
+ {
+ \cs_new:cpe { __color_convert_ #1 _ #3 :w } ##1 \s__color_stop
+ {
+ \exp_not:N \exp_args:NNe \exp_not:N \use:nn
+ \exp_not:c { __color_convert_ #2 _ #3 :w }
+ { \exp_not:c { __color_convert_ #1 _ #2 :w } ##1 \s__color_stop }
+ \c_space_tl \exp_not:N \s__color_stop
+ }
+ }
+\cs_new_protected:Npn \__color_model_separation_CIELAB:nnnnnn #1#2#3#4#5#6
+ {
+ \prop_get:NnNF \l__color_internal_prop { illuminant }
+ \l__color_internal_tl
+ {
+ \msg_error:nnn { color }
+ { CIELAB-requires-illuminant } {#1}
+ \tl_set:Nn \l__color_internal_tl { d50 }
+ }
+ \exp_args:NV \__color_model_separation_CIELAB:nnnnnnn
+ \l__color_internal_tl {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+\cs_new_protected:Npn \__color_model_separation_CIELAB:nnnnnnn #1#2#3#4#5#6#7
+ {
+ \tl_if_exist:cTF { c__color_model_whitepoint_CIELAB_ #1 _tl }
+ {
+ \__color_backend_separation_init_CIELAB:nnn {#1} {#3} { #4 ~ #5 ~ #6 }
+ \tl_const:cn { c__color_fallback_ #2 _tl } { gray }
+ \cs_new:cpn { __color_convert_ #2 _gray:w } ##1 \s__color_stop
+ { 0 }
+ \cs_new:cpn { __color_convert_gray_ #2 :w } ##1 \s__color_stop
+ { 1 }
+ }
+ {
+ \msg_error:nnn { color }
+ { unknown-CIELAB-illuminant } {#1}
+ }
+ }
+\cs_new_protected:Npn \__color_model_devicen:n #1
+ {
+ \prop_get:NnNTF \l__color_internal_prop { names }
+ \l__color_internal_tl
+ {
+ \exp_args:NV \__color_model_devicen:nn
+ \l__color_internal_tl {#1}
+ }
+ {
+ \msg_error:nnn { color }
+ { DeviceN-requires-names } {#1}
+ }
+ }
+\cs_new_protected:Npn \__color_model_devicen:nn #1#2
+ {
+ \tl_clear:N \l__color_model_tl
+ \clist_map_inline:nn {#1}
+ {
+ \prop_get:NnNTF \g__color_alternative_model_prop {##1}
+ \l__color_internal_tl
+ {
+ \tl_if_empty:NTF \l__color_model_tl
+ { \tl_set_eq:NN \l__color_model_tl \l__color_internal_tl }
+ {
+ \str_if_eq:VVF \l__color_model_tl \l__color_internal_tl
+ {
+ \msg_error:nnn { color }
+ { DeviceN-inconsistent-alternative }
+ {#2}
+ \clist_map_break:n { \use_none:nnnn }
+ }
+ }
+ }
+ {
+ \str_if_eq:nnF {##1} { none }
+ {
+ \msg_error:nnn { color }
+ { DeviceN-no-alternative }
+ {#2}
+ }
+ }
+ }
+ \tl_if_empty:NTF \l__color_model_tl
+ {
+ \msg_error:nnn { color }
+ { DeviceN-no-alternative } {#2}
+ }
+ { \exp_args:NV \__color_model_devicen:nnn \l__color_model_tl {#1} {#2} }
+ }
+\cs_new_protected:Npn \__color_model_devicen:nnn #1#2#3
+ {
+ \exp_args:Ne \__color_model_devicen:nnnn
+ { \clist_count:n {#2} } {#1} {#2} {#3}
+ }
+\cs_new_protected:Npn \__color_model_devicen:nnnn #1#2#3#4
+ {
+ \__color_model_init:nne {#4} { devicen }
+ {
+ 0 \prg_replicate:nn { #1 - 1 } { ~ 0 }
+ }
+ \cs_if_exist_use:cF { __color_model_devicen_parse_ #1 :nn }
+ { \__color_model_devicen_parse_generic:nn }
+ {#4} {#1}
+ \__color_model_devicen_init:nnn {#1} {#2} {#3}
+ \__color_model_devicen_convert:nnne {#4} {#2} {#3}
+ {
+ 1 \prg_replicate:nn { #1 - 1 } { ~ 1 }
+ }
+ }
+\cs_new_protected:cpn { __color_model_devicen_parse_1:nn } #1#2
+ {
+ \cs_new:cpn { __color_parse_model_ #1 :w } ##1 , ##2 \s__color_stop
+ { {#1} { \__color_parse_number:n {##1} } }
+ \cs_new_eq:cN { __color_parse_mix_ #1 :nw } \__color_parse_mix_gray:nw
+ }
+\cs_new_protected:cpn { __color_model_devicen_parse_2:nn } #1#2
+ {
+ \cs_new:cpn { __color_parse_model_ #1 :w } ##1 , ##2 , ##3 \s__color_stop
+ { {#1} { \__color_parse_number:n {##1} ~ \__color_parse_number:n {##2} } }
+ \cs_new:cpn { __color_parse_mix_ #1 :nw }
+ ##1##2 ~ ##3 \s__color_mark ##4 ~ ##5 \s__color_stop
+ {
+ \fp_eval:n { ##2 * ##1 + ##4 * ( 1 - ##1 ) } \c_space_tl
+ \fp_eval:n { ##3 * ##1 + ##5 * ( 1 - ##1 ) }
+ }
+ }
+\cs_new_protected:cpn { __color_model_devicen_parse_3:nn } #1#2
+ {
+ \cs_new:cpn { __color_parse_model_ #1 :w } ##1 , ##2 , ##3 , ##4 \s__color_stop
+ {
+ {#1}
+ {
+ \__color_parse_number:n {##1} ~
+ \__color_parse_number:n {##2} ~
+ \__color_parse_number:n {##3}
+ }
+ }
+ \cs_new_eq:cN { __color_parse_mix_ #1 :nw } \__color_parse_mix_rgb:nw
+ }
+\cs_new_protected:cpn { __color_model_devicen_parse_4:nn } #1#2
+ {
+ \cs_new:cpn { __color_parse_model_ #1 :w }
+ ##1 , ##2 , ##3 , ##4 , ##5 \s__color_stop
+ {
+ {#1}
+ {
+ \__color_parse_number:n {##1} ~
+ \__color_parse_number:n {##2} ~
+ \__color_parse_number:n {##3} ~
+ \__color_parse_number:n {##4}
+ }
+ }
+ \cs_new_eq:cN { __color_parse_mix_ #1 :nw } \__color_parse_mix_cmyk:nw
+ }
+\cs_new_protected:Npn \__color_model_devicen_parse_generic:nn #1#2
+ {
+ \cs_new:cpn { __color_parse_model_ #1 :w } ##1 , ##2 \s__color_stop
+ {
+ {#1}
+ { \__color_model_devicen_parse:nw {#2} ##1 , ##2 , \q_nil , \s__color_stop }
+ }
+ \cs_new:cpe { __color_parse_mix_ #1 :nw }
+ ##1 ##2 \s__color_mark ##3 \s__color_stop
+ {
+ \exp_not:N \__color_model_devicen_mix:nw {##1}
+ ##2 \c_space_tl \exp_not:N \q_nil \c_space_tl \exp_not:N \s__color_mark
+ ##3 \c_space_tl \exp_not:N \q_nil \c_space_tl \exp_not:N \s__color_stop
+ }
+ }
+\cs_new:Npn \__color_model_devicen_parse:nw #1#2 , #3 \s__color_stop
+ {
+ \int_compare:nNnT {#1} > 0
+ {
+ \quark_if_nil:nTF {#2}
+ { \prg_replicate:nn {#1} { 0 ~ } }
+ {
+ \__color_parse_number:n {#2}
+ \int_compare:nNnT {#1} > 1 { ~ }
+ \exp_args:Nf \__color_model_devicen_parse:nw
+ { \int_eval:n { #1 - 1 } } #3 \s__color_stop
+ }
+ }
+ }
+\cs_new:Npn \__color_model_devicen_mix:nw #1#2 ~ #3 \s__color_mark #4 ~ #5 \s__color_stop
+ {
+ \fp_eval:n { #2 * #1 + #4 * ( 1 - #1 ) }
+ \quark_if_nil:oF { \tl_head:w #3 \q_stop }
+ {
+ \c_space_tl
+ \__color_model_devicen_mix:nw {#1} #3 \s__color_mark #5 \s__color_stop
+ }
+ }
+\cs_new_protected:Npn \__color_model_devicen_init:nnn #1#2#3
+ {
+ \exp_args:Ne \__color_model_devicen_init:nnnn
+ {
+ \str_case:nn {#2}
+ {
+ { cmyk } { 4 }
+ { gray } { 1 }
+ { rgb } { 3 }
+ }
+ }
+ {#1} {#2} {#3}
+ }
+\cs_new_protected:Npn \__color_model_devicen_init:nnnn #1#2#3#4
+ {
+ \tl_set:Ne \l__color_internal_tl
+ { \prg_replicate:nn {#1} { 1.0 ~ } }
+ \int_zero:N \l__color_internal_int
+ \clist_map_inline:nn {#4}
+ {
+ \int_incr:N \l__color_internal_int
+ \prop_get:NnN \g__color_alternative_values_prop {##1}
+ \l__color_value_tl
+ \exp_after:wN \__color_model_devicen_transform:w
+ \l__color_value_tl , 0 , 0 , 0 , \s__color_stop {#1} {#2}
+ }
+ \tl_put_right:Ne \l__color_internal_tl
+ {
+ \prg_replicate:nn {#1}
+ { neg ~ 1.0 ~ add ~ #1 ~ -1 ~ roll ~ }
+ \int_eval:n { #2 + #1 } ~ #1 ~ roll
+ \prg_replicate:nn {#2} { ~ pop } ~
+ #1 ~ 1 ~ roll
+ }
+ \use:e
+ {
+ \__color_backend_devicen_init:nnn
+ {
+ \clist_map_function:nN {#4}
+ \__color_model_devicen_colorant:n
+ }
+ {
+ \str_case:nn {#3}
+ {
+ { cmyk } { /DeviceCMYK }
+ { gray } { /DeviceGray }
+ { rgb } { /DeviceRGB }
+ }
+ }
+ { \exp_not:V \l__color_internal_tl }
+ }
+ }
+\cs_new_protected:Npn \__color_model_devicen_transform:w
+ #1 , #2 , #3 , #4 , #5 \s__color_stop #6#7
+ {
+ \use:c { __color_model_devicen_transform_ #6 :nnnnn }
+ {#1} {#2} {#3} {#4} {#7}
+ }
+\cs_new_protected:cpn { __color_model_devicen_transform_1:nnnnn } #1#2#3#4#5
+ { \__color_model_devicen_transform:nnn {#5} { 1 } {#1} }
+\cs_new_protected:cpn { __color_model_devicen_transform_3:nnnnn } #1#2#3#4#5
+ {
+ \clist_map_inline:nn { #1 , #2 , #3 }
+ { \__color_model_devicen_transform:nnn {#5} { 3 } {##1} }
+ }
+\cs_new_protected:cpn { __color_model_devicen_transform_4:nnnnn } #1#2#3#4#5
+ {
+ \clist_map_inline:nn { #1 , #2 , #3 , #4 }
+ { \__color_model_devicen_transform:nnn {#5} { 4 } {##1} }
+ }
+\cs_new_protected:Npn \__color_model_devicen_transform:nnn #1#2#3
+ {
+ \tl_put_right:Ne \l__color_internal_tl
+ {
+ \fp_compare:nNnF {#3} = \c_zero_fp
+ {
+ \int_eval:n { #1 - \l__color_internal_int + #2 } ~ index ~
+ -#3 ~ mul ~ 1.0 ~ add ~ mul ~
+ }
+ #2 ~ -1 ~ roll ~
+ }
+ }
+\cs_new:Npn \__color_model_devicen_colorant:n #1
+ {
+ / \prop_item:Nn \g__color_colorants_prop {#1} ~
+ }
+\cs_new_protected:Npn \__color_model_devicen_convert:nnnn #1#2#3
+ {
+ \use:c { __color_model_devicen_convert_ #2 :nnn } {#1} {#3}
+ }
+\cs_generate_variant:Nn \__color_model_devicen_convert:nnnn { nnne }
+\cs_new_protected:Npn \__color_model_devicen_convert_cmyk:nnn #1#2
+ {
+ \tl_const:cn { c__color_fallback_ #1 _tl } { cmyk }
+ \__color_model_devicen_convert:nnnnn {#1} { cmyk } { 4 } {#2}
+ }
+\cs_new_protected:Npn \__color_model_devicen_convert_gray:nnn #1#2
+ {
+ \tl_const:cn { c__color_fallback_ #1 _tl } { gray }
+ \__color_model_devicen_convert:nnnnn {#1} { gray } { 1 } {#2}
+ }
+\cs_new_protected:Npn \__color_model_devicen_convert_rgb:nnn #1#2
+ {
+ \tl_const:cn { c__color_fallback_ #1 _tl } { rgb }
+ \__color_model_devicen_convert:nnnnn {#1} { rgb } { 3 } {#2}
+ }
+\cs_new_protected:Npn \__color_model_devicen_convert:nnnnn #1#2#3#4#5
+ {
+ \cs_new:cpn { __color_convert_ #2 _ #1 :w } ##1 \s__color_stop {#5}
+ \cs_new:cpe { __color_convert_ #1 _ #2 :w } ##1 \s__color_stop
+ {
+ \exp_not:c { __color_convert_devicen_ #2 : \prg_replicate:nn {#3} { n } w }
+ \prg_replicate:nn {#3} { { 1 } }
+ ##1 ~ \exp_not:N \s__color_mark
+ \clist_map_function:nN {#4} \__color_model_devicen_convert:n
+ {}
+ \exp_not:N \s__color_stop
+ }
+ }
+\cs_new:Npn \__color_model_devicen_convert:n #1
+ {
+ {
+ \exp_args:Ne \__color_model_devicen_convert_aux:n
+ { \prop_item:Nn \g__color_alternative_values_prop {#1} }
+ }
+ }
+\cs_new:Npn \__color_model_devicen_convert_aux:n #1
+ { \__color_model_devicen_convert_aux:w #1 , , , , \s__color_stop }
+\cs_new:Npn \__color_model_devicen_convert_aux:w #1 , #2 , #3 , #4 , #5 \s__color_stop
+ {
+ {#1}
+ \tl_if_blank:nF {#2}
+ {
+ {#2}
+ \tl_if_blank:nF {#3}
+ {
+ {#3}
+ \tl_if_blank:nF {#4} { {#4} }
+ }
+ }
+ }
+\cs_new:Npn \__color_convert_devicen_cmyk:nnnnw
+ #1#2#3#4#5 ~ #6 \s__color_mark #7#8 \s__color_stop
+ {
+ \__color_convert_devicen_cmyk:nnnnnnnnn {#5} {#1} {#2} {#3} {#4} #7
+ #6 \s__color_mark #8 \s__color_stop
+ }
+\cs_new:Npn \__color_convert_devicen_cmyk:nnnnnnnnn #1#2#3#4#5#6#7#8#9
+ {
+ \use:e
+ {
+ \exp_not:N \__color_convert_devicen_cmyk_aux:nnnnw
+ { \fp_eval:n { #2 * (1 - (#1 * #6)) } }
+ { \fp_eval:n { #3 * (1 - (#1 * #7)) } }
+ { \fp_eval:n { #4 * (1 - (#1 * #8)) } }
+ { \fp_eval:n { #5 * (1 - (#1 * #9)) } }
+ }
+ }
+\cs_new:Npn \__color_convert_devicen_cmyk_aux:nnnnw
+ #1#2#3#4 #5 \s__color_mark #6 \s__color_stop
+ {
+ \tl_if_blank:nTF {#5}
+ {
+ \fp_eval:n { 1 - #1 } ~
+ \fp_eval:n { 1 - #2 } ~
+ \fp_eval:n { 1 - #3 } ~
+ \fp_eval:n { 1 - #4 }
+ }
+ {
+ \__color_convert_devicen_cmyk:nnnnw {#1} {#2} {#3} {#4}
+ #5 \s__color_mark #6 \s__color_stop
+ }
+ }
+\cs_new:Npn \__color_convert_devicen_gray:nw
+ #1#2 ~ #3 \s__color_mark #4#5 \s__color_stop
+ {
+ \__color_convert_devicen_gray:nnn {#2} {#1} #4
+ #3 \s__color_mark #5 \s__color_stop
+ }
+\cs_new:Npn \__color_convert_devicen_gray:nnn #1#2#3
+ {
+ \exp_arsgs:Ne \__color_convert_devicen_gray_aux:nw
+ { \fp_eval:n { #2 * (1 - (#1 * #3)) } }
+ }
+\cs_new:Npn \__color_convert_devicen_gray_aux:nw
+ #1 #2 \s__color_mark #3 \s__color_stop
+ {
+ \tl_if_blank:nTF {#2}
+ { \fp_eval:n { 1 - #1 } }
+ {
+ \__color_convert_devicen_gray:nw {#1}
+ #2 \s__color_mark #3 \s__color_stop
+ }
+ }
+\cs_new:Npn \__color_convert_devicen_rgb:nnnw
+ #1#2#3#4 ~ #5 \s__color_mark #6#7 \s__color_stop
+ {
+ \__color_convert_devicen_rgb:nnnnnnn {#4} {#1} {#2} {#3} #6
+ #5 \s__color_mark #7 \s__color_stop
+ }
+\cs_new:Npn \__color_convert_devicen_rgb:nnnnnnn #1#2#3#4#5#6#7
+ {
+ \use:e
+ {
+ \exp_not:N \__color_convert_devicen_rgb_aux:nnnw
+ { \fp_eval:n { #2 * (1 - (#1 * #5)) } }
+ { \fp_eval:n { #3 * (1 - (#1 * #6)) } }
+ { \fp_eval:n { #4 * (1 - (#1 * #7)) } }
+ }
+ }
+\cs_new:Npn \__color_convert_devicen_rgb_aux:nnnw
+ #1#2#3 #4 \s__color_mark #5 \s__color_stop
+ {
+ \tl_if_blank:nTF {#4}
+ {
+ \fp_eval:n { 1 - #1 } ~
+ \fp_eval:n { 1 - #2 } ~
+ \fp_eval:n { 1 - #3 }
+ }
+ {
+ \__color_convert_devicen_rgb:nnnw {#1} {#2} {#3}
+ #4 \s__color_mark #5 \s__color_stop
+ }
+ }
+\prop_const_from_keyval:Nn \c__color_icc_colorspace_signatures_prop
+ {
+ 47524159 = {1} {1} {0} {},
+ 52474220 = {3} {0~0~0} {1~1~1} {},
+ 434D594B = {4} {0~0~0~1} {0~0~0~0} {},
+ 4C616220 = {3} {0~0~0} {100~0~0} {0~100~-128~127~-128~127}
+ }
+\cs_new_protected:Npn \__color_model_iccbased:n #1
+ {
+ \prop_get:NnNTF \l__color_internal_prop { file }
+ \l__color_internal_tl
+ {
+ \exp_args:NV \__color_model_iccbased:nn
+ \l__color_internal_tl {#1}
+ }
+ {
+ \msg_error:nnn { color }
+ { ICCBased-requires-file } {#1}
+ }
+ }
+\cs_new_protected:Npn \__color_model_iccbased:nn #1#2
+ {
+ \prop_get:NeNTF \c__color_icc_colorspace_signatures_prop
+ { \file_hex_dump:nnn { #1 } { 17 } { 20 } } \l__color_internal_tl
+ {
+ \exp_last_unbraced:NV \__color_model_iccbased_aux:nnnnnn
+ \l__color_internal_tl { #2 } { #1 }
+ }
+ {
+ \msg_error:nnn { color }
+ { ICCBased-unsupported-colorspace } {#2}
+ }
+ }
+\cs_new_protected:Npn \__color_model_iccbased_aux:nnnnnn #1#2#3#4#5#6
+ {
+ \__color_model_init:nnn {#5} { iccbased } {#3}
+ \tl_const:cn { c__color_fallback_ #5 _tl } { gray }
+ \cs_new:cpn { __color_convert_ #5 _gray:w } ##1 \s__color_stop { 0 }
+ \cs_new:cpn { __color_convert_gray_ #5 :w } ##1 \s__color_stop { #2 }
+ \use:c { __color_model_devicen_parse_ #1 :nn } {#5} {#1}
+ \exp_args:Ne \__color_backend_iccbased_init:nnn
+ { \file_full_name:n {#6} } {#1} {#4}
+ }
+\cs_new_protected:Npn \color_profile_apply:nn #1#2
+ {
+ \exp_args:Ne \__color_profile_apply:nn
+ { \file_full_name:n {#1} } {#2}
+ }
+\cs_new_protected:Npn \__color_profile_apply:nn #1#2
+ {
+ \cs_if_exist_use:cF { __color_profile_apply_ \tl_to_str:n {#2} :n }
+ {
+ \msg_error:nnn { color } { ICC-Device-unknown } {#2}
+ \use_none:n
+ }
+ {#1}
+ }
+\cs_new_protected:Npn \__color_profile_apply_gray:n #1
+ {
+ \int_gincr:N \g__color_model_int
+ \__color_backend_iccbased_device:nnn {#1} { Gray } { 1 }
+ }
+\cs_new_protected:Npn \__color_profile_apply_rgb:n #1
+ {
+ \int_gincr:N \g__color_model_int
+ \__color_backend_iccbased_device:nnn {#1} { RGB } { 3 }
+ }
+\cs_new_protected:Npn \__color_profile_apply_cmyk:n #1
+ {
+ \int_gincr:N \g__color_model_int
+ \__color_backend_iccbased_device:nnn {#1} { CMYK } { 4 }
+ }
+\cs_new_protected:Npn \color_show:n
+ { \__color_show:Nn \msg_show:nneeee }
+\cs_new_protected:Npn \color_log:n
+ { \__color_show:Nn \msg_log:nneeee }
+\cs_new_protected:Npn \__color_show:Nn #1#2
+ {
+ #1 { color } { show }
+ {#2}
+ {
+ \color_if_exist:nT {#2}
+ {
+ \exp_args:Nv \__color_show:n { l__color_named_ #2 _tl }
+ \prop_map_function:cN
+ { l__color_named_ #2 _prop }
+ \msg_show_item_unbraced:nn
+ }
+ }
+ { }
+ { }
+ }
+\cs_new:Npn \__color_show:n #1
+ {
+ \msg_show_item_unbraced:nn { model } {#1}
+ }
+\msg_new:nnnn { color } { CIELAB-requires-illuminant }
+ { CIELAB~color~space~'#1'~require~an~illuminant. }
+ {
+ LaTeX~has~been~asked~to~create~a~separation~color~space~using~
+ CIELAB~specifications,~but~no~\\ \\
+ \iow_indent:n { illuminant~=~<basis> }
+ \\ \\
+ key~was~given~with~the~correct~information.~LaTeX~will~use~illuminant~
+ 'd50'~for~recovery.
+ }
+\msg_new:nnnn { color } { conversion-not-available }
+ { No~model~conversion~available~from~'#1'~to~'#2'. }
+ {
+ LaTeX~has~been~asked~to~convert~a~color~from~model~'#1'~
+ to~model'#2',~but~there~is~no~method~available~to~do~that.
+ }
+\msg_new:nnnn { color } { DeviceN-inconsistent-alternative }
+ { DeviceN~color~spaces~require~a~single~alternative~space. }
+ {
+ LaTeX~has~been~asked~to~create~a~DeviceN~color~space~'#1',~
+ but~the~constituent~colors~do~not~have~a~common~alternative~
+ color.
+ }
+\msg_new:nnnn { color } { DeviceN-no-alternative }
+ { DeviceN~color~spaces~require~an~alternative~space. }
+ {
+ LaTeX~has~been~asked~to~create~a~DeviceN~color~space~'#1',~
+ but~the~constituent~colors~do~not~all~have~a~device-based~alternative.
+ }
+\msg_new:nnnn { color } { DeviceN-requires-names }
+ { DeviceN~color~space~'#1'~require~a~list~of~names. }
+ {
+ LaTeX~has~been~asked~to~create~a~DeviceN~color~space,~
+ but~no~\\ \\
+ \iow_indent:n { names~=~<names> }
+ \\ \\
+ key~was~given~with~the~correct~information.
+ }
+\msg_new:nnnn { color } { ICC-Device-unknown }
+ { Unknown~device~color~space~'#1'. }
+ {
+ LaTeX~has~been~asked~to~apply~an~ICC~profile~but~the~device~color~space~
+ '#1'~is~unknown.
+ }
+\msg_new:nnnn { color } { ICCBased-unsupported-colorspace }
+ { ICCBased~color~space~'#1'~uses~an~unsupported~data~color~space. }
+ {
+ LaTeX~has~been~asked~to~create~a~ICCBased~colorspace,~but~the~
+ used~data~colorspace~is~not~supported.~ICC~profiles~used~for~
+ defining~a~ICCBased~colorspace~should~use~a~Lab,~RGB,~or~
+ CMYK~data~colorspace.~LaTeX~will~ignore~this~request.
+ }
+\msg_new:nnnn { color } { ICCBased-requires-file }
+ { ICCBased~color~space~'#1'~require~an~file. }
+ {
+ LaTeX~has~been~asked~to~create~an~ICCBased~color~space,~but~no~\\ \\
+ \iow_indent:n { file~=~<name> }
+ \\ \\
+ key~was~given~with~the~correct~information.~LaTeX~will~ignore~this~
+ request.
+ }
+\msg_new:nnnn { color } { model-already-defined }
+ { Color~model~'#1'~already~defined. }
+ {
+ LaTeX~was~asked~to~define~a~new~color~model~called~'#1',~but~
+ this~color~model~already~exists.
+ }
+\msg_new:nnnn { color } { out-of-range }
+ { Input~value~#1~out~of~range~[#2,~#3]. }
+ {
+ LaTeX~was~expecting~a~value~in~the~range~[#2,~#3]~as~part~of~a~color,~
+ but~you~gave~#1.~LaTeX~will~assume~you~meant~the~limit~of~the~range~
+ and~continue.
+ }
+\msg_new:nnnn { color } { separation-alternative-model }
+ { Separation~color~space~'#1'~require~an~alternative~model. }
+ {
+ LaTeX~has~been~asked~to~create~a~separation~color~space,~
+ but~no~\\ \\
+ \iow_indent:n { alternative-model~=~<model> }
+ \\ \\
+ key~was~given~with~the~correct~information.
+ }
+\msg_new:nnnn { color } { separation-alternative-values }
+ { Separation~color~space~'#1'~require~values~for~the~alternative~space. }
+ {
+ LaTeX~has~been~asked~to~create~a~separation~color~space,~
+ but~no~\\ \\
+ \iow_indent:n { alternative-values~=~<model> }
+ \\ \\
+ key~was~given~with~the~correct~information.
+ }
+\msg_new:nnnn { color } { separation-requires-name }
+ { Separation~color~space~'#1'~require~a~formal~name. }
+ {
+ LaTeX~has~been~asked~to~create~a~separation~color~space,~
+ but~no~\\ \\
+ \iow_indent:n { name~=~<formal~name> }
+ \\ \\
+ key~was~given~with~the~correct~information.
+ }
+\msg_new:nnn { color } { unhandled-model }
+ {
+ Unhandled~color~model~in~LaTeX2e~value~"#1":
+ \\ \\
+ falling~back~on~grayscale.
+ }
+\msg_new:nnnn { color } { unknown-color }
+ { Unknown~color~'#1'. }
+ {
+ LaTeX~has~been~asked~to~use~a~color~named~'#1',~
+ but~this~has~never~been~defined.
+ }
+\msg_new:nnnn { color } { unknown-alternative-model }
+ { Separation~color~space~'#1'~require~an~valid~alternative~space. }
+ {
+ LaTeX~has~been~asked~to~create~a~separation~color~space,~
+ but~the~model~given~as\\ \\
+ \iow_indent:n { alternative-model~=~<model> }
+ \\ \\
+ is~unknown.
+ }
+\msg_new:nnnn { color } { unknown-export-format }
+ { Unknown~export~format~'#1'. }
+ {
+ LaTeX~has~been~asked~to~export~a~color~in~format~'#1',~
+ but~this~has~never~been~defined.
+ }
+\msg_new:nnnn { color } { unknown-CIELAB-illuminant }
+ { Unknown~illuminant~model~'#1'. }
+ {
+ LaTeX~has~been~asked~to~use~create~a~color~space~using~CIELAB~
+ illuminant~'#1',~but~this~does~not~exist.
+ }
+\msg_new:nnnn { color } { unknown-model }
+ { Unknown~color~model~'#1'. }
+ {
+ LaTeX~has~been~asked~to~use~a~color~model~called~'#1',~
+ but~this~model~is~not~set~up.
+ }
+\msg_new:nnnn { color } { unknown-model-type }
+ { Unknown~color~model~type~'#1'. }
+ {
+ LaTeX~has~been~asked~to~create~a~new~color~model~called~'#1',~
+ but~this~type~of~model~was~never~set~up.
+ }
+\prop_gput:Nnn \g_msg_module_name_prop { color } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { color } { }
+\msg_new:nnn { color } { show }
+ {
+ The~color~#1~
+ \tl_if_empty:nTF {#2}
+ { is~undefined. }
+ { has~the~properties: #2 }
+ }
+%% File: l3pdf.dtx
+\scan_new:N \s__pdf_stop
+\bool_new:N \g__pdf_init_bool
+\bool_lazy_and:nnT
+ { \str_if_eq_p:Vn \fmtname { LaTeX2e } }
+ { \tl_if_exist_p:N \@expl@finalise@setup@@ }
+ {
+ \tl_gput_right:Nn \@expl@finalise@setup@@
+ {
+ \tl_gput_right:Nn \@kernel@after@begindocument
+ { \bool_gset_true:N \g__pdf_init_bool }
+ }
+ }
+\cs_new_protected:Npn \pdf_uncompress:
+ {
+ \bool_if:NF \g__pdf_init_bool
+ {
+ \__pdf_backend_compresslevel:n { 0 }
+ \__pdf_backend_compress_objects:n { \c_false_bool }
+ }
+ }
+\int_new:N \g__pdf_backend_object_int
+\cs_new_protected:Npn \pdf_object_new:n #1
+ {
+ \__pdf_backend_object_new:
+ \__pdf_object_record:nN {#1} \g__pdf_backend_object_int
+ }
+\cs_new_protected:Npn \pdf_object_write:nnn #1#2#3
+ {
+ \exp_args:Ne \__pdf_backend_object_write:nnn
+ { \__pdf_object_retrieve:n {#1} } {#2} {#3}
+ \bool_gset_true:N \g__pdf_init_bool
+ }
+\cs_generate_variant:Nn \pdf_object_write:nnn { nne , nnx }
+\cs_new:Npn \pdf_object_ref:n #1
+ {
+ \exp_args:Ne \__pdf_backend_object_ref:n
+ { \__pdf_object_retrieve:n {#1} }
+ }
+\cs_new:Npn \__kernel_pdf_object_id:n #1
+ {
+ \exp_args:Ne \__pdf_backend_object_id:n
+ { \__pdf_object_retrieve:n {#1} }
+ }
+\sys_if_engine_luatex:F
+ {
+ \cs_new_protected:Npn \__pdf_object_record:nN #1#2
+ {
+ \int_const:cn
+ { c__pdf_object_ #1 _int } {#2}
+ }
+ \cs_new:Npn \__pdf_object_retrieve:n #1
+ {
+ \int_if_exist:cTF { c__pdf_object_ #1 _int }
+ {
+ \int_use:c
+ { c__pdf_object_ #1 _int }
+ }
+ { 0 }
+ }
+ }
+\prg_new_conditional:Npnn \pdf_object_if_exist:n #1 { p , T , F , TF }
+ {
+ \int_compare:nNnTF { \__pdf_object_retrieve:n {#1} } = 0
+ \prg_return_false:
+ \prg_return_true:
+ }
+\cs_new_protected:Npn \pdf_object_new_indexed:nn #1#2
+ {
+ \__pdf_backend_object_new:
+ \__pdf_object_record:neN {#1}
+ { \int_eval:n {#2} } \g__pdf_backend_object_int
+ }
+\cs_new_protected:Npn \pdf_object_write_indexed:nnnn #1#2#3#4
+ {
+ \exp_args:Ne \__pdf_backend_object_write:nnn
+ { \__pdf_object_retrieve:ne {#1} { \int_eval:n {#2} } } {#3} {#4}
+ \bool_gset_true:N \g__pdf_init_bool
+ }
+\cs_generate_variant:Nn \pdf_object_write_indexed:nnnn { nnne }
+\cs_new:Npn \pdf_object_ref_indexed:nn #1#2
+ {
+ \exp_args:Ne \__pdf_backend_object_ref:n
+ { \__pdf_object_retrieve:ne {#1} { \int_eval:n {#2} } }
+ }
+\cs_new:Npn \__kernel_pdf_object_id_indexed:nn #1#2
+ {
+ \exp_args:Ne \__pdf_backend_object_id:n
+ { \__pdf_object_retrieve:ne {#1} { \int_eval:n {#2} } }
+ }
+\sys_if_engine_luatex:F
+ {
+ \cs_new_protected:Npn \__pdf_object_record:nnN #1#2#3
+ {
+ \use:e
+ {
+ \__pdf_object_record:NnN
+ \__pdf_object_index_split:nn {#1} {#2}
+ \exp_not:N #3
+ }
+ }
+ \cs_new_protected:Npn \__pdf_object_record:NnN #1#2#3
+ {
+ \intarray_if_exist:NF #1
+ { \intarray_new:Nn #1 \c__pdf_object_block_size_int }
+ \intarray_gset:Nnn #1 {#2} #3
+ }
+ \cs_new:Npn \__pdf_object_retrieve:nn #1#2
+ {
+ \use:e
+ {
+ \exp_not:N \__pdf_object_retrieve:Nn
+ \__pdf_object_index_split:nn {#1} {#2}
+ }
+ }
+ \cs_new:Npn \__pdf_object_retrieve:Nn #1#2
+ { \intarray_item:Nn #1 {#2} }
+ \cs_new:Npn \__pdf_object_index_split:nn #1#2
+ {
+ \exp_not:c
+ {
+ g__pdf_object_ #1 _
+ \int_eval:n
+ {
+ \int_div_truncate:nn { #2 - 1 }
+ \c__pdf_object_block_size_int + 1
+ }
+ _intarray
+ }
+ {
+ \int_eval:n
+ { \int_mod:nn { #2 - 1 } \c__pdf_object_block_size_int + 1 }
+ }
+ }
+ \int_const:Nn \c__pdf_object_block_size_int { 10000 }
+ }
+\cs_generate_variant:Nn \__pdf_object_record:nnN { ne }
+\cs_generate_variant:Nn \__pdf_object_retrieve:nn { ne }
+\cs_new_protected:Npn \pdf_object_unnamed_write:nn #1#2
+ {
+ \exp_args:Ne \__pdf_backend_object_now:nn {#1} {#2}
+ \bool_gset_true:N \g__pdf_init_bool
+ }
+\cs_generate_variant:Nn \pdf_object_unnamed_write:nn { ne , nx }
+\cs_new:Npn \pdf_object_ref_last: { \__pdf_backend_object_last: }
+\cs_new:Npn \pdf_pageobject_ref:n #1
+ { \__pdf_backend_pageobject_ref:n {#1} }
+\prg_new_conditional:Npnn \pdf_version_compare:Nn #1#2 { p , T , F , TF }
+ { \use:c { __pdf_version_compare_ #1 :w } #2 . . \s__pdf_stop }
+\cs_new:cpn { __pdf_version_compare_=:w } #1 . #2 . #3 \s__pdf_stop
+ {
+ \bool_lazy_and:nnTF
+ { \int_compare_p:nNn \__pdf_backend_version_major: = {#1} }
+ { \int_compare_p:nNn \__pdf_backend_version_minor: = {#2} }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_new:cpn { __pdf_version_compare_<:w } #1 . #2 . #3 \s__pdf_stop
+ {
+ \bool_lazy_or:nnTF
+ { \int_compare_p:nNn \__pdf_backend_version_major: < {#1} }
+ {
+ \bool_lazy_and_p:nn
+ { \int_compare_p:nNn \__pdf_backend_version_major: = {#1} }
+ { \int_compare_p:nNn \__pdf_backend_version_minor: < {#2} }
+ }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_new:cpn { __pdf_version_compare_>:w } #1 . #2 . #3 \s__pdf_stop
+ {
+ \bool_lazy_or:nnTF
+ { \int_compare_p:nNn \__pdf_backend_version_major: > {#1} }
+ {
+ \bool_lazy_and_p:nn
+ { \int_compare_p:nNn \__pdf_backend_version_major: = {#1} }
+ { \int_compare_p:nNn \__pdf_backend_version_minor: > {#2} }
+ }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_new_protected:Npn \pdf_version_gset:n #1
+ { \__pdf_version_gset:w #1 . . \s__pdf_stop }
+\cs_new_protected:Npn \pdf_version_min_gset:n #1
+ {
+ \pdf_version_compare:NnT < {#1}
+ { \__pdf_version_gset:w #1 . . \s__pdf_stop }
+ }
+\cs_new_protected:Npn \__pdf_version_gset:w #1 . #2 . #3\s__pdf_stop
+ {
+ \bool_if:NF \g__pdf_init_bool
+ {
+ \__pdf_backend_version_major_gset:n {#1}
+ \__pdf_backend_version_minor_gset:n {#2}
+ }
+ }
+\cs_new:Npn \pdf_version:
+ { \__pdf_backend_version_major: . \__pdf_backend_version_minor: }
+\cs_new:Npn \pdf_version_major: { \__pdf_backend_version_major: }
+\cs_new:Npn \pdf_version_minor: { \__pdf_backend_version_minor: }
+\cs_new_protected:Npn \pdf_pagesize_gset:nn #1#2
+ { \__pdf_backend_pagesize_gset:nn {#1} {#2} }
+\cs_new_protected:Npn \pdf_destination:nn #1#2
+ { \__pdf_backend_destination:nn {#1} {#2} }
+\cs_new_protected:Npn \pdf_destination:nnnn #1#2#3#4
+ {
+ \hbox_to_zero:n
+ { \__pdf_backend_destination:nnnn {#1} {#2} {#3} {#4} }
+ }
+\cs_if_exist:NT \@kernel@before@begindocument
+ {
+ \tl_gput_right:Nn \@kernel@before@begindocument
+ {
+ \bool_lazy_all:nT
+ {
+ { \cs_if_exist_p:N \stockheight }
+ { \cs_if_exist_p:N \stockwidth }
+ { \cs_if_exist_p:N \IfDocumentMetadataTF }
+ { \IfDocumentMetadataTF { \c_true_bool } { \c_false_bool } }
+ { \int_compare_p:nNn \tex_mag:D = { 1000 } }
+ }
+ {
+ \bool_lazy_and:nnTF
+ { \dim_compare_p:nNn \stockheight > { 0pt } }
+ { \dim_compare_p:nNn \stockwidth > { 0pt } }
+ {
+ \__pdf_backend_pagesize_gset:nn
+ \stockwidth \stockheight
+ }
+ {
+ \bool_lazy_or:nnF
+ { \dim_compare_p:nNn \stockheight < { 0pt } }
+ { \dim_compare_p:nNn \stockwidth < { 0pt } }
+ {
+ \bool_lazy_and:nnT
+ { \dim_compare_p:nNn \paperheight > { 0pt } }
+ { \dim_compare_p:nNn \paperwidth > { 0pt } }
+ {
+ \__pdf_backend_pagesize_gset:nn
+ \paperwidth \paperheight
+ }
+ }
+ }
+ }
+ }
+ }
+%% File: l3coffins.dtx
+\box_new:N \l__coffin_internal_box
+\dim_new:N \l__coffin_internal_dim
+\tl_new:N \l__coffin_internal_tl
+\prop_const_from_keyval:Nn \c__coffin_corners_prop
+ {
+ tl = { 0pt } { 0pt } ,
+ tr = { 0pt } { 0pt } ,
+ bl = { 0pt } { 0pt } ,
+ br = { 0pt } { 0pt } ,
+ }
+\prop_const_from_keyval:Nn \c__coffin_poles_prop
+ {
+ l = { 0pt } { 0pt } { 0pt } { 1000pt } ,
+ hc = { 0pt } { 0pt } { 0pt } { 1000pt } ,
+ r = { 0pt } { 0pt } { 0pt } { 1000pt } ,
+ b = { 0pt } { 0pt } { 1000pt } { 0pt } ,
+ vc = { 0pt } { 0pt } { 1000pt } { 0pt } ,
+ t = { 0pt } { 0pt } { 1000pt } { 0pt } ,
+ B = { 0pt } { 0pt } { 1000pt } { 0pt } ,
+ H = { 0pt } { 0pt } { 1000pt } { 0pt } ,
+ T = { 0pt } { 0pt } { 1000pt } { 0pt } ,
+ }
+\fp_new:N \l__coffin_slope_A_fp
+\fp_new:N \l__coffin_slope_B_fp
+\bool_new:N \l__coffin_error_bool
+\dim_new:N \l__coffin_offset_x_dim
+\dim_new:N \l__coffin_offset_y_dim
+\tl_new:N \l__coffin_pole_a_tl
+\tl_new:N \l__coffin_pole_b_tl
+\dim_new:N \l__coffin_x_dim
+\dim_new:N \l__coffin_y_dim
+\dim_new:N \l__coffin_x_prime_dim
+\dim_new:N \l__coffin_y_prime_dim
+\cs_new_eq:NN \__coffin_to_value:N \tex_number:D
+\prg_new_conditional:Npnn \coffin_if_exist:N #1 { p , T , F , TF }
+ {
+ \cs_if_exist:NTF #1
+ {
+ \cs_if_exist:cTF { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ { \prg_return_false: }
+ }
+\prg_generate_conditional_variant:Nnn \coffin_if_exist:N
+ { c } { p , T , F , TF }
+\cs_new_protected:Npn \__coffin_if_exist:NT #1#2
+ {
+ \coffin_if_exist:NTF #1
+ { #2 }
+ {
+ \msg_error:nne { coffin } { unknown }
+ { \token_to_str:N #1 }
+ }
+ }
+\cs_new_protected:Npn \coffin_clear:N #1
+ {
+ \__coffin_if_exist:NT #1
+ {
+ \box_clear:N #1
+ \__coffin_reset_structure:N #1
+ }
+ }
+\cs_generate_variant:Nn \coffin_clear:N { c }
+\cs_new_protected:Npn \coffin_gclear:N #1
+ {
+ \__coffin_if_exist:NT #1
+ {
+ \box_gclear:N #1
+ \__coffin_greset_structure:N #1
+ }
+ }
+\cs_generate_variant:Nn \coffin_gclear:N { c }
+\cs_new_protected:Npn \coffin_new:N #1
+ {
+ \box_new:N #1
+ \debug_suspend:
+ \prop_gclear_new:c { coffin ~ \__coffin_to_value:N #1 ~ corners }
+ \prop_gclear_new:c { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ \prop_gset_eq:cN { coffin ~ \__coffin_to_value:N #1 ~ corners }
+ \c__coffin_corners_prop
+ \prop_gset_eq:cN { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ \c__coffin_poles_prop
+ \debug_resume:
+ }
+\cs_generate_variant:Nn \coffin_new:N { c }
+\cs_new_protected:Npn \hcoffin_set:Nn #1#2
+ {
+ \__coffin_if_exist:NT #1
+ {
+ \hbox_set:Nn #1
+ {
+ \color_ensure_current:
+ #2
+ }
+ \coffin_reset_poles:N #1
+ }
+ }
+\cs_generate_variant:Nn \hcoffin_set:Nn { c }
+\cs_new_protected:Npn \hcoffin_gset:Nn #1#2
+ {
+ \__coffin_if_exist:NT #1
+ {
+ \hbox_gset:Nn #1
+ {
+ \color_ensure_current:
+ #2
+ }
+ \coffin_greset_poles:N #1
+ }
+ }
+\cs_generate_variant:Nn \hcoffin_gset:Nn { c }
+\cs_new_protected:Npn \vcoffin_set:Nnn #1#2#3
+ {
+ \__coffin_set_vertical:NnnNNN #1 {#2} {#3}
+ \vbox_set:Nn \coffin_reset_poles:N \__coffin_set_pole:Nnn
+ }
+\cs_generate_variant:Nn \vcoffin_set:Nnn { c }
+\cs_new_protected:Npn \vcoffin_gset:Nnn #1#2#3
+ {
+ \__coffin_set_vertical:NnnNNN #1 {#2} {#3}
+ \vbox_gset:Nn \coffin_greset_poles:N \__coffin_gset_pole:Nnn
+ }
+\cs_generate_variant:Nn \vcoffin_gset:Nnn { c }
+\cs_new_protected:Npn \__coffin_set_vertical:NnnNNN #1#2#3#4#5#6
+ {
+ \__coffin_if_exist:NT #1
+ {
+ #4 #1
+ {
+ \dim_set:Nn \tex_hsize:D {#2}
+ \__coffin_set_vertical_aux:
+ #3
+ }
+ #5 #1
+ \vbox_set_top:Nn \l__coffin_internal_box { \vbox_unpack:N #1 }
+ #6 #1 { T }
+ {
+ { 0pt }
+ {
+ \dim_eval:n
+ { \box_ht:N #1 - \box_ht:N \l__coffin_internal_box }
+ }
+ { 1000pt }
+ { 0pt }
+ }
+ \box_clear:N \l__coffin_internal_box
+ }
+ }
+\cs_new_protected:Npe \__coffin_set_vertical_aux:
+ {
+ \bool_lazy_and:nnT
+ { \cs_if_exist_p:N \fmtname }
+ { \str_if_eq_p:Vn \fmtname { LaTeX2e } }
+ {
+ \dim_set_eq:NN \exp_not:N \linewidth \tex_hsize:D
+ \dim_set_eq:NN \exp_not:N \columnwidth \tex_hsize:D
+ }
+ }
+\cs_new_protected:Npn \hcoffin_set:Nw #1
+ {
+ \__coffin_if_exist:NT #1
+ {
+ \hbox_set:Nw #1 \color_ensure_current:
+ \cs_set_protected:Npn \hcoffin_set_end:
+ {
+ \hbox_set_end:
+ \coffin_reset_poles:N #1
+ }
+ }
+ }
+\cs_generate_variant:Nn \hcoffin_set:Nw { c }
+\cs_new_protected:Npn \hcoffin_gset:Nw #1
+ {
+ \__coffin_if_exist:NT #1
+ {
+ \hbox_gset:Nw #1 \color_ensure_current:
+ \cs_set_protected:Npn \hcoffin_gset_end:
+ {
+ \hbox_gset_end:
+ \coffin_greset_poles:N #1
+ }
+ }
+ }
+\cs_generate_variant:Nn \hcoffin_gset:Nw { c }
+\cs_new_protected:Npn \hcoffin_set_end: { }
+\cs_new_protected:Npn \hcoffin_gset_end: { }
+\cs_new_protected:Npn \vcoffin_set:Nnw #1#2
+ {
+ \__coffin_set_vertical:NnNNNNNw #1 {#2} \vbox_set:Nw
+ \vcoffin_set_end:
+ \vbox_set_end: \coffin_reset_poles:N \__coffin_set_pole:Nnn
+ }
+\cs_generate_variant:Nn \vcoffin_set:Nnw { c }
+\cs_new_protected:Npn \vcoffin_gset:Nnw #1#2
+ {
+ \__coffin_set_vertical:NnNNNNNw #1 {#2} \vbox_gset:Nw
+ \vcoffin_gset_end:
+ \vbox_gset_end: \coffin_greset_poles:N \__coffin_gset_pole:Nnn
+ }
+\cs_generate_variant:Nn \vcoffin_gset:Nnw { c }
+\cs_new_protected:Npn \__coffin_set_vertical:NnNNNNNw #1#2#3#4#5#6#7
+ {
+ \__coffin_if_exist:NT #1
+ {
+ #3 #1
+ \dim_set:Nn \tex_hsize:D {#2}
+ \__coffin_set_vertical_aux:
+ \cs_set_protected:Npn #4
+ {
+ #5
+ #6 #1
+ \vbox_set_top:Nn \l__coffin_internal_box { \vbox_unpack:N #1 }
+ #7 #1 { T }
+ {
+ { 0pt }
+ {
+ \dim_eval:n
+ { \box_ht:N #1 - \box_ht:N \l__coffin_internal_box }
+ }
+ { 1000pt }
+ { 0pt }
+ }
+ \box_clear:N \l__coffin_internal_box
+ }
+ }
+ }
+\cs_new_protected:Npn \vcoffin_set_end: { }
+\cs_new_protected:Npn \vcoffin_gset_end: { }
+\cs_new_protected:Npn \coffin_set_eq:NN #1#2
+ {
+ \__coffin_if_exist:NT #1
+ {
+ \box_set_eq:NN #1 #2
+ \prop_set_eq:cc { coffin ~ \__coffin_to_value:N #1 ~ corners }
+ { coffin ~ \__coffin_to_value:N #2 ~ corners }
+ \prop_set_eq:cc { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ { coffin ~ \__coffin_to_value:N #2 ~ poles }
+ }
+ }
+\cs_generate_variant:Nn \coffin_set_eq:NN { c , Nc , cc }
+\cs_new_protected:Npn \coffin_gset_eq:NN #1#2
+ {
+ \__coffin_if_exist:NT #1
+ {
+ \box_gset_eq:NN #1 #2
+ \prop_gset_eq:cc { coffin ~ \__coffin_to_value:N #1 ~ corners }
+ { coffin ~ \__coffin_to_value:N #2 ~ corners }
+ \prop_gset_eq:cc { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ { coffin ~ \__coffin_to_value:N #2 ~ poles }
+ }
+ }
+\cs_generate_variant:Nn \coffin_gset_eq:NN { c , Nc , cc }
+\coffin_new:N \c_empty_coffin
+\coffin_new:N \l__coffin_aligned_coffin
+\coffin_new:N \l__coffin_aligned_internal_coffin
+\coffin_new:N \l_tmpa_coffin
+\coffin_new:N \l_tmpb_coffin
+\coffin_new:N \g_tmpa_coffin
+\coffin_new:N \g_tmpb_coffin
+\cs_new_eq:NN \coffin_dp:N \box_dp:N
+\cs_new_eq:NN \coffin_dp:c \box_dp:c
+\cs_new_eq:NN \coffin_ht:N \box_ht:N
+\cs_new_eq:NN \coffin_ht:c \box_ht:c
+\cs_new_eq:NN \coffin_wd:N \box_wd:N
+\cs_new_eq:NN \coffin_wd:c \box_wd:c
+\cs_new_protected:Npn \__coffin_get_pole:NnN #1#2#3
+ {
+ \prop_get:cnNF
+ { coffin ~ \__coffin_to_value:N #1 ~ poles } {#2} #3
+ {
+ \msg_error:nnee { coffin } { unknown-pole }
+ { \exp_not:n {#2} } { \token_to_str:N #1 }
+ \tl_set:Nn #3 { { 0pt } { 0pt } { 0pt } { 0pt } }
+ }
+ }
+\cs_new_protected:Npn \__coffin_reset_structure:N #1
+ {
+ \prop_set_eq:cN { coffin ~ \__coffin_to_value:N #1 ~ corners }
+ \c__coffin_corners_prop
+ \prop_set_eq:cN { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ \c__coffin_poles_prop
+ }
+\cs_new_protected:Npn \__coffin_greset_structure:N #1
+ {
+ \prop_gset_eq:cN { coffin ~ \__coffin_to_value:N #1 ~ corners }
+ \c__coffin_corners_prop
+ \prop_gset_eq:cN { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ \c__coffin_poles_prop
+ }
+\cs_new_protected:Npn \coffin_set_horizontal_pole:Nnn #1#2#3
+ { \__coffin_set_horizontal_pole:NnnN #1 {#2} {#3} \prop_put:cne }
+\cs_generate_variant:Nn \coffin_set_horizontal_pole:Nnn { c }
+\cs_new_protected:Npn \coffin_gset_horizontal_pole:Nnn #1#2#3
+ { \__coffin_set_horizontal_pole:NnnN #1 {#2} {#3} \prop_gput:cne }
+\cs_generate_variant:Nn \coffin_gset_horizontal_pole:Nnn { c }
+\cs_new_protected:Npn \__coffin_set_horizontal_pole:NnnN #1#2#3#4
+ {
+ \__coffin_if_exist:NT #1
+ {
+ #4 { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ {#2}
+ {
+ { 0pt } { \dim_eval:n {#3} }
+ { 1000pt } { 0pt }
+ }
+ }
+ }
+\cs_new_protected:Npn \coffin_set_vertical_pole:Nnn #1#2#3
+ { \__coffin_set_vertical_pole:NnnN #1 {#2} {#3} \prop_put:cne }
+\cs_generate_variant:Nn \coffin_set_vertical_pole:Nnn { c }
+\cs_new_protected:Npn \coffin_gset_vertical_pole:Nnn #1#2#3
+ { \__coffin_set_vertical_pole:NnnN #1 {#2} {#3} \prop_gput:cne }
+ \cs_generate_variant:Nn \coffin_gset_vertical_pole:Nnn { c }
+\cs_new_protected:Npn \__coffin_set_vertical_pole:NnnN #1#2#3#4
+ {
+ \__coffin_if_exist:NT #1
+ {
+ #4 { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ {#2}
+ {
+ { \dim_eval:n {#3} } { 0pt }
+ { 0pt } { 1000pt }
+ }
+ }
+ }
+\cs_new_protected:Npn \__coffin_set_pole:Nnn #1#2#3
+ {
+ \prop_put:cne { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ {#2} {#3}
+ }
+\cs_new_protected:Npn \__coffin_gset_pole:Nnn #1#2#3
+ {
+ \prop_gput:cne { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ {#2} {#3}
+ }
+\cs_new_protected:Npn \coffin_reset_poles:N #1
+ {
+ \__coffin_reset_structure:N #1
+ \__coffin_update_corners:N #1
+ \__coffin_update_poles:N #1
+ }
+\cs_new_protected:Npn \coffin_greset_poles:N #1
+ {
+ \__coffin_greset_structure:N #1
+ \__coffin_gupdate_corners:N #1
+ \__coffin_gupdate_poles:N #1
+ }
+\cs_new_protected:Npn \__coffin_update_corners:N #1
+ { \__coffin_update_corners:NN #1 \prop_put:Nne }
+\cs_new_protected:Npn \__coffin_gupdate_corners:N #1
+ { \__coffin_update_corners:NN #1 \prop_gput:Nne }
+\cs_new_protected:Npn \__coffin_update_corners:NN #1#2
+ {
+ \exp_args:Nc \__coffin_update_corners:NNN
+ { coffin ~ \__coffin_to_value:N #1 ~ corners }
+ #1 #2
+ }
+\cs_new_protected:Npn \__coffin_update_corners:NNN #1#2#3
+ {
+ #3 #1
+ { tl }
+ { { 0pt } { \dim_eval:n { \box_ht:N #2 } } }
+ #3 #1
+ { tr }
+ {
+ { \dim_eval:n { \box_wd:N #2 } }
+ { \dim_eval:n { \box_ht:N #2 } }
+ }
+ #3 #1
+ { bl }
+ { { 0pt } { \dim_eval:n { -\box_dp:N #2 } } }
+ #3 #1
+ { br }
+ {
+ { \dim_eval:n { \box_wd:N #2 } }
+ { \dim_eval:n { -\box_dp:N #2 } }
+ }
+ }
+\cs_new_protected:Npn \__coffin_update_poles:N #1
+ { \__coffin_update_poles:NN #1 \prop_put:Nne }
+\cs_new_protected:Npn \__coffin_gupdate_poles:N #1
+ { \__coffin_update_poles:NN #1 \prop_gput:Nne }
+\cs_new_protected:Npn \__coffin_update_poles:NN #1#2
+ {
+ \exp_args:Nc \__coffin_update_poles:NNN
+ { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ #1 #2
+ }
+\cs_new_protected:Npn \__coffin_update_poles:NNN #1#2#3
+ {
+ #3 #1 { hc }
+ {
+ { \dim_eval:n { 0.5 \box_wd:N #2 } }
+ { 0pt } { 0pt } { 1000pt }
+ }
+ #3 #1 { r }
+ {
+ { \dim_eval:n { \box_wd:N #2 } }
+ { 0pt } { 0pt } { 1000pt }
+ }
+ #3 #1 { vc }
+ {
+ { 0pt }
+ { \dim_eval:n { ( \box_ht:N #2 - \box_dp:N #2 ) / 2 } }
+ { 1000pt }
+ { 0pt }
+ }
+ #3 #1 { t }
+ {
+ { 0pt }
+ { \dim_eval:n { \box_ht:N #2 } }
+ { 1000pt }
+ { 0pt }
+ }
+ #3 #1 { b }
+ {
+ { 0pt }
+ { \dim_eval:n { -\box_dp:N #2 } }
+ { 1000pt }
+ { 0pt }
+ }
+ }
+\cs_new_protected:Npn \__coffin_calculate_intersection:Nnn #1#2#3
+ {
+ \__coffin_get_pole:NnN #1 {#2} \l__coffin_pole_a_tl
+ \__coffin_get_pole:NnN #1 {#3} \l__coffin_pole_b_tl
+ \bool_set_false:N \l__coffin_error_bool
+ \exp_last_two_unbraced:Noo
+ \__coffin_calculate_intersection:nnnnnnnn
+ \l__coffin_pole_a_tl \l__coffin_pole_b_tl
+ \bool_if:NT \l__coffin_error_bool
+ {
+ \msg_error:nn { coffin } { no-pole-intersection }
+ \dim_zero:N \l__coffin_x_dim
+ \dim_zero:N \l__coffin_y_dim
+ }
+ }
+\cs_new_protected:Npn \__coffin_calculate_intersection:nnnnnnnn
+ #1#2#3#4#5#6#7#8
+ {
+ \dim_compare:nNnTF {#3} = \c_zero_dim
+ {
+ \dim_set:Nn \l__coffin_x_dim {#1}
+ \dim_compare:nNnTF {#7} = \c_zero_dim
+ { \bool_set_true:N \l__coffin_error_bool }
+ {
+ \dim_set:Nn \l__coffin_y_dim
+ {
+ \dim_compare:nNnTF {#8} = \c_zero_dim
+ {#6}
+ {
+ \fp_to_dim:n
+ {
+ ( \dim_to_fp:n {#8} / \dim_to_fp:n {#7} )
+ * ( \dim_to_fp:n {#1} - \dim_to_fp:n {#5} )
+ + \dim_to_fp:n {#6}
+ }
+ }
+ }
+ }
+ }
+ {
+ \dim_compare:nNnTF {#4} = \c_zero_dim
+ {
+ \dim_set:Nn \l__coffin_y_dim {#2}
+ \dim_compare:nNnTF {#8} = { \c_zero_dim }
+ { \bool_set_true:N \l__coffin_error_bool }
+ {
+ \dim_set:Nn \l__coffin_x_dim
+ {
+ \dim_compare:nNnTF {#7} = \c_zero_dim
+ {#5}
+ {
+ \fp_to_dim:n
+ {
+ ( \dim_to_fp:n {#7} / \dim_to_fp:n {#8} )
+ * ( \dim_to_fp:n {#4} - \dim_to_fp:n {#6} )
+ + \dim_to_fp:n {#5}
+ }
+ }
+ }
+ }
+ }
+ {
+ \use:e
+ {
+ \__coffin_calculate_intersection:nnnnnn
+ { \dim_to_fp:n {#4} / \dim_to_fp:n {#3} }
+ { \dim_to_fp:n {#8} / \dim_to_fp:n {#7} }
+ }
+ {#1} {#2} {#5} {#6}
+ }
+ }
+ }
+\cs_new_protected:Npn \__coffin_calculate_intersection:nnnnnn #1#2#3#4#5#6
+ {
+ \fp_compare:nNnTF {#1} = {#2}
+ { \bool_set_true:N \l__coffin_error_bool }
+ {
+ \dim_set:Nn \l__coffin_x_dim
+ {
+ \fp_to_dim:n
+ {
+ (
+ #1 * \dim_to_fp:n {#3}
+ - #2 * \dim_to_fp:n {#5}
+ - \dim_to_fp:n {#4}
+ + \dim_to_fp:n {#6}
+ )
+ /
+ ( #1 - #2 )
+ }
+ }
+ \dim_set:Nn \l__coffin_y_dim
+ {
+ \fp_to_dim:n
+ {
+ #1 * ( \l__coffin_x_dim - \dim_to_fp:n {#3} )
+ + \dim_to_fp:n {#4}
+ }
+ }
+ }
+ }
+\fp_new:N \l__coffin_sin_fp
+\fp_new:N \l__coffin_cos_fp
+\prop_new:N \l__coffin_bounding_prop
+\prop_new:N \l__coffin_corners_prop
+\prop_new:N \l__coffin_poles_prop
+\dim_new:N \l__coffin_bounding_shift_dim
+\dim_new:N \l__coffin_left_corner_dim
+\dim_new:N \l__coffin_right_corner_dim
+\dim_new:N \l__coffin_bottom_corner_dim
+\dim_new:N \l__coffin_top_corner_dim
+\cs_new_protected:Npn \coffin_rotate:Nn #1#2
+ { \__coffin_rotate:NnNNN #1 {#2} \box_rotate:Nn \prop_set_eq:cN \hbox_set:Nn }
+\cs_generate_variant:Nn \coffin_rotate:Nn { c }
+\cs_new_protected:Npn \coffin_grotate:Nn #1#2
+ { \__coffin_rotate:NnNNN #1 {#2} \box_grotate:Nn \prop_gset_eq:cN \hbox_gset:Nn }
+\cs_generate_variant:Nn \coffin_grotate:Nn { c }
+\cs_new_protected:Npn \__coffin_rotate:NnNNN #1#2#3#4#5
+ {
+ \fp_set:Nn \l__coffin_sin_fp { sind ( #2 ) }
+ \fp_set:Nn \l__coffin_cos_fp { cosd ( #2 ) }
+ \prop_set_eq:Nc \l__coffin_corners_prop
+ { coffin ~ \__coffin_to_value:N #1 ~ corners }
+ \prop_set_eq:Nc \l__coffin_poles_prop
+ { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ \prop_map_inline:Nn \l__coffin_corners_prop
+ { \__coffin_rotate_corner:Nnnn #1 {##1} ##2 }
+ \prop_map_inline:Nn \l__coffin_poles_prop
+ { \__coffin_rotate_pole:Nnnnnn #1 {##1} ##2 }
+ \__coffin_set_bounding:N #1
+ \prop_map_inline:Nn \l__coffin_bounding_prop
+ { \__coffin_rotate_bounding:nnn {##1} ##2 }
+ \__coffin_find_corner_maxima:N #1
+ \__coffin_find_bounding_shift:
+ #3 #1 {#2}
+ \hbox_set:Nn \l__coffin_internal_box
+ {
+ \__kernel_kern:n
+ { \l__coffin_bounding_shift_dim - \l__coffin_left_corner_dim }
+ \box_move_down:nn { \l__coffin_bottom_corner_dim }
+ { \box_use:N #1 }
+ }
+ \box_set_ht:Nn \l__coffin_internal_box
+ { \l__coffin_top_corner_dim - \l__coffin_bottom_corner_dim }
+ \box_set_dp:Nn \l__coffin_internal_box { 0pt }
+ \box_set_wd:Nn \l__coffin_internal_box
+ { \l__coffin_right_corner_dim - \l__coffin_left_corner_dim }
+ #5 #1 { \box_use_drop:N \l__coffin_internal_box }
+ \prop_map_inline:Nn \l__coffin_corners_prop
+ { \__coffin_shift_corner:Nnnn #1 {##1} ##2 }
+ \prop_map_inline:Nn \l__coffin_poles_prop
+ { \__coffin_shift_pole:Nnnnnn #1 {##1} ##2 }
+ #4 { coffin ~ \__coffin_to_value:N #1 ~ corners }
+ \l__coffin_corners_prop
+ #4 { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ \l__coffin_poles_prop
+ }
+\cs_new_protected:Npn \__coffin_set_bounding:N #1
+ {
+ \prop_put:Nne \l__coffin_bounding_prop { tl }
+ { { 0pt } { \dim_eval:n { \box_ht:N #1 } } }
+ \prop_put:Nne \l__coffin_bounding_prop { tr }
+ {
+ { \dim_eval:n { \box_wd:N #1 } }
+ { \dim_eval:n { \box_ht:N #1 } }
+ }
+ \dim_set:Nn \l__coffin_internal_dim { -\box_dp:N #1 }
+ \prop_put:Nne \l__coffin_bounding_prop { bl }
+ { { 0pt } { \dim_use:N \l__coffin_internal_dim } }
+ \prop_put:Nne \l__coffin_bounding_prop { br }
+ {
+ { \dim_eval:n { \box_wd:N #1 } }
+ { \dim_use:N \l__coffin_internal_dim }
+ }
+ }
+\cs_new_protected:Npn \__coffin_rotate_bounding:nnn #1#2#3
+ {
+ \__coffin_rotate_vector:nnNN {#2} {#3} \l__coffin_x_dim \l__coffin_y_dim
+ \prop_put:Nne \l__coffin_bounding_prop {#1}
+ { { \dim_use:N \l__coffin_x_dim } { \dim_use:N \l__coffin_y_dim } }
+ }
+\cs_new_protected:Npn \__coffin_rotate_corner:Nnnn #1#2#3#4
+ {
+ \__coffin_rotate_vector:nnNN {#3} {#4} \l__coffin_x_dim \l__coffin_y_dim
+ \prop_put:Nne \l__coffin_corners_prop {#2}
+ { { \dim_use:N \l__coffin_x_dim } { \dim_use:N \l__coffin_y_dim } }
+ }
+\cs_new_protected:Npn \__coffin_rotate_pole:Nnnnnn #1#2#3#4#5#6
+ {
+ \__coffin_rotate_vector:nnNN {#3} {#4} \l__coffin_x_dim \l__coffin_y_dim
+ \__coffin_rotate_vector:nnNN {#5} {#6}
+ \l__coffin_x_prime_dim \l__coffin_y_prime_dim
+ \prop_put:Nne \l__coffin_poles_prop {#2}
+ {
+ { \dim_use:N \l__coffin_x_dim } { \dim_use:N \l__coffin_y_dim }
+ { \dim_use:N \l__coffin_x_prime_dim }
+ { \dim_use:N \l__coffin_y_prime_dim }
+ }
+ }
+\cs_new_protected:Npn \__coffin_rotate_vector:nnNN #1#2#3#4
+ {
+ \dim_set:Nn #3
+ {
+ \fp_to_dim:n
+ {
+ \dim_to_fp:n {#1} * \l__coffin_cos_fp
+ - \dim_to_fp:n {#2} * \l__coffin_sin_fp
+ }
+ }
+ \dim_set:Nn #4
+ {
+ \fp_to_dim:n
+ {
+ \dim_to_fp:n {#1} * \l__coffin_sin_fp
+ + \dim_to_fp:n {#2} * \l__coffin_cos_fp
+ }
+ }
+ }
+\cs_new_protected:Npn \__coffin_find_corner_maxima:N #1
+ {
+ \dim_set:Nn \l__coffin_top_corner_dim { -\c_max_dim }
+ \dim_set:Nn \l__coffin_right_corner_dim { -\c_max_dim }
+ \dim_set:Nn \l__coffin_bottom_corner_dim { \c_max_dim }
+ \dim_set:Nn \l__coffin_left_corner_dim { \c_max_dim }
+ \prop_map_inline:Nn \l__coffin_corners_prop
+ { \__coffin_find_corner_maxima_aux:nn ##2 }
+ }
+\cs_new_protected:Npn \__coffin_find_corner_maxima_aux:nn #1#2
+ {
+ \dim_set:Nn \l__coffin_left_corner_dim
+ { \dim_min:nn { \l__coffin_left_corner_dim } {#1} }
+ \dim_set:Nn \l__coffin_right_corner_dim
+ { \dim_max:nn { \l__coffin_right_corner_dim } {#1} }
+ \dim_set:Nn \l__coffin_bottom_corner_dim
+ { \dim_min:nn { \l__coffin_bottom_corner_dim } {#2} }
+ \dim_set:Nn \l__coffin_top_corner_dim
+ { \dim_max:nn { \l__coffin_top_corner_dim } {#2} }
+ }
+\cs_new_protected:Npn \__coffin_find_bounding_shift:
+ {
+ \dim_set:Nn \l__coffin_bounding_shift_dim { \c_max_dim }
+ \prop_map_inline:Nn \l__coffin_bounding_prop
+ { \__coffin_find_bounding_shift_aux:nn ##2 }
+ }
+\cs_new_protected:Npn \__coffin_find_bounding_shift_aux:nn #1#2
+ {
+ \dim_set:Nn \l__coffin_bounding_shift_dim
+ { \dim_min:nn { \l__coffin_bounding_shift_dim } {#1} }
+ }
+\cs_new_protected:Npn \__coffin_shift_corner:Nnnn #1#2#3#4
+ {
+ \prop_put:Nne \l__coffin_corners_prop {#2}
+ {
+ { \dim_eval:n { #3 - \l__coffin_left_corner_dim } }
+ { \dim_eval:n { #4 - \l__coffin_bottom_corner_dim } }
+ }
+ }
+\cs_new_protected:Npn \__coffin_shift_pole:Nnnnnn #1#2#3#4#5#6
+ {
+ \prop_put:Nne \l__coffin_poles_prop {#2}
+ {
+ { \dim_eval:n { #3 - \l__coffin_left_corner_dim } }
+ { \dim_eval:n { #4 - \l__coffin_bottom_corner_dim } }
+ {#5} {#6}
+ }
+ }
+\fp_new:N \l__coffin_scale_x_fp
+\fp_new:N \l__coffin_scale_y_fp
+\dim_new:N \l__coffin_scaled_total_height_dim
+\dim_new:N \l__coffin_scaled_width_dim
+\cs_new_protected:Npn \coffin_resize:Nnn #1#2#3
+ {
+ \__coffin_resize:NnnNN #1 {#2} {#3}
+ \box_resize_to_wd_and_ht_plus_dp:Nnn
+ \prop_set_eq:cN
+ }
+\cs_generate_variant:Nn \coffin_resize:Nnn { c }
+\cs_new_protected:Npn \coffin_gresize:Nnn #1#2#3
+ {
+ \__coffin_resize:NnnNN #1 {#2} {#3}
+ \box_gresize_to_wd_and_ht_plus_dp:Nnn
+ \prop_gset_eq:cN
+ }
+\cs_generate_variant:Nn \coffin_gresize:Nnn { c }
+\cs_new_protected:Npn \__coffin_resize:NnnNN #1#2#3#4#5
+ {
+ \fp_set:Nn \l__coffin_scale_x_fp
+ { \dim_to_fp:n {#2} / \dim_to_fp:n { \coffin_wd:N #1 } }
+ \fp_set:Nn \l__coffin_scale_y_fp
+ {
+ \dim_to_fp:n {#3}
+ / \dim_to_fp:n { \coffin_ht:N #1 + \coffin_dp:N #1 }
+ }
+ #4 #1 {#2} {#3}
+ \__coffin_resize_common:NnnN #1 {#2} {#3} #5
+ }
+\cs_new_protected:Npn \__coffin_resize_common:NnnN #1#2#3#4
+ {
+ \prop_set_eq:Nc \l__coffin_corners_prop
+ { coffin ~ \__coffin_to_value:N #1 ~ corners }
+ \prop_set_eq:Nc \l__coffin_poles_prop
+ { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ \prop_map_inline:Nn \l__coffin_corners_prop
+ { \__coffin_scale_corner:Nnnn #1 {##1} ##2 }
+ \prop_map_inline:Nn \l__coffin_poles_prop
+ { \__coffin_scale_pole:Nnnnnn #1 {##1} ##2 }
+ \fp_compare:nNnT \l__coffin_scale_x_fp < \c_zero_fp
+ {
+ \prop_map_inline:Nn \l__coffin_corners_prop
+ { \__coffin_x_shift_corner:Nnnn #1 {##1} ##2 }
+ \prop_map_inline:Nn \l__coffin_poles_prop
+ { \__coffin_x_shift_pole:Nnnnnn #1 {##1} ##2 }
+ }
+ #4 { coffin ~ \__coffin_to_value:N #1 ~ corners }
+ \l__coffin_corners_prop
+ #4 { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ \l__coffin_poles_prop
+ }
+\cs_new_protected:Npn \coffin_scale:Nnn #1#2#3
+ { \__coffin_scale:NnnNN #1 {#2} {#3} \box_scale:Nnn \prop_set_eq:cN }
+\cs_generate_variant:Nn \coffin_scale:Nnn { c }
+\cs_new_protected:Npn \coffin_gscale:Nnn #1#2#3
+ { \__coffin_scale:NnnNN #1 {#2} {#3} \box_gscale:Nnn \prop_gset_eq:cN }
+\cs_generate_variant:Nn \coffin_gscale:Nnn { c }
+\cs_new_protected:Npn \__coffin_scale:NnnNN #1#2#3#4#5
+ {
+ \fp_set:Nn \l__coffin_scale_x_fp {#2}
+ \fp_set:Nn \l__coffin_scale_y_fp {#3}
+ #4 #1 { \l__coffin_scale_x_fp } { \l__coffin_scale_y_fp }
+ \dim_set:Nn \l__coffin_internal_dim
+ { \coffin_ht:N #1 + \coffin_dp:N #1 }
+ \dim_set:Nn \l__coffin_scaled_total_height_dim
+ { \fp_abs:n { \l__coffin_scale_y_fp } \l__coffin_internal_dim }
+ \dim_set:Nn \l__coffin_scaled_width_dim
+ { -\fp_abs:n { \l__coffin_scale_x_fp } \coffin_wd:N #1 }
+ \__coffin_resize_common:NnnN #1
+ { \l__coffin_scaled_width_dim } { \l__coffin_scaled_total_height_dim }
+ #5
+ }
+\cs_new_protected:Npn \__coffin_scale_vector:nnNN #1#2#3#4
+ {
+ \dim_set:Nn #3
+ { \fp_to_dim:n { \dim_to_fp:n {#1} * \l__coffin_scale_x_fp } }
+ \dim_set:Nn #4
+ { \fp_to_dim:n { \dim_to_fp:n {#2} * \l__coffin_scale_y_fp } }
+ }
+\cs_new_protected:Npn \__coffin_scale_corner:Nnnn #1#2#3#4
+ {
+ \__coffin_scale_vector:nnNN {#3} {#4} \l__coffin_x_dim \l__coffin_y_dim
+ \prop_put:Nne \l__coffin_corners_prop {#2}
+ { { \dim_use:N \l__coffin_x_dim } { \dim_use:N \l__coffin_y_dim } }
+ }
+\cs_new_protected:Npn \__coffin_scale_pole:Nnnnnn #1#2#3#4#5#6
+ {
+ \__coffin_scale_vector:nnNN {#3} {#4} \l__coffin_x_dim \l__coffin_y_dim
+ \prop_put:Nne \l__coffin_poles_prop {#2}
+ {
+ { \dim_use:N \l__coffin_x_dim } { \dim_use:N \l__coffin_y_dim }
+ {#5} {#6}
+ }
+ }
+\cs_new_protected:Npn \__coffin_x_shift_corner:Nnnn #1#2#3#4
+ {
+ \prop_put:Nne \l__coffin_corners_prop {#2}
+ {
+ { \dim_eval:n { #3 + \box_wd:N #1 } } {#4}
+ }
+ }
+\cs_new_protected:Npn \__coffin_x_shift_pole:Nnnnnn #1#2#3#4#5#6
+ {
+ \prop_put:Nne \l__coffin_poles_prop {#2}
+ {
+ { \dim_eval:n { #3 + \box_wd:N #1 } } {#4}
+ {#5} {#6}
+ }
+ }
+\cs_new_protected:Npn \coffin_join:NnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \__coffin_join:NnnNnnnnN #1 {#2} {#3} #4 {#5} {#6} {#7} {#8}
+ \coffin_set_eq:NN
+ }
+\cs_generate_variant:Nn \coffin_join:NnnNnnnn { c , Nnnc , cnnc }
+\cs_new_protected:Npn \coffin_gjoin:NnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \__coffin_join:NnnNnnnnN #1 {#2} {#3} #4 {#5} {#6} {#7} {#8}
+ \coffin_gset_eq:NN
+ }
+\cs_generate_variant:Nn \coffin_gjoin:NnnNnnnn { c , Nnnc , cnnc }
+\cs_new_protected:Npn \__coffin_join:NnnNnnnnN #1#2#3#4#5#6#7#8#9
+ {
+ \__coffin_align:NnnNnnnnN
+ #1 {#2} {#3} #4 {#5} {#6} {#7} {#8} \l__coffin_aligned_coffin
+ \hbox_set:Nn \l__coffin_aligned_coffin
+ {
+ \dim_compare:nNnT { \l__coffin_offset_x_dim } < \c_zero_dim
+ { \__kernel_kern:n { -\l__coffin_offset_x_dim } }
+ \hbox_unpack:N \l__coffin_aligned_coffin
+ \dim_set:Nn \l__coffin_internal_dim
+ { \l__coffin_offset_x_dim - \box_wd:N #1 + \box_wd:N #4 }
+ \dim_compare:nNnT \l__coffin_internal_dim < \c_zero_dim
+ { \__kernel_kern:n { -\l__coffin_internal_dim } }
+ }
+ \__coffin_reset_structure:N \l__coffin_aligned_coffin
+ \prop_clear:c
+ {
+ coffin ~ \__coffin_to_value:N \l__coffin_aligned_coffin
+ \c_space_tl corners
+ }
+ \__coffin_update_poles:N \l__coffin_aligned_coffin
+ \dim_compare:nNnTF \l__coffin_offset_x_dim < \c_zero_dim
+ {
+ \__coffin_offset_poles:Nnn #1 { -\l__coffin_offset_x_dim } { 0pt }
+ \__coffin_offset_poles:Nnn #4 { 0pt } { \l__coffin_offset_y_dim }
+ \__coffin_offset_corners:Nnn #1 { -\l__coffin_offset_x_dim } { 0pt }
+ \__coffin_offset_corners:Nnn #4 { 0pt } { \l__coffin_offset_y_dim }
+ }
+ {
+ \__coffin_offset_poles:Nnn #1 { 0pt } { 0pt }
+ \__coffin_offset_poles:Nnn #4
+ { \l__coffin_offset_x_dim } { \l__coffin_offset_y_dim }
+ \__coffin_offset_corners:Nnn #1 { 0pt } { 0pt }
+ \__coffin_offset_corners:Nnn #4
+ { \l__coffin_offset_x_dim } { \l__coffin_offset_y_dim }
+ }
+ \__coffin_update_vertical_poles:NNN #1 #4 \l__coffin_aligned_coffin
+ #9 #1 \l__coffin_aligned_coffin
+ }
+\cs_new_protected:Npn \coffin_attach:NnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \__coffin_attach:NnnNnnnnN #1 {#2} {#3} #4 {#5} {#6} {#7} {#8}
+ \coffin_set_eq:NN
+ }
+\cs_generate_variant:Nn \coffin_attach:NnnNnnnn { c , Nnnc , cnnc }
+\cs_new_protected:Npn \coffin_gattach:NnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \__coffin_attach:NnnNnnnnN #1 {#2} {#3} #4 {#5} {#6} {#7} {#8}
+ \coffin_gset_eq:NN
+ }
+\cs_generate_variant:Nn \coffin_gattach:NnnNnnnn { c , Nnnc , cnnc }
+\cs_new_protected:Npn \__coffin_attach:NnnNnnnnN #1#2#3#4#5#6#7#8#9
+ {
+ \__coffin_align:NnnNnnnnN
+ #1 {#2} {#3} #4 {#5} {#6} {#7} {#8} \l__coffin_aligned_coffin
+ \box_set_ht:Nn \l__coffin_aligned_coffin { \box_ht:N #1 }
+ \box_set_dp:Nn \l__coffin_aligned_coffin { \box_dp:N #1 }
+ \box_set_wd:Nn \l__coffin_aligned_coffin { \box_wd:N #1 }
+ \__coffin_reset_structure:N \l__coffin_aligned_coffin
+ \prop_set_eq:cc
+ {
+ coffin ~ \__coffin_to_value:N \l__coffin_aligned_coffin
+ \c_space_tl corners
+ }
+ { coffin ~ \__coffin_to_value:N #1 ~ corners }
+ \__coffin_update_poles:N \l__coffin_aligned_coffin
+ \__coffin_offset_poles:Nnn #1 { 0pt } { 0pt }
+ \__coffin_offset_poles:Nnn #4
+ { \l__coffin_offset_x_dim } { \l__coffin_offset_y_dim }
+ \__coffin_update_vertical_poles:NNN #1 #4 \l__coffin_aligned_coffin
+ #9 #1 \l__coffin_aligned_coffin
+ }
+\cs_new_protected:Npn \__coffin_attach_mark:NnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \__coffin_align:NnnNnnnnN
+ #1 {#2} {#3} #4 {#5} {#6} {#7} {#8} \l__coffin_aligned_coffin
+ \box_set_ht:Nn \l__coffin_aligned_coffin { \box_ht:N #1 }
+ \box_set_dp:Nn \l__coffin_aligned_coffin { \box_dp:N #1 }
+ \box_set_wd:Nn \l__coffin_aligned_coffin { \box_wd:N #1 }
+ \box_set_eq:NN #1 \l__coffin_aligned_coffin
+ }
+\cs_new_protected:Npn \__coffin_align:NnnNnnnnN #1#2#3#4#5#6#7#8#9
+ {
+ \__coffin_calculate_intersection:Nnn #4 {#5} {#6}
+ \dim_set:Nn \l__coffin_x_prime_dim { \l__coffin_x_dim }
+ \dim_set:Nn \l__coffin_y_prime_dim { \l__coffin_y_dim }
+ \__coffin_calculate_intersection:Nnn #1 {#2} {#3}
+ \dim_set:Nn \l__coffin_offset_x_dim
+ { \l__coffin_x_dim - \l__coffin_x_prime_dim + #7 }
+ \dim_set:Nn \l__coffin_offset_y_dim
+ { \l__coffin_y_dim - \l__coffin_y_prime_dim + #8 }
+ \hbox_set:Nn \l__coffin_aligned_internal_coffin
+ {
+ \box_use:N #1
+ \__kernel_kern:n { -\box_wd:N #1 }
+ \__kernel_kern:n { \l__coffin_offset_x_dim }
+ \box_move_up:nn { \l__coffin_offset_y_dim } { \box_use:N #4 }
+ }
+ \coffin_set_eq:NN #9 \l__coffin_aligned_internal_coffin
+ }
+\cs_new_protected:Npn \__coffin_offset_poles:Nnn #1#2#3
+ {
+ \prop_map_inline:cn { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ { \__coffin_offset_pole:Nnnnnnn #1 {##1} ##2 {#2} {#3} }
+ }
+\cs_new_protected:Npn \__coffin_offset_pole:Nnnnnnn #1#2#3#4#5#6#7#8
+ {
+ \dim_set:Nn \l__coffin_x_dim { #3 + #7 }
+ \dim_set:Nn \l__coffin_y_dim { #4 + #8 }
+ \tl_if_in:nnTF {#2} { - }
+ { \tl_set:Nn \l__coffin_internal_tl { {#2} } }
+ { \tl_set:Nn \l__coffin_internal_tl { { #1 - #2 } } }
+ \exp_last_unbraced:NNo \__coffin_set_pole:Nnn \l__coffin_aligned_coffin
+ { \l__coffin_internal_tl }
+ {
+ { \dim_use:N \l__coffin_x_dim } { \dim_use:N \l__coffin_y_dim }
+ {#5} {#6}
+ }
+ }
+\cs_new_protected:Npn \__coffin_offset_corners:Nnn #1#2#3
+ {
+ \prop_map_inline:cn { coffin ~ \__coffin_to_value:N #1 ~ corners }
+ { \__coffin_offset_corner:Nnnnn #1 {##1} ##2 {#2} {#3} }
+ }
+\cs_new_protected:Npn \__coffin_offset_corner:Nnnnn #1#2#3#4#5#6
+ {
+ \prop_put:cne
+ {
+ coffin ~ \__coffin_to_value:N \l__coffin_aligned_coffin
+ \c_space_tl corners
+ }
+ { #1 - #2 }
+ {
+ { \dim_eval:n { #3 + #5 } }
+ { \dim_eval:n { #4 + #6 } }
+ }
+ }
+\cs_new_protected:Npn \__coffin_update_vertical_poles:NNN #1#2#3
+ {
+ \__coffin_get_pole:NnN #3 { #1 -T } \l__coffin_pole_a_tl
+ \__coffin_get_pole:NnN #3 { #2 -T } \l__coffin_pole_b_tl
+ \exp_last_two_unbraced:Noo \__coffin_update_T:nnnnnnnnN
+ \l__coffin_pole_a_tl \l__coffin_pole_b_tl #3
+ \__coffin_get_pole:NnN #3 { #1 -B } \l__coffin_pole_a_tl
+ \__coffin_get_pole:NnN #3 { #2 -B } \l__coffin_pole_b_tl
+ \exp_last_two_unbraced:Noo \__coffin_update_B:nnnnnnnnN
+ \l__coffin_pole_a_tl \l__coffin_pole_b_tl #3
+ }
+\cs_new_protected:Npn \__coffin_update_T:nnnnnnnnN #1#2#3#4#5#6#7#8#9
+ {
+ \dim_compare:nNnTF {#2} < {#6}
+ {
+ \__coffin_set_pole:Nnn #9 { T }
+ { { 0pt } {#6} { 1000pt } { 0pt } }
+ }
+ {
+ \__coffin_set_pole:Nnn #9 { T }
+ { { 0pt } {#2} { 1000pt } { 0pt } }
+ }
+ }
+\cs_new_protected:Npn \__coffin_update_B:nnnnnnnnN #1#2#3#4#5#6#7#8#9
+ {
+ \dim_compare:nNnTF {#2} < {#6}
+ {
+ \__coffin_set_pole:Nnn #9 { B }
+ { { 0pt } {#2} { 1000pt } { 0pt } }
+ }
+ {
+ \__coffin_set_pole:Nnn #9 { B }
+ { { 0pt } {#6} { 1000pt } { 0pt } }
+ }
+ }
+\coffin_new:N \c__coffin_empty_coffin
+\tex_setbox:D \c__coffin_empty_coffin = \tex_hbox:D { }
+\cs_new_protected:Npn \coffin_typeset:Nnnnn #1#2#3#4#5
+ {
+ \mode_leave_vertical:
+ \__coffin_align:NnnNnnnnN \c__coffin_empty_coffin { H } { l }
+ #1 {#2} {#3} {#4} {#5} \l__coffin_aligned_coffin
+ \box_use_drop:N \l__coffin_aligned_coffin
+ }
+\cs_generate_variant:Nn \coffin_typeset:Nnnnn { c }
+\coffin_new:N \l__coffin_display_coffin
+\coffin_new:N \l__coffin_display_coord_coffin
+\coffin_new:N \l__coffin_display_pole_coffin
+\prop_new:N \l__coffin_display_handles_prop
+\prop_put:Nnn \l__coffin_display_handles_prop { tl }
+ { { b } { r } { -1 } { 1 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { thc }
+ { { b } { hc } { 0 } { 1 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { tr }
+ { { b } { l } { 1 } { 1 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { vcl }
+ { { vc } { r } { -1 } { 0 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { vchc }
+ { { vc } { hc } { 0 } { 0 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { vcr }
+ { { vc } { l } { 1 } { 0 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { bl }
+ { { t } { r } { -1 } { -1 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { bhc }
+ { { t } { hc } { 0 } { -1 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { br }
+ { { t } { l } { 1 } { -1 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { Tl }
+ { { t } { r } { -1 } { -1 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { Thc }
+ { { t } { hc } { 0 } { -1 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { Tr }
+ { { t } { l } { 1 } { -1 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { Hl }
+ { { vc } { r } { -1 } { 1 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { Hhc }
+ { { vc } { hc } { 0 } { 1 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { Hr }
+ { { vc } { l } { 1 } { 1 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { Bl }
+ { { b } { r } { -1 } { -1 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { Bhc }
+ { { b } { hc } { 0 } { -1 } }
+\prop_put:Nnn \l__coffin_display_handles_prop { Br }
+ { { b } { l } { 1 } { -1 } }
+\dim_new:N \l__coffin_display_offset_dim
+\dim_set:Nn \l__coffin_display_offset_dim { 2pt }
+\dim_new:N \l__coffin_display_x_dim
+\dim_new:N \l__coffin_display_y_dim
+\prop_new:N \l__coffin_display_poles_prop
+\tl_new:N \l__coffin_display_font_tl
+\bool_lazy_and:nnT
+ { \cs_if_exist_p:N \fmtname }
+ { \str_if_eq_p:Vn \fmtname { LaTeX2e } }
+ {
+ \tl_set:Nn \l__coffin_display_font_tl
+ { \sffamily \tiny }
+ }
+\cs_new_protected:Npn \__coffin_rule:nn #1#2
+ {
+ \mode_leave_vertical:
+ \hbox:n { \tex_vrule:D width #1 height #2 \scan_stop: }
+ }
+\cs_new_protected:Npn \coffin_mark_handle:Nnnn #1#2#3#4
+ {
+ \hcoffin_set:Nn \l__coffin_display_pole_coffin
+ {
+ \color_select:n {#4}
+ \__coffin_rule:nn { 1pt } { 1pt }
+ }
+ \__coffin_attach_mark:NnnNnnnn #1 {#2} {#3}
+ \l__coffin_display_pole_coffin { hc } { vc } { 0pt } { 0pt }
+ \hcoffin_set:Nn \l__coffin_display_coord_coffin
+ {
+ \color_select:n {#4}
+ \l__coffin_display_font_tl
+ ( \tl_to_str:n { #2 , #3 } )
+ }
+ \prop_get:NnN \l__coffin_display_handles_prop
+ { #2 #3 } \l__coffin_internal_tl
+ \quark_if_no_value:NTF \l__coffin_internal_tl
+ {
+ \prop_get:NnN \l__coffin_display_handles_prop
+ { #3 #2 } \l__coffin_internal_tl
+ \quark_if_no_value:NTF \l__coffin_internal_tl
+ {
+ \__coffin_attach_mark:NnnNnnnn #1 {#2} {#3}
+ \l__coffin_display_coord_coffin { l } { vc }
+ { 1pt } { 0pt }
+ }
+ {
+ \exp_last_unbraced:No \__coffin_mark_handle_aux:nnnnNnn
+ \l__coffin_internal_tl #1 {#2} {#3}
+ }
+ }
+ {
+ \exp_last_unbraced:No \__coffin_mark_handle_aux:nnnnNnn
+ \l__coffin_internal_tl #1 {#2} {#3}
+ }
+ }
+\cs_new_protected:Npn \__coffin_mark_handle_aux:nnnnNnn #1#2#3#4#5#6#7
+ {
+ \__coffin_attach_mark:NnnNnnnn #5 {#6} {#7}
+ \l__coffin_display_coord_coffin {#1} {#2}
+ { #3 \l__coffin_display_offset_dim }
+ { #4 \l__coffin_display_offset_dim }
+ }
+\cs_generate_variant:Nn \coffin_mark_handle:Nnnn { c }
+\cs_new_protected:Npn \coffin_display_handles:Nn #1#2
+ {
+ \hcoffin_set:Nn \l__coffin_display_pole_coffin
+ {
+ \color_select:n {#2}
+ \__coffin_rule:nn { 1pt } { 1pt }
+ }
+ \prop_set_eq:Nc \l__coffin_display_poles_prop
+ { coffin ~ \__coffin_to_value:N #1 ~ poles }
+ \__coffin_get_pole:NnN #1 { H } \l__coffin_pole_a_tl
+ \__coffin_get_pole:NnN #1 { T } \l__coffin_pole_b_tl
+ \tl_if_eq:NNT \l__coffin_pole_a_tl \l__coffin_pole_b_tl
+ { \prop_remove:Nn \l__coffin_display_poles_prop { T } }
+ \__coffin_get_pole:NnN #1 { B } \l__coffin_pole_b_tl
+ \tl_if_eq:NNT \l__coffin_pole_a_tl \l__coffin_pole_b_tl
+ { \prop_remove:Nn \l__coffin_display_poles_prop { B } }
+ \coffin_set_eq:NN \l__coffin_display_coffin #1
+ \prop_map_inline:Nn \l__coffin_display_poles_prop
+ {
+ \prop_remove:Nn \l__coffin_display_poles_prop {##1}
+ \__coffin_display_handles_aux:nnnnnn {##1} ##2 {#2}
+ }
+ \box_use_drop:N \l__coffin_display_coffin
+ }
+\cs_new_protected:Npn \__coffin_display_handles_aux:nnnnnn #1#2#3#4#5#6
+ {
+ \prop_map_inline:Nn \l__coffin_display_poles_prop
+ {
+ \bool_set_false:N \l__coffin_error_bool
+ \__coffin_calculate_intersection:nnnnnnnn {#2} {#3} {#4} {#5} ##2
+ \bool_if:NF \l__coffin_error_bool
+ {
+ \dim_set:Nn \l__coffin_display_x_dim { \l__coffin_x_dim }
+ \dim_set:Nn \l__coffin_display_y_dim { \l__coffin_y_dim }
+ \__coffin_display_attach:Nnnnn
+ \l__coffin_display_pole_coffin { hc } { vc }
+ { 0pt } { 0pt }
+ \hcoffin_set:Nn \l__coffin_display_coord_coffin
+ {
+ \color_select:n {#6}
+ \l__coffin_display_font_tl
+ ( \tl_to_str:n { #1 , ##1 } )
+ }
+ \prop_get:NnN \l__coffin_display_handles_prop
+ { #1 ##1 } \l__coffin_internal_tl
+ \quark_if_no_value:NTF \l__coffin_internal_tl
+ {
+ \prop_get:NnN \l__coffin_display_handles_prop
+ { ##1 #1 } \l__coffin_internal_tl
+ \quark_if_no_value:NTF \l__coffin_internal_tl
+ {
+ \__coffin_display_attach:Nnnnn
+ \l__coffin_display_coord_coffin { l } { vc }
+ { 1pt } { 0pt }
+ }
+ {
+ \exp_last_unbraced:No
+ \__coffin_display_handles_aux:nnnn
+ \l__coffin_internal_tl
+ }
+ }
+ {
+ \exp_last_unbraced:No \__coffin_display_handles_aux:nnnn
+ \l__coffin_internal_tl
+ }
+ }
+ }
+ }
+\cs_new_protected:Npn \__coffin_display_handles_aux:nnnn #1#2#3#4
+ {
+ \__coffin_display_attach:Nnnnn
+ \l__coffin_display_coord_coffin {#1} {#2}
+ { #3 \l__coffin_display_offset_dim }
+ { #4 \l__coffin_display_offset_dim }
+ }
+\cs_generate_variant:Nn \coffin_display_handles:Nn { c }
+\cs_new_protected:Npn \__coffin_display_attach:Nnnnn #1#2#3#4#5
+ {
+ \__coffin_calculate_intersection:Nnn #1 {#2} {#3}
+ \dim_set:Nn \l__coffin_x_prime_dim { \l__coffin_x_dim }
+ \dim_set:Nn \l__coffin_y_prime_dim { \l__coffin_y_dim }
+ \dim_set:Nn \l__coffin_offset_x_dim
+ { \l__coffin_display_x_dim - \l__coffin_x_prime_dim + #4 }
+ \dim_set:Nn \l__coffin_offset_y_dim
+ { \l__coffin_display_y_dim - \l__coffin_y_prime_dim + #5 }
+ \hbox_set:Nn \l__coffin_aligned_coffin
+ {
+ \box_use:N \l__coffin_display_coffin
+ \__kernel_kern:n { -\box_wd:N \l__coffin_display_coffin }
+ \__kernel_kern:n { \l__coffin_offset_x_dim }
+ \box_move_up:nn { \l__coffin_offset_y_dim } { \box_use:N #1 }
+ }
+ \box_set_ht:Nn \l__coffin_aligned_coffin
+ { \box_ht:N \l__coffin_display_coffin }
+ \box_set_dp:Nn \l__coffin_aligned_coffin
+ { \box_dp:N \l__coffin_display_coffin }
+ \box_set_wd:Nn \l__coffin_aligned_coffin
+ { \box_wd:N \l__coffin_display_coffin }
+ \box_set_eq:NN \l__coffin_display_coffin \l__coffin_aligned_coffin
+ }
+\cs_new_protected:Npn \coffin_show_structure:N
+ { \__coffin_show_structure:NN \msg_show:nneeee }
+\cs_generate_variant:Nn \coffin_show_structure:N { c }
+\cs_new_protected:Npn \coffin_log_structure:N
+ { \__coffin_show_structure:NN \msg_log:nneeee }
+\cs_generate_variant:Nn \coffin_log_structure:N { c }
+\cs_new_protected:Npn \__coffin_show_structure:NN #1#2
+ {
+ \__coffin_if_exist:NT #2
+ {
+ #1 { coffin } { show }
+ { \token_to_str:N #2 }
+ {
+ \iow_newline: >~ ht ~=~ \dim_eval:n { \coffin_ht:N #2 }
+ \iow_newline: >~ dp ~=~ \dim_eval:n { \coffin_dp:N #2 }
+ \iow_newline: >~ wd ~=~ \dim_eval:n { \coffin_wd:N #2 }
+ }
+ {
+ \prop_map_function:cN
+ { coffin ~ \__coffin_to_value:N #2 ~ poles }
+ \msg_show_item_unbraced:nn
+ }
+ { }
+ }
+ }
+\cs_new_protected:Npn \coffin_show:N #1
+ { \coffin_show:Nnn #1 \c_max_int \c_max_int }
+\cs_generate_variant:Nn \coffin_show:N { c }
+\cs_new_protected:Npn \coffin_log:N #1
+ { \coffin_log:Nnn #1 \c_max_int \c_max_int }
+\cs_generate_variant:Nn \coffin_log:N { c }
+\cs_new_protected:Npn \coffin_show:Nnn
+ { \__coffin_show:NNNnn \msg_term:nneeee \box_show:Nnn }
+\cs_generate_variant:Nn \coffin_show:Nnn { c }
+\cs_new_protected:Npn \coffin_log:Nnn
+ { \__coffin_show:NNNnn \msg_log:nneeee \box_show:Nnn }
+\cs_generate_variant:Nn \coffin_log:Nnn { c }
+\cs_new_protected:Npn \__coffin_show:NNNnn #1#2#3#4#5
+ {
+ \__coffin_if_exist:NT #3
+ {
+ \__coffin_show_structure:NN #1 #3
+ #2 #3 {#4} {#5}
+ }
+ }
+\msg_new:nnnn { coffin } { no-pole-intersection }
+ { No~intersection~between~coffin~poles. }
+ {
+ LaTeX~was~asked~to~find~the~intersection~between~two~poles,~
+ but~they~do~not~have~a~unique~meeting~point:~
+ the~value~(0pt,~0pt)~will~be~used.
+ }
+\msg_new:nnnn { coffin } { unknown }
+ { Unknown~coffin~'#1'. }
+ { The~coffin~'#1'~was~never~defined. }
+\msg_new:nnnn { coffin } { unknown-pole }
+ { Pole~'#1'~unknown~for~coffin~'#2'. }
+ {
+ LaTeX~was~asked~to~find~a~typesetting~pole~for~a~coffin,~
+ but~either~the~coffin~does~not~exist~or~the~pole~name~is~wrong.
+ }
+\msg_new:nnn { coffin } { show }
+ {
+ Size~of~coffin~#1 : #2 \\
+ Poles~of~coffin~#1 : #3 .
+ }
+%% File: l3luatex.dtx
+\cs_new_eq:NN \__lua_escape:n \tex_luaescapestring:D
+\cs_new_eq:NN \__lua_now:n \tex_directlua:D
+\cs_new_eq:NN \__lua_shipout:n \tex_latelua:D
+\cs_undefine:N \lua_escape:e
+\cs_undefine:N \lua_now:e
+\cs_new:Npn \lua_now:e #1 { \__lua_now:n {#1} }
+\cs_new:Npn \lua_now:n #1 { \lua_now:e { \exp_not:n {#1} } }
+\cs_new_protected:Npn \lua_shipout_e:n #1 { \__lua_shipout:n {#1} }
+\cs_new_protected:Npn \lua_shipout:n #1
+ { \lua_shipout_e:n { \exp_not:n {#1} } }
+\cs_new:Npn \lua_escape:e #1 { \__lua_escape:n {#1} }
+\cs_new:Npn \lua_escape:n #1 { \lua_escape:e { \exp_not:n {#1} } }
+\str_new:N \l__lua_err_msg_str
+\cs_new_protected:Npn \lua_load_module:n #1
+ {
+ \bool_if:nF { \__lua_load_module_p:n { #1 } }
+ {
+ \msg_error:nnnV
+ { luatex } { module-not-found } { #1 } \l__lua_err_msg_str
+ }
+ }
+\sys_if_engine_luatex:F
+ {
+ \clist_map_inline:nn
+ {
+ \lua_escape:n , \lua_escape:e ,
+ \lua_now:n , \lua_now:e
+ }
+ {
+ \cs_gset:Npn #1 ##1
+ {
+ \msg_expandable_error:nnn
+ { luatex } { luatex-required } { #1 }
+ }
+ }
+ \clist_map_inline:nn
+ { \lua_shipout_e:n , \lua_shipout:n, \lua_load_module:n }
+ {
+ \cs_gset_protected:Npn #1 ##1
+ {
+ \msg_error:nnn
+ { luatex } { luatex-required } { #1 }
+ }
+ }
+ }
+\msg_new:nnnn { luatex } { luatex-required }
+ { LuaTeX~engine~not~in~use!~Ignoring~#1. }
+ {
+ The~feature~you~are~using~is~only~available~
+ with~the~LuaTeX~engine.~LaTeX3~ignored~'#1'.
+ }
+
+\msg_new:nnnn { luatex } { module-not-found }
+ { Lua~module~`#1'~not~found. }
+ {
+ The~file~`#1.lua'~could~not~be~found.~Please~ensure~
+ that~the~file~was~properly~installed~and~that~the~
+ filename~database~is~current. \\ \\
+ The~Lua~loader~provided~this~additional~information: \\
+ #2
+ }
+
+\prop_gput:Nnn \g_msg_module_name_prop { luatex } { LaTeX }
+\prop_gput:Nnn \g_msg_module_type_prop { luatex } { }
+%% File: l3unicode.dtx
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_new:Npn \codepoint_str_generate:n #1
+ {
+ \int_compare:nNnTF {#1} = { `\ }
+ { ~ }
+ { \char_generate:nn {#1} { 12 } }
+ }
+ \cs_new:Npn \codepoint_generate:nn #1#2
+ {
+ \int_compare:nNnTF {#1} = { `\ }
+ { ~ }
+ {
+ \__kernel_exp_not:w \exp_after:wN \exp_after:wN \exp_after:wN
+ { \char_generate:nn {#1} {#2} }
+ }
+ }
+ }
+ {
+ \cs_new:Npn \codepoint_str_generate:n #1
+ {
+ \int_compare:nNnTF {#1} = { `\ }
+ { ~ }
+ {
+ \use:e
+ {
+ \exp_not:N \__codepoint_str_generate:nnnn
+ \__kernel_codepoint_to_bytes:n {#1}
+ }
+ }
+ }
+ \cs_new:Npn \__codepoint_str_generate:nnnn #1#2#3#4
+ {
+ \char_generate:nn {#1} { 12 }
+ \tl_if_blank:nF {#2}
+ {
+ \char_generate:nn {#2} { 12 }
+ \tl_if_blank:nF {#3}
+ {
+ \char_generate:nn {#3} { 12 }
+ \tl_if_blank:nF {#4}
+ { \char_generate:nn {#4} { 12 } }
+ }
+ }
+ }
+ \cs_new:Npn \codepoint_generate:nn #1#2
+ {
+ \int_compare:nNnTF {#1} = { `\ }
+ { ~ }
+ {
+ \int_compare:nNnTF {#1} < { "80 }
+ {
+ \__kernel_exp_not:w \exp_after:wN \exp_after:wN \exp_after:wN
+ { \char_generate:nn {#1} {#2} }
+ }
+ {
+ \use:e
+ {
+ \exp_not:N \__codepoint_generate:nnnn
+ \__kernel_codepoint_to_bytes:n {#1}
+ }
+ }
+ }
+ }
+ \cs_new:Npn \__codepoint_generate:nnnn #1#2#3#4
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \tex_expanded:D
+ {
+ \__codepoint_generate:n {#1}
+ \__codepoint_generate:n {#2}
+ \tl_if_blank:nF {#3}
+ {
+ \__codepoint_generate:n {#3}
+ \tl_if_blank:nF {#4}
+ { \__codepoint_generate:n {#4} }
+ }
+ }
+ }
+ }
+ \cs_new:Npn \__codepoint_generate:n #1
+ {
+ \__kernel_exp_not:w \exp_after:wN \exp_after:wN \exp_after:wN
+ { \char_generate:nn {#1} { 13 } }
+ }
+ }
+\cs_new:Npn \__kernel_codepoint_to_bytes:n #1
+ {
+ \exp_args:Nf \__codepoint_to_bytes_auxi:n
+ { \int_eval:n {#1} }
+ }
+\cs_new:Npn \__codepoint_to_bytes_auxi:n #1
+ {
+ \if_int_compare:w #1 > "80 \exp_stop_f:
+ \if_int_compare:w #1 < "800 \exp_stop_f:
+ \__codepoint_to_bytes_outputi:nw
+ { \__codepoint_to_bytes_auxii:Nnn C {#1} { 64 } }
+ \__codepoint_to_bytes_outputii:nw
+ { \__codepoint_to_bytes_auxiii:n {#1} }
+ \else:
+ \if_int_compare:w #1 < "10000 \exp_stop_f:
+ \__codepoint_to_bytes_outputi:nw
+ { \__codepoint_to_bytes_auxii:Nnn E {#1} { 64 * 64 } }
+ \__codepoint_to_bytes_outputii:nw
+ {
+ \__codepoint_to_bytes_auxiii:n
+ { \int_div_truncate:nn {#1} { 64 } }
+ }
+ \__codepoint_to_bytes_outputiii:nw
+ { \__codepoint_to_bytes_auxiii:n {#1} }
+ \else:
+ \__codepoint_to_bytes_outputi:nw
+ {
+ \__codepoint_to_bytes_auxii:Nnn F
+ {#1} { 64 * 64 * 64 }
+ }
+ \__codepoint_to_bytes_outputii:nw
+ {
+ \__codepoint_to_bytes_auxiii:n
+ { \int_div_truncate:nn {#1} { 64 * 64 } }
+ }
+ \__codepoint_to_bytes_outputiii:nw
+ {
+ \__codepoint_to_bytes_auxiii:n
+ { \int_div_truncate:nn {#1} { 64 } }
+ }
+ \__codepoint_to_bytes_outputiv:nw
+ { \__codepoint_to_bytes_auxiii:n {#1} }
+ \fi:
+ \fi:
+ \else:
+ \__codepoint_to_bytes_outputi:nw {#1}
+ \fi:
+ \__codepoint_to_bytes_end: { } { } { } { }
+ }
+\cs_new:Npn \__codepoint_to_bytes_auxii:Nnn #1#2#3
+ { "#10 + \int_div_truncate:nn {#2} {#3} }
+\cs_new:Npn \__codepoint_to_bytes_auxiii:n #1
+ { \int_mod:nn {#1} { 64 } + 128 }
+\cs_new:Npn \__codepoint_to_bytes_outputi:nw
+ #1 #2 \__codepoint_to_bytes_end: #3
+ { \__codepoint_to_bytes_output:fnn { \int_eval:n {#1} } { } {#2} }
+\cs_new:Npn \__codepoint_to_bytes_outputii:nw
+ #1 #2 \__codepoint_to_bytes_end: #3#4
+ { \__codepoint_to_bytes_output:fnn { \int_eval:n {#1} } { {#3} } {#2} }
+\cs_new:Npn \__codepoint_to_bytes_outputiii:nw
+ #1 #2 \__codepoint_to_bytes_end: #3#4#5
+ {
+ \__codepoint_to_bytes_output:fnn
+ { \int_eval:n {#1} } { {#3} {#4} } {#2}
+ }
+\cs_new:Npn \__codepoint_to_bytes_outputiv:nw
+ #1 #2 \__codepoint_to_bytes_end: #3#4#5#6
+ {
+ \__codepoint_to_bytes_output:fnn
+ { \int_eval:n {#1} } { {#3} {#4} {#5} } {#2}
+ }
+\cs_new:Npn \__codepoint_to_bytes_output:nnn #1#2#3
+ {
+ #3
+ \__codepoint_to_bytes_end: #2 {#1}
+ }
+\cs_generate_variant:Nn \__codepoint_to_bytes_output:nnn { f }
+\cs_new:Npn \__codepoint_to_bytes_end: { }
+\cs_new:Npn \codepoint_to_category:n #1
+ {
+ \cs:w
+ c__codepoint_category_
+ \tex_romannumeral:D
+ \__kernel_codepoint_data:nn { category } {#1}
+ _str
+ \cs_end:
+ }
+\cs_new:Npn \codepoint_to_nfd:n #1
+ { \exp_args:Ne \__codepoint_to_nfd:n { \int_eval:n {#1} } }
+\cs_new:Npn \__codepoint_to_nfd:n #1
+ { \__codepoint_to_nfd:nn {#1} { \char_value_catcode:n {#1} } }
+\bool_lazy_or:nnF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_gset:Npn \__codepoint_to_nfd:n #1
+ {
+ \int_compare:nNnTF {#1} > { "80 }
+ { \__codepoint_to_nfd:nn {#1} { 12 } }
+ { \__codepoint_to_nfd:nn {#1} { \char_value_catcode:n {#1} } }
+ }
+ }
+\cs_new:Npn \__codepoint_to_nfd:nn #1#2
+ {
+ \exp_args:Ne \__codepoint_to_nfd:nnn
+ { \__codepoint_nfd:n {#1} } {#1} {#2}
+ }
+\cs_new:Npn \__codepoint_to_nfd:nnn #1#2#3 { \__codepoint_to_nfd:nnnn #1 {#2} {#3} }
+\cs_new:Npn \__codepoint_to_nfd:nnnn #1#2#3#4
+ {
+ \int_compare:nNnTF {#1} = {#3}
+ { \codepoint_generate:nn {#1} {#4} }
+ {
+ \__codepoint_to_nfd:nn {#1} {#4}
+ \tl_if_blank:nF {#2}
+ { \__codepoint_to_nfd:nn {#2} {#4} }
+ }
+ }
+\int_const:Nn \c__codepoint_block_size_int { 64 }
+\ior_new:N \g__codepoint_data_ior
+\group_begin:
+ \clist_map_inline:nn
+ { category , uppercase , lowercase }
+ {
+ \cs_set_nopar:cpn { l__codepoint_ #1 _block_clist } { }
+ \cs_set_nopar:cpn { l__codepoint_ #1 _block_tl } { 1 }
+ \cs_set_nopar:cpn { l__codepoint_ #1 _pos_tl } { 0 }
+ \intarray_new:cn { g__codepoint_ #1 _index_intarray }
+ { \int_div_truncate:nn { "110000 } \c__codepoint_block_size_int }
+ }
+ \cs_set_nopar:Npn \l__codepoint_next_codepoint_fint_tl { 0 }
+ \cs_set_nopar:Npn \l__codepoint_matched_block_tl { 0 }
+ \cs_set_protected:Npn \__codepoint_data_auxi:w #1#2
+ {
+ \quark_if_recursion_tail_stop:n {#2}
+ \cs_set_nopar:cpn { l__codepoint_category_ #2 _tl } {#1}
+ \str_const:cn { c__codepoint_category_ \tex_romannumeral:D #1 _str } {#2}
+ \exp_args:Ne \__codepoint_data_auxi:w { \int_eval:n { #1 + 1 } }
+ }
+ \__codepoint_data_auxi:w { 1 }
+ { Lu } { Ll } { Lt } { Lm } { Lo }
+ { Mn } { Me } { Mc }
+ { Nd } { Nl } { No }
+ { Zs } { Zl } { Zp }
+ { Cc } { Cf } { Co } { Cs } { Cn }
+ { Pd } { Ps } { Pe } { Pc } { Po } { Pi } { Pf }
+ { Sm } { Sc } { Sk } { So }
+ \q_recursion_tail
+ \q_recursion_stop
+ \cs_set_protected:Npn \__codepoint_data_auxi:w
+ #1 ; #2 ; #3 ; #4 ; #5 ; #6 ; #7 ; #8 ; #9 ;
+ {
+ \tl_if_blank:nF {#6}
+ {
+ \tl_if_head_eq_charcode:nNF {#6} < % >
+ { \__codepoint_data_auxii:w #1 ; #6 ~ \q_stop }
+ }
+ \__codepoint_data_auxiii:w #1 ; #2 ; #3 ;
+ }
+ \cs_set_protected:Npn \__codepoint_data_auxii:w #1 ; #2 ~ #3 \q_stop
+ {
+ \tl_const:ce
+ { c__codepoint_nfd_ \codepoint_str_generate:n {"#1} _tl }
+ {
+ {"#2}
+ { \tl_if_blank:nF {#3} {"#3} }
+ }
+ }
+ \cs_set_protected:Npn \__codepoint_data_auxiii:w
+ #1 ; #2 ; #3 ; #4 ; #5 ; #6 ; #7 ; #8 ; #9 ~ \q_stop
+ {
+ \use:e
+ {
+ \__codepoint_data_auxiv:w
+ #1 ; #2 ;
+ \__codepoint_data_category:n {#3} ;
+ \__codepoint_data_offset:nn {#1} {#7} ;
+ \__codepoint_data_offset:nn {#1} {#8} ;
+ #9;
+ }
+ }
+ \cs_set:Npn \__codepoint_data_category:n #1
+ { \use:c { l__codepoint_category_ #1 _tl } }
+ \cs_set:Npn \__codepoint_data_offset:nn #1#2
+ {
+ \tl_if_blank:nTF {#2}
+ { 0 }
+ { \int_eval:n { "#2 - "#1 } }
+ }
+ \cs_set_protected:Npn \__codepoint_data_auxiv:w #1 ; #2 ; #3 ; #4 ; #5 ; #6 ;
+ {
+ \int_compare:nNnT {"#1} > \l__codepoint_next_codepoint_fint_tl
+ {
+ \__codepoint_data_auxv:nnnnw {#1} {#3} {#4} {#5}
+ #2 Last> \q_stop
+ }
+ \__codepoint_add:nn { category } {#3}
+ \__codepoint_add:nn { uppercase } {#4}
+ \__codepoint_add:nn { lowercase } {#5}
+ \int_compare:nNnF {#4} = { \__codepoint_data_offset:nn {#1} {#6} }
+ {
+ \tl_const:ce
+ { c__codepoint_titlecase_ \codepoint_str_generate:n {"#1} _tl }
+ { {"#6} { } { } }
+ }
+ \tl_set:Ne \l__codepoint_next_codepoint_fint_tl
+ { \int_eval:n { "#1 + 1 } }
+ }
+ \cs_set_protected:Npn \__codepoint_add:nn #1#2
+ {
+ \clist_put_right:cn { l__codepoint_ #1 _block_clist } {#2}
+ \int_compare:nNnT { \clist_count:c { l__codepoint_ #1 _block_clist } }
+ = \c__codepoint_block_size_int
+ { \__codepoint_save_blocks:nn {#1} { 1 } }
+ }
+ \cs_set_protected:Npe \__codepoint_data_auxv:nnnnw #1#2#3#4#5 Last> #6 \q_stop
+ {
+ \exp_not:N \tl_if_blank:nTF {#6}
+ {
+ \exp_not:N \__codepoint_range:nnn {#1} { category }
+ { \exp_not:V \l__codepoint_category_Cn_tl }
+ \exp_not:N \__codepoint_range:nnn {#1} { uppercase } { 0 }
+ \exp_not:N \__codepoint_range:nnn {#1} { lowercase } { 0 }
+ }
+ {
+ \exp_not:N \__codepoint_range:nnn {#1} { category } {#2}
+ \exp_not:N \__codepoint_range:nnn {#1} { uppercase } {#3}
+ \exp_not:N \__codepoint_range:nnn {#1} { lowercase } {#4}
+ }
+ }
+ \cs_set_protected:Npn \__codepoint_range:nnn #1
+ {
+ \exp_args:Nf \__codepoint_range_aux:nnn
+ { \int_eval:n { "#1 - \l__codepoint_next_codepoint_fint_tl } }
+ }
+ \cs_set_protected:Npn \__codepoint_range_aux:nnn #1#2
+ {
+ \exp_args:Nf \__codepoint_range:nnnn
+ {
+ \int_min:nn
+ {#1}
+ {
+ \c__codepoint_block_size_int
+ - \clist_count:c { l__codepoint_ #2 _block_clist }
+ }
+ }
+ {#1} {#2}
+ }
+ \cs_set_protected:Npn \__codepoint_range:nnnn #1#2#3#4
+ {
+ \prg_replicate:nn {#1}
+ { \clist_put_right:cn { l__codepoint_ #3 _block_clist } {#4} }
+ \int_compare:nNnT { \clist_count:c { l__codepoint_ #3 _block_clist } }
+ = \c__codepoint_block_size_int
+ { \__codepoint_save_blocks:nn {#3} { 1 } }
+ \int_compare:nNnF
+ { \int_div_truncate:nn { #2 - #1 } \c__codepoint_block_size_int } = 0
+ {
+ \tl_set:ce { l__codepoint_ #3 _block_clist }
+ {
+ \exp_args:NNe \use:nn \use_none:n
+ { \prg_replicate:nn { \c__codepoint_block_size_int } { , #4 } }
+ }
+ \__codepoint_save_blocks:nn {#3}
+ { \int_div_truncate:nn { (#2 - #1) } \c__codepoint_block_size_int }
+ }
+ \prg_replicate:nn
+ { \int_mod:nn { #2 - #1 } \c__codepoint_block_size_int }
+ { \clist_put_right:ce { l__codepoint_ #3 _block_clist } {#4} }
+ }
+ \cs_set_protected:Npn \__codepoint_save_blocks:nn #1#2
+ {
+ \tl_set_eq:Nc \l__codepoint_matched_block_tl { l__codepoint_ #1 _block_tl }
+ \int_step_inline:nn { \tl_use:c { l__codepoint_ #1 _block_tl } - 1 }
+ {
+ \tl_if_eq:ccT { l__codepoint_ #1 _block_clist }
+ { l__codepoint_ #1 _block_ ##1 _clist }
+ { \tl_set:Nn \l__codepoint_matched_block_tl {##1} }
+ }
+ \int_compare:nNnT
+ { \tl_use:c { l__codepoint_ #1 _block_tl } } = \l__codepoint_matched_block_tl
+ {
+ \clist_set_eq:cc
+ {
+ l__codepoint_ #1 _block_
+ \tl_use:c { l__codepoint_ #1 _block_tl } _clist
+ }
+ { l__codepoint_ #1 _block_clist }
+ \tl_set:ce { l__codepoint_ #1 _block_tl }
+ { \int_eval:n { \tl_use:c { l__codepoint_ #1 _block_tl } + 1 } }
+ }
+ \prg_replicate:nn {#2}
+ {
+ \tl_set:ce { l__codepoint_ #1 _pos_tl }
+ { \int_eval:n { \tl_use:c { l__codepoint_ #1 _pos_tl } + 1 } }
+ \exp_args:Nc \__kernel_intarray_gset:Nnn
+ { g__codepoint_ #1 _index_intarray }
+ { \tl_use:c { l__codepoint_ #1 _pos_tl } }
+ \l__codepoint_matched_block_tl
+ }
+ \clist_clear:c { l__codepoint_ #1 _block_clist }
+ }
+ \cs_set_protected:Npn \__codepoint_finalise_blocks:
+ {
+ \clist_map_inline:nn { category , uppercase , lowercase }
+ {
+ \__codepoint_range:nnn { 110000 } {##1} { 0 }
+ \__codepoint_finalise_blocks:n {##1}
+ }
+ }
+ \cs_set_protected:Npn \__codepoint_finalise_blocks:n #1
+ {
+ \cs_gset_eq:cc { c__codepoint_ #1 _index_intarray } { g__codepoint_ #1 _index_intarray }
+ \cs_undefine:c { g__codepoint_ #1 _index_intarray }
+ \intarray_new:cn { g__codepoint_ #1 _blocks_intarray }
+ { ( \tl_use:c { l__codepoint_ #1 _block_tl } - 1 ) * \c__codepoint_block_size_int }
+ \int_step_inline:nn { \tl_use:c { l__codepoint_ #1 _block_tl } - 1 }
+ {
+ \exp_args:Nv \__codepoint_finalise_blocks:nnn
+ { l__codepoint_ #1 _block_ ##1 _clist }
+ {##1} {#1}
+ }
+ \cs_gset_eq:cc { c__codepoint_ #1 _blocks_intarray }
+ { g__codepoint_ #1 _blocks_intarray }
+ \cs_undefine:c { g__codepoint_ #1 _blocks_intarray }
+ }
+ \cs_set_protected:Npn \__codepoint_finalise_blocks:nnn #1#2#3
+ {
+ \exp_args:Nnf \__codepoint_finalise_blocks:nnnw { 1 }
+ { \int_eval:n { ( #2 - 1 ) * \c__codepoint_block_size_int } }
+ {#3}
+ #1 , \q_recursion_tail , \q_recursion_stop
+ }
+ \cs_set_protected:Npn \__codepoint_finalise_blocks:nnnw #1#2#3#4 ,
+ {
+ \quark_if_recursion_tail_stop:n {#4}
+ \intarray_gset:cnn { g__codepoint_ #3 _blocks_intarray }
+ { #1 + #2 }
+ {#4}
+ \exp_args:Nf \__codepoint_finalise_blocks:nnnw
+ { \int_eval:n { #1 + 1 } } {#2} {#3}
+ }
+ \ior_open:Nn \g__codepoint_data_ior { UnicodeData.txt }
+ \group_begin:
+ \char_set_catcode_space:n { `\ }%
+ \ior_map_variable:NNn \g__codepoint_data_ior \l__codepoint_tmpa_tl
+ {%
+ \if_meaning:w \l__codepoint_tmpa_tl \c_space_tl
+ \exp_after:wN \ior_map_break:
+ \fi:
+ \exp_after:wN \__codepoint_data_auxi:w \l__codepoint_tmpa_tl \q_stop
+ }%
+ \__codepoint_finalise_blocks:
+ \group_end:
+\group_end:
+\cs_new:Npn \__kernel_codepoint_data:nn #1#2
+ {
+ \exp_args:Nf \__codepoint_data:nnn
+ {
+ \int_eval:n
+ {
+ \c__codepoint_block_size_int *
+ (
+ \intarray_item:cn { c__codepoint_ #1 _index_intarray }
+ {
+ \int_div_truncate:nn {#2}
+ \c__codepoint_block_size_int
+ + 1
+ }
+ - 1
+ )
+ }
+ }
+ {#2} {#1}
+ }
+\cs_new:Npn \__codepoint_data:nnn #1#2#3
+ {
+ \intarray_item:cn { c__codepoint_ #3 _blocks_intarray }
+ { #1 + \int_mod:nn {#2} \c__codepoint_block_size_int + 1 }
+ }
+\group_begin:
+ \ior_open:Nn \g__codepoint_data_ior { CaseFolding.txt }
+ \cs_set_protected:Npn \__codepoint_data_auxi:w #1 ;~ #2 ;~ #3 ; #4 \q_stop
+ {
+ \if:w \tl_head:n { #2 ? } C
+ \reverse_if:N \if_int_compare:w
+ \int_eval:n { \__kernel_codepoint_data:nn { lowercase } {"#1} + "#1 }
+ = "#3 ~
+ \tl_const:ce
+ { c__codepoint_casefold_ \codepoint_str_generate:n {"#1} _tl }
+ { {"#3} { } { } }
+ \fi:
+ \else:
+ \if:w \tl_head:n { #2 ? } F
+ \__codepoint_data_auxii:w #1 ~ #3 ~ \q_stop
+ \fi:
+ \fi:
+ }
+ \cs_set_protected:Npn \__codepoint_data_auxii:w #1 ~ #2 ~ #3 ~ #4 \q_stop
+ {
+ \tl_const:ce { c__codepoint_casefold_ \codepoint_str_generate:n {"#1} _tl }
+ {
+ {"#2}
+ {"#3}
+ { \tl_if_blank:nF {#4} { " \int_to_Hex:n {"#4} } }
+ }
+ }
+ \ior_str_map_inline:Nn \g__codepoint_data_ior
+ {
+ \reverse_if:N \if:w \c_hash_str \tl_head:w #1 \c_hash_str \q_stop
+ \__codepoint_data_auxi:w #1 \q_stop
+ \fi:
+ }
+ \ior_close:N \g__codepoint_data_ior
+ \ior_open:Nn \g__codepoint_data_ior { SpecialCasing.txt }
+ \cs_set_protected:Npn \__codepoint_data_auxi:w
+ #1 ;~ #2 ;~ #3 ;~ #4 ; #5 \q_stop
+ {
+ \use:n { \__codepoint_data_auxii:w #1 ~ lower ~ #2 ~ } ~ \q_stop
+ \use:n { \__codepoint_data_auxii:w #1 ~ upper ~ #4 ~ } ~ \q_stop
+ \str_if_eq:nnF {#3} {#4}
+ { \use:n { \__codepoint_data_auxii:w #1 ~ title ~ #3 ~ } ~ \q_stop }
+ }
+ \cs_set_protected:Npn \__codepoint_data_auxii:w
+ #1 ~ #2 ~ #3 ~ #4 ~ #5 \q_stop
+ {
+ \tl_if_empty:nF {#4}
+ {
+ \tl_const:ce { c__codepoint_ #2 case_ \codepoint_str_generate:n {"#1} _tl }
+ {
+ {"#3}
+ {"#4}
+ { \tl_if_blank:nF {#5} {"#5} }
+ }
+ }
+ }
+ \ior_str_map_inline:Nn \g__codepoint_data_ior
+ {
+ \str_if_eq:eeTF { \tl_head:w #1 \c_hash_str \q_stop } { \c_hash_str }
+ {
+ \str_if_eq:eeT
+ {#1}
+ { \c_hash_str \c_space_tl Conditional~Mappings }
+ { \ior_map_break: }
+ }
+ { \__codepoint_data_auxi:w #1 \q_stop }
+ }
+ \ior_close:N \g__codepoint_data_ior
+\group_end:
+\cs_new:Npn \__kernel_codepoint_case:nn #1#2
+ {
+ \exp_args:Ne \__codepoint_case:nnn
+ { \codepoint_str_generate:n {#2} } {#1} {#2}
+ }
+\cs_new:Npn \__codepoint_case:nnn #1#2#3
+ {
+ \cs_if_exist:cTF { c__codepoint_ #2 _ #1 _tl }
+ {
+ \tl_use:c
+ { c__codepoint_ #2 _ #1 _tl }
+ }
+ { \use:c { __codepoint_ #2 :n } {#3} }
+ }
+\cs_new:Npn \__codepoint_uppercase:n { \__codepoint_case:nn { uppercase } }
+\cs_new:Npn \__codepoint_lowercase:n { \__codepoint_case:nn { lowercase } }
+\cs_new:Npn \__codepoint_titlecase:n { \__codepoint_case:nn { uppercase } }
+\cs_new:Npn \__codepoint_casefold:n { \__codepoint_case:nn { lowercase } }
+\cs_new:Npn \__codepoint_case:nn #1#2
+ {
+ { \int_eval:n { \__kernel_codepoint_data:nn {#1} {#2} + #2 } }
+ { }
+ { }
+ }
+\cs_new:Npn \__codepoint_nfd:n #1
+ { \exp_args:Ne \__codepoint_nfd:nn { \codepoint_str_generate:n {#1} } {#1} }
+\cs_new:Npn \__codepoint_nfd:nn #1#2
+ {
+ \tl_if_exist:cTF { c__codepoint_nfd_ #1 _tl }
+ { \tl_use:c { c__codepoint_nfd_ #1 _tl } }
+ { {#2} { } }
+ }
+\ior_new:N \g__text_data_ior
+\group_begin:
+ \ior_open:Nn \g__text_data_ior { GraphemeBreakProperty.txt }
+ \cs_set_nopar:Npn \l__text_tmpa_str { }
+ \cs_set_nopar:Npn \l__text_tmpb_str { }
+ \cs_set_protected:Npn \__text_data_auxi:w #1 ;~ #2 ~ #3 \q_stop
+ {
+ \str_if_eq:VnF \l__text_tmpb_str {#2}
+ {
+ \str_if_empty:NF \l__text_tmpb_str
+ {
+ \clist_const:ce { c__text_grapheme_ \l__text_tmpb_str _clist }
+ { \exp_after:wN \use_none:n \l__text_tmpa_str }
+ \cs_set_nopar:Npn \l__text_tmpa_str { }
+ }
+ \cs_set_nopar:Npn \l__text_tmpb_str {#2}
+ }
+ \__text_data_auxii:w #1 .. #1 .. #1 \q_stop
+ }
+ \cs_set_protected:Npn \__text_data_auxii:w #1 .. #2 .. #3 \q_stop
+ {
+ \cs_set_nopar:Npe \l__text_tmpa_str
+ {
+ \l__text_tmpa_str ,
+ \tl_trim_spaces:n {#1} .. \tl_trim_spaces:n {#2}
+ }
+ }
+ \ior_str_map_inline:Nn \g__text_data_ior
+ {
+ \str_if_eq:eeF { \tl_head:w #1 \c_hash_str \q_stop } { \c_hash_str }
+ {
+ \tl_if_blank:nF {#1}
+ { \__text_data_auxi:w #1 \q_stop }
+ }
+ }
+ \ior_close:N \g__text_data_ior
+\group_end:
+%% File: l3text.dtx
+\cs_generate_variant:Nn \tl_if_head_eq_meaning_p:nN { o }
+\scan_new:N \s__text_stop
+\quark_new:N \q__text_nil
+\__kernel_quark_new_conditional:Nn \__text_quark_if_nil:n { TF }
+\quark_new:N \q__text_recursion_tail
+\quark_new:N \q__text_recursion_stop
+\cs_new:Npn \__text_use_i_delimit_by_q_recursion_stop:nw
+ #1 #2 \q__text_recursion_stop {#1}
+\__kernel_quark_new_test:N \__text_if_q_recursion_tail_stop_do:Nn
+\__kernel_quark_new_test:N \__text_if_q_recursion_tail_stop_do:nn
+\scan_new:N \s__text_recursion_tail
+\scan_new:N \s__text_recursion_stop
+\cs_new:Npn \__text_use_i_delimit_by_s_recursion_stop:nw
+ #1 #2 \s__text_recursion_stop {#1}
+\cs_new:Npn \__text_if_s_recursion_tail_stop_do:Nn #1
+ {
+ \bool_lazy_and:nnTF
+ { \cs_if_eq_p:NN \s__text_recursion_tail #1 }
+ { \str_if_eq_p:nn { \s__text_recursion_tail } {#1} }
+ { \__text_use_i_delimit_by_s_recursion_stop:nw }
+ { \use_none:n }
+ }
+\group_begin:
+ \char_set_catcode_active:n { 0 }
+ \cs_new:Npn \__text_token_to_explicit:N #1
+ {
+ \if_catcode:w \exp_not:N #1
+ \if_catcode:w \scan_stop: \exp_not:N #1
+ \scan_stop:
+ \else:
+ \exp_not:N ^^@
+ \fi:
+ \exp_after:wN \__text_token_to_explicit_cs:N
+ \else:
+ \exp_after:wN \__text_token_to_explicit_char:N
+ \fi:
+ #1
+ }
+\group_end:
+\cs_new:Npn \__text_token_to_explicit_cs:N #1
+ {
+ \exp_after:wN \if_meaning:w \exp_not:N #1 #1
+ \exp_after:wN \use:nn \exp_after:wN
+ \__text_token_to_explicit_cs_aux:N
+ \else:
+ \exp_after:wN \exp_not:n
+ \fi:
+ {#1}
+ }
+\cs_new:Npn \__text_token_to_explicit_cs_aux:N #1
+ {
+ \bool_lazy_or:nnTF
+ { \token_if_chardef_p:N #1 }
+ { \token_if_mathchardef_p:N #1 }
+ {
+ \char_generate:nn {#1}
+ {
+ \if_int_compare:w \char_value_catcode:n {#1} = 10 \exp_stop_f:
+ 10
+ \else:
+ 12
+ \fi:
+ }
+ }
+ {#1}
+ }
+\cs_new:Npn \__text_token_to_explicit_char:N #1
+ {
+ \if:w
+ \if_catcode:w ^ \exp_args:No \str_tail:n { \token_to_str:N #1 } ^
+ \token_to_str:N #1 #1
+ \else:
+ AB
+ \fi:
+ \exp_after:wN \exp_not:n
+ \else:
+ \exp_after:wN \__text_token_to_explicit:n
+ \fi:
+ {#1}
+ }
+\cs_new:Npn \__text_token_to_explicit:n #1
+ {
+ \exp_after:wN \__text_token_to_explicit_auxi:w
+ \int_value:w
+ \if_catcode:w \c_group_begin_token #1 1 \else:
+ \if_catcode:w \c_group_end_token #1 2 \else:
+ \if_catcode:w \c_math_toggle_token #1 3 \else:
+ \if_catcode:w ## #1 6 \else:
+ \if_catcode:w ^ #1 7 \else:
+ \if_catcode:w \c_math_subscript_token #1 8 \else:
+ \if_catcode:w \c_space_token #1 10 \else:
+ \if_catcode:w A #1 11 \else:
+ \if_catcode:w + #1 12 \else:
+ 4 \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi:
+ \exp_after:wN ;
+ \token_to_meaning:N #1 \s__text_stop
+ }
+\cs_new:Npn \__text_token_to_explicit_auxi:w #1 ; #2 \s__text_stop
+ {
+ \char_generate:nn
+ {
+ \if_int_compare:w #1 < 9 \exp_stop_f:
+ \exp_after:wN \__text_token_to_explicit_auxii:w
+ \else:
+ \exp_after:wN \__text_token_to_explicit_auxiii:w
+ \fi:
+ #2
+ }
+ {#1}
+ }
+\exp_last_unbraced:NNNNo \cs_new:Npn \__text_token_to_explicit_auxii:w
+ #1 { \tl_to_str:n { character ~ } } { ` }
+\cs_new:Npn \__text_token_to_explicit_auxiii:w #1 ~ #2 ~ { ` }
+\cs_new:Npn \__text_char_catcode:N #1
+ {
+ \if_catcode:w \exp_not:N #1 \c_math_toggle_token
+ 3
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_alignment_token
+ 4
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_math_superscript_token
+ 7
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_math_subscript_token
+ 8
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_space_token
+ 10
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_catcode_letter_token
+ 11
+ \else:
+ \if_catcode:w \exp_not:N #1 \c_catcode_other_token
+ 12
+ \else:
+ 13
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ }
+\prg_new_conditional:Npnn \__text_if_expandable:N #1 { T , F , TF }
+ {
+ \token_if_expandable:NTF #1
+ {
+ \bool_lazy_any:nTF
+ {
+ { \token_if_protected_macro_p:N #1 }
+ { \token_if_protected_long_macro_p:N #1 }
+ { \token_if_eq_meaning_p:NN \q__text_recursion_tail #1 }
+ }
+ { \prg_return_false: }
+ { \prg_return_true: }
+ }
+ { \prg_return_false: }
+ }
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_new:Npn \__text_codepoint_process:nN #1#2 { #1 {#2} }
+ }
+ {
+ \cs_new:Npe \__text_codepoint_process:nN #1#2
+ {
+ \exp_not:N \int_compare:nNnTF {`#2} > { "80 }
+ {
+ \sys_if_engine_pdftex:TF
+ { \exp_not:N \__text_codepoint_process_aux:nN }
+ {
+ \exp_not:N \int_compare:nNnTF {`#2} > { "FF }
+ { \exp_not:N \use:n }
+ { \exp_not:N \__text_codepoint_process_aux:nN }
+ }
+ }
+ { \exp_not:N \use:n }
+ {#1} #2
+ }
+ \cs_new:Npn \__text_codepoint_process_aux:nN #1#2
+ {
+ \int_compare:nNnTF { `#2 } < { "E0 }
+ { \__text_codepoint_process:nNN }
+ {
+ \int_compare:nNnTF { `#2 } < { "F0 }
+ { \__text_codepoint_process:nNNN }
+ { \__text_codepoint_process:nNNNN }
+ }
+ {#1} #2
+ }
+ \cs_new:Npn \__text_codepoint_process:nNN #1#2#3
+ { #1 {#2#3} }
+ \cs_new:Npn \__text_codepoint_process:nNNN #1#2#3#4
+ { #1 {#2#3#4} }
+ \cs_new:Npn \__text_codepoint_process:nNNNN #1#2#3#4#5
+ { #1 {#2#3#4#5} }
+ }
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \prg_new_conditional:Npnn
+ \__text_codepoint_compare:nNn #1#2#3 { TF , p }
+ {
+ \int_compare:nNnTF {`#1} #2 {#3}
+ \prg_return_true: \prg_return_false:
+ }
+ \cs_new:Npn \__text_codepoint_from_chars:Nw #1 {`#1}
+ }
+ {
+ \prg_new_conditional:Npnn
+ \__text_codepoint_compare:nNn #1#2#3 { TF , p }
+ {
+ \int_compare:nNnTF { \__text_codepoint_from_chars:Nw #1 }
+ #2 {#3}
+ \prg_return_true: \prg_return_false:
+ }
+ \cs_new:Npe \__text_codepoint_from_chars:Nw #1
+ {
+ \exp_not:N \if_int_compare:w `#1 > "80 \exp_not:N \exp_stop_f:
+ \sys_if_engine_pdftex:TF
+ {
+ \exp_not:N \exp_after:wN
+ \exp_not:N \__text_codepoint_from_chars_aux:Nw
+ }
+ {
+ \exp_not:N \if_int_compare:w `#1 > "FF \exp_not:N \exp_stop_f:
+ \exp_not:N \exp_after:wN \exp_not:N \exp_after:wN
+ \exp_not:N \exp_after:wN
+ \exp_not:N \__text_codepoint_from_chars:N
+ \exp_not:N \else:
+ \exp_not:N \exp_after:wN \exp_not:N \exp_after:wN
+ \exp_not:N \exp_after:wN
+ \exp_not:N \__text_codepoint_from_chars_aux:Nw
+ \exp_not:N \fi:
+ }
+ \exp_not:N \else:
+ \exp_not:N \exp_after:wN \exp_not:N \__text_codepoint_from_chars:N
+ \exp_not:N \fi:
+ #1
+ }
+ \cs_new:Npn \__text_codepoint_from_chars_aux:Nw #1
+ {
+ \if_int_compare:w `#1 < "E0 \exp_stop_f:
+ \exp_after:wN \__text_codepoint_from_chars:NN
+ \else:
+ \if_int_compare:w `#1 < "F0 \exp_stop_f:
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \__text_codepoint_from_chars:NNN
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \__text_codepoint_from_chars:NNNN
+ \fi:
+ \fi:
+ #1
+ }
+ \cs_new:Npn \__text_codepoint_from_chars:N #1 {`#1}
+ \cs_new:Npn \__text_codepoint_from_chars:NN #1#2
+ { (`#1 - "C0) * "40 + `#2 - "80 }
+ \cs_new:Npn \__text_codepoint_from_chars:NNN #1#2#3
+ { (`#1 - "E0) * "1000 + (`#2 - "80) * "40 + `#3 - "80 }
+ \cs_new:Npn \__text_codepoint_from_chars:NNNN #1#2#3#4
+ {
+ (`#1 - "F0) * "40000
+ + (`#2 - "80) * "1000
+ + (`#3 - "80) * "40
+ + `#4 - "80
+ }
+ }
+\tl_new:N \l_text_accents_tl
+\tl_new:N \l_text_letterlike_tl
+\tl_new:N \l_text_case_exclude_arg_tl
+\tl_set:Ne \l_text_case_exclude_arg_tl
+ {
+ \exp_not:n { \begin \cite \end \label \ref }
+ \exp_not:c { cite ~ }
+ \exp_not:n { \babelshorthand }
+ }
+\tl_new:N \l_text_math_arg_tl
+\tl_set:Nn \l_text_math_arg_tl { \ensuremath }
+\tl_new:N \l_text_math_delims_tl
+\tl_set:Nn \l_text_math_delims_tl { $ $ \( \) }
+\tl_new:N \l_text_expand_exclude_tl
+\tl_set:Nn \l_text_expand_exclude_tl
+ { \begin \cite \end \label \ref }
+\bool_lazy_and:nnT
+ { \str_if_eq_p:Vn \fmtname { LaTeX2e } }
+ { \tl_if_exist_p:N \@expl@finalise@setup@@ }
+ {
+ \tl_gput_right:Nn \@expl@finalise@setup@@
+ {
+ \tl_gput_right:Nn \@kernel@after@begindocument
+ {
+ \group_begin:
+ \cs_set_protected:Npn \__text_tmp:w #1
+ {
+ \tl_clear:N \l_text_expand_exclude_tl
+ \tl_map_inline:nn {#1}
+ {
+ \bool_lazy_any:nF
+ {
+ { \token_if_protected_macro_p:N ##1 }
+ { \token_if_protected_long_macro_p:N ##1 }
+ {
+ \str_if_eq_p:ee
+ { \cs_replacement_spec:N ##1 }
+ { \exp_not:n { \protect ##1 } \c_space_tl }
+ }
+ }
+ { \tl_put_right:Nn \l_text_expand_exclude_tl {##1} }
+ }
+ }
+ \exp_args:NV \__text_tmp:w \l_text_expand_exclude_tl
+ \exp_args:NNNV \group_end:
+ \tl_set:Nn \l_text_expand_exclude_tl \l_text_expand_exclude_tl
+ }
+ }
+ }
+\tl_new:N \l__text_math_mode_tl
+\tex_global:D \tex_chardef:D \c__text_chardef_space_token = `\ %
+\tex_global:D \tex_mathchardef:D \c__text_mathchardef_space_token = `\ %
+\tex_global:D \tex_chardef:D \c__text_chardef_group_begin_token = `\{ % `\}
+\tex_global:D \tex_mathchardef:D \c__text_mathchardef_group_begin_token = `\{ % `\} `\{
+\tex_global:D \tex_chardef:D \c__text_chardef_group_end_token = `\} % `\{
+\tex_global:D \tex_mathchardef:D \c__text_mathchardef_group_end_token = `\} %
+\cs_new:Npn \text_expand:n #1
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \exp:w
+ \__text_expand:n {#1}
+ }
+ }
+\cs_new:Npn \__text_expand:n #1
+ {
+ \group_align_safe_begin:
+ \__text_expand_loop:w #1
+ \s__text_recursion_tail \s__text_recursion_stop
+ \__text_expand_result:n { }
+ }
+\cs_new:Npn \__text_expand_store:n #1
+ { \__text_expand_store:nw {#1} }
+\cs_generate_variant:Nn \__text_expand_store:n { o }
+\cs_new:Npn \__text_expand_store:nw #1#2 \__text_expand_result:n #3
+ { #2 \__text_expand_result:n { #3 #1 } }
+\cs_new:Npn \__text_expand_end:w #1 \__text_expand_result:n #2
+ {
+ \group_align_safe_end:
+ \exp_end:
+ #2
+ }
+\cs_new:Npn \__text_expand_loop:w #1 \s__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \__text_expand_N_type:N }
+ {
+ \tl_if_head_is_group:nTF {#1}
+ { \__text_expand_group:n }
+ { \__text_expand_space:w }
+ }
+ #1 \s__text_recursion_stop
+ }
+\cs_new:Npn \__text_expand_group:n #1
+ {
+ \__text_expand_store:o
+ {
+ \exp_after:wN
+ {
+ \exp:w
+ \__text_expand:n {#1}
+ }
+ }
+ \__text_expand_loop:w
+ }
+\exp_last_unbraced:NNo \cs_new:Npn \__text_expand_space:w \c_space_tl
+ {
+ \__text_expand_store:n { ~ }
+ \__text_expand_loop:w
+ }
+\cs_new:Npn \__text_expand_N_type:N #1
+ {
+ \__text_if_s_recursion_tail_stop_do:Nn #1
+ { \__text_expand_end:w }
+ \exp_after:wN \__text_expand_math_search:NNN
+ \exp_after:wN #1 \l_text_math_delims_tl
+ \q__text_recursion_tail \q__text_recursion_tail
+ \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_expand_math_search:NNN #1#2#3
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #2
+ { \__text_expand_explicit:N #1 }
+ \token_if_eq_meaning:NNTF #1 #2
+ {
+ \__text_use_i_delimit_by_q_recursion_stop:nw
+ {
+ \__text_expand_store:n {#1}
+ \__text_expand_math_loop:Nw #3
+ }
+ }
+ { \__text_expand_math_search:NNN #1 }
+ }
+\cs_new:Npn \__text_expand_math_loop:Nw #1#2 \s__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#2}
+ { \__text_expand_math_N_type:NN }
+ {
+ \tl_if_head_is_group:nTF {#2}
+ { \__text_expand_math_group:Nn }
+ { \__text_expand_math_space:Nw }
+ }
+ #1#2 \s__text_recursion_stop
+ }
+\cs_new:Npn \__text_expand_math_N_type:NN #1#2
+ {
+ \__text_if_s_recursion_tail_stop_do:Nn #2
+ { \__text_expand_end:w }
+ \token_if_eq_meaning:NNF #2 \exp_not:N
+ { \__text_expand_store:n {#2} }
+ \token_if_eq_meaning:NNTF #2 #1
+ { \__text_expand_loop:w }
+ { \__text_expand_math_loop:Nw #1 }
+ }
+\cs_new:Npn \__text_expand_math_group:Nn #1#2
+ {
+ \__text_expand_store:n { {#2} }
+ \__text_expand_math_loop:Nw #1
+ }
+\exp_after:wN \cs_new:Npn \exp_after:wN \__text_expand_math_space:Nw
+ \exp_after:wN # \exp_after:wN 1 \c_space_tl
+ {
+ \__text_expand_store:n { ~ }
+ \__text_expand_math_loop:Nw #1
+ }
+\cs_new:Npn \__text_expand_explicit:N #1
+ {
+ \token_if_cs:NTF #1
+ { \__text_expand_exclude:N #1 }
+ {
+ \bool_lazy_and:nnTF
+ { \token_if_active_p:N #1 }
+ {
+ ! \bool_lazy_any_p:n
+ {
+ { \token_if_protected_macro_p:N #1 }
+ { \token_if_protected_long_macro_p:N #1 }
+ { \tl_if_head_eq_meaning_p:oN {#1} \UTFviii@two@octets }
+ { \tl_if_head_eq_meaning_p:oN {#1} \UTFviii@three@octets }
+ { \tl_if_head_eq_meaning_p:oN {#1} \UTFviii@four@octets }
+ { \tl_if_head_eq_meaning_p:oN {#1} \active@prefix }
+ }
+ }
+ { \exp_after:wN \__text_expand_loop:w #1 }
+ {
+ \__text_expand_store:n {#1}
+ \__text_expand_loop:w
+ }
+ }
+ }
+\cs_new:Npn \__text_expand_exclude:N #1
+ {
+ \cs_if_eq:NNTF #1 \text_case_switch:nnnn
+ { \__text_expand_exclude_switch:Nnnnn #1 }
+ {
+ \exp_args:Ne \__text_expand_exclude:nN
+ {
+ \exp_not:V \l_text_math_arg_tl
+ \exp_not:V \l_text_expand_exclude_tl
+ \exp_not:V \l_text_case_exclude_arg_tl
+ }
+ #1
+ }
+ }
+\cs_new:Npn \__text_expand_exclude_switch:Nnnnn #1#2#3#4#5
+ {
+ \__text_expand_store:n { #1 {#2} {#3} {#4} {#5} }
+ \__text_expand_loop:w
+ }
+\cs_new:Npn \__text_expand_exclude:nN #1#2
+ {
+ \__text_expand_exclude:NN #2 #1
+ \q__text_recursion_tail \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_expand_exclude:NN #1#2
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #2
+ { \__text_expand_accent:N #1 }
+ \str_if_eq:nnTF {#1} {#2}
+ {
+ \__text_use_i_delimit_by_q_recursion_stop:nw
+ { \__text_expand_exclude:Nw #1 }
+ }
+ { \__text_expand_exclude:NN #1 }
+ }
+\cs_new:Npn \__text_expand_exclude:Nw #1#2#
+ { \__text_expand_exclude:Nnn #1 {#2} }
+\cs_new:Npn \__text_expand_exclude:Nnn #1#2#3
+ {
+ \__text_expand_store:n { #1#2 {#3} }
+ \__text_expand_loop:w
+ }
+\cs_new:Npn \__text_expand_accent:N #1
+ {
+ \exp_after:wN \__text_expand_accent:NN \exp_after:wN
+ #1 \l_text_accents_tl
+ \q__text_recursion_tail \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_expand_accent:NN #1#2
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #2
+ { \__text_expand_letterlike:N #1 }
+ \cs_if_eq:NNTF #2 #1
+ {
+ \__text_use_i_delimit_by_q_recursion_stop:nw
+ {
+ \__text_expand_store:n {#1}
+ \__text_expand_loop:w
+ }
+ }
+ { \__text_expand_accent:NN #1 }
+ }
+\cs_new:Npn \__text_expand_letterlike:N #1
+ {
+ \exp_after:wN \__text_expand_letterlike:NN \exp_after:wN
+ #1 \l_text_letterlike_tl
+ \q__text_recursion_tail \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_expand_letterlike:NN #1#2
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #2
+ { \__text_expand_cs:N #1 }
+ \cs_if_eq:NNTF #2 #1
+ {
+ \__text_use_i_delimit_by_q_recursion_stop:nw
+ {
+ \__text_expand_store:n {#1}
+ \__text_expand_loop:w
+ }
+ }
+ { \__text_expand_letterlike:NN #1 }
+ }
+\cs_new:Npe \__text_expand_cs:N #1
+ {
+ \exp_not:N \str_if_eq:nnTF {#1} { \exp_not:N \protect }
+ { \exp_not:N \__text_expand_protect:w }
+ {
+ \bool_lazy_and:nnTF
+ { \cs_if_exist_p:N \fmtname }
+ { \str_if_eq_p:Vn \fmtname { LaTeX2e } }
+ { \exp_not:N \__text_expand_testopt:N #1 }
+ { \exp_not:N \__text_expand_replace:N #1 }
+ }
+ }
+\cs_new:Npn \__text_expand_protect:w #1 \s__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \__text_expand_protect:N }
+ {
+ \__text_expand_store:n { \protect }
+ \__text_expand_loop:w
+ }
+ #1 \s__text_recursion_stop
+ }
+\cs_new:Npn \__text_expand_protect:N #1
+ {
+ \__text_if_s_recursion_tail_stop_do:Nn #1
+ {
+ \__text_expand_store:n { \protect }
+ \__text_expand_end:w
+ }
+ \exp_args:Ne \__text_expand_protect:nN
+ { \cs_to_str:N #1 } #1
+ }
+\cs_new:Npn \__text_expand_protect:nN #1#2
+ { \__text_expand_protect:Nw #2 #1 \q__text_nil #1 ~ \q__text_nil \q__text_nil \s__text_stop }
+\cs_new:Npn \__text_expand_protect:Nw #1 #2 ~ \q__text_nil #3 \q__text_nil #4 \s__text_stop
+ {
+ \__text_quark_if_nil:nTF {#4}
+ {
+ \cs_if_exist:cTF {#2}
+ { \exp_args:Ne \__text_expand_store:n { \exp_not:c {#2} } }
+ { \__text_expand_store:n { \protect #1 } }
+ }
+ { \__text_expand_store:n { \protect #1 } }
+ \__text_expand_loop:w
+ }
+\cs_new:Npn \__text_expand_testopt:N #1
+ {
+ \token_if_eq_meaning:NNTF #1 \@protected@testopt
+ { \__text_expand_testopt:NNn }
+ { \__text_expand_encoding:N #1 }
+ }
+\cs_new:Npn \__text_expand_testopt:NNn #1#2#3
+ {
+ \__text_expand_store:n {#1}
+ \__text_expand_loop:w
+ }
+\cs_new:Npn \__text_expand_encoding:N #1
+ {
+ \bool_lazy_or:nnTF
+ { \cs_if_eq_p:NN #1 \@current@cmd }
+ { \cs_if_eq_p:NN #1 \@changed@cmd }
+ { \exp_after:wN \__text_expand_loop:w \__text_expand_encoding_escape:NN }
+ { \__text_expand_replace:N #1 }
+ }
+\cs_new:Npn \__text_expand_encoding_escape:NN #1#2 { \exp_not:n {#1} }
+\cs_new:Npn \__text_expand_replace:N #1
+ {
+ \bool_lazy_and:nnTF
+ { \cs_if_exist_p:c { l__text_expand_ \token_to_str:N #1 _tl } }
+ {
+ \bool_lazy_or_p:nn
+ { \token_if_cs_p:N #1 }
+ { \token_if_active_p:N #1 }
+ }
+ {
+ \exp_args:Nv \__text_expand_replace:n
+ { l__text_expand_ \token_to_str:N #1 _tl }
+ }
+ { \__text_expand_cs_expand:N #1 }
+ }
+\cs_new:Npn \__text_expand_replace:n #1 { \__text_expand_loop:w #1 }
+\cs_new:Npn \__text_expand_cs_expand:N #1
+ {
+ \__text_if_expandable:NTF #1
+ {
+ \token_if_eq_meaning:NNTF #1 \exp_not:n
+ { \__text_expand_unexpanded:w }
+ { \exp_after:wN \__text_expand_loop:w #1 }
+ }
+ {
+ \__text_expand_store:n {#1}
+ \__text_expand_loop:w
+ }
+ }
+\cs_new:Npn \__text_expand_unexpanded:w
+ {
+ \exp_after:wN \__text_expand_unexpanded_test:w
+ \exp:w \exp_end_continue_f:w
+ }
+\cs_new:Npn \__text_expand_unexpanded_test:w #1 \s__text_recursion_stop
+ {
+ \tl_if_head_is_group:nTF {#1}
+ { \__text_expand_unexpanded:n }
+ {
+ \__text_expand_unexpanded:w
+ \tl_if_head_is_N_type:nT {#1} { \__text_expand_unexpanded:N }
+ }
+ #1 \s__text_recursion_stop
+ }
+\cs_new:Npn \__text_expand_unexpanded:N #1
+ {
+ \exp_after:wN \if_meaning:w \exp_not:N #1 #1
+ \else:
+ \exp_after:wN #1
+ \fi:
+ }
+\cs_new:Npn \__text_expand_unexpanded:n #1
+ {
+ \__text_expand_store:n {#1}
+ \__text_expand_loop:w
+ }
+\cs_new_protected:Npn \text_declare_expand_equivalent:Nn #1#2
+ {
+ \tl_clear_new:c { l__text_expand_ \token_to_str:N #1 _tl }
+ \tl_set:cn { l__text_expand_ \token_to_str:N #1 _tl } {#2}
+ }
+\cs_generate_variant:Nn \text_declare_expand_equivalent:Nn { c }
+\tl_map_inline:nn
+ { \` \' \^ \~ \= \u \. \" \r \H \v \d \c \k \b \t }
+ { \text_declare_expand_equivalent:Nn #1 { \exp_not:n {#1} } }
+\tl_map_inline:nn
+ {
+ \AA \aa
+ \AE \ae
+ \DH \dh
+ \DJ \dj
+ \IJ \ij
+ \L \l
+ \NG \ng
+ \O \o
+ \OE \oe
+ \SS \ss
+ \TH \th
+ }
+ { \text_declare_expand_equivalent:Nn #1 { \exp_not:n {#1} } }
+%% File: l3text-case.dtx
+\bool_new:N \l_text_titlecase_check_letter_bool
+\bool_set_true:N \l_text_titlecase_check_letter_bool
+\cs_new:Npn \text_lowercase:n #1
+ { \__text_change_case:nnn { lower } { } {#1} }
+\cs_new:Npn \text_uppercase:n #1
+ { \__text_change_case:nnn { upper } { } {#1} }
+\cs_new:Npn \text_titlecase_all:n #1
+ { \__text_change_case:nnn { title } { } {#1} }
+\cs_new:Npn \text_titlecase_first:n #1
+ { \__text_change_case:nnnn { title } { break } { } {#1} }
+\cs_new:Npn \text_lowercase:nn #1#2
+ { \__text_change_case:nnn { lower } {#1} {#2} }
+\cs_new:Npn \text_uppercase:nn #1#2
+ { \__text_change_case:nnn { upper } {#1} {#2} }
+\cs_new:Npn \text_titlecase_all:nn #1#2
+ { \__text_change_case:nnn { title } {#1} {#2} }
+\cs_new:Npn \text_titlecase_first:nn #1#2
+ { \__text_change_case:nnnn { title } { break } {#1} {#2} }
+\cs_new:Npn \__text_change_case:nnn #1#2#3
+ { \__text_change_case:nnnn {#1} {#1} {#2} {#3} }
+\cs_new:Npn \__text_change_case:nnnn #1#2#3#4
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \exp:w
+ \exp_args:Ne \__text_change_case_auxi:nnnn
+ { \text_expand:n {#4} }
+ {#1} {#2} {#3}
+ }
+ }
+\cs_new:Npn \__text_change_case_auxi:nnnn #1#2#3#4
+ {
+ \exp_args:No \__text_change_case_BCP:nnnn
+ { \tl_to_str:n {#4} } {#1} {#2} {#3}
+ }
+\cs_new:Npe \__text_change_case_BCP:nnnn #1#2#3#4
+ {
+ \exp_not:N \__text_change_case_BCP:nnnw
+ {#2} {#3} {#4} #1 \tl_to_str:n { -x- -x- } \exp_not:N \q__text_stop
+ }
+\use:e
+ {
+ \cs_new:Npn \exp_not:N \__text_change_case_BCP:nnnw
+ #1#2#3#4 \tl_to_str:n { -x- } #5 \tl_to_str:n { -x- } #6
+ \exp_not:N \q__text_stop
+ }
+ { \__text_change_case_BCP:nnnnnw {#1} {#2} {#3} {#5} {#4} #4 - \q__text_stop }
+\cs_new:Npn \__text_change_case_BCP:nnnnnw #1#2#3#4#5#6 - #7 \q__text_stop
+ {
+ \bool_lazy_or:nnTF
+ { \cs_if_exist_p:c { __text_change_case_ #2 _ #6 -x- #4 :nnnnn } }
+ { \tl_if_exist_p:c { l__text_ #2 case_special_ #6 -x- #4 _tl } }
+ { \__text_change_case_auxii:nnnn {#1} {#2} {#3} { #6 -x- #4 } }
+ {
+ \cs_if_exist:cTF { __text_change_case_ #2 _ #6 :nnnnn }
+ { \__text_change_case_auxii:nnnn {#1} {#2} {#3} {#6} }
+ { \__text_change_case_auxii:nnnn {#1} {#2} {#3} {#5} }
+ }
+ }
+\cs_new:Npn \__text_change_case_auxii:nnnn #1#2#3#4
+ {
+ \group_align_safe_begin:
+ \cs_if_exist_use:c { __text_change_case_boundary_ #2 _ #4 :Nnnnw }
+ \__text_change_case_loop:nnnw {#2} {#3} {#4} #1
+ \q__text_recursion_tail \q__text_recursion_stop
+ \__text_change_case_result:n { }
+ }
+\cs_new:Npn \__text_change_case_store:n #1
+ { \__text_change_case_store:nw {#1} }
+\cs_generate_variant:Nn \__text_change_case_store:n { o , e , V , v }
+\cs_new:Npn \__text_change_case_store:nw #1#2 \__text_change_case_result:n #3
+ { #2 \__text_change_case_result:n { #3 #1 } }
+\cs_new:Npn \__text_change_case_end:w #1 \__text_change_case_result:n #2
+ {
+ \group_align_safe_end:
+ \exp_end:
+ #2
+ }
+\cs_new:Npn \__text_change_case_loop:nnnw #1#2#3#4 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#4}
+ { \__text_change_case_N_type:nnnN }
+ {
+ \tl_if_head_is_group:nTF {#4}
+ { \use:c { __text_change_case_group_ #1 :nnnn } }
+ { \__text_change_case_space:nnnw }
+ }
+ {#1} {#2} {#3} #4 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_change_case_break:w
+ { \__text_change_case_break_aux:w \prg_do_nothing: }
+\cs_new:Npn \__text_change_case_break_aux:w
+ #1 \q__text_recursion_tail \q__text_recursion_stop
+ {
+ \__text_change_case_store:o {#1}
+ \__text_change_case_end:w
+ }
+\cs_new:Npn \__text_change_case_group_lower:nnnn #1#2#3#4
+ {
+ \__text_change_case_store:o
+ {
+ \exp_after:wN
+ {
+ \exp:w
+ \__text_change_case_auxii:nnnn {#4} {#1} {#2} {#3}
+ }
+ }
+ \__text_change_case_loop:nnnw {#1} {#2} {#3}
+ }
+\cs_new_eq:NN \__text_change_case_group_upper:nnnn
+ \__text_change_case_group_lower:nnnn
+\cs_new:Npn \__text_change_case_group_title:nnnn #1#2#3#4
+ {
+ \__text_change_case_store:o
+ {
+ \exp_after:wN
+ {
+ \exp:w
+ \__text_change_case_auxii:nnnn {#4} {#1} {#2} {#3}
+ }
+ }
+ \__text_change_case_skip:nnw {#2} {#3}
+ }
+\use:e
+ {
+ \cs_new:Npn \exp_not:N \__text_change_case_space:nnnw #1#2#3 \c_space_tl
+ }
+ {
+ \__text_change_case_store:n { ~ }
+ \cs_if_exist_use:cF { __text_change_case_space_ #2 :nnn }
+ {
+ \cs_if_exist_use:c { __text_change_case_boundary_ #1 _ #3 :Nnnnw }
+ \__text_change_case_loop:nnnw
+ }
+ {#2} {#2} {#3}
+ }
+\cs_new:Npn \__text_change_case_space_break:nnn #1#2#3
+ { \__text_change_case_break:w }
+\cs_new:Npn \__text_change_case_N_type:nnnN #1#2#3#4
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #4
+ { \__text_change_case_end:w }
+ \__text_change_case_N_type_aux:nnnN {#1} {#2} {#3} #4
+ }
+\cs_new:Npn \__text_change_case_N_type_aux:nnnN #1#2#3#4
+ {
+ \exp_args:NV \__text_change_case_N_type:nnnnN
+ \l_text_math_delims_tl {#1} {#2} {#3} #4
+ }
+\cs_new:Npn \__text_change_case_N_type:nnnnN #1#2#3#4#5
+ {
+ \__text_change_case_math_search:nnnNNN {#2} {#3} {#4} #5 #1
+ \q__text_recursion_tail \q__text_recursion_tail
+ \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_change_case_math_search:nnnNNN #1#2#3#4#5#6
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #5
+ { \__text_change_case_cs_check:nnnN {#1} {#2} {#3} #4 }
+ \token_if_eq_meaning:NNTF #4 #5
+ {
+ \__text_use_i_delimit_by_q_recursion_stop:nw
+ {
+ \__text_change_case_store:n {#4}
+ \__text_change_case_math_loop:nnnNw {#1} {#2} {#3} #6
+ }
+ }
+ { \__text_change_case_math_search:nnnNNN {#1} {#2} {#3} #4 }
+ }
+\cs_new:Npn \__text_change_case_math_loop:nnnNw #1#2#3#4#5 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#5}
+ { \__text_change_case_math_N_type:nnnNN }
+ {
+ \tl_if_head_is_group:nTF {#5}
+ { \__text_change_case_math_group:nnnNn }
+ { \__text_change_case_math_space:nnnNw }
+ }
+ {#1} {#2} {#3} #4 #5 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_change_case_math_N_type:nnnNN #1#2#3#4#5
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #5
+ { \__text_change_case_end:w }
+ \__text_change_case_store:n {#5}
+ \token_if_eq_meaning:NNTF #5 #4
+ { \__text_change_case_loop:nnnw {#1} {#2} {#3} }
+ { \__text_change_case_math_loop:nnnNw {#1} {#2} {#3} #4 }
+ }
+\cs_new:Npn \__text_change_case_math_group:nnnNn #1#2#3#4#5
+ {
+ \__text_change_case_store:n { {#5} }
+ \__text_change_case_math_loop:nnnNw {#1} {#2} {#3} #4
+ }
+\use:e
+ {
+ \cs_new:Npn \exp_not:N \__text_change_case_math_space:nnnNw #1#2#3#4
+ \c_space_tl
+ }
+ {
+ \__text_change_case_store:n { ~ }
+ \__text_change_case_math_loop:nnnNw {#1} {#2} {#3} #4
+ }
+\cs_new:Npn \__text_change_case_cs_check:nnnN #1#2#3#4
+ {
+ \token_if_cs:NTF #4
+ { \__text_change_case_exclude:nnnN {#1} {#2} {#3} }
+ {
+ \__text_codepoint_process:nN
+ { \use:c { __text_change_case_custom_ #1 :nnnn } {#1} {#2} {#3} }
+ }
+ #4
+ }
+\cs_new:Npn \__text_change_case_exclude:nnnN #1#2#3#4
+ {
+ \exp_args:Ne \__text_change_case_exclude:nnnnN
+ {
+ \exp_not:V \l_text_math_arg_tl
+ \exp_not:V \l_text_case_exclude_arg_tl
+ }
+ {#1} {#2} {#3} #4
+ }
+\cs_new:Npn \__text_change_case_exclude:nnnnN #1#2#3#4#5
+ {
+ \__text_change_case_exclude:nnnNN {#2} {#3} {#4} #5 #1
+ \q__text_recursion_tail \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_change_case_exclude:nnnNN #1#2#3#4#5
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #5
+ { \__text_change_case_replace:nnnN {#1} {#2} {#3} #4 }
+ \str_if_eq:nnTF {#4} {#5}
+ {
+ \__text_use_i_delimit_by_q_recursion_stop:nw
+ { \__text_change_case_exclude:nnnNw {#1} {#2} {#3} #4 }
+ }
+ { \__text_change_case_exclude:nnnNN {#1} {#2} {#3} #4 }
+ }
+\cs_new:Npn \__text_change_case_exclude:nnnNw #1#2#3#4#5#
+ { \__text_change_case_exclude:nnnNnn {#1} {#2} {#3} {#4} {#5} }
+\cs_new:Npn \__text_change_case_exclude:nnnNnn #1#2#3#4#5#6
+ {
+ \tl_if_blank:nTF {#5}
+ { \__text_change_case_store:n { #4 {#6} } }
+ {
+ \__text_change_case_store:o
+ {
+ \exp_after:wN #4
+ \exp:w \__text_change_case_auxii:nnnn {#5} {#1} {#2} {#3}
+ {#6}
+ }
+ }
+ \__text_change_case_loop:nnnw {#1} {#2} {#3}
+ }
+\cs_new:Npn \__text_change_case_replace:nnnN #1#2#3#4
+ {
+ \cs_if_exist:cTF { l__text_case_ \token_to_str:N #4 _tl }
+ {
+ \__text_change_case_replace:vnnn
+ { l__text_case_ \token_to_str:N #4 _tl } {#1} {#2} {#3}
+ }
+ { \__text_change_case_switch:nnnN {#1} {#2} {#3} #4 }
+ }
+\cs_new:Npn \__text_change_case_replace:nnnn #1#2#3#4
+ { \__text_change_case_loop:nnnw {#2} {#3} {#4} #1 }
+\cs_generate_variant:Nn \__text_change_case_replace:nnnn { v }
+\cs_new:Npn \__text_change_case_switch:nnnN #1#2#3#4
+ {
+ \cs_if_eq:NNTF #4 \text_case_switch:nnnn
+ { \use:c { __text_change_case_switch_ #1 :nnnNnnnn } }
+ { \use:c { __text_change_case_letterlike_ #1 :nnnN } }
+ {#1} {#2} {#3} #4
+ }
+\cs_new:Npn \__text_change_case_switch_lower:nnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \__text_change_case_store:n {#7}
+ \__text_change_case_loop:nnnw {#1} {#2} {#3}
+ }
+\cs_new:Npn \__text_change_case_switch_upper:nnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \__text_change_case_store:n {#6}
+ \__text_change_case_loop:nnnw {#1} {#2} {#3}
+ }
+\cs_new:Npn \__text_change_case_switch_title:nnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \__text_change_case_store:n {#8}
+ \__text_change_case_skip:nnw {#2} {#3}
+ }
+\cs_new:Npn \__text_change_case_skip:nnw #1#2#3 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#3}
+ { \__text_change_case_skip_N_type:nnN }
+ {
+ \tl_if_head_is_group:nTF {#3}
+ { \__text_change_case_skip_group:nnn }
+ { \__text_change_case_skip_space:nnw }
+ }
+ {#1} {#2} #3 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_change_case_skip_N_type:nnN #1#2#3
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #3
+ { \__text_change_case_end:w }
+ \__text_change_case_store:n {#3}
+ \__text_change_case_skip:nnw {#1} {#2}
+ }
+\cs_new:Npn \__text_change_case_skip_group:nnn #1#2#3
+ {
+ \__text_change_case_store:n { {#3} }
+ \__text_change_case_skip:nnw {#1} {#2}
+ }
+\cs_new:Npn \__text_change_case_skip_space:nnw #1#2
+ { \__text_change_case_space:nnnw {#1} {#1} {#2} }
+\cs_new:Npn \__text_change_case_letterlike_lower:nnnN #1#2#3#4
+ { \__text_change_case_letterlike:nnnnnN {#1} {#1} {#1} {#2} {#3} #4 }
+\cs_new_eq:NN \__text_change_case_letterlike_upper:nnnN
+ \__text_change_case_letterlike_lower:nnnN
+\cs_new:Npn \__text_change_case_letterlike_title:nnnN #1#2#3#4
+ { \__text_change_case_letterlike:nnnnnN { upper } { end } {#1} {#2} {#3} #4 }
+\cs_new:Npn \__text_change_case_letterlike:nnnnnN #1#2#3#4#5#6
+ {
+ \cs_if_exist:cTF { c__text_ #1 case_ \token_to_str:N #6 _tl }
+ {
+ \__text_change_case_store:v
+ { c__text_ #1 case_ \token_to_str:N #6 _tl }
+ \use:c { __text_change_case_next_ #2 :nnn } {#2} {#4} {#5}
+ }
+ {
+ \__text_change_case_store:n {#6}
+ \cs_if_exist:cTF
+ {
+ c__text_
+ \str_if_eq:nnTF {#1} { lower } { upper } { lower }
+ case_ \token_to_str:N #6 _tl
+ }
+ { \use:c { __text_change_case_next_ #2 :nnn } {#2} {#4} {#5} }
+ { \__text_change_case_loop:nnnw {#3} {#4} {#5} }
+ }
+ }
+\cs_new:Npn \__text_change_case_custom_lower:nnnn #1#2#3#4
+ {
+ \__text_change_case_custom:nnnnnn {#1} {#1} {#2} {#3} {#4}
+ { \use:c { __text_change_case_codepoint_ #1 :nnnn } {#1} {#2} {#3} {#4} }
+ }
+\cs_new_eq:NN \__text_change_case_custom_upper:nnnn
+ \__text_change_case_custom_lower:nnnn
+\cs_new:Npn \__text_change_case_custom_title:nnnn #1#2#3#4
+ {
+ \__text_change_case_custom:nnnnnn { title } {#1} {#2} {#3} {#4}
+ {
+ \__text_change_case_custom:nnnnnn { upper } {#1} {#2} {#3} {#4}
+ { \use:c { __text_change_case_codepoint_ #1 :nnnn } {#1} {#2} {#3} {#4} }
+ }
+ }
+\cs_new:Npn \__text_change_case_custom:nnnnnn #1#2#3#4#5#6
+ {
+ \tl_if_exist:cTF { l__text_ #1 case _ \tl_to_str:n {#5} _ #4 _tl }
+ {
+ \__text_change_case_replace:vnnn
+ { l__text_ #1 case _ \tl_to_str:n {#5} _ #4 _tl } {#2} {#3} {#4}
+ }
+ {
+ \tl_if_exist:cTF { l__text_ #1 case _ \tl_to_str:n {#5} _tl }
+ {
+ \__text_change_case_replace:vnnn
+ { l__text_ #1 case _ \tl_to_str:n {#5} _tl } {#2} {#3} {#4}
+ }
+ {#6}
+ }
+ }
+\cs_new:Npn \__text_change_case_codepoint_lower:nnnn #1#2#3#4
+ {
+ \cs_if_exist_use:cF { __text_change_case_lower_ #3 :nnnnn }
+ { \__text_change_case_lower_sigma:nnnnn }
+ {#1} {#1} {#2} {#3} {#4}
+ }
+\cs_new:Npn \__text_change_case_codepoint_upper:nnnn #1#2#3#4
+ {
+ \cs_if_exist_use:cF { __text_change_case_upper_ #3 :nnnnn }
+ { \__text_change_case_codepoint:nnnnn }
+ {#1} {#1} {#2} {#3} {#4}
+ }
+\cs_new:Npn \__text_change_case_lower_sigma:nnnnn #1#2#3#4#5
+ {
+ \__text_codepoint_compare:nNnTF {#5} = { "03A3 }
+ { \__text_change_case_lower_sigma:nnnnw {#2} }
+ { \__text_change_case_codepoint:nnnnn {#1} {#2} }
+ {#3} {#4} {#5}
+ }
+\cs_new:Npn \__text_change_case_lower_sigma:nnnnw #1#2#3#4#5 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#5}
+ { \__text_change_case_lower_sigma:nnnnN {#4} }
+ {
+ \__text_change_case_store:e
+ { \codepoint_generate:nn { "03C2 } { \__text_char_catcode:N #4 } }
+ \__text_change_case_loop:nnnw
+ }
+ {#1} {#2} {#3} #5 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_change_case_lower_sigma:nnnnN #1#2#3#4#5
+ {
+ \__text_change_case_store:e
+ {
+ \bool_lazy_or:nnTF
+ { \token_if_letter_p:N #5 }
+ {
+ \bool_lazy_and_p:nn
+ { \token_if_active_p:N #5 }
+ { \int_compare_p:nNn {`#5} > { "80 } }
+ }
+ { \codepoint_generate:nn { "03C3 } { \__text_char_catcode:N #1 } }
+ { \codepoint_generate:nn { "03C2 } { \__text_char_catcode:N #1 } }
+ }
+ \__text_change_case_loop:nnnw {#2} {#3} {#4} #5
+ }
+\cs_new:Npn \__text_change_case_codepoint_title:nnnn #1#2#3#4
+ {
+ \bool_if:NTF \l_text_titlecase_check_letter_bool
+ {
+ \exp_args:Ne \__text_change_case_codepoint_title_auxi:nnnn
+ {
+ \codepoint_to_category:n
+ { \__text_codepoint_from_chars:Nw #4 }
+ }
+ }
+ { \__text_change_case_codepoint_title:nnn }
+ {#2} {#3} {#4}
+ }
+\cs_new:Npn \__text_change_case_codepoint_title_auxi:nnnn #1#2#3#4
+ {
+ \tl_if_head_eq_charcode:nNTF {#1} { L }
+ { \__text_change_case_codepoint_title:nnn }
+ { \__text_change_case_codepoint_title_auxii:nnnn { title } }
+ {#2} {#3} {#4}
+ }
+\cs_new:Npn \__text_change_case_codepoint_title:nnn #1#2#3
+ { \__text_change_case_codepoint_title_auxii:nnnn { end } {#1} {#2} {#3} }
+\cs_new:Npn \__text_change_case_codepoint_title_auxii:nnnn #1#2#3#4
+ {
+ \cs_if_exist_use:cF { __text_change_case_title_ #3 :nnnnn }
+ {
+ \cs_if_exist_use:cF { __text_change_case_upper_ #3 :nnnnn }
+ { \__text_change_case_codepoint:nnnnn }
+ }
+ { title } {#1} {#2} {#3} {#4}
+ }
+\cs_new:Npn \__text_change_case_codepoint:nnnnn #1#2#3#4#5
+ {
+ \bool_lazy_and:nnTF
+ { \tl_if_single_p:n {#5} }
+ { \token_if_active_p:N #5 }
+ { \__text_change_case_store:n {#5} }
+ {
+ \__text_change_case_store:e
+ { \__text_change_case_codepoint:nn {#1} {#5} }
+ }
+ \use:c { __text_change_case_next_ #2 :nnn } {#2} {#3} {#4}
+ }
+\cs_new:Npn \__text_change_case_codepoint:nn #1#2
+ {
+ \__text_change_case_codepoint:fnn
+ { \int_eval:n { \__text_codepoint_from_chars:Nw #2 } } {#1} {#2}
+ }
+\cs_new:Npn \__text_change_case_codepoint:nnn #1#2#3
+ {
+ \exp_args:Ne \__text_change_case_codepoint_aux:nn
+ { \__kernel_codepoint_case:nn { #2 case } {#1} } {#3}
+ }
+\cs_generate_variant:Nn \__text_change_case_codepoint:nnn { f }
+\sys_if_engine_ptex:T
+ {
+ \cs_new_eq:NN \__text_change_case_codepoint_aux:nnn
+ \__text_change_case_codepoint:nnn
+ \cs_gset:Npn \__text_change_case_codepoint:nnn #1#2#3
+ {
+ \int_compare:nNnTF {#1} = { -1 }
+ { \exp_not:n {#3} }
+ { \__text_change_case_codepoint_aux:nnn {#1} {#2} {#3} }
+ }
+ }
+\cs_new:Npn \__text_change_case_codepoint_aux:nn #1#2
+ {
+ \use:e { \__text_change_case_codepoint_aux:nnnn #1 {#2} }
+ }
+\cs_new:Npn \__text_change_case_codepoint_aux:nnnn #1#2#3#4
+ {
+ \__text_codepoint_compare:nNnTF {#4} = {#1}
+ { \exp_not:n {#4} }
+ {
+ \codepoint_generate:nn {#1}
+ { \__text_change_case_catcode:nn {#4} {#1} }
+ \tl_if_blank:nF {#2}
+ {
+ \codepoint_generate:nn {#2}
+ { \char_value_catcode:n {#2} }
+ \tl_if_blank:nF {#3}
+ {
+ \codepoint_generate:nn {#3}
+ { \char_value_catcode:n {#3} }
+ }
+ }
+ }
+ }
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_new:Npn \__text_change_case_catcode:nn #1#2
+ { \__text_char_catcode:N #1 }
+ }
+ {
+ \cs_new:Npn \__text_change_case_catcode:nn #1#2
+ {
+ \__text_codepoint_compare:nNnTF {#1} < { "80 }
+ { \__text_char_catcode:N #1 }
+ {
+ \int_compare:nNnTF {#2} < { "80 }
+ { \char_value_catcode:n {#2} }
+ { 13 }
+ }
+ }
+ }
+\cs_new:Npn \__text_change_case_next_lower:nnn #1#2#3
+ { \__text_change_case_loop:nnnw {#1} {#2} {#3} }
+\cs_new_eq:NN \__text_change_case_next_upper:nnn
+ \__text_change_case_next_lower:nnn
+\cs_new_eq:NN \__text_change_case_next_title:nnn
+ \__text_change_case_next_lower:nnn
+\cs_new:Npn \__text_change_case_next_end:nnn #1#2#3
+ { \__text_change_case_skip:nnw {#2} {#3} }
+\cs_new_protected:Npn \text_declare_case_equivalent:Nn #1#2
+ {
+ \tl_clear_new:c { l__text_case_ \token_to_str:N #1 _tl }
+ \tl_set:cn { l__text_case_ \token_to_str:N #1 _tl } {#2}
+ }
+\cs_new_protected:Npn \text_declare_lowercase_mapping:nn #1#2
+ { \__text_declare_case_mapping:nnn { lower } {#1} {#2} }
+\cs_new_protected:Npn \text_declare_titlecase_mapping:nn #1#2
+ { \__text_declare_case_mapping:nnn { title } {#1} {#2} }
+\cs_new_protected:Npn \text_declare_uppercase_mapping:nn #1#2
+ { \__text_declare_case_mapping:nnn { upper } {#1} {#2} }
+\cs_new_protected:Npn \__text_declare_case_mapping:nnn #1#2#3
+ {
+ \exp_args:Ne \__text_declare_case_mapping_aux:nnn
+ { \codepoint_str_generate:n {#2} } {#1} {#3}
+ }
+\cs_new_protected:Npn \__text_declare_case_mapping_aux:nnn #1#2#3
+ {
+ \tl_clear_new:c { l__text_ #2 case _ #1 _tl }
+ \tl_set:cn { l__text_ #2 case _ #1 _ tl } {#3}
+ }
+\cs_new_protected:Npn \text_declare_lowercase_mapping:nnn #1#2#3
+ { \__text_declare_case_mapping:nnnn { lower } {#1} {#2} {#3} }
+\cs_new_protected:Npn \text_declare_titlecase_mapping:nnn #1#2#3
+ { \__text_declare_case_mapping:nnnn { title } {#1} {#2} {#3} }
+\cs_new_protected:Npn \text_declare_uppercase_mapping:nnn #1#2#3
+ { \__text_declare_case_mapping:nnnn { upper } {#1} {#2} {#3} }
+\cs_new_protected:Npn \__text_declare_case_mapping:nnnn #1#2#3#4
+ {
+ \exp_args:Ne \__text_declare_case_mapping_aux:nnnn
+ { \codepoint_str_generate:n {#3} } {#1} {#2} {#4}
+ }
+\cs_new_protected:Npn \__text_declare_case_mapping_aux:nnnn #1#2#3#4
+ {
+ \tl_clear_new:c { l__text_ #2 case _ #1 _ #3 _tl }
+ \tl_set:cn { l__text_ #2 case _ #1 _ #3 _ tl } {#4}
+ \tl_clear_new:c { l__text_ #2 case_special_ #3 _tl }
+ }
+\cs_new:Npn \text_case_switch:nnnn #1#2#3#4
+ {
+ \__text_case_switch_marker:
+ #1
+ }
+\cs_new:Npn \__text_case_switch_marker: { }
+\cs_new:Npn \__text_change_case_generate:n #1
+ { \codepoint_generate:nn {#1} { \char_value_catcode:n {#1} } }
+\cs_new:cpn { __text_change_case_upper_de-x-eszett:nnnnn } #1#2#3#4#5
+ {
+ \__text_codepoint_compare:nNnTF {#5} = { "00DF }
+ {
+ \__text_change_case_store:e
+ {
+ \codepoint_generate:nn { "1E9E }
+ { \__text_change_case_catcode:nn {#5} { "1E9E } }
+ }
+ \use:c { __text_change_case_next_ #2 :nnn }
+ {#2} {#3} {#4}
+ }
+ { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+\cs_new_eq:cc { __text_change_case_upper_de-alt:nnnnn }
+ { __text_change_case_upper_de-x-eszett:nnnnn }
+\cs_new:Npn \__text_change_case_upper_el:nnnnn #1#2#3#4#5
+ {
+ \bool_lazy_and:nnTF
+ { \__text_change_case_if_greek_p:n {#5} }
+ {
+ ! \bool_lazy_or_p:nn
+ { \__text_codepoint_compare_p:nNn {#5} = { "0374 } }
+ { \__text_codepoint_compare_p:nNn {#5} = { "037E } }
+ }
+ {
+ \__text_change_case_if_greek_spacing_diacritic:nTF {#5}
+ {
+ \__text_change_case_store:n {#5}
+ \__text_change_case_loop:nnnw
+ }
+ {
+ \exp_args:Ne \__text_change_case_upper_el:nnnn
+ {
+ \codepoint_to_nfd:n
+ { \__text_codepoint_from_chars:Nw #5 }
+ }
+ }
+ {#2} {#3} {#4}
+ }
+ {
+ \__text_codepoint_compare:nNnTF {#5} = { "0345 }
+ {
+ \__text_change_case_store:e
+ {
+ \codepoint_generate:nn { "0399 }
+ { \char_value_catcode:n { "0399 } }
+ }
+ \__text_change_case_loop:nnnw {#2} {#3} {#4}
+ }
+ { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+ }
+\cs_new_eq:cN { __text_change_case_upper_el-x-iota:nnnnn }
+ \__text_change_case_upper_el:nnnnn
+\cs_new:Npn \__text_change_case_upper_el:nnnn #1#2#3#4
+ {
+ \__text_codepoint_process:nN
+ { \__text_change_case_upper_el:nnnnw {#2} {#3} {#4} } #1
+ }
+\cs_new:Npn \__text_change_case_upper_el:nnnnw #1#2#3#4#5 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#5}
+ { \__text_change_case_upper_el:nnnnN {#4} }
+ {
+ \__text_change_case_store:e
+ { \__text_change_case_codepoint:nn { upper } {#4} }
+ \__text_change_case_loop:nnnw
+ }
+ {#1} {#2} {#3} #5 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_change_case_upper_el:nnnnN #1#2#3#4#5
+ {
+ \token_if_cs:NTF #5
+ {
+ \__text_change_case_store:e
+ { \__text_change_case_codepoint:nn { upper } {#1} }
+ \__text_change_case_loop:nnnw {#2} {#3} {#4} #5
+ }
+ {
+ \__text_change_case_if_takes_ypogegrammeni:nTF {#1}
+ {
+ \__text_change_case_upper_el_ypogegrammeni:nnnnnnw
+ {#1} {#2} {#3} {#4} { } { } #5
+ }
+ { \__text_change_case_upper_el_aux:nnnnN {#1} {#2} {#3} {#4} #5 }
+ }
+ }
+\cs_new:Npn \__text_change_case_upper_el_ypogegrammeni:nnnnnnw
+ #1#2#3#4#5#6#7 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#7}
+ {
+ \__text_change_case_upper_el_ypogegrammeni:nnnnnnN
+ {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+ { \__text_change_case_upper_el_aux:nnnnN {#1} {#2} {#3} {#4} #5#6 }
+ #7 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_change_case_upper_el_ypogegrammeni:nnnnnnN #1#2#3#4#5#6#7
+ {
+ \token_if_cs:NTF #7
+ { \__text_change_case_upper_el_aux:nnnnN {#1} {#2} {#3} {#4} #5#6 }
+ {
+ \__text_codepoint_process:nN
+ {
+ \__text_change_case_upper_el_ypogegrammeni:nnnnnnn
+ {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+ }
+ #7
+ }
+\cs_new:Npn \__text_change_case_upper_el_ypogegrammeni:nnnnnnn #1#2#3#4#5#6#7
+ {
+ \__text_codepoint_compare:nNnTF {#7} = { "0345 }
+ {
+ \__text_change_case_upper_el_ypogegrammeni:nnnnnnw
+ {#1} {#2} {#3} {#4} {#5} {#7}
+ }
+ {
+ \bool_lazy_or:nnTF
+ { \__text_change_case_if_greek_accent_p:n {#7} }
+ { \__text_change_case_if_greek_breathing_p:n {#7} }
+ {
+ \__text_change_case_upper_el_ypogegrammeni:nnnnnnw
+ {#1} {#2} {#3} {#4} {#5#7} {#6}
+ }
+ { \__text_change_case_upper_el_aux:nnnnN {#1} {#2} {#3} {#4} #5#6 #7 }
+ }
+ }
+\cs_new:Npn \__text_change_case_upper_el_aux:nnnnN #1#2#3#4#5
+ {
+ \__text_codepoint_process:nN
+ { \__text_change_case_upper_el_aux:nnnnn {#1} {#2} {#3} {#4} } #5
+ }
+\cs_new:Npn \__text_change_case_upper_el_aux:nnnnn #1#2#3#4#5
+ {
+ \__text_codepoint_compare:nNnTF {#5} = { "0308 }
+ { \__text_change_case_upper_el_dialytika:nnnn {#2} {#3} {#4} {#1} }
+ {
+ \__text_change_case_if_greek_accent:nTF {#5}
+ { \__text_change_case_upper_el_hiatus:nnnnw {#2} {#3} {#4} {#1} }
+ {
+ \__text_change_case_if_greek_breathing:nTF {#5}
+ { \__text_change_case_upper_el:nnnn {#1} {#2} {#3} {#4} }
+ {
+ \__text_codepoint_compare:nNnTF {#5} = { "0345 }
+ {
+ \__text_change_case_store:e
+ { \use:c { __text_change_case_upper_ #4 _ypogegrammeni:n } {#1} }
+ \__text_change_case_loop:nnnw {#2} {#3} {#4}
+ }
+ {
+ \__text_change_case_if_greek_stress:nTF {#5}
+ {
+ \__text_change_case_store:e
+ { \__text_change_case_upper_el_stress:nn {#1} {#5} }
+ \__text_change_case_loop:nnnw {#2} {#3} {#4}
+
+ }
+ {
+ \__text_change_case_store:e
+ { \__text_change_case_codepoint:nn { upper } {#1} }
+ \__text_change_case_loop:nnnw {#2} {#3} {#4} #5
+ }
+ }
+ }
+ }
+ }
+ }
+\cs_new:Npn \__text_change_case_upper_el_dialytika:nnnn #1#2#3#4
+ {
+ \__text_change_case_if_takes_dialytika:nTF {#4}
+ { \__text_change_case_upper_el_dialytika:n {#4} }
+ {
+ \__text_change_case_store:e
+ { \__text_change_case_codepoint:nn { upper } {#4} }
+ }
+ \__text_change_case_upper_el_gobble:nnnw {#1} {#2} {#3}
+ }
+\cs_new:Npn \__text_change_case_upper_el_dialytika:n #1
+ {
+ \__text_change_case_store:e
+ {
+ \bool_lazy_or:nnTF
+ { \__text_codepoint_compare_p:nNn {#1} = { "0399 } }
+ { \__text_codepoint_compare_p:nNn {#1} = { "03B9 } }
+ {
+ \codepoint_generate:nn { "03AA }
+ { \__text_change_case_catcode:nn {#1} { "03AA } }
+ }
+ {
+ \codepoint_generate:nn { "03AB }
+ { \__text_change_case_catcode:nn {#1} { "03AB } }
+ }
+ }
+ }
+\cs_new:Npn \__text_change_case_upper_el_hiatus:nnnnw
+ #1#2#3#4#5 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#5}
+ { \__text_change_case_upper_el_hiatus:nnnnN {#4} }
+ {
+ \__text_change_case_store:e
+ { \__text_change_case_codepoint:nn { upper } {#4} }
+ \__text_change_case_loop:nnnw
+ }
+ {#1} {#2} {#3} #5 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_change_case_upper_el_hiatus:nnnnN #1#2#3#4#5
+ {
+ \token_if_cs:NTF #5
+ {
+ \__text_change_case_store:e
+ { \__text_change_case_codepoint:nn { upper } {#1} }
+ \__text_change_case_loop:nnnw {#2} {#3} {#4} #5
+ }
+ {
+ \__text_codepoint_process:nN
+ { \__text_change_case_upper_el_hiatus:nnnnn {#1} {#2} {#3} {#4} } #5
+ }
+ }
+\cs_new:Npn \__text_change_case_upper_el_hiatus:nnnnn #1#2#3#4#5
+ {
+ \__text_change_case_if_takes_dialytika:nTF {#5}
+ {
+ \__text_change_case_store:e
+ { \__text_change_case_codepoint:nn { upper } {#1} }
+ \__text_change_case_upper_el_dialytika:n {#5}
+ \__text_change_case_upper_el_gobble:nnnw {#2} {#3} {#4}
+ }
+ { \__text_change_case_upper_el:nnnn {#1} {#2} {#3} {#4} #5 }
+ }
+\cs_new:Npn \__text_change_case_upper_el_ypogegrammeni:n #1
+ {
+ \exp_args:Ne \__text_change_case_generate:n
+ {
+ \int_case:nn
+ { \__text_codepoint_from_chars:Nw #1 }
+ {
+ { "0391 } { "1FBC }
+ { "03B1 } { "1FBC }
+ { "0397 } { "1FCC }
+ { "03B7 } { "1FCC }
+ { "03A9 } { "1FFC }
+ { "03C9 } { "1FFC }
+ }
+ }
+ }
+\cs_new:cpn { __text_change_case_upper_el-x-iota_ypogegrammeni:n } #1
+ {
+ \__text_change_case_codepoint:nn { upper } {#1}
+ \codepoint_generate:nn { "0399 }
+ { \char_value_catcode:n { "0399 } }
+ }
+\cs_new:Npn \__text_change_case_upper_el_stress:nn #1#2
+ {
+ \exp_args:Ne \__text_change_case_generate:n
+ {
+ \int_case:nn
+ { \__text_codepoint_from_chars:Nw #2 }
+ {
+ { "0304 }
+ {
+ \int_case:nn { \__text_codepoint_from_chars:Nw #1 }
+ {
+ { "0391 } { "1FB9 }
+ { "03B1 } { "1FB9 }
+ { "0399 } { "1FD9 }
+ { "03B9 } { "1FD9 }
+ { "03A5 } { "1FE9 }
+ { "03C5 } { "1FE9 }
+ }
+ }
+ { "0306 }
+ {
+ \int_case:nn { \__text_codepoint_from_chars:Nw #1 }
+ {
+ { "0391 } { "1FB8 }
+ { "03B1 } { "1FB8 }
+ { "0399 } { "1FD8 }
+ { "03B9 } { "1FD8 }
+ { "03A5 } { "1FE8 }
+ { "03C5 } { "1FE8 }
+ }
+ }
+ }
+ }
+ }
+\cs_new:Npn \__text_change_case_upper_el_gobble:nnnw
+ #1#2#3#4 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#4}
+ { \__text_change_case_upper_el_gobble:nnnN }
+ { \__text_change_case_loop:nnnw }
+ {#1} {#2} {#3} #4 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_change_case_upper_el_gobble:nnnN #1#2#3#4
+ {
+ \token_if_cs:NTF #4
+ { \__text_change_case_loop:nnnw {#1} {#2} {#3} }
+ {
+ \__text_codepoint_process:nN
+ { \__text_change_case_upper_el_gobble:nnnn {#1} {#2} {#3} }
+ }
+ #4
+ }
+\cs_new:Npn \__text_change_case_upper_el_gobble:nnnn #1#2#3#4
+ {
+ \bool_lazy_or:nnTF
+ { \__text_change_case_if_greek_accent_p:n {#4} }
+ { \__text_change_case_if_greek_breathing_p:n {#4} }
+ { \__text_change_case_upper_el_gobble:nnnw {#1} {#2} {#3} }
+ { \__text_change_case_loop:nnnw {#1} {#2} {#3} #4 }
+ }
+\prg_new_conditional:Npnn \__text_change_case_if_greek:n #1 { p , TF }
+ {
+ \exp_args:Nf \__text_change_case_if_greek:n
+ { \int_eval:n { \__text_codepoint_from_chars:Nw #1 } }
+ }
+\cs_new:Npn \__text_change_case_if_greek:n #1
+ {
+ \if_int_compare:w #1 < "0370 \exp_stop_f:
+ \prg_return_false:
+ \else:
+ \if_int_compare:w #1 > "03FF \exp_stop_f:
+ \if_int_compare:w #1 < "1F00 \exp_stop_f:
+ \prg_return_false:
+ \else:
+ \if_int_compare:w #1 > "1FFF \exp_stop_f:
+ \if_int_compare:w #1 = "2126 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \else:
+ \prg_return_true:
+ \fi:
+ \fi:
+ \else:
+ \prg_return_true:
+ \fi:
+ \fi:
+ }
+\prg_new_conditional:Npnn \__text_change_case_if_greek_accent:n #1 { TF , p }
+ {
+ \exp_args:Nf \__text_change_case_if_greek_accent:n
+ { \int_eval:n { \__text_codepoint_from_chars:Nw #1 } }
+ }
+\cs_new:Npn \__text_change_case_if_greek_accent:n #1
+ {
+ \if_int_compare:w #1 = "0300 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "0301 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "0342 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "0302 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "0303 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "0311 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ }
+\prg_new_conditional:Npnn \__text_change_case_if_greek_spacing_diacritic:n
+ #1 { TF }
+ {
+ \exp_args:Nf \__text_change_case_if_greek_spacing_diacritic:n
+ { \int_eval:n { \__text_codepoint_from_chars:Nw #1 } }
+ }
+\cs_new:Npn \__text_change_case_if_greek_spacing_diacritic:n #1
+ {
+ \if_int_compare:w #1 < "1FBD \exp_stop_f:
+ \if_int_compare:w #1 = "037A \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \else:
+ \if_int_compare:w #1 = "1FBD \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FBF \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FC0 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FC1 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FCD \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FCE \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FCF \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FDD \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FDE \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FDF \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FED \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FEE \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FEF \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FFD \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "1FFE \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ }
+\prg_new_conditional:Npnn \__text_change_case_if_greek_breathing:n
+ #1 { TF , p }
+ {
+ \exp_args:Nf \__text_change_case_if_greek_breathing:n
+ { \int_eval:n { \__text_codepoint_from_chars:Nw #1 } }
+ }
+\cs_new:Npn \__text_change_case_if_greek_breathing:n #1
+ {
+ \if_int_compare:w #1 = "0313 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "0314 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ }
+\prg_new_conditional:Npnn \__text_change_case_if_greek_stress:n
+ #1 { TF , p }
+ {
+ \exp_args:Nf \__text_change_case_if_greek_stress:n
+ { \int_eval:n { \__text_codepoint_from_chars:Nw #1 } }
+ }
+\cs_new:Npn \__text_change_case_if_greek_stress:n #1
+ {
+ \if_int_compare:w #1 = "0304 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "0306 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ }
+\prg_new_conditional:Npnn \__text_change_case_if_takes_dialytika:n #1 { TF }
+ {
+ \exp_args:Nf \__text_change_case_if_takes_dialytika:n
+ { \int_eval:n { \__text_codepoint_from_chars:Nw #1 } }
+ }
+\cs_new:Npn \__text_change_case_if_takes_dialytika:n #1
+ {
+ \if_int_compare:w #1 = "0399 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "03B9 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "03A5 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "03C5 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ }
+\prg_new_conditional:Npnn \__text_change_case_if_takes_ypogegrammeni:n #1 { TF }
+ {
+ \exp_args:Nf \__text_change_case_if_takes_ypogegrammeni:n
+ { \int_eval:n { \__text_codepoint_from_chars:Nw #1 } }
+ }
+\cs_new:Npn \__text_change_case_if_takes_ypogegrammeni:n #1
+ {
+ \if_int_compare:w #1 = "03B1 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "03B7 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \if_int_compare:w #1 = "03C9 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ \fi:
+ }
+\cs_new:Npn \__text_change_case_boundary_upper_el:Nnnnw
+ #1#2#3#4#5 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#5}
+ { \__text_change_case_boundary_upper_el:nnnN }
+ { \__text_change_case_loop:nnnw }
+ {#2} {#3} {#4} #5 \q__text_recursion_stop
+ }
+\cs_new_eq:cN { __text_change_case_boundary_upper_el-x-iota:Nnnnw }
+ \__text_change_case_boundary_upper_el:Nnnnw
+\cs_new:Npn \__text_change_case_boundary_upper_el:nnnN #1#2#3#4
+ {
+ \token_if_cs:NTF #4
+ { \__text_change_case_loop:nnnw {#1} {#2} {#3} }
+ {
+ \__text_codepoint_process:nN
+ { \__text_change_case_boundary_upper_el:nnnn {#1} {#2} {#3} }
+ }
+ #4
+ }
+\cs_new:Npn \__text_change_case_boundary_upper_el:nnnn #1#2#3#4
+ {
+ \bool_lazy_any:nTF
+ {
+ { \__text_codepoint_compare_p:nNn {#4} = { "0389 } }
+ { \__text_codepoint_compare_p:nNn {#4} = { "03AE } }
+ { \__text_codepoint_compare_p:nNn {#4} = { "1F22 } }
+ { \__text_codepoint_compare_p:nNn {#4} = { "1F2A } }
+ }
+ { \__text_change_case_boundary_upper_el:nnnnw {#1} {#2} {#3} {#4} }
+ { \__text_change_case_breathing:nnnn {#1} {#2} {#3} {#4} }
+ }
+\cs_new:Npn \__text_change_case_boundary_upper_el:nnnnw
+ #1#2#3#4#5 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#5}
+ { \__text_change_case_loop:nnnw {#1} {#2} {#3} #4 }
+ {
+ \__text_change_case_store:e
+ {
+ \codepoint_generate:nn { "0389 }
+ { \__text_change_case_catcode:nn {#4} { "0389 } }
+ }
+ \__text_change_case_loop:nnnw {#1} {#2} {#3}
+ }
+ #5 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_change_case_breathing:nnnn #1#2#3#4
+ {
+ \__text_change_case_if_greek:nTF {#4}
+ {
+ \exp_args:Ne \__text_change_case_breathing:nnnnn
+ {
+ \codepoint_to_nfd:n
+ { \__text_codepoint_from_chars:Nw #4 }
+ }
+ {#1} {#2} {#3} {#4}
+ }
+ { \__text_change_case_loop:nnnw {#1} {#2} {#3} #4 }
+ }
+\cs_new:Npn \__text_change_case_breathing:nnnnn #1#2#3#4#5
+ {
+ \__text_codepoint_process:nN
+ { \__text_change_case_breathing:nnnnnw {#2} {#3} {#4} {#5} }
+ #1 \q_mark
+ }
+\cs_new:Npn \__text_change_case_breathing:nnnnnw #1#2#3#4#5#6 \q_mark
+ {
+ \tl_if_blank:nTF {#6}
+ { \__text_change_case_loop:nnnw {#1} {#2} {#3} #4 }
+ {
+ \__text_codepoint_process:nN
+ { \__text_change_case_breathing:nnnnnnw {#1} {#2} {#3} {#4} {#5} }
+ #6 \q_mark
+ }
+ }
+\cs_new:Npn \__text_change_case_breathing:nnnnnnw #1#2#3#4#5#6#7 \q_mark
+ {
+ \tl_if_blank:nTF {#7}
+ {
+ \__text_change_case_breathing_aux:nnnnnn
+ {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+ {
+ \__text_codepoint_process:nN
+ { \__text_change_case_breathing:nnnnnnw {#1} {#2} {#3} {#4} {#5} }
+ #7 \q_mark
+ }
+ }
+\cs_new:Npn \__text_change_case_breathing_aux:nnnnnn #1#2#3#4#5#6
+ {
+ \bool_lazy_or:nnTF
+ { \__text_codepoint_compare_p:nNn {#6} = { "0313 } }
+ { \__text_codepoint_compare_p:nNn {#6} = { "0314 } }
+ { \__text_change_case_breathing_aux:nnnnw {#1} {#2} {#3} {#5} }
+ { \__text_change_case_loop:nnnw {#1} {#2} {#3} #4 }
+ }
+\cs_new:Npn \__text_change_case_breathing_aux:nnnnw #1#2#3#4#5
+ \q__text_recursion_stop
+ {
+ \__text_change_case_store:e
+ { \__text_change_case_codepoint:nn { upper } {#4} }
+ \tl_if_head_is_N_type:nTF {#5}
+ { \__text_change_case_breathing_aux:nnnN }
+ { \__text_change_case_loop:nnnw }
+ {#1} {#2} {#3} #5 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_change_case_breathing_aux:nnnN #1#2#3#4
+ {
+ \__text_codepoint_process:nN
+ { \__text_change_case_breathing_dialytika:nnnn {#1} {#2} {#3} } #4
+ }
+\cs_new:Npn \__text_change_case_breathing_dialytika:nnnn #1#2#3#4
+ {
+ \__text_change_case_if_takes_dialytika:nTF {#4}
+ {
+ \__text_change_case_upper_el_dialytika:n {#4}
+ \__text_change_case_loop:nnnw {#1} {#2} {#3}
+ }
+ { \__text_change_case_loop:nnnw {#1} {#2} {#3} #4 }
+ }
+\cs_new:Npn \__text_change_case_title_el:nnnnn #1#2#3#4#5
+ { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+\cs_new:Npn \__text_change_case_upper_hy:nnnnn #1#2#3#4#5
+ {
+ \__text_codepoint_compare:nNnTF {#5} = { "0587 }
+ {
+ \__text_change_case_store:e
+ {
+ \codepoint_generate:nn { "0535 }
+ { \__text_change_case_catcode:nn {#5} { "0535 } }
+ \codepoint_generate:nn { "054E }
+ { \__text_change_case_catcode:nn {#5} { "054E } }
+ }
+ \use:c { __text_change_case_next_ #2 :nnn }
+ {#2} {#3} {#4}
+ }
+ { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+\cs_new:Npn \__text_change_case_title_hy:nnnnn #1#2#3#4#5
+ {
+ \__text_codepoint_compare:nNnTF {#5} = { "0587 }
+ {
+ \__text_change_case_store:e
+ {
+ \codepoint_generate:nn { "0535 }
+ { \__text_change_case_catcode:nn {#5} { "0535 } }
+ \codepoint_generate:nn { "057E }
+ { \__text_change_case_catcode:nn {#5} { "057E } }
+ }
+ \use:c { __text_change_case_next_ #2 :nnn }
+ {#2} {#3} {#4}
+ }
+ { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+\cs_new:cpn { __text_change_case_upper_hy-x-yiwn:nnnnn } #1#2#3#4#5
+ { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+\cs_new_eq:cc { __text_change_case_title_hy-x-yiwn:nnnnn }
+ { __text_change_case_upper_hy-x-yiwn:nnnnn }
+\cs_new:cpn { __text_change_case_lower_la-x-medieval:nnnnn } #1#2#3#4#5
+ {
+ \__text_codepoint_compare:nNnTF {#5} = { "0056 }
+ {
+ \__text_change_case_store:e
+ { \char_generate:nn { "0075 } { \__text_char_catcode:N #5 } }
+ \use:c { __text_change_case_next_ #2 :nnn }
+ {#2} {#3} {#4}
+ }
+ { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+\cs_new:cpn { __text_change_case_upper_la-x-medieval:nnnnn } #1#2#3#4#5
+ {
+ \__text_codepoint_compare:nNnTF {#5} = { "0075 }
+ {
+ \__text_change_case_store:e
+ { \char_generate:nn { "0056 } { \__text_char_catcode:N #5 } }
+ \use:c { __text_change_case_next_ #2 :nnn }
+ {#2} {#3} {#4}
+ }
+ { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+\cs_new:Npn \__text_change_case_lower_lt:nnnnn #1#2#3#4#5
+ {
+ \exp_args:Ne \__text_change_case_lower_lt_auxi:nnnnn
+ {
+ \int_case:nn { \__text_codepoint_from_chars:Nw #5 }
+ {
+ { "00CC } { "0300 }
+ { "00CD } { "0301 }
+ { "0128 } { "0303 }
+ }
+ }
+ {#2} {#3} {#4} {#5}
+ }
+\cs_new:Npn \__text_change_case_lower_lt_auxi:nnnnn #1#2#3#4#5
+ {
+ \tl_if_blank:nTF {#1}
+ {
+ \exp_args:Ne \__text_change_case_lower_lt_auxii:nnnnn
+ {
+ \int_case:nn { \__text_codepoint_from_chars:Nw #5 }
+ {
+ { "0049 } { "0069 }
+ { "004A } { "006A }
+ { "012E } { "012F }
+ }
+ }
+ {#2} {#3} {#4} {#5}
+ }
+ {
+ \__text_change_case_store:e
+ {
+ \codepoint_generate:nn { "0069 }
+ { \__text_change_case_catcode:nn {#5} { "0069 } }
+ \codepoint_generate:nn { "0307 }
+ { \__text_change_case_catcode:nn {#5} { "0307 } }
+ \codepoint_generate:nn {#1}
+ { \__text_change_case_catcode:nn {#5} {#1} }
+ }
+ \__text_change_case_loop:nnnw {#2} {#3} {#4}
+ }
+ }
+\cs_new:Npn \__text_change_case_lower_lt_auxii:nnnnn #1#2#3#4#5
+ {
+ \tl_if_blank:nTF {#1}
+ { \__text_change_case_codepoint:nnnnn {#2} {#2} {#3} {#4} {#5} }
+ {
+ \__text_change_case_store:e
+ {
+ \codepoint_generate:nn {#1}
+ { \__text_change_case_catcode:nn {#5} {#1} }
+ }
+ \__text_change_case_lower_lt:nnnw {#2} {#3} {#4}
+ }
+ }
+\cs_new:Npn \__text_change_case_lower_lt:nnnw #1#2#3#4 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#4}
+ { \__text_change_case_lower_lt:nnnN }
+ { \__text_change_case_loop:nnnw }
+ {#1} {#2} {#3} #4 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_change_case_lower_lt:nnnN #1#2#3#4
+ {
+ \__text_codepoint_process:nN
+ { \__text_change_case_lower_lt:nnnn {#1} {#2} {#3} } #4
+ }
+\cs_new:Npn \__text_change_case_lower_lt:nnnn #1#2#3#4
+ {
+ \bool_lazy_and:nnT
+ {
+ \bool_lazy_or_p:nn
+ { ! \tl_if_single_p:n {#4} }
+ { ! \token_if_cs_p:N #4 }
+ }
+ {
+ \bool_lazy_any_p:n
+ {
+ { \__text_codepoint_compare_p:nNn {#4} = { "0300 } }
+ { \__text_codepoint_compare_p:nNn {#4} = { "0301 } }
+ { \__text_codepoint_compare_p:nNn {#4} = { "0303 } }
+ }
+ }
+ {
+ \__text_change_case_store:e
+ {
+ \codepoint_generate:nn { "0307 }
+ { \__text_change_case_catcode:nn {#4} { "0307 } }
+ }
+ }
+ \__text_change_case_loop:nnnw {#1} {#2} {#3} #4
+ }
+\cs_new:Npn \__text_change_case_upper_lt:nnnnn #1#2#3#4#5
+ {
+ \exp_args:Ne \__text_change_case_upper_lt_aux:nnnnn
+ {
+ \int_case:nn { \__text_codepoint_from_chars:Nw #5 }
+ {
+ { "0069 } { "0049 }
+ { "006A } { "004A }
+ { "012F } { "012E }
+ }
+ }
+ {#2} {#3} {#4} {#5}
+ }
+\cs_new:Npn \__text_change_case_upper_lt_aux:nnnnn #1#2#3#4#5
+ {
+ \tl_if_blank:nTF {#1}
+ { \__text_change_case_codepoint:nnnnn { upper } {#2} {#3} {#4} {#5} }
+ {
+ \__text_change_case_store:e
+ {
+ \codepoint_generate:nn {#1}
+ { \__text_change_case_catcode:nn {#5} {#1} }
+ }
+ \__text_change_case_upper_lt:nnnw {#2} {#3} {#4}
+ }
+ }
+\cs_new:Npn \__text_change_case_upper_lt:nnnw #1#2#3#4 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#4}
+ { \__text_change_case_upper_lt:nnnN }
+ { \use:c { __text_change_case_next_ #1 :nnn } }
+ {#1} {#2} {#3} #4 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_change_case_upper_lt:nnnN #1#2#3#4
+ {
+ \__text_codepoint_process:nN
+ { \__text_change_case_upper_lt:nnnn {#1} {#2} {#3} } #4
+ }
+\cs_new:Npn \__text_change_case_upper_lt:nnnn #1#2#3#4
+ {
+ \bool_lazy_and:nnTF
+ {
+ \bool_lazy_or_p:nn
+ { ! \tl_if_single_p:n {#4} }
+ { ! \token_if_cs_p:N #4 }
+ }
+ { \__text_codepoint_compare_p:nNn {#4} = { "0307 } }
+ { \use:c { __text_change_case_next_ #1 :nnn } {#1} {#2} {#3} }
+ { \use:c { __text_change_case_next_ #1 :nnn } {#1} {#2} {#3} #4 }
+ }
+\cs_new:Npn \__text_change_case_title_nl:nnnnn #1#2#3#4#5
+ {
+ \tl_if_single:nTF {#5}
+ { \__text_change_case_title_nl_aux:nnnnn }
+ { \__text_change_case_codepoint:nnnnn }
+ {#1} {#2} {#3} {#4} {#5}
+ }
+\cs_new:Npn \__text_change_case_title_nl_aux:nnnnn #1#2#3#4#5
+ {
+ \bool_lazy_or:nnTF
+ { \int_compare_p:nNn {`#5} = { "0049 } }
+ { \int_compare_p:nNn {`#5} = { "0069 } }
+ {
+ \__text_change_case_store:e
+ { \char_generate:nn { "0049 } { \__text_char_catcode:N #5 } }
+ \__text_change_case_title_nl:nnnw {#2} {#3} {#4}
+ }
+ { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+\cs_new:Npn \__text_change_case_title_nl:nnnw #1#2#3#4 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#4}
+ { \__text_change_case_title_nl:nnnN }
+ { \use:c { __text_change_case_next_ #1 :nnn } }
+ {#1} {#2} {#3} #4 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_change_case_title_nl:nnnN #1#2#3#4
+ {
+ \bool_lazy_and:nnTF
+ { ! \token_if_cs_p:N #4 }
+ {
+ \bool_lazy_or_p:nn
+ { \int_compare_p:nNn {`#4} = { "004A } }
+ { \int_compare_p:nNn {`#4} = { "006A } }
+ }
+ {
+ \__text_change_case_store:e
+ { \char_generate:nn { "004A } { \__text_char_catcode:N #4 } }
+ \use:c { __text_change_case_next_ #1 :nnn } {#1} {#2} {#3}
+ }
+ { \use:c { __text_change_case_next_ #1 :nnn } {#1} {#2} {#3} #4 }
+ }
+\cs_new:Npn \__text_change_case_lower_tr:nnnnn #1#2#3#4#5
+ {
+ \__text_codepoint_compare:nNnTF {#5} = { "0049 }
+ { \__text_change_case_lower_tr:nnnNw {#1} {#3} {#4} #5 }
+ {
+ \__text_codepoint_compare:nNnTF {#5} = { "0130 }
+ {
+ \__text_change_case_store:e
+ {
+ \codepoint_generate:nn { "0069 }
+ { \__text_change_case_catcode:nn {#5} { "0069 } }
+ }
+ \__text_change_case_loop:nnnw {#1} {#3} {#4}
+ }
+ { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+ }
+\cs_new:Npn \__text_change_case_lower_tr:nnnNw #1#2#3#4#5 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#5}
+ { \__text_change_case_lower_tr:NnnnN #4 {#1} {#2} {#3} }
+ {
+ \__text_change_case_store:e
+ {
+ \codepoint_generate:nn { "0131 }
+ { \__text_change_case_catcode:nn {#4} { "0131 } }
+ }
+ \__text_change_case_loop:nnnw {#1} {#2} {#3}
+ }
+ #5 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_change_case_lower_tr:NnnnN #1#2#3#4#5
+ {
+ \__text_codepoint_process:nN
+ { \__text_change_case_lower_tr:Nnnnn #1 {#2} {#3} {#4} } #5
+ }
+\cs_new:Npn \__text_change_case_lower_tr:Nnnnn #1#2#3#4#5
+ {
+ \bool_lazy_or:nnTF
+ {
+ \bool_lazy_and_p:nn
+ { \tl_if_single_p:n {#5} }
+ { \token_if_cs_p:N #5 }
+ }
+ { ! \__text_codepoint_compare_p:nNn {#5} = { "0307 } }
+ {
+ \__text_change_case_store:e
+ {
+ \codepoint_generate:nn { "0131 }
+ { \__text_change_case_catcode:nn {#1} { "0131 } }
+ }
+ \__text_change_case_loop:nnnw {#2} {#3} {#4} #5
+ }
+ {
+ \__text_change_case_store:e
+ {
+ \codepoint_generate:nn { "0069 }
+ { \__text_change_case_catcode:nn {#1} { "0069 } }
+ }
+ \__text_change_case_loop:nnnw {#2} {#3} {#4}
+ }
+ }
+\cs_new:Npn \__text_change_case_upper_tr:nnnnn #1#2#3#4#5
+ {
+ \__text_codepoint_compare:nNnTF {#5} = { "0069 }
+ {
+ \__text_change_case_store:e
+ {
+ \codepoint_generate:nn { "0130 }
+ { \__text_change_case_catcode:nn {#5} { "0130 } }
+ }
+ \use:c { __text_change_case_next_ #2 :nnn } {#2} {#3} {#4}
+ }
+ { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+ }
+\cs_new_eq:NN \__text_change_case_lower_az:nnnnn
+ \__text_change_case_lower_tr:nnnnn
+\cs_new_eq:NN \__text_change_case_upper_az:nnnnn
+ \__text_change_case_upper_tr:nnnnn
+\group_begin:
+ \cs_set_protected:Npn \__text_change_case_setup:NN #1#2
+ {
+ \quark_if_recursion_tail_stop:N #1
+ \tl_const:cn { c__text_lowercase_ \token_to_str:N #1 _tl }
+ { #2 }
+ \tl_const:cn { c__text_uppercase_ \token_to_str:N #2 _tl }
+ { #1 }
+ \__text_change_case_setup:NN
+ }
+ \__text_change_case_setup:NN
+ \AA \aa
+ \AE \ae
+ \DH \dh
+ \DJ \dj
+ \IJ \ij
+ \L \l
+ \NG \ng
+ \O \o
+ \OE \oe
+ \SS \ss
+ \TH \th
+ \q_recursion_tail ?
+ \q_recursion_stop
+ \tl_const:cn { c__text_uppercase_ \token_to_str:N \i _tl } { I }
+ \tl_const:cn { c__text_uppercase_ \token_to_str:N \j _tl } { J }
+\group_end:
+\tl_if_exist:NT \@expl@finalise@setup@@
+ {
+ \tl_gput_right:Nn \@expl@finalise@setup@@
+ {
+ \tl_gput_right:Nn \@kernel@after@begindocument
+ {
+ \group_begin:
+ \cs_set_protected:Npn \__text_change_case_setup:Nn #1#2
+ {
+ \quark_if_recursion_tail_stop:N #1
+ \tl_if_single_token:nT {#2}
+ {
+ \cs_if_exist:cF
+ { c__text_uppercase_ \token_to_str:N #1 _tl }
+ {
+ \tl_const:cn
+ { c__text_uppercase_ \token_to_str:N #1 _tl }
+ { #2 }
+ }
+ \cs_if_exist:cF
+ { c__text_lowercase_ \token_to_str:N #2 _tl }
+ {
+ \tl_const:cn
+ { c__text_lowercase_ \token_to_str:N #2 _tl }
+ { #1 }
+ }
+ }
+ \__text_change_case_setup:Nn
+ }
+ \exp_after:wN \__text_change_case_setup:Nn \@uclclist
+ \q_recursion_tail ?
+ \q_recursion_stop
+ \group_end:
+ }
+ }
+ }
+\bool_lazy_or:nnF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \text_declare_uppercase_mapping:nn { "01F0 } { \v { J } }
+ }
+%% File: l3text-map.dtx
+\cs_new:Npn \text_map_function:nN #1#2
+ { \exp_args:Ne \__text_map_function:nN { \text_expand:n {#1} } #2 }
+\cs_new:Npn \__text_map_function:nN #1#2
+ {
+ \__text_map_loop:Nnw #2 { } #1
+ \q__text_recursion_tail \q__text_recursion_stop
+ \prg_break_point:Nn \text_map_break: { }
+ }
+\cs_new:Npn \__text_map_loop:Nnw #1#2#3 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#3}
+ { \__text_map_N_type:NnN }
+ {
+ \tl_if_head_is_group:nTF {#3}
+ { \__text_map_group:Nnn }
+ { \__text_map_space:Nnw }
+ }
+ #1 {#2} #3 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_map_group:Nnn #1#2#3
+ {
+ \__text_map_output:Nn #1 {#2}
+ {
+ \__text_map_loop:Nnw #1 { } #2
+ \q__text_recursion_tail \q__text_recursion_stop
+ \prg_break_point:Nn \text_map_break: { }
+ }
+ \__text_map_loop:Nnw #1 { }
+ }
+\use:e
+ { \cs_new:Npn \exp_not:N \__text_map_space:Nnw #1#2 \c_space_tl }
+ {
+ \__text_map_output:Nn #1 {#2}
+ #1 { ~ }
+ \__text_map_loop:Nnw #1 { }
+ }
+\cs_new:Npn \__text_map_N_type:NnN #1#2#3
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #3
+ {
+ \__text_map_output:Nn #1 {#2}
+ \text_map_break:
+ }
+ \token_if_cs:NTF #3
+ {
+ \__text_map_output:Nn #1 {#2}
+ #1 {#3}
+ \__text_map_loop:Nnw #1 { }
+ }
+ {
+ \__text_codepoint_process:nN
+ { \__text_map_codepoint:Nnn #1 {#2} } #3
+ }
+ }
+\cs_new:Npn \__text_map_codepoint:Nnn #1#2#3
+ {
+ \__text_codepoint_compare:nNnTF {#3} = { "0D }
+ {
+ \__text_map_output:Nn #1 {#2}
+ \__text_map_CR:Nnw #1 {#3}
+ }
+ {
+ \__text_codepoint_compare:nNnTF {#3} = { "200D }
+ { \__text_map_loop:Nnw #1 {#2#3} }
+ { \__text_map_class:Nnnn #1 {#2} {#3} { Control } }
+ }
+ }
+\cs_new:Npn \__text_map_CR:Nnw #1#2#3 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#3}
+ { \__text_map_CR:NnN #1 {#2} }
+ {
+ #1 {#2}
+ \__text_map_loop:Nnw #1 { }
+ }
+ #3 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_map_CR:NnN #1#2#3
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #3
+ {
+ #1 {#2}
+ \text_map_break:
+ }
+ \bool_lazy_and:nnTF
+ { ! \token_if_cs_p:N #3 }
+ { \int_compare_p:nNn { `#3 } = { "0A } }
+ {
+ \__text_map_output:Nn #1 {#2#3}
+ \__text_map_loop:Nnw #1 { }
+ }
+ { \__text_map_loop:Nnw #1 { } #3 }
+ }
+\cs_new:Npn \__text_map_class:Nnnn #1#2#3#4
+ {
+ \exp_args:Nv \__text_map_class:nNnnn { c__text_grapheme_ #4 _clist }
+ #1 {#2} {#3} {#4}
+ }
+\cs_new:Npn \__text_map_class:nNnnn #1#2#3#4#5
+ {
+ \__text_map_class_loop:Nnnnw #2 {#3} {#4} {#5}
+ #1 , \q__text_recursion_tail .. , \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_map_class_loop:Nnnnw #1#2#3#4 #5 .. #6 ,
+ {
+ \__text_if_q_recursion_tail_stop_do:nn {#5}
+ { \use:c { __text_map_not_ #4 :Nnn } #1 {#2} {#3} }
+ \__text_codepoint_compare:nNnTF {#3} < { "#5 }
+ {
+ \__text_map_class_end:nw
+ { \use:c { __text_map_not_ #4 :Nnn } #1 {#2} {#3} }
+ }
+ {
+ \__text_codepoint_compare:nNnTF {#3} > { "#6 }
+ { \__text_map_class_loop:Nnnnw #1 {#2} {#3} {#4} }
+ {
+ \__text_map_class_end:nw
+ { \use:c { __text_map_ #4 :Nnn } #1 {#2} {#3} }
+ }
+ }
+ }
+\cs_new:Npn \__text_map_class_end:nw #1#2 \q__text_recursion_stop {#1}
+\cs_new:Npn \__text_map_Control:Nnn #1#2#3
+ {
+ \__text_map_output:Nn #1 {#2}
+ \__text_map_output:Nn #1 {#3}
+ \__text_map_loop:Nnw #1 { }
+ }
+\cs_new:Npn \__text_map_Extend:Nnn #1#2#3
+ { \__text_map_loop:Nnw #1 {#2#3} }
+\cs_new_eq:NN \__text_map_SpacingMark:Nnn \__text_map_Extend:Nnn
+\cs_new:Npn \__text_map_Prepend:Nnn #1#2#3
+ {
+ \__text_map_output:Nn #1 {#2}
+ \__text_map_lookahead:NnNw #1 {#3} \__text_map_Prepend_aux:Nnn
+ }
+\cs_new:Npn \__text_map_Prepend_aux:Nnn #1#2#3
+ {
+ \bool_lazy_or:nnTF
+ { \__text_codepoint_compare_p:nNn {#3} = { "0A } }
+ { \__text_codepoint_compare_p:nNn {#3} = { "0D } }
+ {
+ #1 {#2}
+ \__text_map_loop:Nnw #1 {#3}
+ }
+ {
+ \exp_args:NV \__text_map_Prepend:nNnn
+ \c__text_grapheme_Control_clist
+ #1 {#2} {#3}
+ }
+ }
+\cs_new:Npn \__text_map_Prepend:nNnn #1#2#3#4
+ {
+ \__text_map_Prepend_loop:Nnnw #2 {#3} {#4}
+ #1 , \q__text_recursion_tail .. , \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_map_Prepend_loop:Nnnw #1#2#3 #4 .. #5 ,
+ {
+ \__text_if_q_recursion_tail_stop_do:nn {#4}
+ { \__text_map_loop:Nnw #1 {#2#3} }
+ \__text_codepoint_compare:nNnTF {#3} < { "#4 }
+ {
+ \__text_map_class_end:nw
+ { \__text_map_loop:Nnw #1 {#2#3} }
+ }
+ {
+ \__text_codepoint_compare:nNnTF {#3} > { "#5 }
+ { \__text_map_Prepend_loop:Nnnw #1 {#2} {#3} }
+ {
+ \__text_map_class_end:nw
+ { \__text_map_loop:Nnw #1 {#2} #3 }
+ }
+ }
+ }
+\cs_new:Npn \__text_map_not_Control:Nnn #1#2#3
+ { \__text_map_class:Nnnn #1 {#2} {#3} { Extend } }
+\cs_new:Npn \__text_map_not_Extend:Nnn #1#2#3
+ { \__text_map_class:Nnnn #1 {#2} {#3} { SpacingMark } }
+\cs_new:Npn \__text_map_not_SpacingMark:Nnn #1#2#3
+ { \__text_map_class:Nnnn #1 {#2} {#3} { Prepend } }
+\cs_new:Npn \__text_map_not_Prepend:Nnn #1#2#3
+ { \__text_map_class:Nnnn #1 {#2} {#3} { L } }
+\cs_new:Npn \__text_map_not_L:Nnn #1#2#3
+ { \__text_map_class:Nnnn #1 {#2} {#3} { LV } }
+\cs_new:Npn \__text_map_not_LV:Nnn #1#2#3
+ { \__text_map_class:Nnnn #1 {#2} {#3} { V } }
+\cs_new:Npn \__text_map_not_V:Nnn #1#2#3
+ { \__text_map_class:Nnnn #1 {#2} {#3} { LVT } }
+\cs_new:Npn \__text_map_not_LVT:Nnn #1#2#3
+ { \__text_map_class:Nnnn #1 {#2} {#3} { T } }
+\cs_new:Npn \__text_map_not_T:Nnn #1#2#3
+ { \__text_map_class:Nnnn #1 {#2} {#3} { Regional_Indicator } }
+\cs_new:Npn \__text_map_not_Regional_Indicator:Nnn #1#2#3
+ {
+ \__text_map_output:Nn #1 {#2}
+ \__text_map_loop:Nnw #1 {#3}
+ }
+\cs_new:Npn \__text_map_L:Nnn #1#2#3
+ {
+ \__text_map_output:Nn #1 {#2}
+ \__text_map_hangul:Nnnw
+ #1 {#3} { L ; V ; LV ; LVT }
+ }
+\cs_new:Npn \__text_map_LV:Nnn #1#2#3
+ {
+ \__text_map_output:Nn #1 {#2}
+ \__text_map_hangul:Nnnw
+ #1 {#3} { V ; T }
+ }
+\cs_new_eq:NN \__text_map_V:Nnn \__text_map_LV:Nnn
+\cs_new:Npn \__text_map_LVT:Nnn #1#2#3
+ {
+ \__text_map_output:Nn #1 {#2}
+ \__text_map_hangul:Nnnw
+ #1 {#3} { T }
+ }
+\cs_new_eq:NN \__text_map_T:Nnn \__text_map_LVT:Nnn
+\cs_new:Npn \__text_map_hangul:Nnnw #1#2#3#4 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#4}
+ { \__text_map_hangul:NnnN #1 {#2} {#3} }
+ {
+ #1 {#2}
+ \__text_map_loop:Nnw #1 { }
+ }
+ #4 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_map_hangul:NnnN #1#2#3#4
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #4
+ {
+ #1 {#2}
+ \text_map_break:
+ }
+ \token_if_cs:NTF #4
+ {
+ #1 {#2}
+ \__text_map_loop:Nnw #1 { }
+ }
+ {
+ \__text_codepoint_process:nN
+ { \__text_map_hangul:Nnnn #1 {#2} {#3} } #4
+ }
+ }
+\cs_new:Npn \__text_map_hangul:Nnnn #1#2#3#4
+ {
+ \__text_map_hangul_aux:Nnnw #1 {#2} {#4}
+ #3 ; \q_recursion_tail ; \q_recursion_stop
+ }
+\cs_new:Npn \__text_map_hangul_aux:Nnnw #1#2#3#4 ;
+ {
+ \quark_if_recursion_tail_stop_do:nn {#4}
+ { \__text_map_loop:Nnw #1 {#2} #3 }
+ \exp_args:Nv \__text_map_hangul:nNnnnw { c__text_grapheme_ #4 _clist }
+ #1 {#2} {#3} {#4}
+ }
+\cs_new:Npn \__text_map_hangul:nNnnnw #1#2#3#4#5#6 \q_recursion_stop
+ {
+ \__text_map_hangul_loop:Nnnnnw #2 {#3} {#4} {#5} {#6}
+ #1 , \q__text_recursion_tail .. , \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_map_hangul_loop:Nnnnnw #1#2#3#4#5 #6 .. #7 ,
+ {
+ \__text_if_q_recursion_tail_stop_do:nn {#6}
+ { \__text_map_hangul_next:Nnnn #1 {#2} {#3} {#5} }
+ \__text_codepoint_compare:nNnTF {#3} < { "#6 }
+ {
+ \__text_map_hangul_end:nw
+ { \__text_map_hangul_next:Nnnn #1 {#2} {#3} {#5} }
+ }
+ {
+ \__text_codepoint_compare:nNnTF {#3} > { "#7 }
+ { \__text_map_hangul_loop:Nnnnnw #1 {#2} {#3} {#4} {#5} }
+ {
+ \__text_map_hangul_end:nw
+ { \use:c { __text_map_hangul_ #4 :Nnn } #1 {#2} {#3} }
+ }
+ }
+ }
+\cs_new:Npn \__text_map_hangul_next:Nnnn #1#2#3#4
+ { \__text_map_hangul_aux:Nnnw #1 {#2} {#3} #4 \q_recursion_stop }
+\cs_new:Npn \__text_map_hangul_end:nw #1#2 \q__text_recursion_stop {#1}
+\cs_new:Npn \__text_map_hangul_L:Nnn #1#2#3
+ {
+ \__text_map_hangul:Nnnw
+ #1 {#2#3} { L V { LV } { LVT } }
+ }
+\cs_new:Npn \__text_map_hangul_LV:Nnn #1#2#3
+ {
+ \__text_map_hangul:Nnnw
+ #1 {#2#3} { VT }
+ }
+\cs_new_eq:NN \__text_map_hangul_V:Nnn \__text_map_hangul_LV:Nnn
+\cs_new:Npn \__text_map_hangul_LVT:Nnn #1#2#3
+ {
+ \__text_map_hangul:Nnnw
+ #1 {#2#3} { T }
+ }
+\cs_new_eq:NN \__text_map_hangul_T:Nnn \__text_map_hangul_LVT:Nnn
+\cs_new:Npn \__text_map_Regional_Indicator:Nnn #1#2#3
+ {
+ \__text_map_output:Nn #1 {#2}
+ \__text_map_lookahead:NnNw #1 {#3} \__text_map_Regional_Indicator_aux:Nnn
+ }
+\cs_new:Npn \__text_map_Regional_Indicator_aux:Nnn #1#2#3
+ {
+ \bool_lazy_or:nnTF
+ { \__text_codepoint_compare_p:nNn {#3} < { "1F1E6 } }
+ { \__text_codepoint_compare_p:nNn {#3} > { "1F1FF } }
+ {
+ \__text_map_loop:Nnw #1 {#2} #3
+ }
+ { \__text_map_loop:Nnw #1 {#2#3} }
+ }
+\cs_new:Npn \__text_map_lookahead:NnNw #1#2#3#4 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#4}
+ { \__text_map_lookahead:NnNN #1 {#2} #3 }
+ { \__text_map_loop:Nnw #1 {#2} }
+ #4 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_map_lookahead:NnNN #1#2#3#4
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #4 { #1 {#2} }
+ \token_if_cs:NTF #4
+ {
+ #1 {#2}
+ \__text_map_loop:Nnw #1 { }
+ }
+ { \__text_codepoint_process:nN { #3 #1 {#2} } }
+ #4
+ }
+\cs_new:Npn \__text_map_output:Nn #1#2
+ { \tl_if_blank:nF {#2} { #1 {#2} } }
+\cs_new:Npn \text_map_break:
+ { \prg_map_break:Nn \text_map_break: { } }
+\cs_new:Npn \text_map_break:n
+ { \prg_map_break:Nn \text_map_break: }
+\cs_new_protected:Npn \text_map_inline:nn #1#2
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \cs_gset_protected:cpn
+ { __text_map_ \int_use:N \g__kernel_prg_map_int :w } ##1 {#2}
+ \exp_args:Nnc \text_map_function:nN {#1}
+ { __text_map_ \int_use:N \g__kernel_prg_map_int :w }
+ \prg_break_point:Nn \text_map_break:
+ { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+%% File: l3text-purify.dtx
+\__kernel_quark_new_test:N \__text_if_recursion_tail_stop:N
+\cs_new:Npn \text_purify:n #1
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \exp:w
+ \exp_args:Ne \__text_purify:n
+ { \text_expand:n {#1} }
+ }
+ }
+\cs_new:Npn \__text_purify:n #1
+ {
+ \group_align_safe_begin:
+ \__text_purify_loop:w #1
+ \q__text_recursion_tail \q__text_recursion_stop
+ \__text_purify_result:n { }
+ }
+\cs_new:Npn \__text_purify_store:n #1
+ { \__text_purify_store:nw {#1} }
+\cs_new:Npn \__text_purify_store:nw #1#2 \__text_purify_result:n #3
+ { #2 \__text_purify_result:n { #3 #1 } }
+\cs_new:Npn \__text_purify_end:w #1 \__text_purify_result:n #2
+ {
+ \group_align_safe_end:
+ \exp_end:
+ #2
+ }
+\cs_new:Npn \__text_purify_loop:w #1 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \__text_purify_N_type:N }
+ {
+ \tl_if_head_is_group:nTF {#1}
+ { \__text_purify_group:n }
+ { \__text_purify_space:w }
+ }
+ #1 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_purify_group:n #1 { \__text_purify_loop:w #1 }
+\exp_last_unbraced:NNo \cs_new:Npn \__text_purify_space:w \c_space_tl
+ {
+ \__text_purify_store:n { ~ }
+ \__text_purify_loop:w
+ }
+\cs_new:Npn \__text_purify_N_type:N #1
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #1 { \__text_purify_end:w }
+ \__text_purify_N_type_aux:N #1
+ }
+\cs_new:Npn \__text_purify_N_type_aux:N #1
+ {
+ \exp_after:wN \__text_purify_math_search:NNN
+ \exp_after:wN #1 \l_text_math_delims_tl
+ \q__text_recursion_tail ?
+ \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_purify_math_search:NNN #1#2#3
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #2
+ { \__text_purify_math_cmd:N #1 }
+ \token_if_eq_meaning:NNTF #1 #2
+ {
+ \__text_use_i_delimit_by_q_recursion_stop:nw
+ { \__text_purify_math_start:NNw #2 #3 }
+ }
+ { \__text_purify_math_search:NNN #1 }
+ }
+\cs_new:Npn \__text_purify_math_start:NNw #1#2#3 \q__text_recursion_stop
+ {
+ \__text_purify_math_loop:NNw #1#2#3 \q__text_recursion_stop
+ \__text_purify_math_result:n { }
+ }
+\cs_new:Npn \__text_purify_math_store:n #1
+ { \__text_purify_math_store:nw {#1} }
+\cs_new:Npn \__text_purify_math_store:nw #1#2 \__text_purify_math_result:n #3
+ { #2 \__text_purify_math_result:n { #3 #1 } }
+\cs_new:Npn \__text_purify_math_end:w #1 \__text_purify_math_result:n #2
+ {
+ \__text_purify_store:n { $ #2 $ }
+ \__text_purify_loop:w #1
+ }
+\cs_new:Npn \__text_purify_math_stop:Nw #1 \__text_purify_math_result:n #2
+ {
+ \__text_purify_store:n {#1#2}
+ \__text_purify_end:w
+ }
+\cs_new:Npn \__text_purify_math_loop:NNw #1#2#3 \q__text_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#3}
+ { \__text_purify_math_N_type:NNN }
+ {
+ \tl_if_head_is_group:nTF {#3}
+ { \__text_purify_math_group:NNn }
+ { \__text_purify_math_space:NNw }
+ }
+ #1#2#3 \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_purify_math_N_type:NNN #1#2#3
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #3
+ { \__text_purify_math_stop:Nw #1 }
+ \token_if_eq_meaning:NNTF #3 #2
+ { \__text_purify_math_end:w }
+ {
+ \__text_purify_math_store:n {#3}
+ \__text_purify_math_loop:NNw #1#2
+ }
+ }
+\cs_new:Npn \__text_purify_math_group:NNn #1#2#3
+ {
+ \__text_purify_math_store:n { {#3} }
+ \__text_purify_math_loop:NNw #1#2
+ }
+\exp_after:wN \cs_new:Npn \exp_after:wN \__text_purify_math_space:NNw
+ \exp_after:wN # \exp_after:wN 1
+ \exp_after:wN # \exp_after:wN 2 \c_space_tl
+ {
+ \__text_purify_math_store:n { ~ }
+ \__text_purify_math_loop:NNw #1#2
+ }
+\cs_new:Npn \__text_purify_math_cmd:N #1
+ {
+ \exp_after:wN \__text_purify_math_cmd:NN \exp_after:wN #1
+ \l_text_math_arg_tl \q__text_recursion_tail \q__text_recursion_stop
+ }
+\cs_new:Npn \__text_purify_math_cmd:NN #1#2
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #2
+ { \__text_purify_replace:N #1 }
+ \cs_if_eq:NNTF #2 #1
+ {
+ \__text_use_i_delimit_by_q_recursion_stop:nw
+ { \__text_purify_math_cmd:n }
+ }
+ { \__text_purify_math_cmd:NN #1 }
+ }
+\cs_new:Npn \__text_purify_math_cmd:n #1
+ { \__text_purify_math_end:w \__text_purify_math_result:n {#1} }
+\cs_new:Npn \__text_purify_replace:N #1
+ {
+ \bool_lazy_and:nnTF
+ { \cs_if_exist_p:c { l__text_purify_ \token_to_str:N #1 _tl } }
+ {
+ \bool_lazy_or_p:nn
+ { \token_if_cs_p:N #1 }
+ { \token_if_active_p:N #1 }
+ }
+ {
+ \exp_args:Nv \__text_purify_replace_auxi:n
+ { l__text_purify_ \token_to_str:N #1 _tl }
+ }
+ {
+ \exp_args:Ne \__text_purify_replace_auxii:n
+ { \__text_token_to_explicit:N #1 }
+ }
+ }
+\cs_new:Npn \__text_purify_replace_auxi:n #1 { \__text_purify_loop:w #1 }
+\cs_new:Npn \__text_purify_replace_auxii:n #1
+ {
+ \token_if_cs:NTF #1
+ { \__text_purify_expand:N #1 }
+ {
+ \__text_purify_store:n {#1}
+ \__text_purify_loop:w
+ }
+ }
+\cs_new:Npn \__text_purify_expand:N #1
+ {
+ \str_if_eq:nnTF {#1} { \protect }
+ { \__text_purify_protect:N }
+ { \__text_purify_encoding:N #1 }
+ }
+\cs_new:Npn \__text_purify_protect:N #1
+ {
+ \__text_if_q_recursion_tail_stop_do:Nn #1 { \__text_purify_end:w }
+ \__text_purify_loop:w
+ }
+\cs_new:Npn \__text_purify_encoding:N #1
+ {
+ \bool_lazy_or:nnTF
+ { \cs_if_eq_p:NN #1 \@current@cmd }
+ { \cs_if_eq_p:NN #1 \@changed@cmd }
+ { \__text_purify_encoding_escape:NN }
+ {
+ \__text_if_expandable:NTF #1
+ { \exp_after:wN \__text_purify_loop:w #1 }
+ { \__text_purify_loop:w }
+ }
+ }
+\cs_new:Npn \__text_purify_encoding_escape:NN #1#2
+ {
+ \__text_purify_store:n {#1}
+ \__text_purify_loop:w
+ }
+\cs_new_protected:Npn \text_declare_purify_equivalent:Nn #1#2
+ {
+ \tl_clear_new:c { l__text_purify_ \token_to_str:N #1 _tl }
+ \tl_set:cn { l__text_purify_ \token_to_str:N #1 _tl } {#2}
+ }
+\cs_generate_variant:Nn \text_declare_purify_equivalent:Nn { Ne }
+\tl_map_inline:nn
+ {
+ \fontencoding
+ \fontfamily
+ \fontseries
+ \fontshape
+ }
+ { \text_declare_purify_equivalent:Nn #1 { \use_none:n } }
+\text_declare_purify_equivalent:Nn \fontsize { \use_none:nn }
+\text_declare_purify_equivalent:Nn \selectfont { }
+\text_declare_purify_equivalent:Nn \usefont { \use_none:nnnn }
+\tl_map_inline:nn
+ {
+ \emph
+ \text
+ \textnormal
+ \textrm
+ \textsf
+ \texttt
+ \textbf
+ \textmd
+ \textit
+ \textsl
+ \textup
+ \textsc
+ \textulc
+ }
+ { \text_declare_purify_equivalent:Nn #1 { \use:n } }
+\tl_map_inline:nn
+ {
+ \normalfont
+ \rmfamily
+ \sffamily
+ \ttfamily
+ \bfseries
+ \mdseries
+ \itshape
+ \scshape
+ \slshape
+ \upshape
+ \em
+ \Huge
+ \LARGE
+ \Large
+ \footnotesize
+ \huge
+ \large
+ \normalsize
+ \scriptsize
+ \small
+ \tiny
+ }
+ { \text_declare_purify_equivalent:Nn #1 { } }
+\exp_args:Nc \text_declare_purify_equivalent:Nn
+ { @protected@testopt } { \use_none:nnn }
+\text_declare_purify_equivalent:Nn \begin { \use:c }
+\text_declare_purify_equivalent:Nn \end { \__text_end_env:n }
+\cs_new:Npn \__text_end_env:n #1 { \cs:w end #1 \cs_end: }
+\text_declare_purify_equivalent:Nn \\ { }
+\tl_map_inline:nn
+ { \{ \} \# \$ \% \_ }
+ { \text_declare_purify_equivalent:Ne #1 { \cs_to_str:N #1 } }
+\text_declare_purify_equivalent:Nn \label { \use_none:n }
+\group_begin:
+\char_set_catcode_active:N \~
+\use:n
+ {
+ \group_end:
+ \text_declare_purify_equivalent:Ne ~ { \c_space_tl }
+ }
+\text_declare_purify_equivalent:Nn \nobreakspace { ~ }
+\text_declare_purify_equivalent:Nn \ { ~ }
+\text_declare_purify_equivalent:Nn \, { ~ }
+\cs_set_protected:Npn \__text_loop:Nn #1#2
+ {
+ \quark_if_recursion_tail_stop:N #1
+ \text_declare_purify_equivalent:Ne #1
+ {
+ \codepoint_generate:nn {"#2}
+ { \char_value_catcode:n {"#2} }
+ }
+ \__text_loop:Nn
+ }
+\__text_loop:Nn
+ \AA { 00C5 }
+ \AE { 00C6 }
+ \DH { 00D0 }
+ \DJ { 0110 }
+ \IJ { 0132 }
+ \L { 0141 }
+ \NG { 014A }
+ \O { 00D8 }
+ \OE { 0152 }
+ \TH { 00DE }
+ \aa { 00E5 }
+ \ae { 00E6 }
+ \dh { 00F0 }
+ \dj { 0111 }
+ \i { 0131 }
+ \j { 0237 }
+ \ij { 0132 }
+ \l { 0142 }
+ \ng { 014B }
+ \o { 00F8 }
+ \oe { 0153 }
+ \ss { 00DF }
+ \th { 00FE }
+ \q_recursion_tail ?
+ \q_recursion_stop
+\text_declare_purify_equivalent:Nn \SS { SS }
+\cs_new:Npn \__text_purify_accent:NN #1#2
+ {
+ \cs_if_exist:cTF
+ { c__text_purify_ \token_to_str:N #1 _ \token_to_str:N #2 _tl }
+ {
+ \exp_not:v
+ { c__text_purify_ \token_to_str:N #1 _ \token_to_str:N #2 _tl }
+ }
+ {
+ \exp_not:n {#2}
+ \exp_not:v { c__text_purify_ \token_to_str:N #1 _tl }
+ }
+ }
+\tl_map_inline:nn { \` \' \^ \~ \= \u \. \" \r \H \v \d \c \k \b \t }
+ { \text_declare_purify_equivalent:Nn #1 { \__text_purify_accent:NN #1 } }
+\group_begin:
+ \cs_set_protected:Npn \__text_loop:Nn #1#2
+ {
+ \quark_if_recursion_tail_stop:N #1
+ \tl_const:ce { c__text_purify_ \token_to_str:N #1 _tl }
+ { \codepoint_generate:nn {"#2} { \char_value_catcode:n { "#2 } } }
+ \__text_loop:Nn
+ }
+ \__text_loop:Nn
+ \` { 0300 }
+ \' { 0301 }
+ \^ { 0302 }
+ \~ { 0303 }
+ \= { 0304 }
+ \u { 0306 }
+ \. { 0307 }
+ \" { 0308 }
+ \r { 030A }
+ \H { 030B }
+ \v { 030C }
+ \d { 0323 }
+ \c { 0327 }
+ \k { 0328 }
+ \b { 0331 }
+ \t { 0361 }
+ \q_recursion_tail { }
+ \q_recursion_stop
+ \cs_set_protected:Npn \__text_loop:NNn #1#2#3
+ {
+ \quark_if_recursion_tail_stop:N #1
+ \tl_const:ce
+ { c__text_purify_ \token_to_str:N #1 _ \token_to_str:N #2 _tl }
+ { \codepoint_generate:nn {"#3} { \char_value_catcode:n { "#3 } } }
+ \__text_loop:NNn
+ }
+ \__text_loop:NNn
+ \` A { 00C0 }
+ \' A { 00C1 }
+ \^ A { 00C2 }
+ \~ A { 00C3 }
+ \" A { 00C4 }
+ \r A { 00C5 }
+ \c C { 00C7 }
+ \` E { 00C8 }
+ \' E { 00C9 }
+ \^ E { 00CA }
+ \" E { 00CB }
+ \` I { 00CC }
+ \' I { 00CD }
+ \^ I { 00CE }
+ \" I { 00CF }
+ \~ N { 00D1 }
+ \` O { 00D2 }
+ \' O { 00D3 }
+ \^ O { 00D4 }
+ \~ O { 00D5 }
+ \" O { 00D6 }
+ \` U { 00D9 }
+ \' U { 00DA }
+ \^ U { 00DB }
+ \" U { 00DC }
+ \' Y { 00DD }
+ \` a { 00E0 }
+ \' a { 00E1 }
+ \^ a { 00E2 }
+ \~ a { 00E3 }
+ \" a { 00E4 }
+ \r a { 00E5 }
+ \c c { 00E7 }
+ \` e { 00E8 }
+ \' e { 00E9 }
+ \^ e { 00EA }
+ \" e { 00EB }
+ \` i { 00EC }
+ \` \i { 00EC }
+ \' i { 00ED }
+ \' \i { 00ED }
+ \^ i { 00EE }
+ \^ \i { 00EE }
+ \" i { 00EF }
+ \" \i { 00EF }
+ \~ n { 00F1 }
+ \` o { 00F2 }
+ \' o { 00F3 }
+ \^ o { 00F4 }
+ \~ o { 00F5 }
+ \" o { 00F6 }
+ \` u { 00F9 }
+ \' u { 00FA }
+ \^ u { 00FB }
+ \" u { 00FC }
+ \' y { 00FD }
+ \" y { 00FF }
+ \= A { 0100 }
+ \= a { 0101 }
+ \u A { 0102 }
+ \u a { 0103 }
+ \k A { 0104 }
+ \k a { 0105 }
+ \' C { 0106 }
+ \' c { 0107 }
+ \^ C { 0108 }
+ \^ c { 0109 }
+ \. C { 010A }
+ \. c { 010B }
+ \v C { 010C }
+ \v c { 010D }
+ \v D { 010E }
+ \v d { 010F }
+ \= E { 0112 }
+ \= e { 0113 }
+ \u E { 0114 }
+ \u e { 0115 }
+ \. E { 0116 }
+ \. e { 0117 }
+ \k E { 0118 }
+ \k e { 0119 }
+ \v E { 011A }
+ \v e { 011B }
+ \^ G { 011C }
+ \^ g { 011D }
+ \u G { 011E }
+ \u g { 011F }
+ \. G { 0120 }
+ \. g { 0121 }
+ \c G { 0122 }
+ \c g { 0123 }
+ \^ H { 0124 }
+ \^ h { 0125 }
+ \~ I { 0128 }
+ \~ i { 0129 }
+ \~ \i { 0129 }
+ \= I { 012A }
+ \= i { 012B }
+ \= \i { 012B }
+ \u I { 012C }
+ \u i { 012D }
+ \u \i { 012D }
+ \k I { 012E }
+ \k i { 012F }
+ \k \i { 012F }
+ \. I { 0130 }
+ \^ J { 0134 }
+ \^ j { 0135 }
+ \^ \j { 0135 }
+ \c K { 0136 }
+ \c k { 0137 }
+ \' L { 0139 }
+ \' l { 013A }
+ \c L { 013B }
+ \c l { 013C }
+ \v L { 013D }
+ \v l { 013E }
+ \. L { 013F }
+ \. l { 0140 }
+ \' N { 0143 }
+ \' n { 0144 }
+ \c N { 0145 }
+ \c n { 0146 }
+ \v N { 0147 }
+ \v n { 0148 }
+ \= O { 014C }
+ \= o { 014D }
+ \u O { 014E }
+ \u o { 014F }
+ \H O { 0150 }
+ \H o { 0151 }
+ \' R { 0154 }
+ \' r { 0155 }
+ \c R { 0156 }
+ \c r { 0157 }
+ \v R { 0158 }
+ \v r { 0159 }
+ \' S { 015A }
+ \' s { 015B }
+ \^ S { 015C }
+ \^ s { 015D }
+ \c S { 015E }
+ \c s { 015F }
+ \v S { 0160 }
+ \v s { 0161 }
+ \c T { 0162 }
+ \c t { 0163 }
+ \v T { 0164 }
+ \v t { 0165 }
+ \~ U { 0168 }
+ \~ u { 0169 }
+ \= U { 016A }
+ \= u { 016B }
+ \u U { 016C }
+ \u u { 016D }
+ \r U { 016E }
+ \r u { 016F }
+ \H U { 0170 }
+ \H u { 0171 }
+ \k U { 0172 }
+ \k u { 0173 }
+ \^ W { 0174 }
+ \^ w { 0175 }
+ \^ Y { 0176 }
+ \^ y { 0177 }
+ \" Y { 0178 }
+ \' Z { 0179 }
+ \' z { 017A }
+ \. Z { 017B }
+ \. z { 017C }
+ \v Z { 017D }
+ \v z { 017E }
+ \v A { 01CD }
+ \v a { 01CE }
+ \v I { 01CF }
+ \v \i { 01D0 }
+ \v i { 01D0 }
+ \v O { 01D1 }
+ \v o { 01D2 }
+ \v U { 01D3 }
+ \v u { 01D4 }
+ \v G { 01E6 }
+ \v g { 01E7 }
+ \v K { 01E8 }
+ \v k { 01E9 }
+ \k O { 01EA }
+ \k o { 01EB }
+ \v \j { 01F0 }
+ \v j { 01F0 }
+ \' G { 01F4 }
+ \' g { 01F5 }
+ \` N { 01F8 }
+ \` n { 01F9 }
+ \' \AE { 01FC }
+ \' \ae { 01FD }
+ \' \O { 01FE }
+ \' \o { 01FF }
+ \v H { 021E }
+ \v h { 021F }
+ \. A { 0226 }
+ \. a { 0227 }
+ \c E { 0228 }
+ \c e { 0229 }
+ \. O { 022E }
+ \. o { 022F }
+ \= Y { 0232 }
+ \= y { 0233 }
+ \q_recursion_tail ? { }
+ \q_recursion_stop
+\group_end:
+%% File: l3legacy.dtx
+\prg_new_conditional:Npnn \legacy_if:n #1 { p , T , F , TF }
+ {
+ \exp_after:wN \reverse_if:N
+ \cs:w if#1 \cs_end:
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ }
+\cs_new_protected:Npn \legacy_if_set_true:n #1
+ { \cs_set_eq:cN { if#1 } \if_true: }
+\cs_new_protected:Npn \legacy_if_set_false:n #1
+ { \cs_set_eq:cN { if#1 } \if_false: }
+\cs_new_protected:Npn \legacy_if_gset_true:n #1
+ { \cs_gset_eq:cN { if#1 } \if_true: }
+\cs_new_protected:Npn \legacy_if_gset_false:n #1
+ { \cs_gset_eq:cN { if#1 } \if_false: }
+\cs_new_protected:Npn \legacy_if_set:nn #1#2
+ {
+ \bool_if:nTF {#2} \legacy_if_set_true:n \legacy_if_set_false:n
+ {#1}
+ }
+\cs_new_protected:Npn \legacy_if_gset:nn #1#2
+ {
+ \bool_if:nTF {#2} \legacy_if_gset_true:n \legacy_if_gset_false:n
+ {#1}
+ }
+%% File: l3deprecation.dtx
+\cs_new_protected:Npn \__kernel_patch_deprecation:nnNNpn #1#2#3#4#5#
+ { \__deprecation_patch_aux:nnNNnn {#1} {#2} #3 #4 {#5} }
+\cs_new_protected:Npn \__deprecation_patch_aux:nnNNnn #1#2#3#4#5#6
+ {
+ \__kernel_deprecation_code:nn
+ {
+ \tex_let:D #4 \scan_stop:
+ \__kernel_deprecation_error:Nnn #4 {#2} {#1}
+ }
+ { \tex_let:D #4 \scan_stop: }
+ \cs_if_eq:NNTF #3 \cs_gset_protected:Npn
+ { \__deprecation_warn_once:nnNnn {#1} {#2} #4 {#5} {#6} }
+ { \__deprecation_patch_aux:Nn #3 { #4 #5 {#6} } }
+ }
+\cs_new_protected:Npn \__deprecation_warn_once:nnNnn #1#2#3#4#5
+ {
+ \cs_gset_protected:Npe #3
+ {
+ \__kernel_if_debug:TF
+ {
+ \exp_not:N \msg_warning:nneee
+ { deprecation } { deprecated-command }
+ {#1}
+ { \token_to_str:N #3 }
+ { \tl_to_str:n {#2} }
+ }
+ { }
+ \exp_not:n { \cs_gset_protected:Npn #3 #4 {#5} }
+ \exp_not:N #3
+ }
+ \__kernel_deprecation_code:nn { }
+ { \cs_set_protected:Npn #3 #4 {#5} }
+ }
+\cs_new_protected:Npn \__deprecation_patch_aux:Nn #1#2
+ {
+ #1 #2
+ \cs_if_eq:NNTF #1 \cs_gset_protected:Npn
+ { \__kernel_deprecation_code:nn { } { \cs_set_protected:Npn #2 } }
+ { \__kernel_deprecation_code:nn { } { \cs_set:Npn #2 } }
+ }
+\cs_new_protected:Npn \__kernel_deprecation_error:Nnn #1#2#3
+ {
+ \tex_protected:D \tex_outer:D \tex_edef:D #1
+ {
+ \exp_not:N \msg_expandable_error:nnnnn
+ { deprecation } { deprecated-command }
+ { \tl_to_str:n {#3} } { \token_to_str:N #1 } { \tl_to_str:n {#2} }
+ \exp_not:N \msg_error:nneee
+ { deprecation } { deprecated-command }
+ { \tl_to_str:n {#3} } { \token_to_str:N #1 } { \tl_to_str:n {#2} }
+ }
+ }
+\msg_new:nnn { deprecation } { deprecated-command }
+ {
+ \tl_if_blank:nF {#3} { Use~ \tl_trim_spaces:n {#3} ~not~ }
+ #2~deprecated~on~#1.
+ }
+\cs_new:Npn \cs_argument_spec:N { \cs_parameter_spec:N }
+\cs_new_protected:Npn \iow_shipout_x:Nn { \iow_shipout_e:Nn }
+\cs_generate_variant:Nn \iow_shipout_x:Nn { Nx , c, cx }
+\cs_new_protected:cpn { \c__keys_props_root_str .str_set_x:N } #1
+ { \__keys_variable_set:NnnN #1 { str } { } x }
+\cs_new_protected:cpn { \c__keys_props_root_str .str_set_x:c } #1
+ { \__keys_variable_set:cnnN {#1} { str } { } x }
+\cs_new_protected:cpn { \c__keys_props_root_str .str_gset_x:N } #1
+ { \__keys_variable_set:NnnN #1 { str } { g } x }
+\cs_new_protected:cpn { \c__keys_props_root_str .str_gset_x:c } #1
+ { \__keys_variable_set:cnnN {#1} { str } { g } x }
+\cs_new_protected:cpn { \c__keys_props_root_str .tl_set_x:N } #1
+ { \__keys_variable_set:NnnN #1 { tl } { } x }
+\cs_new_protected:cpn { \c__keys_props_root_str .tl_set_x:c } #1
+ { \__keys_variable_set:cnnN {#1} { tl } { } x }
+\cs_new_protected:cpn { \c__keys_props_root_str .tl_gset_x:N } #1
+ { \__keys_variable_set:NnnN #1 { tl } { g } x }
+\cs_new_protected:cpn { \c__keys_props_root_str .tl_gset_x:c } #1
+ { \__keys_variable_set:cnnN {#1} { tl } { g } x }
+\cs_new_protected:Npn \keys_set_filter:nnn { \keys_set_exclude_groups:nnn }
+\cs_generate_variant:Nn \keys_set_filter:nnn { nnV , nnv , nno }
+\cs_new_protected:Npn \keys_set_filter:nnnN { \keys_set_exclude_groups:nnnN }
+\cs_generate_variant:Nn \keys_set_filter:nnnN { nnV , nnv , nno }
+\cs_new_protected:Npn \keys_set_filter:nnnnN { \keys_set_exclude_groups:nnnnN }
+\cs_generate_variant:Nn \keys_set_filter:nnnnN { nnV , nnv , nno }
+\__kernel_patch_deprecation:nnNNpn { 2024-02-13 } { \msg_set:nnnn }
+\cs_new_protected:Npn \msg_gset:nnnn { \msg_set:nnnn }
+\__kernel_patch_deprecation:nnNNpn { 2024-02-13 } { \msg_set:nnn }
+\cs_new_protected:Npn \msg_gset:nnn { \msg_set:nnn }
+\prop_new:N \g__pdf_object_prop
+\__kernel_patch_deprecation:nnNNpn { 2022-08-30 } { [\pdf_object_new:n] }
+\cs_new_protected:Npn \pdf_object_new:nn #1#2
+ {
+ \prop_gput:Nnn \g__pdf_object_prop {#1} {#2}
+ \__pdf_backend_object_new:n {#1}
+ }
+\__kernel_patch_deprecation:nnNNpn { 2022-08-30 } { [\pdf_object_write:n] }
+\cs_new_protected:Npn \pdf_object_write:nn #1#2
+ {
+ \exp_args:Nne \__pdf_backend_object_write:nnn
+ {#1} { \prop_item:Nn \g__pdf_object_prop {#1} } {#2}
+ \bool_gset_true:N \g__pdf_init_bool
+ }
+\cs_generate_variant:Nn \pdf_object_write:nn { nx }
+\__kernel_patch_deprecation:nnNNpn { 2023-05-03 } { \bool_case:n }
+\cs_new:Npn \bool_case_true:n { \bool_case:n }
+\__kernel_patch_deprecation:nnNNpn { 2023-05-03 } { \bool_case:nT }
+\cs_new:Npn \bool_case_true:nT { \bool_case:nT }
+\__kernel_patch_deprecation:nnNNpn { 2023-05-03 } { \bool_case:nF }
+\cs_new:Npn \bool_case_true:nF { \bool_case:nF }
+\__kernel_patch_deprecation:nnNNpn { 2023-05-03 } { \bool_case:nTF }
+\cs_new:Npn \bool_case_true:nTF { \bool_case:nTF }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_lowercase:n }
+\cs_new:Npn \str_lower_case:n { \str_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_lowercase:f }
+\cs_new:Npn \str_lower_case:f { \str_lowercase:f }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_uppercase:n }
+\cs_new:Npn \str_upper_case:n { \str_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_uppercase:f }
+\cs_new:Npn \str_upper_case:f { \str_uppercase:f }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_casefold:n }
+\cs_new:Npn \str_fold_case:n { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_casefold:V }
+\cs_new:Npn \str_fold_case:V { \str_casefold:V }
+\__kernel_patch_deprecation:nnNNpn { 2020-10-17 } { \str_casefold:n }
+\cs_new:Npn \str_foldcase:n { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_casefold:V }
+\cs_new:Npn \str_foldcase:V { \str_casefold:V }
+\__kernel_patch_deprecation:nnNNpn { 2020-08-20 } { }
+\cs_new_protected:Npn \str_declare_eight_bit_encoding:nnn #1
+ { \__str_declare_eight_bit_encoding:nnnn {#1} { 1114112 } }
+\__kernel_patch_deprecation:nnNNpn { 2020-06-18 } { \seq_map_indexed_inline:Nn }
+\cs_new_protected:Npn \seq_indexed_map_inline:Nn { \seq_map_indexed_inline:Nn }
+\__kernel_patch_deprecation:nnNNpn { 2020-06-18 } { \seq_map_indexed_function:NN }
+\cs_new:Npn \seq_indexed_map_function:NN { \seq_map_indexed_function:NN }
+\__kernel_patch_deprecation:nnNNpn { 2023-05-10 } { \seq_map_pairwise_function:NNN }
+\cs_new:Npn \seq_mapthread_function:NNN { \seq_map_pairwise_function:NNN }
+\__kernel_patch_deprecation:nnNNpn { 2023-10-26 } { \seq_set_map_e:NNn }
+\cs_new_protected:Npn \seq_set_map_x:NNn { \seq_set_map_e:NNn }
+\__kernel_patch_deprecation:nnNNpn { 2023-10-26 } { \seq_gset_map_e:NNn }
+\cs_new_protected:Npn \seq_gset_map_x:NNn { \seq_gset_map_e:NNn }
+\__kernel_patch_deprecation:nnNNpn { 2021-01-11 } { (no~longer~required) }
+\cs_new_protected:Npn \sys_load_deprecation: { }
+\__kernel_patch_deprecation:nnNNpn { 2023-07-08 } { \text_titlecase_first:n }
+\cs_new:Npn \text_titlecase:n #1
+ { \text_titlecase_first:n { \text_lowercase:n {#1} } }
+\__kernel_patch_deprecation:nnNNpn { 2023-07-08 } { \text_titlecase_first:nn }
+\cs_new:Npn \text_titlecase:nn #1#2
+ { \text_titlecase_first:nn {#1} { \text_lowercase:n {#2} } }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_lowercase:n }
+\cs_new:Npn \tl_lower_case:n #1
+ { \text_lowercase:n {#1} }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_lowercase:nn }
+\cs_new:Npn \tl_lower_case:nn #1#2
+ { \text_lowercase:nn {#1} {#2} }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_uppercase:n }
+\cs_new:Npn \tl_upper_case:n #1
+ { \text_uppercase:n {#1} }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_uppercase:nn }
+\cs_new:Npn \tl_upper_case:nn #1#2
+ { \text_uppercase:nn {#1} {#2} }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase_first:n }
+\cs_new:Npn \tl_mixed_case:n #1
+ { \text_titlecase_first:n {#1} }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase_first:nn }
+\cs_new:Npn \tl_mixed_case:nn #1#2
+ { \text_titlecase_first:nn {#1} {#2} }
+\__kernel_patch_deprecation:nnNNpn { 2022-05-23 } { \token_case_meaning:Nn }
+\cs_new:Npn \tl_case:Nn { \token_case_meaning:Nn }
+\__kernel_patch_deprecation:nnNNpn { 2022-05-23 } { \token_case_meaning:NnT }
+\cs_new:Npn \tl_case:NnT { \token_case_meaning:NnT }
+\__kernel_patch_deprecation:nnNNpn { 2022-05-23 } { \token_case_meaning:NnF }
+\cs_new:Npn \tl_case:NnF { \token_case_meaning:NnF }
+\__kernel_patch_deprecation:nnNNpn { 2022-05-23 } { \token_case_meaning:NnTF }
+\cs_new:Npn \tl_case:NnTF { \token_case_meaning:NnTF }
+\cs_generate_variant:Nn \tl_case:Nn { c }
+\prg_generate_conditional_variant:Nnn \tl_case:Nn
+ { c } { T , F , TF }
+\__kernel_patch_deprecation:nnNNpn { 2023-10-18 } { \tl_build_begin:N }
+\cs_new_protected:Npn \tl_build_clear:N { \tl_build_begin:N }
+\__kernel_patch_deprecation:nnNNpn { 2023-10-18 } { \tl_build_gbegin:N }
+\cs_new_protected:Npn \tl_build_gclear:N { \tl_build_gbegin:N }
+\__kernel_patch_deprecation:nnNNpn { 2023-10-25 } { \tl_build_get_intermediate:NN }
+\cs_new_protected:Npn \tl_build_get:NN { \tl_build_get_intermediate:NN }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-09 } { [ \codepoint_generate:nn ] }
+\cs_new:Npn \char_to_utfviii_bytes:n { \__kernel_codepoint_to_bytes:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-09 } { \codepoint_to_nfd:n }
+\cs_new:Npn \char_to_nfd:N #1 { \codepoint_to_nfd:n {`#1} }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-09 } { \codepoint_to_nfd:n }
+\cs_new:Npn \char_to_nfd:n { \codepoint_to_nfd:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_lowercase:n }
+\cs_new:Npn \char_lower_case:N { \text_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_uppercase:n }
+\cs_new:Npn \char_upper_case:N { \text_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase_first:n }
+\cs_new:Npn \char_mixed_case:N { \text_titlecase_first:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_casefold:n }
+\cs_new:Npn \char_fold_case:N { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_lowercase:n }
+\cs_new:Npn \char_str_lower_case:N { \str_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_uppercase:n }
+\cs_new:Npn \char_str_upper_case:N { \str_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_titlecase:n }
+\cs_new:Npn \char_str_mixed_case:N { \str_titlecase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_casefold:n }
+\cs_new:Npn \char_str_fold_case:N { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \text_lowercase:n }
+\cs_new:Npn \char_lowercase:N { \text_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \text_uppercase:n }
+\cs_new:Npn \char_uppercase:N { \text_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \text_titlecase_first:n }
+\cs_new:Npn \char_titlecase:N { \text_titlecase_first:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_casefold:n }
+\cs_new:Npn \char_foldcase:N { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_lowercase:n }
+\cs_new:Npn \char_str_lowercase:N { \str_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 }
+ { \tl_to_str:e { \text_titlecase_first:n } }
+\cs_new:Npn \char_str_titlecase:N #1
+ { \tl_to_str:e { \text_titlecase_first:n {#1} } }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_uppercase:n }
+\cs_new:Npn \char_str_uppercase:N { \str_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_casefold:n }
+\cs_new:Npn \char_str_foldcase:N { \str_casefold:n }
+\tl_map_inline:nn
+ {
+ { catcode } { catcode_remove }
+ { charcode } { charcode_remove }
+ { meaning } { meaning_remove }
+ }
+ {
+ \use:e
+ {
+ \__kernel_patch_deprecation:nnNNpn { 2022-01-11 } { \peek_remove_spaces:n }
+ \cs_gset_protected:Npn \exp_not:c { peek_ #1 _ignore_spaces:NTF } ##1##2##3
+ {
+ \peek_remove_spaces:n
+ { \exp_not:c { peek_ #1 :NTF } ##1 {##2} {##3} }
+ }
+ \__kernel_patch_deprecation:nnNNpn { 2022-01-11 } { \peek_remove_spaces:n }
+ \cs_gset_protected:Npn \exp_not:c { peek_ #1 _ignore_spaces:NT } ##1##2
+ {
+ \peek_remove_spaces:n
+ { \exp_not:c { peek_ #1 :NT } ##1 {##2} }
+ }
+ \__kernel_patch_deprecation:nnNNpn { 2022-01-11 } { \peek_remove_spaces:n }
+ \cs_gset_protected:Npn \exp_not:c { peek_ #1 _ignore_spaces:NF } ##1##2
+ {
+ \peek_remove_spaces:n
+ { \exp_not:c { peek_ #1 :NF } ##1 {##2} }
+ }
+ }
+ }
+\cs_new_protected:Npn \prop_put_if_new:Nnn { \prop_put_if_not_in:Nnn }
+\cs_new_protected:Npn \prop_gput_if_new:Nnn { \prop_gput_if_not_in:Nnn }
+\cs_generate_variant:Nn \prop_put_if_new:Nnn
+ { NnV , NV , c , cnV , cV }
+\cs_generate_variant:Nn \prop_gput_if_new:Nnn
+ { NnV , NV , c , cnV , cV }
+%%
+%%
+%% End of file `expl3-code.tex'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/expl3-generic.tex b/Master/texmf-dist/tex/latex-dev/l3kernel/expl3-generic.tex
new file mode 100644
index 00000000000..dd19a6f698a
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/expl3-generic.tex
@@ -0,0 +1,110 @@
+%%
+%% This is file `expl3-generic.tex',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% expl3.dtx (with options: `generic,loader')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: expl3.dtx
+\def\ExplFileDate{2024-04-11}%
+\let\ExplLoaderFileDate\ExplFileDate
+\begingroup
+ \catcode`\_=11
+ \expandafter
+ \ifx\csname c__kernel_expl_date_tl\endcsname\relax
+ \global\let\c__kernel_expl_date_tl\ExplFileDate
+ \fi
+\endgroup
+\begingroup
+ \def\tempa{LaTeX2e}%
+ \def\next{}%
+ \ifx\fmtname\tempa
+ \def\next
+ {%
+ \PackageInfo{expl3}{Switching from generic to LaTeX2e loader}%
+ \RequirePackage{expl3}\relax \endinput
+ }%
+ \fi
+\expandafter\endgroup
+\next
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname ver@expl3-generic.tex\endcsname\relax
+\else
+ \immediate\write-1
+ {%
+ Package expl3 Info: The package is already loaded.%
+ }%
+ \expandafter\endinput
+\fi
+\immediate\write-1
+ {%
+ Package: expl3
+ \ExplFileDate\space
+ L3 programming layer (loader)%
+ }%
+\expandafter\edef\csname ver@expl3-generic.tex\endcsname
+ {\ExplFileDate\space L3 programming layer}%
+\ifnum\currentgrouplevel=0 %
+\else
+ \errhelp{expl3 cannot be loaded inside a group:
+ reading the code has been aborted.}%
+ \errmessage{expl3 Error: Loading attempted inside a group!}%
+ \expandafter\endinput
+\fi
+\expandafter\edef\csname l@expl@tidy@tl\endcsname
+ {%
+ \catcode64=\the\catcode64\relax
+ \let\expandafter\noexpand\csname l@expl@tidy@tl\endcsname
+ \noexpand\undefined
+ }%
+\catcode64=11 %
+\let\expl@AtBeginDocument\AtBeginDocument
+\def\AtBeginDocument#1{#1}%
+\expandafter\def\expandafter\l@expl@tidy@tl\expandafter
+ {%
+ \l@expl@tidy@tl
+ \let\AtBeginDocument\expl@AtBeginDocument
+ \let\expl@AtBeginDocument\undefined
+ }%
+\input expl3-code.tex %
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname tex\string _let:D\endcsname\relax
+ \expandafter\endinput
+\fi
+\cs_gset_protected:Npn \__kernel_sys_configuration_load:n #1
+ {
+ \group_begin:
+ \cs_set_protected:Npn \ProvidesExplFile
+ {
+ \char_set_catcode_space:n { `\ }
+ \ProvidesExplFileAux
+ }
+ \cs_set_protected:Npn \ProvidesExplFileAux ##1##2##3##4
+ {
+ \group_end:
+ \iow_log:e { File:~##1~##2~v##3~##4 }
+ }
+ \tex_input:D #1 .def \scan_stop:
+ }
+\__kernel_sys_everyjob:
+\bool_new:N \g__kernel_backend_header_bool
+\bool_gset_true:N \g__kernel_backend_header_bool
+\sys_load_backend:n { }
+\ExplSyntaxOff
+\l@expl@tidy@tl
+%%
+%%
+%% End of file `expl3-generic.tex'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/expl3.ltx b/Master/texmf-dist/tex/latex-dev/l3kernel/expl3.ltx
new file mode 100644
index 00000000000..9c853155636
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/expl3.ltx
@@ -0,0 +1,199 @@
+%%
+%% This is file `expl3.ltx',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% expl3.dtx (with options: `2ekernel,loader')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: expl3.dtx
+\def\ExplFileDate{2024-04-11}%
+\let\ExplLoaderFileDate\ExplFileDate
+\begingroup
+ \catcode`\_=11
+ \expandafter
+ \ifx\csname c__kernel_expl_date_tl\endcsname\relax
+ \global\let\c__kernel_expl_date_tl\ExplFileDate
+ \fi
+\endgroup
+\ifdefined\LaTeXReleaseInfo
+ \LaTeXReleaseInfo\expandafter{\the\LaTeXReleaseInfo
+ \show@release@info{L3 programming layer <\ExplFileDate>}%
+ }%
+\else
+ \everyjob\expandafter{\the\everyjob
+ \message{L3 programming layer <\ExplFileDate>}%
+ }%
+\fi
+\protected\def\ProvidesExplPackage
+ {\@expl@provides@file@@Nnnnnn\ProvidesPackage{Package}}
+\protected\def\ProvidesExplClass
+ {\@expl@provides@file@@Nnnnnn\ProvidesClass{Document Class}}
+\protected\def\ProvidesExplFile
+ {\@expl@provides@file@@Nnnnnn\ProvidesFile{File}}
+\protected\long\def\@expl@provides@file@@Nnnnnn#1#2#3#4#5#6%
+ {%
+ \ifnum0%
+ \ifdefined#11\fi
+ \ifx\relax#1\else1\fi
+ =11
+ \expandafter#1%
+ \else
+ \@expl@provides@generic@@wnnw{#2}%
+ \fi
+ {#3}[{#4 \ifx\relax#5\relax\else\@expl@provides@file@@N#5\space\fi #6}]%
+ \ExplSyntaxOn
+ }
+\long\def\@expl@provides@file@@N#1{v\if#1v\else#1\fi}
+\protected\long\def\@expl@provides@generic@@wnnw#1\fi#2[#3]%
+ {%
+ \immediate\write-1{#1: #2 #3}%
+ }
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname tex\string _let:D\endcsname\relax
+ \expandafter\@firstofone
+\else
+ \expandafter\@gobble
+\fi
+ {\input expl3-code.tex }%
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname tex\string _let:D\endcsname\relax
+ \expandafter\endinput
+\fi
+\ifcsname\detokenize{g__expl_reload_bool}\endcsname
+ \ExplSyntaxOn
+ \bool_gset_true:N \g__expl_reload_bool
+\else
+ \bool_new:N \g__expl_reload_bool
+\fi
+\tl_if_exist:NF \c__expl_def_ext_tl
+ { \tl_const:Nn \c__expl_def_ext_tl { def } }
+\cs_gset_protected:Npn \__kernel_sys_configuration_load:n #1
+ {
+ \cs_undefine:c { ver@ #1 .def }
+ \@onefilewithoptions {#1} [ ] [ ]
+ \c__expl_def_ext_tl
+ }
+\keys_define:nn { sys }
+ {
+ backend .choices:nn =
+ { dvipdfmx , dvips , dvisvgm , luatex , pdftex , pdfmode , xdvipdfmx , xetex }
+ { \sys_load_backend:n {#1} } ,
+ check-declarations .choice: ,
+ check-declarations / true .code:n =
+ { \debug_on:n { check-declarations } } ,
+ check-declarations / false .code:n =
+ {
+ \__kernel_if_debug:TF
+ { \debug_off:n { check-declarations } }
+ { }
+ } ,
+ check-declarations .default:n = true ,
+ driver .meta:n = { backend = #1 } ,
+ enable-debug .code:n = {} , % A stub
+ log-functions .choice: ,
+ log-functions / true .code:n =
+ { \debug_on:n { log-functions } } ,
+ log-functions / false .code:n =
+ {
+ \__kernel_if_debug:TF
+ { \debug_off:n { log-functions } }
+ { }
+ } ,
+ log-functions .default:n = true ,
+ suppress-backend-headers .bool_gset_inverse:N
+ = \g__kernel_backend_header_bool ,
+ suppress-backend-headers .initial:n = false ,
+ undo-recent-deprecations .code:n = {} % A stub
+ }
+\cs_if_exist:NF \@expl@sys@load@backend@@
+ {
+ \tl_put_left:Nn \document
+ {
+ \endgroup
+ \@expl@sys@load@backend@@
+ \begingroup
+ }
+ }
+\cs_gset_protected:Npn \@expl@sys@load@backend@@
+ {
+ \str_if_exist:NF \c_sys_backend_str
+ { \sys_load_backend:n { } }
+ }
+\cs_if_free:cTF { ver@expl3.sty }
+ {
+ \tex_everyjob:D \exp_after:wN
+ {
+ \tex_the:D \tex_everyjob:D
+ \__kernel_sys_everyjob:
+ }
+ }
+ { \__kernel_sys_everyjob: }
+\reverse_if:N \if_cs_exist:N \s__expl_stop
+ \scan_new:N \s__expl_stop
+\fi:
+\cs_if_exist:NF \@expl@push@filename@@
+ {
+ \tl_put_left:Nn \@pushfilename { \@expl@push@filename@@ }
+ \tl_put_right:Nn \@pushfilename { \@expl@push@filename@aux@@ }
+ }
+\cs_gset_protected:Npn \@expl@push@filename@@
+ {
+ \exp_args:Ne \__kernel_file_input_push:n
+ {
+ \tl_to_str:N \@currname .
+ \tl_to_str:N \@currext
+ }
+ \tl_put_left:Ne \l__expl_status_stack_tl
+ {
+ \bool_if:NTF \l__kernel_expl_bool
+ { 1 }
+ { 0 }
+ }
+ \ExplSyntaxOff
+ }
+\cs_gset_protected:Npn \@expl@push@filename@aux@@ #1#2#3
+ {
+ \str_gset:Nn \g_file_curr_name_str {#3}
+ #1 #2 {#3}
+ }
+\cs_if_exist:NF \@expl@pop@filename@@
+ {
+ \tl_put_right:Nn \@popfilename
+ { \@expl@pop@filename@@ }
+ }
+\cs_gset_protected:Npn \@expl@pop@filename@@
+ {
+ \__kernel_file_input_pop:
+ \tl_if_empty:NTF \l__expl_status_stack_tl
+ { \ExplSyntaxOff }
+ { \exp_after:wN \__expl_status_pop:w \l__expl_status_stack_tl \s__expl_stop }
+ }
+\cs_gset_protected:Npn \__expl_status_pop:w #1#2 \s__expl_stop
+ {
+ \tl_set:Nn \l__expl_status_stack_tl {#2}
+ \int_if_odd:nTF {#1}
+ { \ExplSyntaxOn }
+ { \ExplSyntaxOff }
+ }
+\tl_if_exist:NF \l__expl_status_stack_tl
+ {
+ \tl_new:N \l__expl_status_stack_tl
+ \tl_set:Nn \l__expl_status_stack_tl { 0 }
+ }
+\ExplSyntaxOff
+%%
+%%
+%% End of file `expl3.ltx'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/expl3.lua b/Master/texmf-dist/tex/latex-dev/l3kernel/expl3.lua
new file mode 100644
index 00000000000..f436dc4daf3
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/expl3.lua
@@ -0,0 +1,618 @@
+--
+-- This is file `expl3.lua',
+-- generated with the docstrip utility.
+--
+-- The original source files were:
+--
+-- l3luatex.dtx (with options: `package,lua')
+-- l3names.dtx (with options: `package,lua')
+-- l3sys.dtx (with options: `package,lua')
+-- l3token.dtx (with options: `package,lua')
+-- l3intarray.dtx (with options: `package,lua')
+-- l3pdf.dtx (with options: `package,lua')
+--
+-- Copyright (C) 1990-2024 The LaTeX Project
+--
+-- It may be distributed and/or modified under the conditions of
+-- the LaTeX Project Public License (LPPL), either version 1.3c of
+-- this license or (at your option) any later version. The latest
+-- version of this license is in the file:
+--
+-- https://www.latex-project.org/lppl.txt
+--
+-- This file is part of the "l3kernel bundle" (The Work in LPPL)
+-- and all files in that bundle must be distributed together.
+--
+-- File: l3luatex.dtx
+ltx = ltx or {utils={}}
+ltx.utils = ltx.utils or { }
+local ltxutils = ltx.utils
+local io = io
+local kpse = kpse
+local lfs = lfs
+local math = math
+local md5 = md5
+local os = os
+local string = string
+local tex = tex
+local texio = texio
+local tonumber = tonumber
+local abs = math.abs
+local byte = string.byte
+local floor = math.floor
+local format = string.format
+local gsub = string.gsub
+local lfs_attr = lfs.attributes
+local open = io.open
+local os_date = os.date
+local setcatcode = tex.setcatcode
+local sprint = tex.sprint
+local cprint = tex.cprint
+local write = tex.write
+local write_nl = texio.write_nl
+local utf8_char = utf8.char
+local package_loaded = package.loaded
+local package_searchers = package.searchers
+local table_concat = table.concat
+
+local scan_int = token.scan_int or token.scan_integer
+local scan_string = token.scan_string
+local scan_keyword = token.scan_keyword
+local put_next = token.put_next
+local token_create = token.create
+local token_new = token.new
+local set_macro = token.set_macro
+local token_create_safe
+do
+ local is_defined = token.is_defined
+ local set_char = token.set_char
+ local runtoks = tex.runtoks
+ local let_token = token_create'let'
+
+ function token_create_safe(s)
+ local orig_token = token_create(s)
+ if is_defined(s, true) then
+ return orig_token
+ end
+ set_char(s, 0)
+ local new_token = token_create(s)
+ runtoks(function()
+ put_next(let_token, new_token, orig_token)
+ end)
+ return new_token
+ end
+end
+
+local true_tok = token_create_safe'prg_return_true:'
+local false_tok = token_create_safe'prg_return_false:'
+local command_id = token.command_id
+if not command_id and tokens and tokens.commands then
+ local id_map = tokens.commands
+ function command_id(name)
+ return id_map[name]
+ end
+end
+local kpse_find = (resolvers and resolvers.findfile) or kpse.find_file
+local function escapehex(str)
+ return (gsub(str, ".",
+ function (ch) return format("%02X", byte(ch)) end))
+end
+local function filedump(name,offset,length)
+ local file = kpse_find(name,"tex",true)
+ if not file then return end
+ local f = open(file,"rb")
+ if not f then return end
+ if offset and offset > 0 then
+ f:seek("set", offset)
+ end
+ local data = f:read(length or 'a')
+ f:close()
+ return escapehex(data)
+end
+ltxutils.filedump = filedump
+local md5_HEX = md5.HEX
+if not md5_HEX then
+ local md5_sum = md5.sum
+ function md5_HEX(data)
+ return escapehex(md5_sum(data))
+ end
+ md5.HEX = md5_HEX
+end
+local function filemd5sum(name)
+ local file = kpse_find(name, "tex", true) if not file then return end
+ local f = open(file, "rb") if not f then return end
+
+ local data = f:read("*a")
+ f:close()
+ return md5_HEX(data)
+end
+ltxutils.filemd5sum = filemd5sum
+local filemoddate
+if os_date'%z':match'^[+-]%d%d%d%d$' then
+ local pattern = lpeg.Cs(16 *
+ (lpeg.Cg(lpeg.S'+-' * '0000' * lpeg.Cc'Z')
+ + 3 * lpeg.Cc"'" * 2 * lpeg.Cc"'"
+ + lpeg.Cc'Z')
+ * -1)
+ function filemoddate(name)
+ local file = kpse_find(name, "tex", true)
+ if not file then return end
+ local date = lfs_attr(file, "modification")
+ if not date then return end
+ return pattern:match(os_date("D:%Y%m%d%H%M%S%z", date))
+ end
+else
+ local function filemoddate(name)
+ local file = kpse_find(name, "tex", true)
+ if not file then return end
+ local date = lfs_attr(file, "modification")
+ if not date then return end
+ local d = os_date("*t", date)
+ local u = os_date("!*t", date)
+ local off = 60 * (d.hour - u.hour) + d.min - u.min
+ if d.year ~= u.year then
+ if d.year > u.year then
+ off = off + 1440
+ else
+ off = off - 1440
+ end
+ elseif d.yday ~= u.yday then
+ if d.yday > u.yday then
+ off = off + 1440
+ else
+ off = off - 1440
+ end
+ end
+ local timezone
+ if off == 0 then
+ timezone = "Z"
+ else
+ if off < 0 then
+ timezone = "-"
+ off = -off
+ else
+ timezone = "+"
+ end
+ timezone = format("%s%02d'%02d'", timezone, hours // 60, hours % 60)
+ end
+ return format("D:%04d%02d%02d%02d%02d%02d%s",
+ d.year, d.month, d.day, d.hour, d.min, d.sec, timezone)
+ end
+end
+ltxutils.filemoddate = filemoddate
+local function filesize(name)
+ local file = kpse_find(name, "tex", true)
+ if file then
+ local size = lfs_attr(file, "size")
+ if size then
+ return size
+ end
+ end
+end
+ltxutils.filesize = filesize
+local luacmd do
+ local set_lua = token.set_lua
+ local undefined_cs = command_id'undefined_cs'
+
+ if not context and not luatexbase then require'ltluatex' end
+ if luatexbase then
+ local new_luafunction = luatexbase.new_luafunction
+ local functions = lua.get_functions_table()
+ function luacmd(name, func, ...)
+ local id
+ local tok = token_create(name)
+ if tok.command == undefined_cs then
+ id = new_luafunction(name)
+ set_lua(name, id, ...)
+ else
+ id = tok.index or tok.mode
+ end
+ functions[id] = func
+ end
+ elseif context then
+ local register = context.functions.register
+ local functions = context.functions.known
+ function luacmd(name, func, ...)
+ local tok = token_create(name)
+ if tok.command == undefined_cs then
+ token.set_lua(name, register(func), ...)
+ else
+ functions[tok.index or tok.mode] = func
+ end
+ end
+ end
+end
+local function try_require(name)
+ if package_loaded[name] then
+ return true, package_loaded[name]
+ end
+
+ local failure_details = {}
+ for _, searcher in ipairs(package_searchers) do
+ local loader, data = searcher(name)
+ if type(loader) == 'function' then
+ package_loaded[name] = loader(name, data) or true
+ return true, package_loaded[name]
+ elseif type(loader) == 'string' then
+ failure_details[#failure_details + 1] = loader
+ end
+ end
+
+ return false, table_concat(failure_details, '\n')
+end
+local char_given = command_id'char_given'
+local c_true_bool = token_create(1, char_given)
+local c_false_bool = token_create(0, char_given)
+local c_str_cctab = token_create('c_str_cctab').mode
+
+luacmd('__lua_load_module_p:n', function()
+ local success, result = try_require(scan_string())
+ if success then
+ set_macro(c_str_cctab, 'l__lua_err_msg_str', '')
+ put_next(c_true_bool)
+ else
+ set_macro(c_str_cctab, 'l__lua_err_msg_str', result)
+ put_next(c_false_bool)
+ end
+end)
+local register_luadata, get_luadata
+
+if luatexbase then
+ local register = token_create'@expl@luadata@bytecode'.index
+ if status.ini_version then
+ local luadata, luadata_order = {}, {}
+
+ function register_luadata(name, func)
+ if luadata[name] then
+ error(format("LaTeX error: data name %q already in use", name))
+ end
+ luadata[name] = func
+ luadata_order[#luadata_order + 1] = func and name
+ end
+ luatexbase.add_to_callback("pre_dump", function()
+ if next(luadata) then
+ local str = "return {"
+ for i=1, #luadata_order do
+ local name = luadata_order[i]
+ str = format('%s[%q]=%s,', str, name, luadata[name]())
+ end
+ lua.bytecode[register] = assert(load(str .. "}"))
+ end
+ end, "ltx.luadata")
+ else
+ local luadata = lua.bytecode[register]
+ if luadata then
+ lua.bytecode[register] = nil
+ luadata = luadata()
+ end
+ function get_luadata(name)
+ if not luadata then return end
+ local data = luadata[name]
+ luadata[name] = nil
+ return data
+ end
+ end
+end
+-- File: l3names.dtx
+local minus_tok = token_new(string.byte'-', 12)
+local zero_tok = token_new(string.byte'0', 12)
+local one_tok = token_new(string.byte'1', 12)
+luacmd('tex_strcmp:D', function()
+ local first = scan_string()
+ local second = scan_string()
+ if first < second then
+ put_next(minus_tok, one_tok)
+ else
+ put_next(first == second and zero_tok or one_tok)
+ end
+end, 'global')
+local sprint = tex.sprint
+local cprint = tex.cprint
+luacmd('tex_Ucharcat:D', function()
+ local charcode = scan_int()
+ local catcode = scan_int()
+ if catcode == 10 then
+ sprint(token_new(charcode, 10))
+ else
+ cprint(catcode, utf8_char(charcode))
+ end
+end, 'global')
+luacmd('tex_filesize:D', function()
+ local size = filesize(scan_string())
+ if size then write(size) end
+end, 'global')
+luacmd('tex_mdfivesum:D', function()
+ local hash
+ if scan_keyword"file" then
+ hash = filemd5sum(scan_string())
+ else
+ hash = md5_HEX(scan_string())
+ end
+ if hash then write(hash) end
+end, 'global')
+luacmd('tex_filemoddate:D', function()
+ local date = filemoddate(scan_string())
+ if date then write(date) end
+end, 'global')
+luacmd('tex_filedump:D', function()
+ local offset = scan_keyword'offset' and scan_int() or nil
+ local length = scan_keyword'length' and scan_int()
+ or not scan_keyword'whole' and 0 or nil
+ local data = filedump(scan_string(), offset, length)
+ if data then write(data) end
+end, 'global')
+-- File: l3sys.dtx
+do
+ local os_exec = os.execute
+
+ local function shellescape(cmd)
+ local status,msg = os_exec(cmd)
+ if status == nil then
+ write_nl("log","runsystem(" .. cmd .. ")...(" .. msg .. ")\n")
+ elseif status == 0 then
+ write_nl("log","runsystem(" .. cmd .. ")...executed\n")
+ else
+ write_nl("log","runsystem(" .. cmd .. ")...failed " .. (msg or "") .. "\n")
+ end
+ end
+ luacmd("__sys_shell_now:e", function()
+ shellescape(scan_string())
+ end, "global", "protected")
+ local new_latelua = nodes and nodes.nuts and nodes.nuts.pool and nodes.nuts.pool.latelua or (function()
+ local whatsit_id = node.id'whatsit'
+ local latelua_sub = node.subtype'late_lua'
+ local node_new = node.direct.new
+ local setfield = node.direct.setwhatsitfield or node.direct.setfield
+ return function(f)
+ local n = node_new(whatsit_id, latelua_sub)
+ setfield(n, 'data', f)
+ return n
+ end
+ end)()
+ local node_write = node.direct.write
+
+ luacmd("__sys_shell_shipout:e", function()
+ local cmd = scan_string()
+ node_write(new_latelua(function() shellescape(cmd) end))
+ end, "global", "protected")
+end
+ local gettimeofday = os.gettimeofday
+ local epoch = gettimeofday() - os.clock()
+ local write = tex.write
+ local tointeger = math.tointeger
+ luacmd('__sys_elapsedtime:', function()
+ write(tointeger((gettimeofday() - epoch)*65536 // 1))
+ end, 'global')
+-- File: l3token.dtx
+do
+ local get_next = token.get_next
+ local get_command = token.get_command
+ local get_index = token.get_index
+ local get_mode = token.get_mode or token.get_index
+ local cmd = command_id
+ local set_font = cmd'get_font'
+ local biggest_char = token.biggest_char and token.biggest_char()
+ or status.getconstants().max_character_code
+
+ local mode_below_biggest_char = {}
+ local index_not_nil = {}
+ local mode_not_null = {}
+ local non_primitive = {
+ [cmd'left_brace'] = true,
+ [cmd'right_brace'] = true,
+ [cmd'math_shift'] = true,
+ [cmd'mac_param' or cmd'parameter'] = mode_below_biggest_char,
+ [cmd'sup_mark' or cmd'superscript'] = true,
+ [cmd'sub_mark' or cmd'subscript'] = true,
+ [cmd'endv' or cmd'ignore'] = true,
+ [cmd'spacer'] = true,
+ [cmd'letter'] = true,
+ [cmd'other_char'] = true,
+ [cmd'tab_mark' or cmd'alignment_tab'] = mode_below_biggest_char,
+ [cmd'char_given'] = true,
+ [cmd'math_given' or 'math_char_given'] = true,
+ [cmd'xmath_given' or 'math_char_xgiven'] = true,
+ [cmd'set_font'] = mode_not_null,
+ [cmd'undefined_cs'] = true,
+ [cmd'call'] = true,
+ [cmd'long_call' or cmd'protected_call'] = true,
+ [cmd'outer_call' or cmd'tolerant_call'] = true,
+ [cmd'long_outer_call' or cmd'tolerant_protected_call'] = true,
+ [cmd'assign_glue' or cmd'register_glue'] = index_not_nil,
+ [cmd'assign_mu_glue' or cmd'register_mu_glue' or cmd'register_muglue'] = index_not_nil,
+ [cmd'assign_toks' or cmd'register_toks'] = index_not_nil,
+ [cmd'assign_int' or cmd'register_int' or cmd'register_integer'] = index_not_nil,
+ [cmd'assign_attr' or cmd'register_attribute'] = true,
+ [cmd'assign_dimen' or cmd'register_dimen' or cmd'register_dimension'] = index_not_nil,
+ }
+
+ luacmd("__token_if_primitive_lua:N", function()
+ local tok = get_next()
+ local is_non_primitive = non_primitive[get_command(tok)]
+ return put_next(
+ is_non_primitive == true
+ and false_tok
+ or is_non_primitive == nil
+ and true_tok
+ or is_non_primitive == mode_not_null
+ and (get_mode(tok) == 0 and true_tok or false_tok)
+ or is_non_primitive == index_not_nil
+ and (get_index(tok) and false_tok or true_tok)
+ or is_non_primitive == mode_below_biggest_char
+ and (get_mode(tok) > biggest_char and true_tok or false_tok))
+ end, "global")
+end
+-- File: l3intarray.dtx
+luacmd('__intarray:w', function()
+ scan_int()
+ tex.error'LaTeX Error: Isolated intarray ignored'
+end, 'protected', 'global')
+
+local scan_token = token.scan_token
+local put_next = token.put_next
+local intarray_marker = token_create_safe'__intarray:w'
+local use_none = token_create_safe'use_none:n'
+local use_i = token_create_safe'use:n'
+local expand_after_scan_stop = {token_create_safe'exp_after:wN',
+ token_create_safe'scan_stop:'}
+local comma = token_create(string.byte',')
+local __intarray_table do
+ local tables = get_luadata and get_luadata'__intarray' or {[0] = {}}
+ function __intarray_table()
+ local t = scan_token()
+ if t ~= intarray_marker then
+ put_next(t)
+ tex.error'LaTeX Error: intarray expected'
+ return tables[0]
+ end
+ local i = scan_int()
+ local current_table = tables[i]
+ if current_table then return current_table end
+ current_table = {}
+ tables[i] = current_table
+ return current_table
+ end
+ if register_luadata then
+ register_luadata('__intarray', function()
+ local t = "{[0]={},"
+ for i=1, #tables do
+ t = string.format("%s{%s},", t, table.concat(tables[i], ','))
+ end
+ return t .. "}"
+ end)
+ end
+end
+
+local sprint = tex.sprint
+
+luacmd('__intarray_gset_count:Nw', function()
+ local t = __intarray_table()
+ local n = scan_int()
+ for i=#t+1, n do t[i] = 0 end
+end, 'protected', 'global')
+
+luacmd('intarray_count:N', function()
+ sprint(-2, #__intarray_table())
+end, 'global')
+luacmd('__intarray_gset:wF', function()
+ local i = scan_int()
+ local t = __intarray_table()
+ if t[i] then
+ t[i] = scan_int()
+ put_next(use_none)
+ else
+ tex.count.l__intarray_bad_index_int = i
+ scan_int()
+ put_next(use_i)
+ end
+end, 'protected', 'global')
+
+luacmd('__intarray_gset:w', function()
+ local i = scan_int()
+ local t = __intarray_table()
+ t[i] = scan_int()
+end, 'protected', 'global')
+luacmd('intarray_gzero:N', function()
+ local t = __intarray_table()
+ for i=1, #t do
+ t[i] = 0
+ end
+end, 'global', 'protected')
+luacmd('__intarray_item:wF', function()
+ local i = scan_int()
+ local t = __intarray_table()
+ local item = t[i]
+ if item then
+ put_next(use_none)
+ else
+ tex.l__intarray_bad_index_int = i
+ put_next(use_i)
+ end
+ put_next(expand_after_scan_stop)
+ scan_token()
+ if item then
+ sprint(-2, item)
+ end
+end, 'global')
+
+luacmd('__intarray_item:w', function()
+ local i = scan_int()
+ local t = __intarray_table()
+ sprint(-2, t[i])
+end, 'global')
+local concat = table.concat
+luacmd('__intarray_to_clist:Nn', function()
+ local t = __intarray_table()
+ local sep = token.scan_string()
+ sprint(-2, concat(t, sep))
+end, 'global')
+luacmd('__intarray_range_to_clist:w', function()
+ local t = __intarray_table()
+ local from = scan_int()
+ local to = scan_int()
+ sprint(-2, concat(t, ',', from, to))
+end, 'global')
+luacmd('__intarray_gset_range:w', function()
+ local from = scan_int()
+ local t = __intarray_table()
+ while true do
+ local tok = scan_token()
+ if tok == comma then
+ repeat
+ tok = scan_token()
+ until tok ~= comma
+ break
+ else
+ put_next(tok)
+ end
+ t[from] = scan_int()
+ scan_token()
+ from = from + 1
+ end
+ end, 'global', 'protected')
+-- File: l3pdf.dtx
+
+local scan_int = token.scan_int
+local scan_string = token.scan_string
+local cprint = tex.cprint
+
+local __pdf_objects_named = {}
+local __pdf_objects_indexed = {}
+
+luacmd('__pdf_object_record:nN', function()
+ local name = scan_string()
+ local n = scan_int()
+ __pdf_objects_named[name] = n
+end,'protected','global')
+
+local function object_id(name,index)
+ if index then
+ return __pdf_objects_indexed[name][index] or 0
+ else
+ return __pdf_objects_named[name] or 0
+ end
+end
+
+luacmd('__pdf_object_retrieve:n', function()
+ local name = scan_string()
+ return cprint(12,tostring(object_id(name)))
+end,'global')
+
+ltx.pdf = ltx.pdf or {}
+ltx.pdf.object_id = object_id
+
+
+luacmd('__pdf_object_record:nnN', function()
+ local name = scan_string()
+ local index = tonumber(scan_string())
+ local n = scan_int()
+ __pdf_objects_indexed[name] = __pdf_objects_indexed[name] or {}
+ __pdf_objects_indexed[name][index] = n
+end,'protected','global')
+
+luacmd('__pdf_object_retrieve:nn', function()
+ local name = scan_string()
+ local index = tonumber(scan_string())
+ return cprint(12,tostring(object_id(name,index)))
+end,'global')
+
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/expl3.sty b/Master/texmf-dist/tex/latex-dev/l3kernel/expl3.sty
new file mode 100644
index 00000000000..17f9599acdb
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/expl3.sty
@@ -0,0 +1,227 @@
+%%
+%% This is file `expl3.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% expl3.dtx (with options: `package,loader')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: expl3.dtx
+\def\ExplFileDate{2024-04-11}%
+\let\ExplLoaderFileDate\ExplFileDate
+\ProvidesPackage{expl3}
+ [%
+ \ExplFileDate\space
+ L3 programming layer (loader)
+ ]%
+\protected\def\ProvidesExplPackage
+ {\@expl@provides@file@@Nnnnnn\ProvidesPackage{Package}}
+\protected\def\ProvidesExplClass
+ {\@expl@provides@file@@Nnnnnn\ProvidesClass{Document Class}}
+\protected\def\ProvidesExplFile
+ {\@expl@provides@file@@Nnnnnn\ProvidesFile{File}}
+\protected\long\def\@expl@provides@file@@Nnnnnn#1#2#3#4#5#6%
+ {%
+ \ifnum0%
+ \ifdefined#11\fi
+ \ifx\relax#1\else1\fi
+ =11
+ \expandafter#1%
+ \else
+ \@expl@provides@generic@@wnnw{#2}%
+ \fi
+ {#3}[{#4 \ifx\relax#5\relax\else\@expl@provides@file@@N#5\space\fi #6}]%
+ \ExplSyntaxOn
+ }
+\long\def\@expl@provides@file@@N#1{v\if#1v\else#1\fi}
+\protected\long\def\@expl@provides@generic@@wnnw#1\fi#2[#3]%
+ {%
+ \immediate\write-1{#1: #2 #3}%
+ }
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname tex\string _let:D\endcsname\relax
+ \expandafter\@firstofone
+\else
+ \expandafter\@gobble
+\fi
+ {\input expl3-code.tex }%
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname tex\string _let:D\endcsname\relax
+ \expandafter\endinput
+\fi
+\ifcsname\detokenize{c__kernel_expl_date_tl}\endcsname
+ \expandafter\@firstofone
+\else
+ \ifodd\csname\detokenize{l__kernel_expl_bool}\endcsname
+ \global\expandafter\let\csname\detokenize
+ {c__kernel_expl_date_tl}\endcsname\ExplLoaderFileDate
+ \expandafter\expandafter
+ \expandafter\@gobble
+ \else
+ \expandafter\expandafter
+ \expandafter\@firstofone
+ \fi
+\fi
+ {\csname\detokenize{__kernel_dependency_version_check:Nn}\endcsname
+ \ExplLoaderFileDate{expl3.sty}}%
+\ifcsname\detokenize{g__expl_reload_bool}\endcsname
+ \ExplSyntaxOn
+ \bool_gset_true:N \g__expl_reload_bool
+\else
+ \bool_new:N \g__expl_reload_bool
+\fi
+\tl_if_exist:NF \c__expl_def_ext_tl
+ { \tl_const:Nn \c__expl_def_ext_tl { def } }
+\cs_gset_protected:Npn \__kernel_sys_configuration_load:n #1
+ {
+ \ExplSyntaxOff
+ \cs_undefine:c { ver@ #1 .def }
+ \@onefilewithoptions {#1} [ ] [ ]
+ \c__expl_def_ext_tl
+ \ExplSyntaxOn
+ }
+\cs_gset_protected:Npn \__kernel_sys_configuration_load_std:n #1
+ {
+ \cs_undefine:c { ver@ #1 .def }
+ \@onefilewithoptions {#1} [ ] [ ]
+ \c__expl_def_ext_tl
+ }
+\clist_if_exist:NF \l__expl_options_clist
+ { \clist_new:N \l__expl_options_clist }
+\DeclareOption*
+ { \clist_put_right:NV \l__expl_options_clist \CurrentOption }
+\ProcessOptions \relax
+\keys_define:nn { sys }
+ {
+ backend .choices:nn =
+ { dvipdfmx , dvips , dvisvgm , luatex , pdftex , pdfmode , xdvipdfmx , xetex }
+ { \sys_load_backend:n {#1} } ,
+ check-declarations .choice: ,
+ check-declarations / true .code:n =
+ { \debug_on:n { check-declarations } } ,
+ check-declarations / false .code:n =
+ {
+ \__kernel_if_debug:TF
+ { \debug_off:n { check-declarations } }
+ { }
+ } ,
+ check-declarations .default:n = true ,
+ driver .meta:n = { backend = #1 } ,
+ enable-debug .code:n = {} , % A stub
+ log-functions .choice: ,
+ log-functions / true .code:n =
+ { \debug_on:n { log-functions } } ,
+ log-functions / false .code:n =
+ {
+ \__kernel_if_debug:TF
+ { \debug_off:n { log-functions } }
+ { }
+ } ,
+ log-functions .default:n = true ,
+ suppress-backend-headers .bool_gset_inverse:N
+ = \g__kernel_backend_header_bool ,
+ suppress-backend-headers .initial:n = false ,
+ undo-recent-deprecations .code:n = {} % A stub
+ }
+\cs_if_exist:NF \@expl@sys@load@backend@@
+ {
+ \tl_put_left:Nn \document
+ {
+ \endgroup
+ \@expl@sys@load@backend@@
+ \begingroup
+ }
+ }
+\cs_gset_protected:Npn \@expl@sys@load@backend@@
+ {
+ \str_if_exist:NF \c_sys_backend_str
+ { \sys_load_backend:n { } }
+ }
+\keys_set:nV { sys } \l__expl_options_clist
+\str_if_exist:NF \c_sys_backend_str
+ { \sys_load_backend:n { } }
+\bool_if:NT \g__expl_reload_bool
+ {
+ \cs_gset_eq:NN \__kernel_sys_configuration_load:n
+ \__kernel_sys_configuration_load_std:n
+ \ExplSyntaxOff
+ \file_input_stop:
+ }
+\cs_if_free:cTF { ver@expl3.sty }
+ {
+ \tex_everyjob:D \exp_after:wN
+ {
+ \tex_the:D \tex_everyjob:D
+ \__kernel_sys_everyjob:
+ }
+ }
+ { \__kernel_sys_everyjob: }
+\reverse_if:N \if_cs_exist:N \s__expl_stop
+ \scan_new:N \s__expl_stop
+\fi:
+\cs_if_exist:NF \@expl@push@filename@@
+ {
+ \tl_put_left:Nn \@pushfilename { \@expl@push@filename@@ }
+ \tl_put_right:Nn \@pushfilename { \@expl@push@filename@aux@@ }
+ }
+\cs_gset_protected:Npn \@expl@push@filename@@
+ {
+ \exp_args:Ne \__kernel_file_input_push:n
+ {
+ \tl_to_str:N \@currname .
+ \tl_to_str:N \@currext
+ }
+ \tl_put_left:Ne \l__expl_status_stack_tl
+ {
+ \bool_if:NTF \l__kernel_expl_bool
+ { 1 }
+ { 0 }
+ }
+ \ExplSyntaxOff
+ }
+\cs_gset_protected:Npn \@expl@push@filename@aux@@ #1#2#3
+ {
+ \str_gset:Nn \g_file_curr_name_str {#3}
+ #1 #2 {#3}
+ }
+\cs_if_exist:NF \@expl@pop@filename@@
+ {
+ \tl_put_right:Nn \@popfilename
+ { \@expl@pop@filename@@ }
+ }
+\cs_gset_protected:Npn \@expl@pop@filename@@
+ {
+ \__kernel_file_input_pop:
+ \tl_if_empty:NTF \l__expl_status_stack_tl
+ { \ExplSyntaxOff }
+ { \exp_after:wN \__expl_status_pop:w \l__expl_status_stack_tl \s__expl_stop }
+ }
+\cs_gset_protected:Npn \__expl_status_pop:w #1#2 \s__expl_stop
+ {
+ \tl_set:Nn \l__expl_status_stack_tl {#2}
+ \int_if_odd:nTF {#1}
+ { \ExplSyntaxOn }
+ { \ExplSyntaxOff }
+ }
+\tl_if_exist:NF \l__expl_status_stack_tl
+ {
+ \tl_new:N \l__expl_status_stack_tl
+ \tl_set:Nn \l__expl_status_stack_tl { 0 }
+ }
+\cs_gset_eq:NN \__kernel_sys_configuration_load:n
+ \__kernel_sys_configuration_load_std:n
+%%
+%%
+%% End of file `expl3.sty'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3debug.def b/Master/texmf-dist/tex/latex-dev/l3kernel/l3debug.def
new file mode 100644
index 00000000000..8b210aa484a
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3debug.def
@@ -0,0 +1,1055 @@
+%%
+%% This is file `l3debug.def',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3debug.dtx (with options: `package')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3debug.dtx
+\ProvidesExplFile{l3debug.def}{2024-04-11}{}{L3 Debugging support}
+\scan_new:N \s__debug_stop
+\cs_new:Npn \__debug_use_i_delimit_by_s_stop:nw #1 #2 \s__debug_stop {#1}
+\quark_new:N \q__debug_recursion_tail
+\quark_new:N \q__debug_recursion_stop
+\cs_new:Npn \__debug_use_none_delimit_by_q_recursion_stop:w
+ #1 \q__debug_recursion_stop { }
+\__kernel_quark_new_test:N \__debug_if_recursion_tail_stop:N
+\cs_gset_protected:Npn \debug_on:n #1
+ {
+ \exp_args:No \clist_map_inline:nn { \tl_to_str:n {#1} }
+ {
+ \cs_if_exist_use:cF { __debug_ ##1 _on: }
+ { \msg_error:nnn { debug } { debug } {##1} }
+ }
+ }
+\cs_gset_protected:Npn \debug_off:n #1
+ {
+ \exp_args:No \clist_map_inline:nn { \tl_to_str:n {#1} }
+ {
+ \cs_if_exist_use:cF { __debug_ ##1 _off: }
+ { \msg_error:nnn { debug } { debug } {##1} }
+ }
+ }
+\cs_new_protected:Npn \__debug_all_on:
+ {
+ \debug_on:n
+ {
+ check-declarations ,
+ check-expressions ,
+ deprecation ,
+ log-functions ,
+ }
+ }
+\cs_new_protected:Npn \__debug_all_off:
+ {
+ \debug_off:n
+ {
+ check-declarations ,
+ check-expressions ,
+ deprecation ,
+ log-functions ,
+ }
+ }
+\tl_new:N \l__debug_suspended_tl { }
+\cs_gset_protected:Npn \debug_suspend:
+ {
+ \tl_put_right:Nn \l__debug_suspended_tl { . }
+ \cs_set_eq:NN \__debug_suspended:T \use:n
+ }
+\cs_gset_protected:Npn \debug_resume:
+ {
+ \__kernel_tl_set:Nx \l__debug_suspended_tl
+ { \tl_tail:N \l__debug_suspended_tl }
+ \tl_if_empty:NT \l__debug_suspended_tl
+ {
+ \cs_set_eq:NN \__debug_suspended:T \use_none:n
+ }
+ }
+\cs_new_eq:NN \__debug_suspended:T \use_none:n
+\cs_new_protected:Npn \__kernel_chk_var_exist:N #1 { }
+\cs_new_protected:Npn \__kernel_chk_cs_exist:N #1 { }
+\cs_generate_variant:Nn \__kernel_chk_cs_exist:N { c }
+\cs_new:Npn \__kernel_chk_flag_exist:NN { }
+\cs_new_protected:Npn \__kernel_chk_var_local:N #1 { }
+\cs_new_protected:Npn \__kernel_chk_var_global:N #1 { }
+\cs_new_protected:Npn \__kernel_chk_var_scope:NN #1#2 { }
+\cs_new_protected:cpn { __debug_check-declarations_on: }
+ {
+ \cs_set_protected:Npn \__kernel_chk_var_exist:N ##1
+ {
+ \__debug_suspended:T \use_none:nnn
+ \cs_if_exist:NF ##1
+ {
+ \msg_error:nne { debug } { non-declared-variable }
+ { \token_to_str:N ##1 }
+ }
+ }
+ \cs_set_protected:Npn \__kernel_chk_cs_exist:N ##1
+ {
+ \__debug_suspended:T \use_none:nnn
+ \cs_if_exist:NF ##1
+ {
+ \msg_error:nne { kernel } { command-not-defined }
+ { \token_to_str:N ##1 }
+ }
+ }
+ \cs_set:Npn \__kernel_chk_flag_exist:NN ##1##2
+ {
+ \__debug_suspended:T \use_iii:nnnn
+ \flag_if_exist:NTF ##2
+ { ##1 ##2 }
+ {
+ \msg_expandable_error:nnn { kernel } { bad-variable } {##2}
+ ##1 \l_tmpa_flag
+ }
+ }
+ \cs_set_protected:Npn \__kernel_chk_var_scope:NN
+ {
+ \__debug_suspended:T \use_none:nnn
+ \__debug_chk_var_scope_aux:NN
+ }
+ \cs_set_protected:Npn \__kernel_chk_var_local:N ##1
+ {
+ \__debug_suspended:T \use_none:nnnnn
+ \__kernel_chk_var_exist:N ##1
+ \__debug_chk_var_scope_aux:NN l ##1
+ }
+ \cs_set_protected:Npn \__kernel_chk_var_global:N ##1
+ {
+ \__debug_suspended:T \use_none:nnnnn
+ \__kernel_chk_var_exist:N ##1
+ \__debug_chk_var_scope_aux:NN g ##1
+ }
+ }
+\cs_new_protected:cpn { __debug_check-declarations_off: }
+ {
+ \cs_set_protected:Npn \__kernel_chk_var_exist:N ##1 { }
+ \cs_set_protected:Npn \__kernel_chk_cs_exist:N ##1 { }
+ \cs_set:Npn \__kernel_chk_flag_exist:NN { }
+ \cs_set_protected:Npn \__kernel_chk_var_local:N ##1 { }
+ \cs_set_protected:Npn \__kernel_chk_var_global:N ##1 { }
+ \cs_set_protected:Npn \__kernel_chk_var_scope:NN ##1##2 { }
+ }
+\cs_new_protected:Npn \__debug_chk_var_scope_aux:NN #1#2
+ { \exp_args:NNf \__debug_chk_var_scope_aux:Nn #1 { \cs_to_str:N #2 } }
+\cs_new_protected:Npn \__debug_chk_var_scope_aux:Nn #1#2
+ {
+ \if:w _ \use_i:nn \__debug_use_i_delimit_by_s_stop:nw #2 ? ? \s__debug_stop
+ \exp_after:wN \__debug_chk_var_scope_aux:NNn
+ \__debug_use_i_delimit_by_s_stop:nw #2 ? \s__debug_stop
+ #1 {#2}
+ \else:
+ \exp_args:Nc \__debug_chk_var_scope_aux:NNn
+ { __debug_chk_/ #2 }
+ #1 {#2}
+ \fi:
+ }
+\cs_new_protected:Npn \__debug_chk_var_scope_aux:NNn #1#2#3
+ {
+ \if:w #1 #2
+ \else:
+ \if:w #1 \scan_stop:
+ \cs_gset_nopar:Npn #1 {#2}
+ \else:
+ \msg_error:nneee { debug } { local-global }
+ {#1} {#2} { \iow_char:N \\ #3 }
+ \fi:
+ \fi:
+ }
+\use:c { __debug_check-declarations_off: }
+\cs_new_protected:cpn { __debug_log-functions_on: }
+ {
+ \cs_set_protected:Npn \__kernel_debug_log:e
+ { \__debug_suspended:T \use_none:nn \iow_log:e }
+ }
+\cs_new_protected:cpn { __debug_log-functions_off: }
+ { \cs_set_protected:Npn \__kernel_debug_log:e { \use_none:n } }
+\cs_new_protected:Npn \__kernel_debug_log:e { \use_none:n }
+\cs_new_protected:cpn { __debug_check-expressions_on: }
+ {
+ \cs_set:Npn \__kernel_chk_expr:nNnN ##1##2
+ {
+ \__debug_suspended:T { ##1 \use_none:nnnnnnn }
+ \exp_after:wN \__debug_chk_expr_aux:nNnN
+ \exp_after:wN { \tex_the:D ##2 ##1 \scan_stop: }
+ ##2
+ }
+ }
+\cs_new_protected:cpn { __debug_check-expressions_off: }
+ { \cs_set:Npn \__kernel_chk_expr:nNnN ##1##2##3##4 {##1} }
+\cs_new:Npn \__kernel_chk_expr:nNnN #1#2#3#4 {#1}
+\cs_new:Npn \__debug_chk_expr_aux:nNnN #1#2#3#4
+ {
+ \tl_if_empty:oF
+ {
+ \tex_romannumeral:D - 0
+ \exp_after:wN \use_none:n
+ \int_value:w #3 #2 #1 \scan_stop:
+ }
+ {
+ \msg_expandable_error:nnnn
+ { debug } { expr } {#4} {#1}
+ }
+ #1
+ }
+\cs_new_protected:Npn \__debug_deprecation_on:
+ { \g__debug_deprecation_on_tl }
+\cs_new_protected:Npn \__debug_deprecation_off:
+ { \g__debug_deprecation_off_tl }
+\tl_new:N \l__debug_internal_tl
+\tl_new:N \l__debug_tmpa_tl
+\tl_new:N \l__debug_tmpb_tl
+\cs_new_protected:Npn \__debug_generate_parameter_list:NNN #1#2#3
+ {
+ \__kernel_tl_set:Nx \l__debug_internal_tl
+ { \exp_last_unbraced:Nf \use_ii:nnn \cs_split_function:N #1 }
+ \__kernel_tl_set:Nx #2
+ { \exp_args:NV \__debug_build_parm_text:n \l__debug_internal_tl }
+ \__kernel_tl_set:Nx #3
+ { \exp_args:NV \__debug_build_arg_list:n \l__debug_internal_tl }
+ }
+\cs_new:Npn \__debug_build_parm_text:n #1
+ {
+ \__debug_arg_list_from_signature:nNN { 1 } \c_false_bool #1
+ \q__debug_recursion_tail \q__debug_recursion_stop
+ }
+\cs_new:Npn \__debug_build_arg_list:n #1
+ {
+ \__debug_arg_list_from_signature:nNN { 1 } \c_true_bool #1
+ \q__debug_recursion_tail \q__debug_recursion_stop
+ }
+\cs_new:Npn \__debug_arg_list_from_signature:nNN #1 #2 #3
+ {
+ \__debug_if_recursion_tail_stop:N #3
+ \__debug_arg_check_invalid:N #3
+ \bool_if:NT #2 { \__debug_arg_if_braced:NT #3 { \use_none:n } }
+ \use:n { \c_hash_str \int_eval:n {#1} }
+ \exp_args:Nf \__debug_arg_list_from_signature:nNN
+ { \int_eval:n {#1+1} } #2
+ }
+\cs_new:Npn \__debug_arg_check_invalid:N #1
+ {
+ \if:w w #1 \__debug_parm_terminate:w \else:
+ \if:w p #1 \__debug_parm_terminate:w \else:
+ \if:w T #1 \__debug_parm_terminate:w \else:
+ \if:w F #1 \__debug_parm_terminate:w \else:
+ \exp:w
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \exp_end:
+ }
+\cs_new:Npn \__debug_parm_terminate:w
+ { \exp_after:wN \__debug_use_none_delimit_by_q_recursion_stop:w \exp:w }
+\prg_new_conditional:Npnn \__debug_arg_if_braced:N #1 { T }
+ { \exp_args:Nf \__debug_arg_if_braced:n { \__debug_get_base_form:N #1 } }
+\cs_new:Npn \__debug_arg_if_braced:n #1
+ {
+ \if:w n #1 \prg_return_true: \else:
+ \if:w N #1 \prg_return_false: \else:
+ \msg_expandable_error:nnn
+ { debug } { bad-arg-type } {#1}
+ \fi:
+ \fi:
+ }
+\msg_new:nnn { debug } { bad-arg-type }
+ { Wrong~argument~type~#1. }
+\cs_new:Npn \__debug_get_base_form:N #1
+ {
+ \if:w n #1 \__debug_arg_return:N n \else:
+ \if:w N #1 \__debug_arg_return:N N \else:
+ \if:w c #1 \__debug_arg_return:N N \else:
+ \if:w o #1 \__debug_arg_return:N n \else:
+ \if:w V #1 \__debug_arg_return:N n \else:
+ \if:w v #1 \__debug_arg_return:N n \else:
+ \if:w f #1 \__debug_arg_return:N n \else:
+ \if:w e #1 \__debug_arg_return:N n \else:
+ \if:w x #1 \__debug_arg_return:N n \else:
+ \__debug_arg_return:N \scan_stop:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \fi:
+ \exp_stop_f:
+ }
+\cs_new:Npn \__debug_arg_return:N #1
+ { \exp_after:wN #1 \exp:w \exp_end_continue_f:w }
+\group_begin:
+ \cs_set_protected:Npn \__kernel_patch:nnn
+ {
+ \group_begin:
+ \char_set_catcode_other:N \#
+ \__kernel_patch_aux:nnn
+ }
+ \cs_set_protected:Npn \__kernel_patch_aux:nnn #1#2#3
+ {
+ \char_set_catcode_parameter:N \#
+ \char_set_catcode_space:N \ %
+ \tex_endlinechar:D -1 \scan_stop:
+ \tl_map_inline:nn {#3}
+ {
+ \cs_if_exist:cTF { __debug_ \cs_to_str:N ##1 }
+ { \__debug_add_to_debug_code:Nnn }
+ { \__debug_setup_debug_code:Nnn }
+ ##1 {#1} {#2}
+ }
+ \group_end:
+ }
+ \cs_set_protected:Npn \__debug_setup_debug_code:Nnn #1#2#3
+ {
+ \cs_gset_eq:cN { __debug_ \cs_to_str:N #1 } #1
+ \__debug_generate_parameter_list:NNN #1 \l__debug_tmpa_tl \l__debug_tmpb_tl
+ \exp_args:Ne \tex_scantokens:D
+ {
+ \tex_global:D \cs_prefix_spec:N #1
+ \tex_def:D \exp_not:N #1
+ \tl_use:N \l__debug_tmpa_tl
+ {
+ \tl_to_str:n {#2}
+ \exp_not:c { __debug_ \cs_to_str:N #1 }
+ \tl_use:N \l__debug_tmpb_tl
+ \tl_to_str:n {#3}
+ }
+ }
+ }
+ \cs_set_protected:Npn \__debug_add_to_debug_code:Nnn #1#2#3
+ {
+ \use:e
+ {
+ \cs_set:Npn \exp_not:N \__debug_tmp:w
+ ##1 \tl_to_str:n { macro: }
+ ##2 \tl_to_str:n { -> }
+ ##3 \c_backslash_str \tl_to_str:n { __debug_ }
+ \cs_to_str:N #1
+ ##4 \s__debug_stop
+ {
+ \exp_not:N \exp_args:Ne \exp_not:N \tex_scantokens:D
+ {
+ \tex_global:D ##1
+ \tex_def:D \exp_not:N #1 ##2
+ {
+ ##3 \tl_to_str:n {#2}
+ \c_backslash_str __debug_ \cs_to_str:N #1
+ ##4 \tl_to_str:n {#3}
+ }
+ }
+ }
+ }
+ \exp_after:wN \__debug_tmp:w \cs_meaning:N #1 \s__debug_stop
+ }
+ \cs_set_protected:Npn \__kernel_patch_weird:nnn
+ {
+ \group_begin:
+ \char_set_catcode_other:N \#
+ \__kernel_patch_weird_aux:nnn
+ }
+ \cs_set_protected:Npn \__kernel_patch_weird_aux:nnn #1#2#3
+ {
+ \char_set_catcode_parameter:N \#
+ \char_set_catcode_space:N \ %
+ \tex_endlinechar:D -1 \scan_stop:
+ \tl_map_inline:nn {#3}
+ { \__debug_patch_weird:Nnn ##1 {#1} {#2} }
+ \group_end:
+ }
+ \cs_set_protected:Npn \__debug_patch_weird:Nnn #1#2#3
+ {
+ \use:e
+ {
+ \tex_endlinechar:D -1 \scan_stop:
+ \exp_not:N \tex_scantokens:D
+ {
+ \tex_global:D \cs_prefix_spec:N #1
+ \tex_def:D \exp_not:N #1
+ \cs_parameter_spec:N #1
+ {
+ \tl_to_str:n {#2}
+ \cs_replacement_spec:N #1
+ \tl_to_str:n {#3}
+ }
+ }
+ }
+ }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_exist:N #2 }
+ { }
+ {
+ \bool_set_eq:NN
+ \bool_gset_eq:NN
+ \clist_set_eq:NN
+ \clist_gset_eq:NN
+ \fp_set_eq:NN
+ \fp_gset_eq:NN
+ \prop_set_eq:NN
+ \prop_gset_eq:NN
+ \seq_set_eq:NN
+ \seq_gset_eq:NN
+ \str_set_eq:NN
+ \str_gset_eq:NN
+ \tl_set_eq:NN
+ \tl_gset_eq:NN
+ }
+ \__kernel_patch:nnn
+ {
+ \__kernel_chk_var_exist:N #2
+ \__kernel_chk_var_exist:N #3
+ }
+ { }
+ {
+ \clist_concat:NNN
+ \clist_gconcat:NNN
+ \prop_concat:NNN
+ \prop_gconcat:NNN
+ \seq_concat:NNN
+ \seq_gconcat:NNN
+ \str_concat:NNN
+ \str_gconcat:NNN
+ \tl_concat:NNN
+ \tl_gconcat:NNN
+ }
+\cs_gset_protected:Npn \__kernel_tl_set:Nx { \cs_set_nopar:Npe }
+\cs_gset_protected:Npn \__kernel_tl_gset:Nx { \cs_gset_nopar:Npe }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_local:N #1 }
+ { }
+ {
+ \bool_set:Nn
+ \bool_set_eq:NN
+ \bool_set_true:N
+ \bool_set_false:N
+ \box_set_eq:NN
+ \box_set_eq_drop:NN
+ \box_set_to_last:N
+ \clist_clear:N
+ \clist_set_eq:NN
+ \dim_zero:N
+ \dim_set:Nn
+ \dim_set_eq:NN
+ \dim_add:Nn
+ \dim_sub:Nn
+ \fp_set_eq:NN
+ \int_zero:N
+ \int_set_eq:NN
+ \int_add:Nn
+ \int_sub:Nn
+ \int_incr:N
+ \int_decr:N
+ \int_set:Nn
+ \hbox_set:Nn
+ \hbox_set_to_wd:Nnn
+ \hbox_set:Nw
+ \hbox_set_to_wd:Nnw
+ \muskip_zero:N
+ \muskip_set:Nn
+ \muskip_add:Nn
+ \muskip_sub:Nn
+ \muskip_set_eq:NN
+ \prop_clear:N
+ \prop_concat:NNN
+ \prop_pop:NnN
+ \prop_pop:NnNT
+ \prop_pop:NnNF
+ \prop_pop:NnNTF
+ \prop_put:Nnn
+ \prop_put_if_not_in:Nnn
+ \prop_put_from_keyval:Nn
+ \prop_remove:Nn
+ \prop_set_eq:NN
+ \prop_set_from_keyval:Nn
+ \seq_set_eq:NN
+ \skip_zero:N
+ \skip_set:Nn
+ \skip_set_eq:NN
+ \skip_add:Nn
+ \skip_sub:Nn
+ \str_clear:N
+ \str_set_eq:NN
+ \str_put_left:Nn
+ \str_put_right:Nn
+ \__kernel_tl_set:Nx
+ \tl_clear:N
+ \tl_set_eq:NN
+ \tl_put_left:Nn
+ \tl_put_left:NV
+ \tl_put_left:Nv
+ \tl_put_left:Ne
+ \tl_put_left:No
+ \tl_put_right:Nn
+ \tl_put_right:NV
+ \tl_put_right:Nv
+ \tl_put_right:Ne
+ \tl_put_right:No
+ \tl_build_begin:N
+ \tl_build_put_right:Nn
+ \tl_build_put_left:Nn
+ \vbox_set:Nn
+ \vbox_set_top:Nn
+ \vbox_set_to_ht:Nnn
+ \vbox_set:Nw
+ \vbox_set_to_ht:Nnw
+ \vbox_set_split_to_ht:NNn
+ }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_global:N #1 }
+ { }
+ {
+ \bool_gset:Nn
+ \bool_gset_eq:NN
+ \bool_gset_true:N
+ \bool_gset_false:N
+ \box_gset_eq:NN
+ \box_gset_eq_drop:NN
+ \box_gset_to_last:N
+ \cctab_gset:Nn
+ \clist_gclear:N
+ \clist_gset_eq:NN
+ \dim_gset_eq:NN
+ \dim_gzero:N
+ \dim_gset:Nn
+ \dim_gadd:Nn
+ \dim_gsub:Nn
+ \fp_gset_eq:NN
+ \int_gzero:N
+ \int_gset_eq:NN
+ \int_gadd:Nn
+ \int_gsub:Nn
+ \int_gincr:N
+ \int_gdecr:N
+ \int_gset:Nn
+ \hbox_gset:Nn
+ \hbox_gset_to_wd:Nnn
+ \hbox_gset:Nw
+ \hbox_gset_to_wd:Nnw
+ \muskip_gzero:N
+ \muskip_gset:Nn
+ \muskip_gadd:Nn
+ \muskip_gsub:Nn
+ \muskip_gset_eq:NN
+ \prop_gclear:N
+ \prop_gconcat:NNN
+ \prop_gpop:NnN
+ \prop_gpop:NnNT
+ \prop_gpop:NnNF
+ \prop_gpop:NnNTF
+ \prop_gput:Nnn
+ \prop_gput_if_not_in:Nnn
+ \prop_gput_from_keyval:Nn
+ \prop_gremove:Nn
+ \prop_gset_eq:NN
+ \prop_gset_from_keyval:Nn
+ \seq_gset_eq:NN
+ \skip_gzero:N
+ \skip_gset:Nn
+ \skip_gset_eq:NN
+ \skip_gadd:Nn
+ \skip_gsub:Nn
+ \str_gclear:N
+ \str_gset_eq:NN
+ \str_gput_left:Nn
+ \str_gput_right:Nn
+ \__kernel_tl_gset:Nx
+ \tl_gclear:N
+ \tl_gset_eq:NN
+ \tl_gput_left:Nn
+ \tl_gput_left:NV
+ \tl_gput_left:Nv
+ \tl_gput_left:Ne
+ \tl_gput_left:No
+ \tl_gput_right:Nn
+ \tl_gput_right:NV
+ \tl_gput_right:Nv
+ \tl_gput_right:Ne
+ \tl_gput_right:No
+ \tl_build_gbegin:N
+ \tl_build_gput_right:Nn
+ \tl_build_gput_left:Nn
+ \vbox_gset:Nn
+ \vbox_gset_top:Nn
+ \vbox_gset_to_ht:Nnn
+ \vbox_gset:Nw
+ \vbox_gset_to_ht:Nnw
+ \vbox_gset_split_to_ht:NNn
+ }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_scope:NN c #1 }
+ { }
+ {
+ \bool_const:Nn
+ \cctab_const:Nn
+ \dim_const:Nn
+ \int_const:Nn
+ \intarray_const_from_clist:Nn
+ \muskip_const:Nn
+ \prop_const_from_keyval:Nn
+ \prop_const_linked_from_keyval:Nn
+ \skip_const:Nn
+ \str_const:Nn
+ \tl_const:Nn
+ }
+ \__kernel_patch:nnn
+ { \__kernel_chk_flag_exist:NN }
+ { }
+ {
+ \flag_ensure_raised:N
+ \flag_height:N
+ \flag_if_raised:NT
+ \flag_if_raised:NF
+ \flag_if_raised:NTF
+ \flag_if_raised_p:N
+ \flag_raise:N
+ }
+ \__kernel_patch:nnn
+ { \__kernel_chk_cs_exist:N #1 }
+ { }
+ { \cs_generate_variant:Nn }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_scope:NN g #1 }
+ { }
+ { \cctab_new:N }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_scope:NN l #1 }
+ { }
+ { \flag_new:N }
+ \__kernel_patch:nnn
+ {
+ \__kernel_chk_var_scope:NN l #1
+ \__kernel_chk_flag_exist:NN
+ }
+ { }
+ { \flag_clear:N }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_scope:NN g #1 }
+ { }
+ { \intarray_new:Nn }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_scope:NN q #1 }
+ { }
+ { \quark_new:N }
+ \__kernel_patch:nnn
+ { \__kernel_chk_var_scope:NN s #1 }
+ { }
+ { \scan_new:N }
+ \__kernel_patch:nnn
+ { }
+ {
+ \__kernel_debug_log:e
+ { Defining~\token_to_str:N #1~ \msg_line_context: }
+ }
+ { \__kernel_chk_if_free_cs:N }
+ \__kernel_patch_weird:nnn
+ {
+ \cs_if_free:NF #4
+ {
+ \__kernel_debug_log:e
+ {
+ Variant~\token_to_str:N #4~%
+ already~defined;~ not~ changing~ it~ \msg_line_context:
+ }
+ }
+ }
+ { }
+ { \__cs_generate_variant:wwNN }
+ \__kernel_patch:nnn
+ {
+ \cs_if_exist:cF { \c__keys_code_root_str #1 }
+ { \__kernel_debug_log:e { Defining~key~#1~\msg_line_context: } }
+ }
+ { }
+ { \__keys_cmd_set_direct:nn }
+ \__kernel_patch:nnn
+ { }
+ {
+ \__kernel_debug_log:e
+ { Defining~message~ #1 / #2 ~\msg_line_context: }
+ }
+ { \__msg_chk_free:nn }
+ \__kernel_patch_weird:nnn
+ { \__kernel_chk_cs_exist:c { #5 _p : #6 } }
+ { }
+ { \__prg_set_eq_conditional_p_form:wNnnnn }
+ \__kernel_patch_weird:nnn
+ { \__kernel_chk_cs_exist:c { #5 : #6 TF } }
+ { }
+ { \__prg_set_eq_conditional_TF_form:wNnnnn }
+ \__kernel_patch_weird:nnn
+ { \__kernel_chk_cs_exist:c { #5 : #6 T } }
+ { }
+ { \__prg_set_eq_conditional_T_form:wNnnnn }
+ \__kernel_patch_weird:nnn
+ { \__kernel_chk_cs_exist:c { #5 : #6 F } }
+ { }
+ { \__prg_set_eq_conditional_F_form:wNnnnn }
+ \__kernel_patch:nnn
+ {
+ \__regex_trace_push:nnN { regex } { 1 } \__regex_escape_use:nnnn
+ \group_begin:
+ \__kernel_tl_set:Nx \l__regex_internal_a_tl
+ { \__regex_trace_pop:nnN { regex } { 1 } \__regex_escape_use:nnnn }
+ \use_none:nnn
+ }
+ { }
+ { \__regex_escape_use:nnn }
+ \__kernel_patch:nnn
+ { \__regex_trace_push:nnN { regex } { 1 } \__regex_build:N }
+ {
+ \__regex_trace_states:n { 2 }
+ \__regex_trace_pop:nnN { regex } { 1 } \__regex_build:N
+ }
+ { \__regex_build:N }
+ \__kernel_patch:nnn
+ { \__regex_trace_push:nnN { regex } { 1 } \__regex_build_for_cs:n }
+ {
+ \__regex_trace_states:n { 2 }
+ \__regex_trace_pop:nnN { regex } { 1 } \__regex_build_for_cs:n
+ }
+ { \__regex_build_for_cs:n }
+ \__kernel_patch:nnn
+ {
+ \__regex_trace:nne { regex } { 2 }
+ {
+ regex~new~state~
+ L=\int_use:N \l__regex_left_state_int ~ -> ~
+ R=\int_use:N \l__regex_right_state_int ~ -> ~
+ M=\int_use:N \l__regex_max_state_int ~ -> ~
+ \int_eval:n { \l__regex_max_state_int + 1 }
+ }
+ }
+ { }
+ { \__regex_build_new_state: }
+ \__kernel_patch:nnn
+ { \__regex_trace_push:nnN { regex } { 1 } \__regex_group_aux:nnnnN }
+ { \__regex_trace_pop:nnN { regex } { 1 } \__regex_group_aux:nnnnN }
+ { \__regex_group_aux:nnnnN }
+ \__kernel_patch:nnn
+ { \__regex_trace_push:nnN { regex } { 1 } \__regex_branch:n }
+ { \__regex_trace_pop:nnN { regex } { 1 } \__regex_branch:n }
+ { \__regex_branch:n }
+ \__kernel_patch:nnn
+ {
+ \__regex_trace_push:nnN { regex } { 1 } \__regex_match:n
+ \__regex_trace:nne { regex } { 1 } { analyzing~query~token~list }
+ }
+ { \__regex_trace_pop:nnN { regex } { 1 } \__regex_match:n }
+ { \__regex_match:n }
+ \__kernel_patch:nnn
+ {
+ \__regex_trace_push:nnN { regex } { 1 } \__regex_match_cs:n
+ \__regex_trace:nne { regex } { 1 } { analyzing~query~token~list }
+ }
+ { \__regex_trace_pop:nnN { regex } { 1 } \__regex_match_cs:n }
+ { \__regex_match_cs:n }
+ \__kernel_patch:nnn
+ { \__regex_trace:nne { regex } { 1 } { initializing } }
+ { }
+ { \__regex_match_init: }
+ \__kernel_patch:nnn
+ {
+ \__regex_trace:nne { regex } { 2 }
+ { state~\int_use:N \l__regex_curr_state_int }
+ }
+ { }
+ { \__regex_use_state: }
+ \__kernel_patch:nnn
+ { \__regex_trace_push:nnN { regex } { 1 } \__regex_replacement:n }
+ { \__regex_trace_pop:nnN { regex } { 1 } \__regex_replacement:n }
+ { \__regex_replacement:n }
+\group_end:
+\group_begin:
+ \cs_set_protected:Npn \__kernel_patch:Nn #1
+ {
+ \group_begin:
+ \char_set_catcode_other:N \#
+ \__kernel_patch_aux:Nn #1
+ }
+ \cs_set_protected:Npn \__kernel_patch_aux:Nn #1#2
+ {
+ \char_set_catcode_parameter:N \#
+ \tex_endlinechar:D -1 \scan_stop:
+ \exp_args:Ne \tex_scantokens:D
+ {
+ \tex_global:D \cs_prefix_spec:N #1 \tex_def:D \exp_not:N #1
+ \cs_parameter_spec:N #1
+ { \exp_args:No \tl_to_str:n { #1 #2 } }
+ }
+ \group_end:
+ }
+ \cs_set_protected:Npn \__kernel_patch_eval:nn #1#2
+ {
+ \tl_map_inline:nn {#1}
+ {
+ \exp_args:NNe \__kernel_patch:Nn ##1
+ {
+ { \c_hash_str 1 }
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 2 }
+ \exp_not:n {#2}
+ \exp_not:N ##1
+ }
+ }
+ }
+ }
+ \__kernel_patch_eval:nn
+ {
+ \dim_set:Nn
+ \dim_gset:Nn
+ \dim_add:Nn
+ \dim_gadd:Nn
+ \dim_sub:Nn
+ \dim_gsub:Nn
+ \dim_const:Nn
+ }
+ { \__dim_eval:w { } }
+ \__kernel_patch_eval:nn
+ {
+ \int_set:Nn
+ \int_gset:Nn
+ \int_add:Nn
+ \int_gadd:Nn
+ \int_sub:Nn
+ \int_gsub:Nn
+ \int_const:Nn
+ }
+ { \__int_eval:w { } }
+ \__kernel_patch_eval:nn
+ {
+ \muskip_set:Nn
+ \muskip_gset:Nn
+ \muskip_add:Nn
+ \muskip_gadd:Nn
+ \muskip_sub:Nn
+ \muskip_gsub:Nn
+ \muskip_const:Nn
+ }
+ { \tex_muexpr:D { \tex_mutoglue:D } }
+ \__kernel_patch_eval:nn
+ {
+ \skip_set:Nn
+ \skip_gset:Nn
+ \skip_add:Nn
+ \skip_gadd:Nn
+ \skip_sub:Nn
+ \skip_gsub:Nn
+ \skip_const:Nn
+ }
+ { \tex_glueexpr:D { } }
+ \cs_set_protected:Npn \__kernel_patch_eval:nn #1#2
+ {
+ \tl_map_inline:nn {#1}
+ {
+ \exp_args:NNe \__kernel_patch:Nn ##1
+ {
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 1 }
+ \exp_not:n {#2}
+ \exp_not:N ##1
+ }
+ }
+ }
+ }
+ \__kernel_patch_eval:nn
+ { \__box_dim_eval:n }
+ { \__box_dim_eval:w { } }
+ \__kernel_patch_eval:nn
+ {
+ \dim_eval:n
+ \dim_to_decimal:n
+ \dim_to_decimal_in_sp:n
+ \dim_abs:n
+ \dim_sign:n
+ }
+ { \__dim_eval:w { } }
+ \__kernel_patch_eval:nn
+ {
+ \int_eval:n
+ \int_abs:n
+ \int_sign:n
+ }
+ { \__int_eval:w { } }
+ \__kernel_patch_eval:nn
+ {
+ \skip_eval:n
+ \skip_horizontal:n
+ \skip_vertical:n
+ }
+ { \tex_glueexpr:D { } }
+ \__kernel_patch_eval:nn
+ {
+ \muskip_eval:n
+ }
+ { \tex_muexpr:D { \tex_mutoglue:D } }
+ \cs_set_protected:Npn \__kernel_patch_eval:nn #1#2
+ {
+ \tl_map_inline:nn {#1}
+ {
+ \exp_args:NNe \__kernel_patch:Nn ##1
+ {
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 1 }
+ \exp_not:n {#2}
+ \exp_not:N ##1
+ }
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 2 }
+ \exp_not:n {#2}
+ \exp_not:N ##1
+ }
+ }
+ }
+ }
+ \__kernel_patch_eval:nn
+ {
+ \dim_max:nn
+ \dim_min:nn
+ }
+ { \__dim_eval:w { } }
+ \__kernel_patch_eval:nn
+ {
+ \int_max:nn
+ \int_min:nn
+ \int_div_truncate:nn
+ \int_mod:nn
+ }
+ { \__int_eval:w { } }
+ \cs_set_protected:Npn \__kernel_patch_cond:nn #1#2
+ {
+ \clist_map_inline:nn { :nNnT , :nNnF , :nNnTF , _p:nNn }
+ {
+ \exp_args:Nce \__kernel_patch:Nn { #1 ##1 }
+ {
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 1 }
+ \exp_not:n {#2}
+ \exp_not:c { #1 ##1 }
+ }
+ { \c_hash_str 2 }
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 3 }
+ \exp_not:n {#2}
+ \exp_not:c { #1 ##1 }
+ }
+ }
+ }
+ }
+ \__kernel_patch_cond:nn { dim_compare } { \__dim_eval:w { } }
+ \__kernel_patch_cond:nn { int_compare } { \__int_eval:w { } }
+ \cs_set_protected:Npn \__kernel_patch_cond:nn #1#2
+ {
+ \clist_map_inline:nn { :nT , :nF , :nTF , _p:n }
+ {
+ \exp_args:Nce \__kernel_patch:Nn { #1 ##1 }
+ {
+ {
+ \exp_not:N \__kernel_chk_expr:nNnN { \c_hash_str 1 }
+ \exp_not:n {#2}
+ \exp_not:c { #1 ##1 }
+ }
+ }
+ }
+ }
+ \__kernel_patch_cond:nn { int_if_even } { \__int_eval:w { } }
+ \__kernel_patch_cond:nn { int_if_odd } { \__int_eval:w { } }
+ \__kernel_patch:Nn \dim_step_function:nnnN
+ {
+ {
+ \__kernel_chk_expr:nNnN {#1} \__dim_eval:w { }
+ \dim_step_function:nnnN
+ }
+ {
+ \__kernel_chk_expr:nNnN {#2} \__dim_eval:w { }
+ \dim_step_function:nnnN
+ }
+ {
+ \__kernel_chk_expr:nNnN {#3} \__dim_eval:w { }
+ \dim_step_function:nnnN
+ }
+ }
+ \__kernel_patch:Nn \int_step_function:nnnN
+ {
+ {
+ \__kernel_chk_expr:nNnN {#1} \__int_eval:w { }
+ \int_step_function:nnnN
+ }
+ {
+ \__kernel_chk_expr:nNnN {#2} \__int_eval:w { }
+ \int_step_function:nnnN
+ }
+ {
+ \__kernel_chk_expr:nNnN {#3} \__int_eval:w { }
+ \int_step_function:nnnN
+ }
+ }
+ \__kernel_patch:Nn \dim_to_fp:n { { (#1) } }
+\group_end:
+\cs_set_protected:Npn \__skip_tmp:w #1
+ {
+ \prg_set_conditional:Npnn \skip_if_finite:n ##1 { p , T , F , TF }
+ {
+ \exp_after:wN \__skip_if_finite:wwNw
+ \skip_use:N \tex_glueexpr:D
+ \__kernel_chk_expr:nNnN
+ {##1} \tex_glueexpr:D { } \skip_if_finite:n
+ ; \prg_return_false:
+ #1 ; \prg_return_true: \s__skip_stop
+ }
+ }
+\exp_args:No \__skip_tmp:w { \tl_to_str:n { fil } }
+\msg_new:nnnn { debug } { debug }
+ { The~debugging~option~'#1'~does~not~exist~\msg_line_context:. }
+ {
+ The~functions~'\iow_char:N\\debug_on:n'~and~
+ '\iow_char:N\\debug_off:n'~only~accept~the~arguments~
+ 'all',~'check-declarations',~'check-expressions',~
+ 'deprecation',~'log-functions',~not~'#1'.
+ }
+\msg_new:nnn { debug } { expr } { '#2'~in~#1 }
+\msg_new:nnnn { debug } { local-global }
+ { Inconsistent~local/global~assignment }
+ {
+ \c__msg_coding_error_text_tl
+ \if:w l #2 Local
+ \else:
+ \if:w g #2 Global \else: Constant \fi:
+ \fi:
+ \ %
+ assignment~to~a~
+ \if:w l #1 local
+ \else:
+ \if:w g #1 global \else: constant \fi:
+ \fi:
+ \ %
+ variable~'#3'.
+ }
+\msg_new:nnnn { debug } { non-declared-variable }
+ { The~variable~#1~has~not~been~declared~\msg_line_context:. }
+ {
+ \c__msg_coding_error_text_tl
+ Checking~is~active,~and~you~have~tried~do~so~something~like: \\
+ \ \ \tl_set:Nn ~ #1 ~ \{ ~ ... ~ \} \\
+ without~first~having: \\
+ \ \ \tl_new:N ~ #1 \\
+ \\
+ LaTeX~will~continue,~creating~the~variable~where~it~is~the~one~being~set.
+ }
+\cs_set_protected:Npn \__kernel_if_debug:TF #1#2 {#1}
+%%
+%%
+%% End of file `l3debug.def'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3doc.cls b/Master/texmf-dist/tex/latex-dev/l3kernel/l3doc.cls
new file mode 100644
index 00000000000..55284d08838
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3doc.cls
@@ -0,0 +1,2445 @@
+%%
+%% This is file `l3doc.cls',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3doc.dtx (with options: `class')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3doc.dtx
+\RequirePackage{calc}
+\ProvidesExplClass{l3doc}{2024-04-11}{}
+ {L3 Experimental documentation class}
+\clist_new:N \g_docinput_clist
+\seq_new:N \g_doc_functions_seq
+\seq_new:N \g_doc_macros_seq
+\bool_new:N \l__codedoc_detect_internals_bool
+\bool_set_true:N \l__codedoc_detect_internals_bool
+\tl_new:N \l__codedoc_detect_internals_tl
+\tl_new:N \l__codedoc_detect_internals_cs_tl
+\coffin_new:N \l__codedoc_output_coffin
+\coffin_new:N \l__codedoc_functions_coffin
+\coffin_new:N \l__codedoc_descr_coffin
+\coffin_new:N \l__codedoc_syntax_coffin
+\box_new:N \g__codedoc_syntax_box
+\bool_new:N \l__codedoc_in_function_bool
+\bool_new:N \l__codedoc_long_name_bool
+\dim_new:N \l__codedoc_trial_width_dim
+\int_new:N \l__codedoc_nested_macro_int
+\bool_new:N \l__codedoc_macro_tested_bool
+\prop_new:N \g__codedoc_missing_tests_prop
+\seq_new:N \g__codedoc_not_tested_seq
+\seq_new:N \g__codedoc_testfiles_seq
+\bool_new:N \l__codedoc_macro_deprecated_bool
+\bool_new:N \l__codedoc_macro_internal_bool
+\bool_new:N \l__codedoc_macro_nodoc_bool
+\bool_new:N \l__codedoc_macro_TF_bool
+\bool_new:N \l__codedoc_macro_pTF_bool
+\bool_new:N \l__codedoc_macro_noTF_bool
+\bool_new:N \l__codedoc_macro_EXP_bool
+\bool_new:N \l__codedoc_macro_rEXP_bool
+\bool_new:N \l__codedoc_macro_var_bool
+\tl_new:N \l__codedoc_override_module_tl
+\tl_set:Nn \l__codedoc_override_module_tl { \q_no_value }
+\tl_new:N \l__codedoc_macro_documented_tl
+\bool_new:N \g__codedoc_lmodern_bool
+\bool_new:N \g__codedoc_checkfunc_bool
+\bool_new:N \g__codedoc_checktest_bool
+\bool_new:N \g__codedoc_kernel_bool
+\bool_new:N \g__codedoc_cs_break_bool
+\bool_new:N \g__codedoc_show_notes_bool
+\bool_gset_true:N \g__codedoc_cs_break_bool
+\tl_new:N \l__codedoc_tmpa_tl
+\tl_new:N \l__codedoc_tmpb_tl
+\int_new:N \l__codedoc_tmpa_int
+\int_new:N \l__codedoc_tmpa_seq
+\tl_new:N \l__codedoc_names_block_tl
+\seq_new:N \g__codedoc_variants_seq
+\bool_new:N \l__codedoc_names_verb_bool
+\seq_new:N \l__codedoc_names_seq
+\seq_new:N \g__codedoc_nested_names_seq
+\tl_new:N \l__codedoc_index_macro_tl
+\tl_new:N \l__codedoc_index_key_tl
+\tl_new:N \l__codedoc_index_module_tl
+\tl_new:N \l__codedoc_macro_do_not_index_tl
+\bool_new:N \l__codedoc_index_internal_bool
+\tl_new:N \g__codedoc_module_name_tl
+\tl_const:Nn \c__codedoc_iow_rule_tl
+ { ======================================== }
+\tl_const:Nn \c__codedoc_iow_mid_rule_tl
+ { -------------------------------------- }
+\box_new:N \l__codedoc_macro_box
+\box_new:N \l__codedoc_macro_index_box
+\int_new:N \l__codedoc_macro_int
+\tl_new:N \l__codedoc_cmd_tl
+\tl_new:N \l__codedoc_cmd_index_tl
+\tl_new:N \l__codedoc_cmd_module_tl
+\bool_new:N \l__codedoc_cmd_noindex_bool
+\bool_new:N \l__codedoc_cmd_replace_bool
+\bool_new:N \l__codedoc_in_implementation_bool
+\bool_new:N \g__codedoc_typeset_documentation_bool
+\bool_new:N \g__codedoc_typeset_implementation_bool
+\bool_set_true:N \g__codedoc_typeset_documentation_bool
+\bool_set_true:N \g__codedoc_typeset_implementation_bool
+\tl_new:N \g__codedoc_base_name_tl
+\prop_new:N \l__codedoc_variants_prop
+\clist_new:N \l__codedoc_function_label_clist
+\bool_new:N \l__codedoc_no_label_bool
+\tl_new:N \l__codedoc_date_added_tl
+\tl_new:N \l__codedoc_date_updated_tl
+\tl_new:N \l__codedoc_macro_argument_tl
+\cs_new_eq:NN \__codedoc_tmpa:w ?
+\cs_new_eq:NN \__codedoc_tmpb:w ?
+\cs_generate_variant:Nn \seq_set_split:Nnn { NoV }
+\cs_generate_variant:Nn \tl_to_str:n { f }
+\prg_new_protected_conditional:Npnn \__codedoc_if_almost_str:n #1 { TF , T , F }
+ {
+ \int_compare:nNnTF
+ { \tl_count:n {#1} }
+ < { \tl_count:e { \tl_to_str:f {#1} } }
+ { \prg_return_false: }
+ { \prg_return_true: }
+ }
+\prg_generate_conditional_variant:Nnn \__codedoc_if_almost_str:n { V } { T }
+\cs_new_protected:Npn \__codedoc_trim_right:Nn #1#2
+ {
+ \cs_set:Npn \__codedoc_tmp:w ##1 #2 ##2 \q_stop { \exp_not:n {##1} }
+ \__kernel_tl_set:Nx #1 { \exp_after:wN \__codedoc_tmp:w #1 #2 \q_stop }
+ }
+\cs_generate_variant:Nn \__codedoc_trim_right:Nn { No }
+\prg_new_protected_conditional:Npnn \__codedoc_str_if_begin:nn #1#2 { TF , T , F }
+ {
+ \tl_if_in:ooTF
+ { \exp_after:wN \scan_stop: \tl_to_str:n {#1} }
+ { \exp_after:wN \scan_stop: \tl_to_str:n {#2} }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\prg_generate_conditional_variant:Nnn \__codedoc_str_if_begin:nn
+ { oo } { TF , T , F }
+\cs_new_protected:Npn \__codedoc_replace_at_at:N #1
+ {
+ \tl_if_empty:NF \g__codedoc_module_name_tl
+ {
+ \exp_args:NNo \__codedoc_replace_at_at_aux:Nn
+ #1 \g__codedoc_module_name_tl
+ }
+ }
+\cs_new_protected:Npe \__codedoc_replace_at_at_aux:Nn #1#2
+ {
+ \tl_replace_all:Nnn #1 { \token_to_str:N @ } { @ }
+ \tl_replace_all:Nnn #1 { \token_to_str:N _ } { _ }
+ \tl_replace_all:Nnn #1 { @ @ @ @ } { \token_to_str:N a a }
+ \tl_replace_all:Nnn #1 { _ _ @ @ } { _ _ #2 }
+ \tl_replace_all:Nnn #1 { _ @ @ } { _ _ #2 }
+ \tl_replace_all:Nnn #1 { @ @ } { _ _ #2 }
+ \tl_replace_all:Nnn #1 { \token_to_str:N a a } { @ @ }
+ }
+\cs_new_protected:Npn \__codedoc_detect_internals:N #1
+ {
+ \bool_if:NT \l__codedoc_detect_internals_bool
+ { \__codedoc_detect_internals_aux:N #1 }
+ }
+\group_begin:
+ \char_set_catcode_active:N \^^M
+ \cs_new_protected:Npn \__codedoc_detect_internals_aux:N #1
+ {
+ \tl_set_eq:NN \l__codedoc_detect_internals_tl #1
+ \tl_replace_all:NVn \l__codedoc_detect_internals_tl \c_underscore_str { _ }
+ \seq_set_split:NnV \l__codedoc_tmpa_seq { _ _ } \l__codedoc_detect_internals_tl
+ \seq_pop_left:NN \l__codedoc_tmpa_seq \l__codedoc_detect_internals_tl
+ \seq_map_variable:NNn \l__codedoc_tmpa_seq \l__codedoc_detect_internals_tl
+ {
+ \__codedoc_trim_right:No \l__codedoc_detect_internals_tl
+ \c_catcode_active_space_tl
+ \__codedoc_trim_right:Nn \l__codedoc_detect_internals_tl ^^M
+ \__codedoc_if_detect_internals_ok:NF \l__codedoc_detect_internals_tl
+ {
+ \tl_set_eq:NN \l__codedoc_detect_internals_cs_tl \l__codedoc_detect_internals_tl
+ \__codedoc_trim_right:Nn \l__codedoc_detect_internals_tl _
+ \__codedoc_trim_right:Nn \l__codedoc_detect_internals_tl :
+ \__codedoc_trim_right:No \l__codedoc_detect_internals_tl { \token_to_str:N : }
+ \msg_warning:nneee { l3doc } { foreign-internal }
+ { \tl_to_str:N \l__codedoc_detect_internals_cs_tl }
+ { \tl_to_str:N \l__codedoc_detect_internals_tl }
+ { \tl_to_str:N \g__codedoc_module_name_tl }
+ }
+ }
+ }
+\group_end:
+\prg_new_protected_conditional:Npnn \__codedoc_if_detect_internals_ok:N #1 { F }
+ {
+ \__codedoc_str_if_begin:ooTF {#1} { \g__codedoc_module_name_tl _ }
+ { \prg_return_true: }
+ {
+ \__codedoc_str_if_begin:ooTF {#1} { \g__codedoc_module_name_tl : }
+ { \prg_return_true: }
+ {
+ \bool_if:NTF \g__codedoc_kernel_bool
+ {
+ \__codedoc_str_if_begin:ooTF {#1} { kernel _ }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ { \prg_return_false: }
+ }
+ }
+ }
+\cs_new:Npn \__codedoc_signature_base_form:n #1
+ { \__codedoc_signature_base_form_aux:n #1 \q_stop }
+\cs_new:Npn \__codedoc_signature_base_form_aux:n #1
+ {
+ \str_case:nnTF {#1}
+ {
+ { N } { N }
+ { c } { N }
+ { n } { n }
+ { o } { n }
+ { f } { n }
+ { e } { n }
+ { x } { n }
+ { V } { n }
+ { v } { n }
+ }
+ { \__codedoc_signature_base_form_aux:n }
+ { \__codedoc_signature_base_form_aux:w #1 }
+ }
+\cs_new:Npn \__codedoc_signature_base_form_aux:w #1 \q_stop
+ { \exp_not:n {#1} }
+\cs_new:Npn \__codedoc_predicate_from_base:n #1
+ {
+ \__codedoc_get_function_name:n {#1}
+ \tl_to_str:n { _p: }
+ \__codedoc_get_function_signature:n {#1}
+ }
+\cs_new:Npn \__codedoc_get_function_name:n #1
+ { \__codedoc_split_function_do:nn {#1} { \use_i:nnn } }
+\cs_new:Npn \__codedoc_get_function_signature:n #1
+ { \__codedoc_split_function_do:nn {#1} { \use_ii:nnn } }
+\cs_set_protected:Npn \__codedoc_tmpa:w #1
+ {
+ \cs_new:Npn \__codedoc_split_function_do:nn ##1
+ {
+ \exp_after:wN \__codedoc_split_function_auxi:w
+ \tl_to_str:n {##1} \q_mark \c_true_bool
+ #1 \q_mark \c_false_bool
+ \q_stop
+ }
+ \cs_new:Npn \__codedoc_split_function_auxi:w
+ ##1 #1 ##2 \q_mark ##3##4 \q_stop ##5
+ { \__codedoc_split_function_auxii:w {##5} ##1 \q_mark \q_stop {##2} ##3 }
+ \cs_new:Npn \__codedoc_split_function_auxii:w
+ ##1##2 \q_mark ##3 \q_stop
+ { ##1 {##2} }
+ }
+\exp_args:No \__codedoc_tmpa:w { \token_to_str:N : }
+\cs_generate_variant:Nn \__codedoc_split_function_do:nn { o }
+\cs_new_protected:Npn \__codedoc_key_get_base:nN #1#2
+ {
+ \__codedoc_if_almost_str:nTF {#1}
+ {
+ \__codedoc_key_get_base_TF:nN {#1} \l__codedoc_tmpa_tl
+ \__kernel_tl_set:Nx #2
+ { \__codedoc_split_function_do:on \l__codedoc_tmpa_tl { \__codedoc_base_form_aux:nnN } }
+ }
+ { \tl_set:Nn #2 {#1} }
+ }
+\cs_new:Npe \__codedoc_key_get_base_TF:nN #1#2
+ {
+ \__kernel_tl_set:Nx #2 { \exp_not:N \tl_to_str:n {#1} }
+ \tl_if_in:NoF #2 { \tl_to_str:n {:} }
+ { \exp_not:N \prg_break: }
+ \tl_if_in:onT { #2 z } { \tl_to_str:n {TF} z }
+ { \exp_not:N \prg_break: }
+ \tl_if_in:onT { #2 z } { \tl_to_str:n {T} z }
+ {
+ \tl_put_right:Nn #2 { \tl_to_str:n {F} }
+ \exp_not:N \prg_break:
+ }
+ \tl_if_in:onT { #2 z } { \tl_to_str:n {F} z }
+ {
+ \tl_put_right:Nn #2 { z }
+ \tl_replace_once:Nnn #2 { \tl_to_str:n {F} z } { \tl_to_str:n {TF} }
+ \exp_not:N \prg_break:
+ }
+ \exp_not:N \prg_break_point:
+ }
+\cs_new:Npn \__codedoc_base_form_aux:nnN #1#2#3
+ {
+ \exp_not:n {#1}
+ \bool_if:NT #3
+ {
+ \token_to_str:N :
+ \bool_lazy_or:nnTF
+ { \str_if_eq_p:nn { #1 ~ } { \exp_args } }
+ { \str_if_eq_p:nn { #1 ~ } { \exp_last_unbraced } }
+ { \exp_not:n {#2} }
+ { \__codedoc_signature_base_form:n {#2} }
+ }
+ }
+\cs_new_protected:Npn \__codedoc_base_form_signature_do:nnn #1#2#3
+ {
+ \__codedoc_split_function_do:nn {#1}
+ { \__codedoc_base_form_aux:nnnnnN {#1} {#2} {#3} }
+ }
+\cs_new_protected:Npn \__codedoc_base_form_aux:nnnnnN #1#2#3#4#5#6
+ {
+ \bool_if:NTF #6
+ {
+ \tl_if_head_eq_charcode:nNTF {#4} :
+ { #2 {#1} }
+ {
+ \use:e
+ {
+ \exp_not:n {#3}
+ { \__codedoc_base_form_aux:nnN {#4} {#5} #6 }
+ }
+ {#4} {#5}
+ }
+ }
+ { #2 {#1} }
+ }
+\prg_new_conditional:Npnn \__codedoc_date_compare:nNn #1#2#3 { TF , T , F , p }
+ { \__codedoc_date_compare_aux:w #1--- \q_mark #2 #3--- \q_stop }
+\cs_new:Npn \__codedoc_date_compare_aux:w
+ #1 - #2 - #3 - #4 \q_mark #5 #6 - #7 - #8 - #9 \q_stop
+ {
+ \__codedoc_date_compare_aux:nnnNnnn
+ { \tl_if_empty:nTF {#1} { 0 } {#1} }
+ { \tl_if_empty:nTF {#2} { 0 } {#2} }
+ { \tl_if_empty:nTF {#3} { 0 } {#3} }
+ #5
+ { \tl_if_empty:nTF {#6} { 0 } {#6} }
+ { \tl_if_empty:nTF {#7} { 0 } {#7} }
+ { \tl_if_empty:nTF {#8} { 0 } {#8} }
+ }
+\cs_new:Npn \__codedoc_date_compare_aux:nnnNnnn #1#2#3#4#5#6#7
+ {
+ \int_compare:nNnTF {#1} = {#5}
+ {
+ \int_compare:nNnTF {#2} = {#6}
+ {
+ \int_compare:nNnTF {#3} #4 {#7}
+ { \prg_return_true: } { \prg_return_false: }
+ }
+ {
+ \int_compare:nNnTF {#2} #4 {#6}
+ { \prg_return_true: } { \prg_return_false: }
+ }
+ }
+ {
+ \int_compare:nNnTF {#1} #4 {#5}
+ { \prg_return_true: } { \prg_return_false: }
+ }
+ \use_none:n
+ \q_stop
+ }
+\cs_new:Npn \__codedoc_gprop_name:n #1 { g__codedoc ~ \tl_to_str:n {#1} }
+\cs_new:Npn \__codedoc_lseq_name:n #1 { l__codedoc ~ \tl_to_str:n {#1} }
+\msg_new:nnnn { l3doc } { no-signature-TF }
+ { Function/macro~'#1'~cannot~be~turned~into~a~conditional. }
+ {
+ A~function~or~macro~environment~with~option~pTF,~TF~or~noTF~
+ received~the~argument~'#1'.~This~function's~name~has~no~
+ ':'~hence~it~is~not~clear~where~to~add~'_p'~or~'TF'.~
+ Please~follow~expl3~naming~conventions.
+ }
+\msg_new:nnn { l3doc } { date-format }
+ { The~date~'#1'~should~be~given~in~YYYY-MM-DD~format. }
+\msg_new:nnn { l3doc } { future-date }
+ { The~added/updated~date~'#2'~of~'#1'~is~in~the~future. }
+\msg_new:nnn { l3doc } { syntax-nested-function }
+ {
+ The~'syntax'~environment~should~be~used~in~the~
+ innermost~'function'~environment.
+ }
+\msg_new:nnn { l3doc } { multiple-syntax }
+ {
+ The~'syntax'~environment~should~only~be~used~once~in~
+ a~'function'~environment.
+ }
+\msg_new:nnn { l3doc } { deprecated-option }
+ { The~option~'#1'~has~been~deprecated~for~'#2'. }
+\msg_new:nnn { l3doc } { foreign-internal }
+ {
+ A~control~sequence~of~the~form~'...__#1'~was~used.~
+ It~should~only~be~used~in~the~module~'#2'
+ \tl_if_empty:nF {#3} { ,~not~in~'#3' } .
+ }
+\DeclareKeys [ l3doc / options ]
+ {
+ a5paper .code:n = \@latexerr { Option~not~supported } { } ,
+ full .code:n =
+ {
+ \bool_gset_true:N \g__codedoc_typeset_documentation_bool
+ \bool_gset_true:N \g__codedoc_typeset_implementation_bool
+ } ,
+ onlydoc .code:n =
+ {
+ \bool_gset_true:N \g__codedoc_typeset_documentation_bool
+ \bool_gset_false:N \g__codedoc_typeset_implementation_bool
+ } ,
+ check .bool_gset:N = \g__codedoc_checkfunc_bool ,
+ checktest .bool_gset:N = \g__codedoc_checktest_bool ,
+ kernel .bool_gset:N = \g__codedoc_kernel_bool ,
+ stdmodule .bool_gset_inverse:N = \g__codedoc_kernel_bool ,
+ lm-default .bool_gset:N = \g__codedoc_lmodern_bool ,
+ cs-break .bool_gset_inverse:N = \g__codedoc_cs_break_bool ,
+ cs-break-nohyphen .code:n = \PassOptionsToPackage{nohyphen}{underscore} ,
+ show-notes .bool_gset:N = \g__codedoc_show_notes_bool,
+ hide-notes .bool_gset_inverse:N = \g__codedoc_show_notes_bool
+ }
+\DeclareUnknownKeyHandler [ l3doc / options ]
+ { \PassOptionsToClass { \CurrentOption } { article } }
+\SetKeys [ l3doc / options ]
+ { full , check = false , checktest = false , lm-default }
+\PassOptionsToClass { a4paper } { article }
+\msg_new:nnn { l3doc } { input-cfg }
+ { Local~config~file~l3doc.cfg~loaded. }
+\file_if_exist:nT { l3doc.cfg }
+ {
+ \file_input:n { l3doc.cfg }
+ \cs_if_exist:cF { ExplMakeTitle }
+ { \msg_info:nn { l3doc } { input-cfg } }
+ }
+\ProcessKeyOptions [ l3doc / options ]
+\LoadClass{article}
+\RequirePackage{doc}
+\RequirePackage
+ {
+ array,
+ alphalph,
+ amsmath,
+ amssymb,
+ booktabs,
+ color,
+ colortbl,
+ hologo,
+ enumitem,
+ pifont,
+ textcomp,
+ trace,
+ csquotes,
+ fancyvrb,
+ underscore,
+ verbatim
+ }
+\raggedbottom
+\bool_if:NT \g__codedoc_lmodern_bool
+ {
+ \RequirePackage[T1]{fontenc}
+ \RequirePackage{lmodern}
+ \group_begin:
+ \ttfamily
+ \DeclareFontShape{T1}{lmtt}{m}{it}{<->ec-lmtto10}{}
+ \group_end:
+ }
+\RequirePackage{hypdoc}
+\cs_gset:Npn \MakePrivateLetters
+ {
+ \char_set_catcode_letter:N \@
+ \char_set_catcode_letter:N \_
+ \char_set_catcode_letter:N \:
+ }
+\setcounter{StandardModuleDepth}{1}
+\@addtoreset{CodelineNo}{part}
+\tl_replace_once:Nnn \theCodelineNo
+ { \HDorg@theCodelineNo }
+ { \textcolor[gray]{0.5} { \sffamily\tiny\arabic{CodelineNo} } }
+\fvset{gobble=2}
+\cs_gset_eq:NN \verbatim \Verbatim
+\cs_gset_eq:NN \endverbatim \endVerbatim
+\cs_set_protected:Npn \ifnot@excluded
+ {
+ \exp_args:Nee \expanded@notin
+ { \c_backslash_str \tl_to_str:N \macro@namepart , }
+ { \exp_args:NV \tl_to_str:n \index@excludelist }
+ }
+\cs_new:Npn \pdfstringnewline { : ~ }
+\DeclareExpandableDocumentCommand
+ { \__codedoc_pdfstring_newline:w } { s o m } { \pdfstringnewline #3 }
+\pdfstringdefDisableCommands
+ { \cs_set_eq:NN \\ \__codedoc_pdfstring_newline:w }
+\setlength \textwidth { 385pt }
+\addtolength \marginparwidth { 30pt }
+\addtolength \oddsidemargin { 20pt }
+\addtolength \evensidemargin { 20pt }
+\cs_new_eq:NN \__codedoc_oldlist:nn \list
+\cs_gset:Npn \list #1 #2
+ { \__codedoc_oldlist:nn {#1} { #2 \dim_zero:N \listparindent } }
+\setlength \parindent { 2em }
+\setlength \itemindent { 0pt }
+\setlength \parskip { 0pt plus 3pt minus 0pt }
+\tl_gset:Nn \partname {File}
+\@addtoreset{section}{part}
+\cs_gset:Npn \l@section #1#2
+ {
+ \ifnum \c@tocdepth >\z@
+ \addpenalty\@secpenalty
+ \addvspace{1.0em \@plus\p@}
+ \setlength\@tempdima{2.5em} % was 1.5em
+ \begingroup
+ \parindent \z@ \rightskip \@pnumwidth
+ \parfillskip -\@pnumwidth
+ \leavevmode \bfseries
+ \advance\leftskip\@tempdima
+ \hskip -\leftskip
+ #1\nobreak\hfil \nobreak\hb@xt@\@pnumwidth{\hss #2}\par
+ \endgroup
+ \fi
+ }
+\cs_gset:Npn \l@subsection
+ { \@dottedtocline{2}{2.5em}{2.3em} } % #2 = 1.5em
+\AtBeginDocument
+ {
+ \MakeShortVerb \"
+ \MakeShortVerb \|
+ }
+\AtEndDocument
+ {
+ \DeleteShortVerb \"
+ \DeleteShortVerb \|
+ }
+\providecommand*\eTeX{\hologo{eTeX}}
+\providecommand*\IniTeX{\hologo{iniTeX}}
+\providecommand*\Lua{Lua}
+\providecommand*\LuaTeX{\hologo{LuaTeX}}
+\providecommand*\pdfTeX{\hologo{pdfTeX}}
+\providecommand*\XeTeX{\hologo{XeTeX}}
+\providecommand*\pTeX{p\kern-.2em\hologo{TeX}}
+\providecommand*\upTeX{up\kern-.2em\hologo{TeX}}
+\providecommand*\epTeX{$\varepsilon$-\pTeX}
+\providecommand*\eupTeX{$\varepsilon$-\upTeX}
+\providecommand*\ConTeXt{\hologo{ConTeXt}}
+\DeclareDocumentCommand \cmd { O{} m }
+ { \__codedoc_cmd:no {#1} { \token_to_str:N #2 } }
+\DeclareDocumentCommand \cs { O{} m }
+ { \__codedoc_cmd:no {#1} { \c_backslash_str #2 } }
+\DeclareDocumentCommand \tn { O{} m }
+ {
+ \__codedoc_cmd:no
+ { module = TeX , replace = false , #1 }
+ { \c_backslash_str #2 }
+ }
+\DeclareDocumentCommand \meta { m }
+ { \texttt{ \__codedoc_meta:n {#1} } }
+\DeclareExpandableDocumentCommand
+ { \__codedoc_pdfstring_cmd:w } { o m } { \token_to_str:N #2 }
+\DeclareExpandableDocumentCommand
+ { \__codedoc_pdfstring_cs:w } { o m } { \textbackslash \tl_to_str:n {#2} }
+\cs_new:Npn \__codedoc_pdfstring_meta:w #1
+ { < \tl_to_str:n {#1} > }
+\pdfstringdefDisableCommands
+ {
+ \cs_set_eq:NN \cmd \__codedoc_pdfstring_cmd:w
+ \cs_set_eq:NN \cs \__codedoc_pdfstring_cs:w
+ \cs_set_eq:NN \tn \__codedoc_pdfstring_cs:w
+ \cs_set_eq:NN \meta \__codedoc_pdfstring_meta:w
+ \cs_set_eq:NN \__codedoc_meta:n \__codedoc_pdfstring_meta:w
+ }
+\newcommand\Arg[1]
+ { \texttt{\char`\{} \__codedoc_meta:n {#1} \texttt{\char`\}} }
+\providecommand\marg[1]{ \Arg{#1} }
+\providecommand\oarg[1]{ \texttt[ \__codedoc_meta:n {#1} \texttt] }
+\providecommand\parg[1]{ \texttt( \__codedoc_meta:n {#1} \texttt) }
+\DeclareRobustCommand \file {\nolinkurl}
+\DeclareRobustCommand \env {\texttt}
+\DeclareRobustCommand \pkg {\textsf}
+\DeclareRobustCommand \cls {\textsf}
+\NewDocumentCommand \EnableDocumentation { }
+ { \bool_gset_true:N \g__codedoc_typeset_documentation_bool }
+\NewDocumentCommand \EnableImplementation { }
+ { \bool_gset_true:N \g__codedoc_typeset_implementation_bool }
+\NewDocumentCommand \DisableDocumentation { }
+ { \bool_gset_false:N \g__codedoc_typeset_documentation_bool }
+\NewDocumentCommand \DisableImplementation { }
+ { \bool_gset_false:N \g__codedoc_typeset_implementation_bool }
+\NewDocumentEnvironment { documentation } { }
+ {
+ \bool_if:NTF \g__codedoc_typeset_documentation_bool
+ { \bool_set_false:N \l__codedoc_in_implementation_bool }
+ { \comment }
+ }
+ { \bool_if:NF \g__codedoc_typeset_documentation_bool { \endcomment } }
+\NewDocumentEnvironment { implementation } { }
+ {
+ \bool_if:NTF \g__codedoc_typeset_implementation_bool
+ { \bool_set_true:N \l__codedoc_in_implementation_bool }
+ { \comment }
+ }
+ { \bool_if:NF \g__codedoc_typeset_implementation_bool { \endcomment } }
+\DeclareDocumentEnvironment { variable } { O{} +v }
+ {
+ \bool_if:NTF \l__codedoc_in_implementation_bool
+ { \__codedoc_macro:nnw { var , #1 } {#2} }
+ { \__codedoc_function:nnw {#1} {#2} }
+ }
+ {
+ \bool_if:NTF \l__codedoc_in_implementation_bool
+ { \__codedoc_macro_end: }
+ { \__codedoc_function_end: }
+ }
+\DeclareDocumentEnvironment { function } { O{} +v }
+ { \__codedoc_function:nnw {#1} {#2} }
+ { \__codedoc_function_end: }
+\DeclareDocumentEnvironment { macro } { O{} +v }
+ { \__codedoc_macro:nnw {#1} {#2} }
+ { \__codedoc_macro_end: }
+\NewDocumentEnvironment { syntax } { }
+ { \__codedoc_syntax:w }
+ {
+ \__codedoc_syntax_end:
+ \ignorespacesafterend
+ }
+\NewDocumentEnvironment { texnote } { }
+ {
+ \endgraf
+ \vspace{3mm}
+ \small\textbf{\TeX~hackers~note:}
+ }
+ {
+ \vspace{3mm}
+ }
+\NewDocumentEnvironment { arguments } { }
+ {
+ \enumerate [
+ nolistsep ,
+ label = \texttt{\#\arabic*} ~ : ,
+ labelsep = * ,
+ ]
+ }
+ {
+ \endenumerate
+ }
+\NewDocumentCommand { \CodedocExplain } { }
+ { \CodedocExplainEXP \ \CodedocExplainREXP \ \CodedocExplainTF }
+\NewDocumentCommand { \CodedocExplainEXP } { }
+ {
+ \raisebox{\baselineskip}[0pt][0pt]{\hypertarget{expstar}{}}%
+ \write \@auxout { \def \string \Codedoc@expstar { } }
+ \__codedoc_typeset_exp:\ indicates~fully~expandable~functions,~which~
+ can~be~used~within~an~\texttt{e}-type~argument~(inside~an~\tn{expanded}),~
+ \texttt{x}-type~argument~(in~plain~\TeX{}~terms,~inside~an~\tn{edef}),~
+ as~well~as~within~an~\texttt{f}-type~argument.
+ }
+\NewDocumentCommand { \CodedocExplainREXP } { }
+ {
+ \raisebox{\baselineskip}[0pt][0pt]{\hypertarget{rexpstar}{}}%
+ \write \@auxout { \def \string \Codedoc@rexpstar { } }
+ \__codedoc_typeset_rexp:\ indicates~
+ restricted~expandable~functions,~which~can~be~used~within~an~
+ \texttt{x}-type~argument~or~an~\texttt{e}-type~argument,~
+ but~cannot~be~fully~expanded~within~an~\texttt{f}-type~argument.
+ }
+\NewDocumentCommand { \CodedocExplainTF } { }
+ {
+ \raisebox{\baselineskip}[0pt][0pt]{\hypertarget{explTF}{}}%
+ \write \@auxout { \def \string \Codedoc@explTF { } }
+ \__codedoc_typeset_TF:\ indicates~conditional~(\texttt{if})~functions~
+ whose~variants~with~\texttt{T},~\texttt{F}~and~\texttt{TF}~
+ argument~specifiers~expect~different~
+ \enquote{true}/\enquote{false}~branches.
+ }
+\keys_define:nn { l3doc/cmd }
+ {
+ index .tl_set:N = \l__codedoc_cmd_index_tl ,
+ module .tl_set:N = \l__codedoc_cmd_module_tl ,
+ no-index .bool_set:N = \l__codedoc_cmd_noindex_bool ,
+ replace .bool_set:N = \l__codedoc_cmd_replace_bool ,
+ }
+\cs_new_protected:Npn \__codedoc_cmd:nn #1#2
+ {
+ \bool_set_false:N \l__codedoc_cmd_noindex_bool
+ \bool_set_true:N \l__codedoc_cmd_replace_bool
+ \tl_set:Nn \l__codedoc_cmd_index_tl { \q_no_value }
+ \tl_set:Nn \l__codedoc_cmd_module_tl { \q_no_value }
+ \keys_set:nn { l3doc/cmd } {#1}
+ \tl_set:Nn \l__codedoc_cmd_tl {#2}
+ \bool_if:NT \l__codedoc_cmd_replace_bool
+ {
+ \tl_set_rescan:Nnn \l__codedoc_tmpb_tl { } { _ }
+ \tl_replace_all:NVn \l__codedoc_cmd_tl \l__codedoc_tmpb_tl { _ }
+ \__codedoc_replace_at_at:N \l__codedoc_cmd_tl
+ \tl_replace_all:NnV \l__codedoc_cmd_tl { _ } \l__codedoc_tmpb_tl
+ }
+ \mode_if_math:T { \mbox }
+ {
+ \bool_if:NT \l__codedoc_allow_indexing_bool { \__codedoc_target: }
+ \verbatim@font
+ \__codedoc_if_almost_str:VT \l__codedoc_cmd_tl
+ {
+ \__kernel_tl_set:Nx \l__codedoc_cmd_tl { \tl_to_str:N \l__codedoc_cmd_tl }
+ \bool_if:NT \g__codedoc_cs_break_bool
+ {
+ \regex_replace_all:nnN
+ { ([^\\\_]\_*) \_ ([^\_]) }
+ { \1 \c{BreakableUnderscore} \2 }
+ \l__codedoc_cmd_tl
+ }
+ }
+ \tl_replace_all:Nnn \l__codedoc_cmd_tl { ~ } { \@xobeysp }
+ \l__codedoc_cmd_tl
+ \@
+ }
+ \bool_if:NT \l__codedoc_allow_indexing_bool
+ {
+ \bool_if:NF \l__codedoc_cmd_noindex_bool
+ {
+ \quark_if_no_value:NF \l__codedoc_cmd_index_tl
+ {
+ \__kernel_tl_set:Nx \l__codedoc_cmd_tl
+ { \c_backslash_str \exp_not:o { \l__codedoc_cmd_index_tl } }
+ }
+ \exp_args:No \__codedoc_key_get:n { \l__codedoc_cmd_tl }
+ \quark_if_no_value:NF \l__codedoc_cmd_module_tl
+ {
+ \__kernel_tl_set:Nx \l__codedoc_index_module_tl
+ { \tl_to_str:N \l__codedoc_cmd_module_tl }
+ }
+ \__codedoc_special_index_module:ooonN
+ { \l__codedoc_index_key_tl }
+ { \l__codedoc_index_macro_tl }
+ { \l__codedoc_index_module_tl }
+ { usage }
+ \l__codedoc_index_internal_bool
+ }
+ }
+ }
+\cs_generate_variant:Nn \__codedoc_cmd:nn { no }
+\cs_new_protected:Npn \__codedoc_meta:n #1
+ {
+ \tl_set:Nn \l__codedoc_tmpa_tl {#1}
+ \tl_map_inline:nn
+ { { 3 } { 4 } { 7 } { 8 } { 11 } { 12 } { 13 } }
+ {
+ \tl_set_rescan:Nnn \l__codedoc_tmpb_tl
+ { \char_set_catcode:nn { `_ } {##1} } { _ }
+ \tl_replace_all:NVn \l__codedoc_tmpa_tl \l__codedoc_tmpb_tl
+ { \__codedoc_ensuremath_sb:n }
+ }
+ \exp_args:NV \__codedoc_meta_original:n \l__codedoc_tmpa_tl
+ }
+\cs_new_protected:Npn \__codedoc_ensuremath_sb:n #1
+ { \ensuremath { \sb {#1} } }
+\cs_new_protected:Npn \__codedoc_meta_original:n #1
+ {
+ \ensuremath \langle
+ \mode_if_math:T { \nfss@text }
+ {
+ \meta@font@select
+ \edef \meta@hyphen@restore
+ { \hyphenchar \the \font \the \hyphenchar \font }
+ \hyphenchar \font \m@ne
+ \language \l@nohyphenation
+ #1 \/
+ \meta@hyphen@restore
+ }
+ \ensuremath \rangle
+ }
+\cs_new_protected:Npn \__codedoc_typeset_exp:
+ {
+ \cs_if_exist:NTF \Codedoc@expstar
+ { \hyperlink { expstar } }
+ { \mbox }
+ {$\star$}
+ }
+\cs_new_protected:Npn \__codedoc_typeset_rexp:
+ {
+ \cs_if_exist:NTF \Codedoc@rexpstar
+ { \hyperlink { rexpstar } }
+ { \mbox }
+ { \ding { 73 } } % hollow star
+ }
+\cs_new_protected:Npn \__codedoc_typeset_TF:
+ {
+ \cs_if_exist:NTF \Codedoc@explTF
+ { \hyperlink { explTF } }
+ { \mbox }
+ {
+ \itshape TF
+ \makebox[0pt][r]
+ {
+ \underline { \phantom{\itshape TF} \kern-0.1em }
+ }
+ }
+ }
+\cs_new_protected:Npn \__codedoc_typeset_aux:n #1
+ {
+ { \color[gray]{0.5} #1 }
+ }
+\cs_new_protected:Npn \__codedoc_get_hyper_target:nN #1#2
+ {
+ \__kernel_tl_set:Nx #2 { \tl_to_str:n {#1} }
+ \tl_replace_all:NVn #2 \c_underscore_str { / }
+ \tl_remove_all:NV #2 \c_backslash_str
+ \tl_put_left:Nn #2 { doc/function// }
+ }
+\cs_generate_variant:Nn \__codedoc_get_hyper_target:nN { o , e }
+\cs_new_protected:Npn \__codedoc_names_get_seq:nN #1#2
+ {
+ \__kernel_tl_set:Nx \l__codedoc_tmpa_tl { \tl_to_str:n {#1} }
+ \bool_if:NTF \l__codedoc_names_verb_bool
+ {
+ \seq_clear:N #2
+ \seq_put_right:NV #2 \l__codedoc_tmpa_tl
+ }
+ {
+ \tl_remove_all:Ne \l__codedoc_tmpa_tl
+ { \iow_char:N \^^M \c_percent_str }
+ \tl_remove_all:Ne \l__codedoc_tmpa_tl
+ { \token_to_str:N \obeyedline \c_space_tl \c_percent_str }
+ \tl_remove_all:Ne \l__codedoc_tmpa_tl { \tl_to_str:n { ^ ^ A } }
+ \tl_remove_all:Ne \l__codedoc_tmpa_tl { \iow_char:N \^^I }
+ \tl_remove_all:Ne \l__codedoc_tmpa_tl { \iow_char:N \^^M }
+ \__codedoc_detect_internals:N \l__codedoc_tmpa_tl
+ \__codedoc_replace_at_at:N \l__codedoc_tmpa_tl
+ \exp_args:NNe \seq_set_from_clist:Nn #2
+ { \tl_to_str:N \l__codedoc_tmpa_tl }
+ }
+ }
+\cs_new_protected:Npn \__codedoc_names_parse:
+ {
+ \tl_clear:N \l__codedoc_names_block_tl
+ \seq_map_function:NN
+ \l__codedoc_names_seq
+ \__codedoc_names_parse_one:n
+ }
+\cs_new_protected:Npn \__codedoc_names_parse_one:n #1
+ {
+ \__codedoc_split_function_do:nn {#1}
+ { \__codedoc_names_parse_one_aux:nnNn }
+ {#1}
+ }
+\cs_new_protected:Npn \__codedoc_names_parse_one_aux:nnNn #1#2#3#4
+ {
+ \bool_if:NTF #3
+ {
+ \tl_if_head_eq_charcode:nNTF {#2} :
+ { \__codedoc_names_parse_aux:nnn {#4} {#4} { \scan_stop: } }
+ {
+ \exp_args:Ne \__codedoc_names_parse_aux:nnn
+ { \__codedoc_base_form_aux:nnN {#1} {#2} #3 }
+ {#1} {#2}
+ }
+ }
+ {
+ \bool_if:NT \l__codedoc_macro_TF_bool
+ { \msg_error:nne { l3doc } { no-signature-TF } {#4} }
+ \__codedoc_names_parse_aux:nnn {#4} {#4} { \scan_stop: }
+ }
+ }
+\cs_new_protected:Npn \__codedoc_names_parse_aux:nnn #1
+ { \exp_args:Nc \__codedoc_names_parse_aux:Nnn { \__codedoc_lseq_name:n {#1} } }
+\cs_new_protected:Npn \__codedoc_names_parse_aux:Nnn #1#2#3
+ {
+ \tl_if_in:NnF \l__codedoc_names_block_tl {#1}
+ {
+ \tl_put_right:Nn \l__codedoc_names_block_tl {#1}
+ \seq_clear_new:N #1
+ \seq_put_right:Nn #1 {#2}
+ }
+ \seq_put_right:Nn #1 {#3}
+ }
+\cs_new_protected:Npn \__codedoc_names_typeset:
+ {
+ \tl_map_function:NN \l__codedoc_names_block_tl
+ \__codedoc_names_typeset_auxi:n
+ }
+\cs_new_protected:Npn \__codedoc_names_typeset_auxi:n #1
+ {
+ \seq_pop:NN #1 \l__codedoc_tmpa_tl
+ \tl_gset_eq:NN \g__codedoc_base_name_tl \l__codedoc_tmpa_tl
+ \tl_greplace_all:NnV \g__codedoc_base_name_tl
+ { ~ } \c_catcode_other_space_tl
+ \seq_get:NN #1 \l__codedoc_tmpa_tl
+ \str_if_eq:VnTF \l__codedoc_tmpa_tl { \scan_stop: }
+ {
+ \seq_gclear:N \g__codedoc_variants_seq
+ \__codedoc_names_typeset_auxii:e { \g__codedoc_base_name_tl }
+ }
+ {
+ \seq_gset_eq:NN \g__codedoc_variants_seq #1
+ \seq_gpop:NN \g__codedoc_variants_seq \l__codedoc_tmpb_tl
+ \__codedoc_names_typeset_auxii:e
+ { \g__codedoc_base_name_tl : \l__codedoc_tmpb_tl }
+ }
+ }
+\cs_new_protected:Npn \__codedoc_names_typeset_auxii:n #1
+ {
+ \bool_if:NT \l__codedoc_macro_pTF_bool
+ {
+ \__codedoc_names_typeset_block:eN
+ { \__codedoc_predicate_from_base:n {#1} }
+ \c_false_bool
+ }
+ \bool_if:NT \l__codedoc_macro_noTF_bool
+ { \__codedoc_names_typeset_block:nN {#1} \c_false_bool }
+ \__codedoc_names_typeset_block:nN {#1} \l__codedoc_macro_TF_bool
+ }
+\cs_generate_variant:Nn \__codedoc_names_typeset_auxii:n { e }
+\cs_new_protected:Npn \__codedoc_names_typeset_block:nN
+ {
+ \int_compare:nNnTF \l__codedoc_nested_macro_int = 0
+ { \__codedoc_typeset_function_block:nN }
+ { \__codedoc_macro_typeset_block:nN }
+ }
+\cs_generate_variant:Nn \__codedoc_names_typeset_block:nN { e }
+\prg_new_conditional:Npnn \__codedoc_if_macro_internal:n #1 { p , T , F , TF }
+ {
+ \bool_if:NTF \l__codedoc_macro_internal_bool
+ { \prg_return_true: }
+ {
+ \tl_if_empty:eTF
+ {
+ \exp_after:wN \__codedoc_if_macro_internal_aux:w
+ \tl_to_str:n { #1 ~ __ }
+ }
+ { \prg_return_false: } { \prg_return_true: }
+ }
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new:Npn \__codedoc_if_macro_internal_aux:w #1 { \tl_to_str:n { __ } } { }
+\cs_new_protected:Npn \__codedoc_names_block_base_map:N #1
+ {
+ \tl_map_inline:Nn \l__codedoc_names_block_tl
+ {
+ \group_begin:
+ \seq_set_eq:NN \l__codedoc_tmpa_seq ##1
+ \seq_pop:NN \l__codedoc_tmpa_seq \l__codedoc_tmpa_tl
+ \seq_get:NN \l__codedoc_tmpa_seq \l__codedoc_tmpb_tl
+ \exp_args:NNe
+ \group_end:
+ #1
+ {
+ \l__codedoc_tmpa_tl
+ \str_if_eq:VnF \l__codedoc_tmpb_tl { \scan_stop: }
+ { : \l__codedoc_tmpb_tl }
+ \bool_if:NT \l__codedoc_macro_TF_bool { TF }
+ }
+ }
+ }
+\keys_define:nn { l3doc/function }
+ {
+ TF .value_forbidden:n = true ,
+ TF .code:n =
+ {
+ \bool_set_true:N \l__codedoc_macro_TF_bool
+ } ,
+ EXP .value_forbidden:n = true ,
+ EXP .code:n =
+ {
+ \bool_set_true:N \l__codedoc_macro_EXP_bool
+ \bool_set_false:N \l__codedoc_macro_rEXP_bool
+ } ,
+ rEXP .value_forbidden:n = true ,
+ rEXP .code:n =
+ {
+ \bool_set_false:N \l__codedoc_macro_EXP_bool
+ \bool_set_true:N \l__codedoc_macro_rEXP_bool
+ } ,
+ pTF .value_forbidden:n = true ,
+ pTF .code:n =
+ {
+ \bool_set_true:N \l__codedoc_macro_pTF_bool
+ \bool_set_true:N \l__codedoc_macro_TF_bool
+ \bool_set_true:N \l__codedoc_macro_EXP_bool
+ \bool_set_false:N \l__codedoc_macro_rEXP_bool
+ } ,
+ noTF .value_forbidden:n = true ,
+ noTF .code:n =
+ {
+ \bool_set_true:N \l__codedoc_macro_noTF_bool
+ \bool_set_true:N \l__codedoc_macro_TF_bool
+ } ,
+ added .code:n = { \__codedoc_date_set_past:Nn \l__codedoc_date_added_tl {#1} },
+ updated .code:n = { \__codedoc_date_set_past:Nn \l__codedoc_date_updated_tl {#1} } ,
+ deprecated .bool_set:N = \l__codedoc_macro_deprecated_bool ,
+ no-user-doc .bool_set:N = \l__codedoc_macro_nodoc_bool ,
+ tested .code:n = { } ,
+ label .code:n =
+ {
+ \clist_set:Nn \l__codedoc_function_label_clist {#1}
+ \bool_set_true:N \l__codedoc_no_label_bool
+ } ,
+ verb .value_forbidden:n = true ,
+ verb .bool_set:N = \l__codedoc_names_verb_bool ,
+ module .tl_set:N = \l__codedoc_override_module_tl ,
+ }
+\cs_new_protected:Npn \__codedoc_date_set:Nn #1#2
+ {
+ \tl_set:Nn #1 {#2}
+ \regex_replace_once:nnNF
+ { \A(\d\d\d\d)[-/](\d\d?)[-/](\d\d?)\Z } { \1-\2-\3 } #1
+ {
+ \msg_error:nnn { l3doc } { date-format } {#2}
+ \tl_set:Nn #1 { 1970-01-01 }
+ }
+ }
+\cs_new_protected:Npn \__codedoc_date_set_past:Nn #1#2
+ {
+ \__codedoc_date_set:Nn #1 {#2}
+ \exp_args:No \__codedoc_date_compare:nNnT
+ {#1} > { \c_sys_year_int - \c_sys_month_int - \c_sys_day_int }
+ {
+ \msg_error:nnee { l3doc } { future-date }
+ { \tl_to_str:N \l__codedoc_macro_argument_tl }
+ {#1}
+ }
+ }
+\cs_new_protected:Npn \__codedoc_function:nnw #1#2
+ {
+ \__codedoc_function_typeset_start:
+ \__codedoc_function_init:
+ \tl_set:Nn \l__codedoc_macro_argument_tl {#2}
+ \keys_set:nn { l3doc/function } {#1}
+ \__codedoc_names_get_seq:nN {#2} \l__codedoc_names_seq
+ \__codedoc_names_parse:
+ \__codedoc_function_typeset:
+ \__codedoc_function_reset:
+ \__codedoc_function_descr_start:w
+ }
+\cs_new_protected:Npn \__codedoc_function_end:
+ {
+ \__codedoc_function_descr_stop:
+ \__codedoc_function_assemble:
+ \__codedoc_function_typeset_stop:
+ }
+\cs_new_protected:Npn \__codedoc_function_typeset_start:
+ {
+ \par \bigskip \noindent
+ }
+\cs_new_protected:Npn \__codedoc_function_typeset_stop:
+ {
+ \par
+ \dim_set:Nn \prevdepth { \box_dp:N \l__codedoc_descr_coffin }
+ \allowbreak
+ }
+\cs_new_protected:Npn \__codedoc_function_init:
+ {
+ \box_if_empty:NF \g__codedoc_syntax_box
+ { \msg_error:nn { l3doc } { syntax-nested-function } }
+ \coffin_clear:N \l__codedoc_descr_coffin
+ \box_gclear:N \g__codedoc_syntax_box
+ \coffin_clear:N \l__codedoc_syntax_coffin
+ \coffin_clear:N \l__codedoc_functions_coffin
+ \bool_set_false:N \l__codedoc_macro_TF_bool
+ \bool_set_false:N \l__codedoc_macro_pTF_bool
+ \bool_set_false:N \l__codedoc_macro_noTF_bool
+ \bool_set_false:N \l__codedoc_macro_EXP_bool
+ \bool_set_false:N \l__codedoc_macro_rEXP_bool
+ \bool_set_false:N \l__codedoc_no_label_bool
+ \bool_set_false:N \l__codedoc_names_verb_bool
+ \bool_set_true:N \l__codedoc_in_function_bool
+ \clist_clear:N \l__codedoc_function_label_clist
+ \tl_set:Nn \l__codedoc_override_module_tl { \q_no_value }
+ \char_set_active_eq:NN \< \__codedoc_shorthand_meta:
+ \char_set_catcode_active:N \<
+ }
+\cs_new_protected:Npn \__codedoc_shorthand_meta:
+ { \mode_if_math:TF { < } { \__codedoc_shorthand_meta:w } }
+\cs_new_protected_nopar:Npn \__codedoc_shorthand_meta:w #1 > { \meta {#1} }
+\cs_new_protected:Npn \__codedoc_function_reset:
+ {
+ \tl_set:Nn \l__codedoc_override_module_tl { \q_no_value }
+ }
+\cs_new_protected:Npn \__codedoc_function_typeset:
+ {
+ \dim_zero:N \l__codedoc_trial_width_dim
+ \hcoffin_set:Nn \l__codedoc_functions_coffin { \__codedoc_typeset_functions: }
+ \dim_set:Nn \l__codedoc_trial_width_dim
+ { \box_wd:N \l__codedoc_functions_coffin }
+ \bool_set:Nn \l__codedoc_long_name_bool
+ { \dim_compare_p:nNn \l__codedoc_trial_width_dim > \marginparwidth }
+ }
+\cs_new_protected:Npn \__codedoc_function_descr_start:w
+ {
+ \vcoffin_set:Nnw \l__codedoc_descr_coffin { \textwidth }
+ \noindent \ignorespaces
+ }
+\cs_new_protected:Npn \__codedoc_function_descr_stop:
+ { \vcoffin_set_end: }
+\cs_new_protected:Npn \__codedoc_function_assemble:
+ {
+ \hcoffin_set:Nn \l__codedoc_syntax_coffin
+ { \box_use_drop:N \g__codedoc_syntax_box }
+ \bool_if:NTF \l__codedoc_long_name_bool
+ {
+ \coffin_join:NnnNnnnn
+ \l__codedoc_output_coffin {hc} {vc}
+ \l__codedoc_syntax_coffin {l} {T}
+ {0pt} {0pt}
+ \coffin_join:NnnNnnnn
+ \l__codedoc_output_coffin {l} {t}
+ \l__codedoc_functions_coffin {r} {t}
+ {-\marginparsep} {0pt}
+ \coffin_join:NnnNnnnn
+ \l__codedoc_output_coffin {l} {b}
+ \l__codedoc_descr_coffin {l} {t}
+ {0.75\marginparwidth + \marginparsep} {-\medskipamount}
+ \coffin_typeset:Nnnnn \l__codedoc_output_coffin
+ {\l__codedoc_descr_coffin-l} {\l__codedoc_descr_coffin-t}
+ {0pt} {0pt}
+ }
+ {
+ \coffin_join:NnnNnnnn
+ \l__codedoc_output_coffin {hc} {vc}
+ \l__codedoc_syntax_coffin {l} {t}
+ {0pt} {0pt}
+ \coffin_join:NnnNnnnn
+ \l__codedoc_output_coffin {l} {b}
+ \l__codedoc_descr_coffin {l} {t}
+ {0pt} {-\medskipamount}
+ \coffin_join:NnnNnnnn
+ \l__codedoc_output_coffin {l} {t}
+ \l__codedoc_functions_coffin {r} {t}
+ {-\marginparsep} {0pt}
+ \coffin_typeset:Nnnnn \l__codedoc_output_coffin
+ {\l__codedoc_syntax_coffin-l} {\l__codedoc_syntax_coffin-T}
+ {0pt} {0pt}
+ }
+ }
+\cs_new_protected:Npn \__codedoc_typeset_functions:
+ {
+ \small\ttfamily
+ \__codedoc_target:
+ \Hy@MakeCurrentHref { HD. \int_use:N \c@HD@hypercount }
+ \begin{tabular} [t] { @{} l @{} >{\hspace{\tabcolsep}} r @{} }
+ \toprule
+ \__codedoc_function_extra_labels:
+ \__codedoc_names_typeset:
+ \__codedoc_typeset_dates:
+ \bottomrule
+ \end{tabular}
+ \normalfont\normalsize
+ }
+\cs_new_protected:Npn \__codedoc_typeset_function_block:nN #1#2
+ {
+ \__codedoc_function_index:e
+ { #1 \bool_if:NT #2 { \tl_to_str:n {TF} } }
+ \__codedoc_function_label:eN {#1} #2
+ #1
+ \bool_if:NT #2 { \__codedoc_typeset_TF: }
+ \__codedoc_typeset_expandability:
+ \seq_if_empty:NF \g__codedoc_variants_seq
+ { \__codedoc_typeset_variant_list:nN {#1} #2 }
+ \\
+ }
+\cs_generate_variant:Nn \__codedoc_typeset_function_block:nN { e }
+\cs_new_protected:Npn \__codedoc_function_index:n #1
+ {
+ \seq_gput_right:Nn \g_doc_functions_seq {#1}
+ \__codedoc_special_index:nn {#1} { usage }
+ }
+\cs_generate_variant:Nn \__codedoc_function_index:n { e }
+\cs_new_protected:Npn \__codedoc_typeset_expandability:
+ {
+ &
+ \bool_if:NT \l__codedoc_macro_EXP_bool { \__codedoc_typeset_exp: }
+ \bool_if:NT \l__codedoc_macro_rEXP_bool { \__codedoc_typeset_rexp: }
+ }
+\cs_new_protected:Npn \__codedoc_typeset_variant_list:nN #1#2
+ {
+ \\
+ \__codedoc_typeset_aux:n { \__codedoc_get_function_name:n {#1} }
+ :
+ \int_compare:nTF { \seq_count:N \g__codedoc_variants_seq == 1 }
+ {
+ \seq_use:Nn \g__codedoc_variants_seq { }
+ \bool_if:NT #2 { \__codedoc_typeset_TF: }
+ }
+ {
+ \hbox_set:Nn \l_tmpa_box
+ { \seq_use:Nn \g__codedoc_variants_seq { \textrm| \nolinebreak[2] } }
+ \textrm(
+ \dim_compare:nNnTF { \box_wd:N \l_tmpa_box } > { .4\columnwidth }
+ {
+ \parbox[t]{.4\columnwidth}
+ {
+ \raggedright
+ \hbox_unpack_drop:N \l_tmpa_box
+ \textrm)
+ \bool_if:NT #2 { \__codedoc_typeset_TF: }
+ }
+ }
+ {
+ \hbox_unpack_drop:N \l_tmpa_box
+ \textrm)
+ \bool_if:NT #2 { \__codedoc_typeset_TF: }
+ }
+ }
+ \__codedoc_typeset_expandability:
+ }
+\cs_new_protected:Npn \__codedoc_function_extra_labels:
+ {
+ \bool_if:NT \l__codedoc_no_label_bool
+ {
+ \clist_map_inline:Nn \l__codedoc_function_label_clist
+ {
+ \__codedoc_get_hyper_target:oN { \token_to_str:N ##1 }
+ \l__codedoc_tmpa_tl
+ \exp_args:No \label { \l__codedoc_tmpa_tl }
+ }
+ }
+ }
+\cs_new_protected:Npn \__codedoc_function_label:nN #1#2
+ {
+ \bool_if:NF \l__codedoc_no_label_bool
+ {
+ \__codedoc_get_hyper_target:eN
+ {
+ \exp_not:n {#1}
+ \bool_if:NT #2 { \tl_to_str:n {TF} }
+ }
+ \l__codedoc_tmpa_tl
+ \exp_args:No \label { \l__codedoc_tmpa_tl }
+ }
+ }
+\cs_generate_variant:Nn \__codedoc_function_label:nN { e }
+\cs_new:Npn \__codedoc_typeset_dates:
+ {
+ \bool_lazy_and:nnF
+ { \tl_if_empty_p:N \l__codedoc_date_added_tl }
+ { \tl_if_empty_p:N \l__codedoc_date_updated_tl }
+ { \midrule }
+ \tl_if_empty:NF \l__codedoc_date_added_tl
+ {
+ \multicolumn { 2 } { @{} r @{} }
+ { \scriptsize New: \, \l__codedoc_date_added_tl } \\
+ }
+
+ \tl_if_empty:NF \l__codedoc_date_updated_tl
+ {
+ \multicolumn { 2 } { @{} r @{} }
+ { \scriptsize Updated: \, \l__codedoc_date_updated_tl } \\
+ }
+ }
+\dim_new:N \l__codedoc_syntax_dim
+\cs_new_protected:Npn \__codedoc_syntax:w
+ {
+ \box_if_empty:NF \g__codedoc_syntax_box
+ { \msg_error:nn { l3doc } { multiple-syntax } }
+ \dim_set:Nn \l__codedoc_syntax_dim
+ {
+ \textwidth
+ \bool_if:NT \l__codedoc_long_name_bool
+ { + 0.75 \marginparwidth - \l__codedoc_trial_width_dim }
+ }
+ \hbox_gset:Nw \g__codedoc_syntax_box
+ \small \ttfamily
+ \arrayrulecolor{white}
+ \begin{tabular} { @{} l @{} }
+ \toprule
+ \begin{minipage}[t]{\l__codedoc_syntax_dim}
+ \raggedright
+ \obeyspaces
+ \obeylines
+ }
+\cs_new_protected:Npn \__codedoc_syntax_end:
+ {
+ \end{minipage}
+ \end{tabular}
+ \arrayrulecolor{black}
+ \hbox_gset_end:
+ \bool_if:NF \l__codedoc_in_function_bool
+ {
+ \begin{quote}
+ \mode_leave_vertical:
+ \box_use_drop:N \g__codedoc_syntax_box
+ \end{quote}
+ }
+ }
+\keys_define:nn { l3doc/macro }
+ {
+ aux .value_forbidden:n = true ,
+ aux .code:n =
+ {
+ \msg_warning:nnnn { l3doc } { deprecated-option }
+ { aux } { function/macro }
+ } ,
+ deprecated .bool_set:N = \l__codedoc_macro_deprecated_bool ,
+ internal .value_forbidden:n = true ,
+ internal .code:n =
+ { \bool_set_true:N \l__codedoc_macro_internal_bool } ,
+ int .value_forbidden:n = true ,
+ int .code:n =
+ { \bool_set_true:N \l__codedoc_macro_internal_bool } ,
+ no-user-doc .bool_set:N = \l__codedoc_macro_nodoc_bool ,
+ var .value_forbidden:n = true ,
+ var .code:n =
+ { \bool_set_true:N \l__codedoc_macro_var_bool } ,
+ TF .value_forbidden:n = true ,
+ TF .code:n =
+ { \bool_set_true:N \l__codedoc_macro_TF_bool } ,
+ pTF .value_forbidden:n = true ,
+ pTF .code:n =
+ {
+ \bool_set_true:N \l__codedoc_macro_TF_bool
+ \bool_set_true:N \l__codedoc_macro_pTF_bool
+ \bool_set_true:N \l__codedoc_macro_EXP_bool
+ \bool_set_false:N \l__codedoc_macro_rEXP_bool
+ } ,
+ noTF .value_forbidden:n = true ,
+ noTF .code:n =
+ {
+ \bool_set_true:N \l__codedoc_macro_TF_bool
+ \bool_set_true:N \l__codedoc_macro_noTF_bool
+ } ,
+ EXP .value_forbidden:n = true ,
+ EXP .code:n =
+ {
+ \bool_set_true:N \l__codedoc_macro_EXP_bool
+ \bool_set_false:N \l__codedoc_macro_rEXP_bool
+ } ,
+ rEXP .value_forbidden:n = true ,
+ rEXP .code:n =
+ {
+ \bool_set_false:N \l__codedoc_macro_EXP_bool
+ \bool_set_true:N \l__codedoc_macro_rEXP_bool
+ } ,
+ tested .code:n =
+ {
+ \bool_set_true:N \l__codedoc_macro_tested_bool
+ } ,
+ added .code:n = {} , % TODO
+ updated .code:n = {} , % TODO
+ verb .bool_set:N = \l__codedoc_names_verb_bool ,
+ module .tl_set:N = \l__codedoc_override_module_tl ,
+ documented-as .tl_set:N = \l__codedoc_macro_documented_tl ,
+ do-not-index .value_required:n = true ,
+ do-not-index .tl_set:N = \l__codedoc_macro_do_not_index_tl ,
+ % do-not-index .default:n = \q_no_value ,
+ }
+\cs_new_protected:Npn \__codedoc_macro:nnw #1#2
+ {
+ \__codedoc_macro_init:
+ \tl_set:Nn \l__codedoc_macro_argument_tl {#2}
+ \keys_set:nn { l3doc/macro } {#1}
+ \__codedoc_names_get_seq:nN {#2} \l__codedoc_names_seq
+ \__codedoc_names_parse:
+ \__codedoc_macro_exclude_index:
+ \__codedoc_macro_save_names:
+ \__codedoc_names_typeset:
+ \__codedoc_macro_dump:
+ \__codedoc_macro_reset:
+ }
+\cs_new_protected:Npn \__codedoc_macro_init:
+ {
+ \int_incr:N \l__codedoc_nested_macro_int
+ \bool_set_false:N \l__codedoc_macro_deprecated_bool
+ \bool_set_false:N \l__codedoc_macro_internal_bool
+ \bool_set_false:N \l__codedoc_macro_TF_bool
+ \bool_set_false:N \l__codedoc_macro_pTF_bool
+ \bool_set_false:N \l__codedoc_macro_noTF_bool
+ \bool_set_false:N \l__codedoc_macro_EXP_bool
+ \bool_set_false:N \l__codedoc_macro_rEXP_bool
+ \bool_set_false:N \l__codedoc_macro_var_bool
+ \bool_set_false:N \l__codedoc_macro_tested_bool
+ \bool_set_false:N \l__codedoc_names_verb_bool
+ \tl_set:Nn \l__codedoc_override_module_tl { \q_no_value }
+ \tl_clear:N \l__codedoc_macro_documented_tl
+ \cs_set_eq:NN \testfile \__codedoc_print_testfile:n
+ \box_clear:N \l__codedoc_macro_index_box
+ \vbox_set:Nn \l__codedoc_macro_box
+ {
+ \hbox:n
+ {
+ \strut
+ \int_compare:nNnT \l__codedoc_macro_int = 0 { \__codedoc_target: }
+ }
+ \vskip \int_eval:n { \l__codedoc_macro_int - 1 } \baselineskip
+ }
+ }
+\cs_new_protected:Npn \__codedoc_macro_reset:
+ {
+ \tl_set:Nn \l__codedoc_override_module_tl { \q_no_value }
+ }
+\cs_new_protected:Npn \__codedoc_macro_save_names:
+ {
+ \tl_if_empty:NTF \l__codedoc_macro_documented_tl
+ { \__codedoc_names_block_base_map:N \__codedoc_macro_save_names_aux:n }
+ {
+ \seq_gput_right:Ne \g__codedoc_nested_names_seq
+ {
+ \exp_after:wN \__codedoc_macro_save_names_aux:w
+ \l__codedoc_macro_documented_tl \q__codedoc_stop
+ }
+ }
+ }
+\cs_new:Npn \__codedoc_macro_save_names_aux:w #1#2 \q__codedoc_stop
+ { \token_to_str:N #1 \tl_to_str:n {#2} }
+\cs_new_protected:Npn \__codedoc_macro_save_names_aux:n #1
+ { \seq_gput_right:Nn \g__codedoc_nested_names_seq {#1} }
+\cs_new_protected:Npn \__codedoc_macro_exclude_index:
+ {
+ \tl_if_empty:NF \l__codedoc_macro_do_not_index_tl
+ {
+ \tl_set_rescan:NnV \l__codedoc_macro_do_not_index_tl
+ { \MakePrivateLetters \catcode`\\12 }
+ \l__codedoc_macro_do_not_index_tl
+ \exp_args:NNV \seq_set_from_clist:Nn
+ \l__codedoc_tmpa_seq \l__codedoc_macro_do_not_index_tl
+ \__kernel_tl_set:Nx \l__codedoc_macro_do_not_index_tl
+ { \seq_use:Nn \l__codedoc_tmpa_seq { , } }
+ \exp_args:NV \DoNotIndex \l__codedoc_macro_do_not_index_tl
+ }
+ }
+\cs_new_protected:Npn \__codedoc_macro_dump:
+ {
+ \topsep\MacroTopsep
+ \trivlist
+ \cs_set:Npn \makelabel ##1
+ {
+ \llap
+ {
+ \hbox_unpack_drop:N \l__codedoc_macro_index_box
+ \vtop to \baselineskip
+ {
+ \vbox_unpack_drop:N \l__codedoc_macro_box
+ \vss
+ }
+ }
+ }
+ \item [ ]
+ }
+\cs_new_protected:Npn \__codedoc_macro_typeset_block:nN #1#2
+ {
+ \__codedoc_macro_single:nNN {#1} \c_true_bool #2
+ \seq_if_empty:NF \g__codedoc_variants_seq
+ {
+ \__codedoc_macro_typeset_variant_list:eN
+ { \__codedoc_get_function_name:n {#1} } #2
+ }
+ }
+\cs_new_protected:Npn \__codedoc_macro_typeset_variant_list:nN #1#2
+ {
+ \seq_map_inline:Nn \g__codedoc_variants_seq
+ { \__codedoc_macro_single:nNN { #1 : ##1 } \c_false_bool #2 }
+ }
+\cs_generate_variant:Nn \__codedoc_macro_typeset_variant_list:nN { e }
+\cs_new_protected:Npn \__codedoc_macro_single:nNN #1#2#3
+ {
+ \tl_set:Nn \saved@macroname {#1}
+ \__codedoc_macro_typeset_one:nN {#1} #3
+ \bool_if:NT #3 { \DoNotIndex {#1} }
+ \exp_args:Ne \__codedoc_macro_index:nN
+ { #1 \bool_if:NT #3 { \tl_to_str:n { TF } } }
+ #2
+ }
+\cs_new_protected:Npn \__codedoc_macro_index:nN #1#2
+ {
+ \DoNotIndex {#1}
+ \bool_if:NT #2
+ {
+ \bool_lazy_any:nF
+ {
+ { \__codedoc_if_macro_internal_p:n {#1} }
+ { \l__codedoc_macro_deprecated_bool }
+ { \l__codedoc_macro_nodoc_bool }
+ }
+ { \seq_gput_right:Nn \g_doc_macros_seq {#1} }
+ \hbox_set:Nw \l__codedoc_macro_index_box
+ \hbox_unpack_drop:N \l__codedoc_macro_index_box
+ \int_gincr:N \c@CodelineNo
+ \__codedoc_special_index:nn {#1} { main }
+ \int_gdecr:N \c@CodelineNo
+ \exp_args:NNNo \hbox_set_end:
+ \tl_set:Nn \saved@indexname { \l__codedoc_index_key_tl }
+ }
+ }
+\cs_new_protected:Npn \__codedoc_macro_typeset_one:nN #1#2
+ {
+ \vbox_set:Nn \l__codedoc_macro_box
+ {
+ \vbox_unpack_drop:N \l__codedoc_macro_box
+ \hbox { \llap { \__codedoc_print_macroname:nN {#1} #2
+ \MacroFont \
+ } }
+ }
+ \int_incr:N \l__codedoc_macro_int
+ }
+\cs_new_protected:Npn \__codedoc_print_macroname:nN #1#2
+ {
+ \strut
+ \__codedoc_get_hyper_target:eN
+ {
+ \exp_not:n {#1}
+ \bool_if:NT #2 { \tl_to_str:n {TF} }
+ }
+ \l__codedoc_tmpa_tl
+ \cs_if_exist:cTF { r@ \l__codedoc_tmpa_tl }
+ { \exp_last_unbraced:NNo \hyperref [ \l__codedoc_tmpa_tl ] }
+ { \use:n }
+ {
+ \int_compare:nTF { \str_count:n {#1} <= 28 }
+ { \MacroFont } { \MacroLongFont }
+ \tl_set:Nn \l__codedoc_tmpa_tl {#1}
+ \tl_replace_all:NnV \l__codedoc_tmpa_tl
+ { ~ } \c_catcode_other_space_tl
+ \__codedoc_print_macroname_aux:on
+ \l__codedoc_tmpa_tl { \bool_if:NT #2 { \__codedoc_typeset_TF: } }
+ }
+ }
+\cs_new_protected:Npn \__codedoc_print_macroname_aux:nn #1#2
+ {
+ \__codedoc_if_macro_internal:nTF {#1}
+ { \__codedoc_typeset_aux:n { #1 #2 } } { #1 #2 }
+ }
+\cs_generate_variant:Nn \__codedoc_print_macroname_aux:nn { o }
+\providecommand \MacroLongFont
+ {
+ \fontfamily{lmtt}\fontseries{lc}\small
+ }
+\cs_new_protected:Npn \__codedoc_print_testfile:n #1
+ {
+ \bool_set_true:N \l__codedoc_macro_tested_bool
+ \tl_if_eq:nnF {#1} {*}
+ {
+ \seq_if_in:NnF \g__codedoc_testfiles_seq {#1}
+ {
+ \seq_gput_right:Nn \g__codedoc_testfiles_seq {#1}
+ \par
+ \__codedoc_print_testfile_aux:n {#1}
+ }
+ }
+ }
+\cs_new_protected:Npn \__codedoc_print_testfile_aux:n #1
+ {
+ \footnotesize
+ (
+ \textit
+ {
+ The~ test~ suite~ for~ this~ command,~
+ and~ others~ in~ this~ file,~ is~ \textsf{#1}
+ }.
+ )\par
+ }
+\DeclareDocumentCommand \TestFiles {m}
+ {
+ \par
+ \textit
+ {
+ The~ following~ test~ files~ are~
+ used~ for~ this~ code:~ \textsf{#1}.
+ }
+ \par \ignorespaces
+ }
+\DeclareDocumentCommand \UnitTested { } { \testfile* }
+\DeclareDocumentCommand \TestMissing { m }
+ { \__codedoc_test_missing:n {#1} }
+\cs_new_protected:Npn \__codedoc_test_missing:n #1
+ {
+ \__codedoc_test_missing_aux:Nen
+ \g__codedoc_missing_tests_prop
+ { \seq_use:Nn \l__codedoc_names_seq { , } }
+ { { \g_file_curr_name_str \c_space_tl (#1) } }
+ }
+\cs_new_protected:Npn \__codedoc_test_missing_aux:Nnn #1#2#3
+ {
+ \prop_get:NnNTF #1 {#2} \l__codedoc_tmpa_tl
+ { \tl_put_right:Nn \l__codedoc_tmpa_tl { , #3 } }
+ { \tl_set:Nn \l__codedoc_tmpa_tl {#3} }
+ \prop_put:Nno #1 {#2} \l__codedoc_tmpa_tl
+ }
+\cs_generate_variant:Nn \__codedoc_test_missing_aux:Nnn { Ne }
+\cs_new_protected:Npn \__codedoc_macro_end:
+ {
+ \endtrivlist
+ \__codedoc_macro_end_check_tested:
+ \int_compare:nNnT \l__codedoc_nested_macro_int = 1
+ { \__codedoc_macro_end_style:n { \__codedoc_print_end_definition: } }
+ }
+\cs_new_protected:Npn \__codedoc_macro_end_check_tested:
+ {
+ \bool_lazy_all:nT
+ {
+ { \g__codedoc_checktest_bool }
+ { ! \l__codedoc_macro_var_bool }
+ { ! \l__codedoc_macro_tested_bool }
+ }
+ {
+ \seq_set_filter:NNn \l__codedoc_tmpa_seq \l__codedoc_names_seq
+ { ! \__codedoc_if_macro_internal_p:n {##1} }
+ \seq_gput_right:Ne \g__codedoc_not_tested_seq
+ {
+ \seq_use:Nn \l__codedoc_tmpa_seq { , }
+ \bool_if:NTF \l__codedoc_macro_pTF_bool {~(pTF)}
+ { \bool_if:NT \l__codedoc_macro_TF_bool {~(TF)} }
+ }
+ }
+ }
+\cs_new_protected:Npn \__codedoc_macro_end_style:n #1
+ {
+ \nobreak \noindent
+ { \footnotesize ( \emph{#1} ) \par }
+ }
+\cs_new_protected:Npn \__codedoc_macro_end_wrap_item:n #1
+ {
+ \tl_set:Nn \l__codedoc_tmpa_tl {#1}
+ \tl_replace_all:NVn \l__codedoc_tmpa_tl
+ \c_underscore_str { \_ }
+ \texttt { \l__codedoc_tmpa_tl }
+ }
+\cs_new_protected:Npn \__codedoc_print_end_definition:
+ {
+ \seq_set_map:NNn \l__codedoc_tmpa_seq
+ \g__codedoc_nested_names_seq
+ { \__codedoc_macro_end_wrap_item:n {##1} }
+ End~ of~ definition~ for~
+ \int_compare:nTF { \seq_count:N \l__codedoc_tmpa_seq <= 3 }
+ {
+ \seq_use:Nnnn \l__codedoc_tmpa_seq
+ { \,~and~ } { \,,~ } { \,,~and~ }
+ }
+ { \seq_item:Nn \l__codedoc_tmpa_seq {1}\,~and~others }
+ \@.
+ \__codedoc_print_documented:
+ }
+\cs_new_protected:Npn \__codedoc_print_documented:
+ {
+ \seq_gset_filter:NNn \g__codedoc_nested_names_seq
+ \g__codedoc_nested_names_seq
+ {
+ ! \bool_lazy_any_p:n
+ {
+ { \__codedoc_if_macro_internal_p:n {##1} }
+ { \l__codedoc_macro_deprecated_bool }
+ { \l__codedoc_macro_nodoc_bool }
+ }
+ }
+ \seq_if_empty:NF \g__codedoc_nested_names_seq
+ {
+ \int_set:Nn \l__codedoc_tmpa_int
+ { \seq_count:N \g__codedoc_nested_names_seq }
+ \int_compare:nNnTF \l__codedoc_tmpa_int = 1 {~This~} {~These~}
+ \bool_if:NTF \l__codedoc_macro_var_bool {variable} {function}
+ \int_compare:nNnTF \l__codedoc_tmpa_int = 1 {~is~} {s~are~}
+ documented~on~page~
+ \__codedoc_get_hyper_target:eN
+ { \seq_item:Nn \g__codedoc_nested_names_seq { 1 } }
+ \l__codedoc_tmpa_tl
+ \exp_args:Ne \pageref { \l__codedoc_tmpa_tl } .
+ }
+ \seq_gclear:N \g__codedoc_nested_names_seq
+ }
+\NewDocElement[idxtype = option, idxgroup = options]{Option}{optionenv}
+\font \manual = manfnt \scan_stop:
+\cs_gset:Npn \dbend { {\manual\char127} }
+\newenvironment {danger}
+ {
+ \begin{trivlist}\item[]\noindent
+ \begingroup\hangindent=2pc\hangafter=-2
+ \cs_set:Npn \par{\endgraf\endgroup}
+ \hbox to0pt{\hskip-\hangindent\dbend\hfill}\ignorespaces
+ }
+ {
+ \par\end{trivlist}
+ }
+\newenvironment {ddanger}
+ {
+ \begin{trivlist}\item[]\noindent
+ \begingroup\hangindent=3.5pc\hangafter=-2
+ \cs_set:Npn \par{\endgraf\endgroup}
+ \hbox to0pt{\hskip-\hangindent\dbend\kern2pt\dbend\hfill}\ignorespaces
+ }{
+ \par\end{trivlist}
+ }
+\bool_if:NTF \g__codedoc_show_notes_bool
+ {
+ \NewDocumentCommand\NB{mm}
+ {
+ (\emph{Note}\footnote{\ttfamily [#1]:~\detokenize{#2}})
+ }
+ }
+ {
+ \NewDocumentCommand\NB{mm}{}
+ }
+\bool_if:NTF \g__codedoc_show_notes_bool
+ {
+ \NewDocumentEnvironment{NOTE}{m}
+ {
+ \par\noindent (\emph{Note}~[\texttt{#1}]:\par
+ \verbatim
+ }
+ {
+ \endverbatim
+ \par\noindent \emph{Note~end})\par
+ }
+ }
+ {
+ \NewDocumentEnvironment{NOTE}{m}{\comment}{\endcomment}
+ }
+\providecommand\Hy@footnote@currentHref{}
+\prop_new:N\g__codedoc_fnmark_prop
+\cs_new_protected:Npn \__codedoc_fn_store:
+ {
+ \prop_gput:Nee\g__codedoc_fnmark_prop
+ {fn\int_use:N\c@footnote}{{\Hy@footnote@currentHref}{\int_use:N\c@footnote}}
+ }
+\cs_new_protected:Npn \__codedoc_fn_restore:n #1
+ {
+ \prop_get:NnN \g__codedoc_fnmark_prop {fn#1}\l__codedoc_tmpa_tl
+ \tl_gset:Ne\Hy@footnote@currentHref
+ {\exp_last_unbraced:NV\use_i:nn \l__codedoc_tmpa_tl }
+ \setcounter{footnote}{\exp_last_unbraced:NV\use_ii:nn \l__codedoc_tmpa_tl}
+ }
+
+\cs_generate_variant:Nn \hook_gput_next_code:nn {ne}
+\cs_new_protected:Npn \__codedoc_fn_footnote:nn #1 #2
+ {
+ \footnotemark
+ \__codedoc_fn_store:
+ \hook_gput_next_code:ne {env/#1/after}
+ {\exp_not:N\__codedoc_fn_restore:n{\int_use:N\c@footnote}{\exp_not:n{\footnotetext{#2}}}}}
+
+\AddToHook{env/function/begin}{\def\footnote{\__codedoc_fn_footnote:nn{function}}}
+\AddToHook{env/variable/begin}{\def\footnote{\__codedoc_fn_footnote:nn{variable}}}
+\newenvironment{TemplateInterfaceDescription}[1]
+ {
+ \subsection{The~object~type~`#1'}
+ \begingroup
+ \@beginparpenalty\@M
+ \description
+ \def\TemplateArgument##1##2{\item[Arg:~##1]##2\par}
+ \def\TemplateSemantics
+ {
+ \enddescription\endgroup
+ \subsubsection*{Semantics:}
+ }
+ }
+ {
+ \par\bigskip
+ }
+\newenvironment{TemplateDescription}[2]
+ {
+ \subsection{The~template~`#2'~(object~type~#1)}
+ \subsubsection*{Attributes:}
+ \begingroup
+ \@beginparpenalty\@M
+ \description
+ \def\TemplateKey##1##2##3##4
+ {
+ \item[##1~(##2)]##3%
+ \ifx\TemplateKey##4\TemplateKey\else
+ \hfill\penalty500\hbox{}\hfill Default:~##4%
+ \nobreak\hskip-\parfillskip\hskip0pt\relax
+ \fi
+ \par
+ }
+ \def\TemplateSemantics
+ {
+ \enddescription\endgroup
+ \subsubsection*{Semantics~\&~Comments:}
+ }
+ }
+ { \par \bigskip }
+\newenvironment{InstanceDescription}[4][xxxxxxxxxxxxxxx]
+ {
+ \subsubsection{The~instance~`#3'~(template~#2/#4)}
+ \subsubsection*{Attribute~values:}
+ \begingroup
+ \@beginparpenalty\@M
+ \def\InstanceKey##1##2{\>\textbf{##1}\>##2\\}
+ \def\InstanceSemantics{\endtabbing\endgroup
+ \vskip-30pt\vskip0pt
+ \subsubsection*{Layout~description~\&~Comments:}}
+ \tabbing
+ xxxx\=#1\=\kill
+ }
+ { \par \bigskip }
+\DeclareDocumentCommand \OnlyDescription { }
+ { \bool_gset_false:N \g__codedoc_typeset_implementation_bool }
+\DeclareDocumentCommand \AlsoImplementation { }
+ { \bool_gset_true:N \g__codedoc_typeset_implementation_bool }
+\DeclareDocumentCommand \StopEventually { m }
+ {
+ \bool_if:NTF \g__codedoc_typeset_implementation_bool
+ {
+ \@bsphack
+ \tl_gset:Nn \g__codedoc_finale_tl { #1 \check@checksum }
+ \init@checksum
+ \@esphack
+ }
+ { #1 \endinput }
+ }
+\cs_if_exist:NT \MaybeStop
+ { \RenewCommandCopy \MaybeStop \StopEventually }
+\DeclareDocumentCommand \Finale { }
+ { \tl_use:N \g__codedoc_finale_tl }
+\tl_new:N \g__codedoc_finale_tl
+\cs_new_protected:Npn \__codedoc_input:n #1
+ {
+ \tl_gclear:N \g__codedoc_module_name_tl
+ \MakePercentIgnore
+ \input{#1}
+ \MakePercentComment
+ }
+\DeclareDocumentCommand \DocInput { m }
+ {
+ \clist_map_inline:nn {#1}
+ {
+ \clist_put_right:Nn \g_docinput_clist {##1}
+ \__codedoc_input:n {##1}
+ }
+ }
+\DeclareDocumentCommand \DocInputAgain { }
+ { \clist_map_function:NN \g_docinput_clist \__codedoc_input:n }
+\msg_new:nnn { l3doc } { missing-endgroup }
+ {
+ \str_if_eq:VnTF \@currenvir { document }
+ {
+ There~are~\int_use:N \tex_currentgrouplevel:D
+ \c_space_tl unclosed~groups~in~#1.
+ }
+ {
+ The~\@currenvir \c_space_tl environment~on~line~\@currenvline
+ \c_space_tl doesn't~have~a~matching~\iow_char:N\\end{\@currenvir}.
+ }
+ }
+\NewDocumentCommand \DocInclude { m }
+ {
+ \relax\clearpage
+ \docincludeaux
+ \IfFileExists{#1.fdd}
+ { \cs_set:Npn \currentfile{#1.fdd} }
+ { \cs_set:Npn \currentfile{#1.dtx} }
+ \int_compare:nNnTF \@auxout = \@partaux
+ { \@latexerr{\string\include\space cannot~be~nested}\@eha }
+ { \@docinclude {#1} }
+ % check missing \endgroup, e.g., missing "\end{macro}" in time
+ \int_compare:nNnF { \tex_currentgrouplevel:D } = { 0 }
+ {
+ \int_compare:nNnT { \tex_interactionmode:D } = { 0 }
+ { \int_gset:Nn \tex_interactionmode:D { 1 } }
+ \msg_fatal:nne { l3doc } { missing-endgroup } { \currentfile }
+ }
+ }
+\cs_gset:Npn \@docinclude #1
+ {
+ \clearpage
+ \immediate\write\@mainaux{\string\@input{#1.aux}}
+ \@tempswatrue
+ \if@partsw
+ \@tempswafalse
+ \cs_set:Npe \@tempb {#1}
+ \clist_map_inline:Nn \@partlist
+ {
+ \if_meaning:w \@tempa \@tempb
+ \@tempswatrue
+ \fi:
+ }
+ \fi
+ \if@tempswa
+ \cs_set_eq:NN \@auxout \@partaux
+ \immediate\openout\@partaux #1.aux
+ \immediate\write\@partaux{\relax}
+ \cs_set_eq:NN \@ltxdoc@PrintIndex \PrintIndex
+ \cs_set_eq:NN \PrintIndex \relax
+ \cs_set_eq:NN \@ltxdoc@PrintChanges \PrintChanges
+ \cs_set_eq:NN \PrintChanges \relax
+ \cs_set_eq:NN \@ltxdoc@theglossary \theglossary
+ \cs_set_eq:NN \@ltxdoc@endtheglossary \endtheglossary
+ \part{\currentfile}
+ {
+ \cs_set_eq:NN \ttfamily\relax
+ \cs_gset:Npe \filekey
+ { \filekey,~ \thepart = { \ttfamily \currentfile } }
+ }
+ \DocInput{\currentfile}
+ \cs_set_eq:NN \PrintIndex \@ltxdoc@PrintIndex
+ \cs_set_eq:NN \PrintChanges \@ltxdoc@PrintChanges
+ \cs_set_eq:NN \theglossary \@ltxdoc@theglossary
+ \cs_set_eq:NN \endtheglossary \@ltxdoc@endtheglossary
+ \clearpage
+ \@writeckpt{#1}
+ \immediate \closeout \@partaux
+ \else
+ \@nameuse{cp@#1}
+ \fi
+ \cs_set_eq:NN \@auxout \@mainaux
+ }
+\cs_gset_protected:Npn \@wrindex #1
+ {
+ \protected@write \@indexfile {}
+ { \string \indexentry {#1} { MMMMI - \thepage } }
+ \endgroup \@esphack
+ }
+\cs_gset_protected:Npn \codeline@wrindex #1
+ {
+ \immediate\write\@indexfile
+ {
+ \string\indexentry{#1}
+ { MMMMV - \filesep \int_use:N \c@CodelineNo }
+ }
+ }
+\tl_gclear:N \filesep
+\cs_new_protected:Npn \__codedoc_index_page_hc:nn #1#2
+ {
+ \protected@write \@indexfile {}
+ {
+ \string \indexentry { #1 \encapchar hdpindex{#2} }
+ { MMMMI - \thepage }
+ }
+ }
+\cs_new_protected:Npn \__codedoc_index_codeline_hc:nn #1#2
+ {
+ \immediate\write\@indexfile
+ {
+ \string \indexentry { #1 \encapchar hdclindex{\the\c@HD@hypercount}{#2} }
+ { MMMMV - \filesep \int_use:N \c@CodelineNo - MMMD - \the\c@HD@hypercount - M }
+ }
+ }
+\cs_gset:Npn \docincludeaux
+ {
+ \tl_set:Nn \thepart { \alphalph { part } }
+ \tl_set:Nn \filesep { \thepart - }
+ \cs_set_eq:NN \filekey \use_none:n
+ \tl_gput_right:Nn \index@prologue
+ {
+ \cs_gset:Npn \@oddfoot
+ {
+ \parbox { \textwidth }
+ {
+ \strut \footnotesize
+ \raggedright { \bfseries File~Key: } ~ \filekey
+ }
+ }
+ \cs_set_eq:NN \@evenfoot \@oddfoot
+ }
+ \cs_gset_eq:NN \docincludeaux \relax
+ \cs_gset:Npn \@oddfoot
+ {
+ \cs_if_exist:cTF { ver @ \currentfile }
+ { File~\thepart :~{\ttfamily\currentfile}~ }
+ {
+ \GetFileInfo{\currentfile}
+ File~\thepart :~{\ttfamily\filename}~
+ Date:~\ExplFileDate\ % space
+ Version~\ExplFileVersion
+ }
+ \hfill \thepage
+ }
+ \cs_set_eq:NN \@evenfoot \@oddfoot
+ }
+\group_begin:
+ \char_set_catcode_other:N \^^A
+ \char_set_catcode_active:N \^^S
+ \char_set_catcode_active:N \^^B
+ \char_set_catcode_other:N \^^L
+ \char_set_catcode_other:N \^^R
+ \char_set_lccode:nn { `\^^A } { `\% }
+ \char_set_lccode:nn { `\^^S } { `\ }
+ \char_set_lccode:nn { `\^^B } { `\\ }
+ \char_set_lccode:nn { `\^^L } { `\{ }
+ \char_set_lccode:nn { `\^^R } { `\} }
+ \tex_lowercase:D
+ {
+ \group_end:
+ \cs_set_protected:Npn \xmacro@code
+ #1 ^^A ^^S^^S^^S^^S ^^Bend ^^Lmacrocode^^R
+ { \__codedoc_xmacro_code:n {#1} \end{macrocode} }
+ }
+\group_begin:
+ \char_set_catcode_active:N \<
+ \char_set_catcode_active:N \>
+ \cs_new_protected:Npn \__codedoc_xmacro_code:n #1
+ {
+ \tl_clear:N \l__codedoc_tmpa_tl
+ \tl_if_in:nnTF {#1} { < @ @ = }
+ { \__codedoc_xmacro_code:w #1 < @ @ = \q_recursion_tail > \q_recursion_stop }
+ {
+ \tl_set:Nn \l__codedoc_tmpa_tl {#1}
+ \__codedoc_detect_internals:N \l__codedoc_tmpa_tl
+ \__codedoc_replace_at_at:N \l__codedoc_tmpa_tl
+ \tl_use:N \l__codedoc_tmpa_tl
+ }
+ }
+ \cs_new_protected:Npn \__codedoc_xmacro_code:w #1 < @ @ = #2 >
+ {
+ % Add code before <@@=...>
+ \tl_set:Nn \l__codedoc_tmpb_tl {#1}
+ \__codedoc_detect_internals:N \l__codedoc_tmpb_tl
+ \__codedoc_replace_at_at:N \l__codedoc_tmpb_tl
+ \tl_put_right:NV \l__codedoc_tmpa_tl \l__codedoc_tmpb_tl
+ % Check for \q_recursion_tail
+ \quark_if_recursion_tail_stop_do:nn {#2}
+ { \tl_use:N \l__codedoc_tmpa_tl }
+ % Change module name and add <@@=#2> to typeset output
+ \tl_gset:Nn \g__codedoc_module_name_tl {#2}
+ \tl_put_right:Nn \l__codedoc_tmpa_tl { < \text { \verbatim@font @ @ = #2 } > }
+ % Loop
+ \__codedoc_xmacro_code:w
+ }
+\group_end:
+\iow_new:N \g__codedoc_func_iow
+\tl_new:N \l__codedoc_doc_def_tl
+\tl_new:N \l__codedoc_doc_undef_tl
+\tl_new:N \l__codedoc_undoc_def_tl
+\tl_const:Nn \c__codedoc_iow_separator_tl { ---- }
+\tl_const:Nn \c__codedoc_iow_midrule_tl { -- }
+\cs_new_protected:Npn \__codedoc_show_functions_defined:
+ {
+ \bool_lazy_and:nnT
+ { \g__codedoc_typeset_implementation_bool } { \g__codedoc_checkfunc_bool }
+ {
+ \iow_term:e { \c__codedoc_iow_separator_tl \iow_newline: }
+ \iow_open:Nn \g__codedoc_func_iow { \c_sys_jobname_str .cmds }
+
+ \tl_clear:N \l__codedoc_doc_def_tl
+ \tl_clear:N \l__codedoc_doc_undef_tl
+ \tl_clear:N \l__codedoc_undoc_def_tl
+ \seq_gremove_duplicates:N \g_doc_functions_seq
+ \seq_gremove_duplicates:N \g_doc_macros_seq
+ \seq_map_inline:Nn \g_doc_functions_seq
+ {
+ \seq_if_in:NnTF \g_doc_macros_seq {##1}
+ {
+ \tl_put_right:Ne \l__codedoc_doc_def_tl
+ { \iow_newline: > ~ ##1 }
+ }
+ {
+ \tl_put_right:Ne \l__codedoc_doc_undef_tl
+ { \iow_newline: ! ~ ##1 }
+ }
+ }
+ \seq_map_inline:Nn \g_doc_macros_seq
+ {
+ \seq_if_in:NnF \g_doc_functions_seq {##1}
+ {
+ \tl_put_right:Ne \l__codedoc_undoc_def_tl
+ { \iow_newline: ? ~ ##1 }
+ }
+ }
+ \__codedoc_functions_typeout:nN
+ {
+ Functions~both~documented~and~defined: \iow_newline:
+ (In~order~of~being~documented)
+ }
+ \l__codedoc_doc_def_tl
+ \__codedoc_functions_typeout:nN
+ { Functions~documented~but~not~defined: }
+ \l__codedoc_doc_undef_tl
+ \__codedoc_functions_typeout:nN
+ { Functions~defined~but~not~documented: }
+ \l__codedoc_undoc_def_tl
+
+ \iow_close:N \g__codedoc_func_iow
+ \iow_term:e { \c__codedoc_iow_separator_tl }
+ }
+ }
+\AtEndDocument { \__codedoc_show_functions_defined: }
+\cs_new_protected:Npn \__codedoc_functions_typeout:nN #1#2
+ {
+ \tl_if_empty:NF #2
+ {
+ \iow_now:Ne \g__codedoc_func_iow
+ {
+ \c__codedoc_iow_midrule_tl \iow_newline:
+ #1 \iow_newline:
+ \c__codedoc_iow_midrule_tl
+ #2
+ }
+ \tl_clear:N #2
+ }
+ }
+\cs_new_protected:Npn \__codedoc_show_not_tested:
+ {
+ \bool_if:NT \g__codedoc_checktest_bool
+ {
+ \tl_clear:N \l__codedoc_tmpa_tl
+ \prop_if_empty:NF \g__codedoc_missing_tests_prop
+ {
+ \cs_set:Npn \__codedoc_tmpa:w ##1##2
+ {
+ \iow_newline:
+ \space\space\space\space \exp_not:n {##1}
+ \clist_map_function:nN {##2} \__codedoc_tmpb:w
+ }
+ \cs_set:Npn \__codedoc_tmpb:w ##1
+ {
+ \iow_newline:
+ \space\space\space\space\space\space * ~ ##1
+ }
+ \tl_put_right:Ne \l__codedoc_tmpa_tl
+ {
+ \iow_newline: \iow_newline:
+ The~ following~ macro(s)~ have~ incomplete~ tests:
+ \iow_newline:
+ \prop_map_function:NN
+ \g__codedoc_missing_tests_prop \__codedoc_tmpa:w
+ }
+ }
+ \seq_if_empty:NF \g__codedoc_not_tested_seq
+ {
+ \cs_set:Npn \__codedoc_tmpa:w ##1
+ { \clist_map_function:nN {##1} \__codedoc_tmpb:w }
+ \cs_set:Npn \__codedoc_tmpb:w ##1
+ {
+ \iow_newline:
+ \space\space\space\space ##1
+ }
+ \tl_put_right:Ne \l__codedoc_tmpa_tl
+ {
+ \iow_newline:
+ \iow_newline:
+ The~ following~ macro(s)~ do~ not~ have~ any~ tests:
+ \iow_newline:
+ \seq_map_function:NN
+ \g__codedoc_not_tested_seq \__codedoc_tmpa:w
+ }
+ }
+ \tl_if_empty:NF \l__codedoc_tmpa_tl
+ {
+ \int_set:Nn \l__codedoc_tmpa_int { \tex_interactionmode:D }
+ \errorstopmode
+ \ClassError { l3doc } { \l__codedoc_tmpa_tl } { }
+ \int_gset:Nn \tex_interactionmode:D { \l__codedoc_tmpa_int }
+ }
+ }
+ }
+\AtEndDocument { \__codedoc_show_not_tested: }
+\cs_new_protected:Npn \__codedoc_target:
+ {
+ \mode_leave_vertical:
+ \group_begin:
+ \HD@savedestfalse \HD@target
+ \group_end:
+ }
+\cs_set_nopar:Npe \theCodelineNo
+ {
+ \group_begin:
+ \exp_not:N \HD@savedestfalse
+ \exp_not:o \theCodelineNo
+ \group_end:
+ }
+\bool_new:N \l__codedoc_allow_indexing_bool
+\bool_set_true:N \l__codedoc_allow_indexing_bool
+\use:e
+ {
+ \exp_not:n { \cs_set_nopar:Npn \@starttoc #1 }
+ {
+ \group_begin:
+ \bool_set_false:N \l__codedoc_allow_indexing_bool
+ \exp_not:o { \@starttoc {#1} }
+ \group_end:
+ }
+ }
+\g@addto@macro \theindex { \MakePrivateLetters }
+\cs_gset:Npn \verbatimchar {&}
+\setcounter { IndexColumns } { 2 }
+\IndexPrologue
+ {
+ \part*{Index}
+ \markboth{Index}{Index}
+ \addcontentsline{toc}{part}{Index}
+ The~italic~numbers~denote~the~pages~where~the~
+ corresponding~entry~is~described,~
+ numbers~underlined~point~to~the~definition,~
+ all~others~indicate~the~places~where~it~is~used.
+ }
+\cs_gset_protected:Npn \SpecialIndex #1
+ {
+ \@bsphack
+ \__codedoc_special_index:nn {#1} { }
+ \@esphack
+ }
+\msg_new:nnn { l3doc } { print-index-howto }
+ {
+ Generate~the~index~by~executing\\
+ \iow_indent:n
+ { makeindex~-s~gind.ist~-o~\c_sys_jobname_str.ind~\c_sys_jobname_str.idx }
+ }
+\tl_gput_right:Nn \PrintIndex
+ { \AtEndDocument { \msg_info:nn { l3doc } { print-index-howto } } }
+\cs_gset_protected:Npn \it@is@a #1
+ {
+ \use:e
+ {
+ \__codedoc_special_index_module:nnnnN
+ {#1}
+ { \bslash #1 }
+ { }
+ { }
+ \c_false_bool
+ }
+ }
+\cs_new_protected:Npn \__codedoc_special_index:nn #1#2
+ {
+ \__codedoc_key_get:n {#1}
+ \quark_if_no_value:NF \l__codedoc_override_module_tl
+ { \tl_set_eq:NN \l__codedoc_index_module_tl \l__codedoc_override_module_tl }
+ \__codedoc_special_index_module:ooonN
+ { \l__codedoc_index_key_tl }
+ { \l__codedoc_index_macro_tl }
+ { \l__codedoc_index_module_tl }
+ {#2}
+ \l__codedoc_index_internal_bool
+ }
+\cs_generate_variant:Nn \__codedoc_special_index:nn { o }
+\tl_new:N \l__codedoc_index_escaped_macro_tl
+\tl_new:N \l__codedoc_index_escaped_key_tl
+\cs_new_protected:Npn \__codedoc_special_index_module:nnnnN #1#2#3#4#5
+ {
+ \use:e
+ {
+ \exp_not:n { \__codedoc_special_index_aux:nnnnnn {#1} {#2} }
+ \tl_if_empty:nTF {#3}
+ { { } { } { } }
+ {
+ \str_if_eq:nnTF {#3} { TeX }
+ {
+ { TeX~and~LaTeX2e }
+ { \string\TeX{}~and~\string\LaTeXe{} }
+ }
+ {
+ {#3}
+ { \string\pkg{#3} }
+ }
+ { \bool_if:NT #5 { ~internal } ~commands: }
+ }
+ }
+ {#4}
+ }
+\cs_generate_variant:Nn \__codedoc_special_index_module:nnnnN { ooo }
+\cs_new_protected:Npn \__codedoc_special_index_aux:nnnnnn #1#2#3#4#5#6
+ {
+ \tl_set:Nn \l__codedoc_index_escaped_key_tl {#1}
+ \__codedoc_quote_special_char:N \l__codedoc_index_escaped_key_tl
+ \__codedoc_special_index_set:Nn \l__codedoc_index_escaped_macro_tl {#2}
+ \str_if_eq:onTF { \@currenvir } { macrocode }
+ { \__codedoc_index_codeline_hc:nn }
+ {
+ \str_case:nnF {#6}
+ {
+ { main } { \__codedoc_index_codeline_hc:nn }
+ { usage } { \__codedoc_index_page_hc:nn }
+ }
+ { \__codedoc_target: \__codedoc_index_page_hc:nn }
+ }
+ {
+ \tl_if_empty:nF { #3 #4 #5 }
+ { #3 #5 \actualchar #4 #5 \levelchar }
+ \l__codedoc_index_escaped_key_tl
+ \actualchar
+ {
+ \token_to_str:N \verbatim@font \c_space_tl
+ \l__codedoc_index_escaped_macro_tl
+ }
+ }
+ {#6}
+ }
+\group_begin:
+\char_set_active_eq:NN - \scan_stop:
+\tl_const:Ne \c__codedoc_active_minus_tl { \char_generate:nn { `- } { 13 } }
+\group_end:
+\cs_new_eq:NN \__codedoc_old_hdpindex:nn \hdpindex
+\cs_new_eq:NN \__codedoc_old_hdclindex:nnn \hdclindex
+\cs_gset_protected:Npn \hdpindex #1
+ { \__codedoc_hdindex:nn { \__codedoc_old_hdpindex:nn {#1} } }
+\cs_gset_protected:Npn \hdclindex #1#2
+ { \__codedoc_hdindex:nn { \__codedoc_old_hdclindex:nnn {#1} {#2} } }
+\cs_new_protected:Npn \__codedoc_hdindex:nn #1#2
+ {
+ \tl_set:Nn \l__codedoc_tmpa_tl {#2}
+ \tl_replace_all:Nen \l__codedoc_tmpa_tl
+ { \exp_not:V \c__codedoc_active_minus_tl \exp_not:V \c__codedoc_active_minus_tl }
+ { -- }
+ \seq_set_split:NnV \l__codedoc_tmpa_seq { -- } \l__codedoc_tmpa_tl
+ \seq_set_map:NNn \l__codedoc_tmpa_seq \l__codedoc_tmpa_seq
+ { \__codedoc_hdindex_aux:nn {#1} {##1} }
+ \seq_use:Nn \l__codedoc_tmpa_seq { -- }
+ }
+\cs_new_protected:Npn \__codedoc_hdindex_aux:nn #1#2
+ {
+ \tl_set:Nn \l__codedoc_tmpa_tl {#2}
+ \tl_replace_all:Nnn \l__codedoc_tmpa_tl { MMMM } { \use_none:nn }
+ \tl_if_in:NnT \l__codedoc_tmpa_tl { MMMD }
+ {
+ \tl_replace_all:Nen \l__codedoc_tmpa_tl
+ { \exp_not:V \c__codedoc_active_minus_tl MMMD } { - MMMD }
+ \tl_replace_all:Nnn \l__codedoc_tmpa_tl { - MMMD } { \__codedoc_hdindex_aux:w }
+ }
+ \use:e { \exp_not:n {#1} { \exp_not:V \l__codedoc_tmpa_tl } }
+ }
+\cs_new_protected:Npn \__codedoc_hdindex_aux:w #1 M { }
+\cs_new_protected:Npn \__codedoc_special_index_set:Nn #1#2
+ {
+ \__kernel_tl_set:Nx #1 { \tl_to_str:n {#2} }
+ \__codedoc_if_almost_str:nTF {#2}
+ {
+ \tl_replace_all:Nen #1 { \tl_to_str:n { __ } }
+ {
+ \verbatimchar
+ \token_to_str:N \_ \token_to_str:N \_
+ \token_to_str:N \verb * \verbatimchar
+ }
+ \exp_args:Ne \tl_map_inline:nn
+ { \tl_to_str:N \verbatimchar \token_to_str:N _ }
+ {
+ \tl_replace_all:Nnn #1 {##1}
+ {
+ \verbatimchar \c_backslash_str ##1
+ \token_to_str:N \verb * \verbatimchar
+ }
+ }
+ \__kernel_tl_set:Nx #1
+ {
+ \token_to_str:N \verb * \verbatimchar
+ #1 \verbatimchar
+ }
+ }
+ {
+ \tl_set:Nn #1 {#2}
+ \tl_replace_all:NVn #1
+ \c_backslash_str
+ { \token_to_str:N \bslash \c_space_tl }
+ }
+ \__codedoc_quote_special_char:N #1
+ }
+\cs_new_protected:Npn \__codedoc_quote_special_char:N #1
+ {
+ \tl_map_inline:nn { \quotechar \actualchar \encapchar \levelchar \bslash }
+ {
+ \tl_replace_all:Nen #1
+ { \tl_to_str:N ##1 } { \quotechar \tl_to_str:N ##1 }
+ }
+ }
+\cs_new_protected:Npe \__codedoc_key_get:n #1
+ {
+ \exp_not:N \__codedoc_key_get_base:nN {#1} \exp_not:N \l__codedoc_index_macro_tl
+ \__kernel_tl_set:Nx \exp_not:N \l__codedoc_index_key_tl
+ { \exp_not:N \tl_to_str:N \exp_not:N \l__codedoc_index_macro_tl }
+ \tl_clear:N \exp_not:N \l__codedoc_index_module_tl
+ \tl_if_in:NnTF \exp_not:N \l__codedoc_index_key_tl { \tl_to_str:n { __ } }
+ { \bool_set_true:N \exp_not:N \l__codedoc_index_internal_bool }
+ { \bool_set_false:N \exp_not:N \l__codedoc_index_internal_bool }
+ \exp_not:N \tl_if_head_eq_charcode:VNT
+ \exp_not:N \l__codedoc_index_key_tl \c_backslash_str
+ { \exp_not:N \__codedoc_key_pop: }
+ \tl_if_in:NnTF \exp_not:N \l__codedoc_index_key_tl { \token_to_str:N : }
+ { \exp_not:N \__codedoc_key_func: }
+ {
+ \tl_if_in:NnTF \exp_not:N \l__codedoc_index_key_tl { \token_to_str:N _ }
+ { \exp_not:N \__codedoc_key_var: }
+ {
+ \tl_if_in:NnT \exp_not:N \l__codedoc_index_key_tl { \token_to_str:N @ }
+ { \tl_set:Nn \exp_not:N \l__codedoc_index_module_tl { TeX } }
+ }
+ }
+ }
+\cs_new_protected:Npn \__codedoc_key_pop:
+ {
+ \__kernel_tl_set:Nx \l__codedoc_index_key_tl
+ { \tl_tail:N \l__codedoc_index_key_tl }
+ }
+\cs_new_protected:Npn \__codedoc_key_trim_module:n #1
+ {
+ \cs_set:Npn \__codedoc_tmpa:w ##1 #1 ##2 \q_stop
+ { \exp_not:n {##1} }
+ \__kernel_tl_set:Nx \l__codedoc_index_module_tl
+ { \exp_after:wN \__codedoc_tmpa:w \l__codedoc_index_module_tl #1 \q_stop }
+ }
+\cs_new_protected:Npn \__codedoc_key_drop_underscores:
+ {
+ \tl_if_head_eq_charcode:VNT \l__codedoc_index_key_tl _
+ { \__codedoc_key_pop: \__codedoc_key_drop_underscores: }
+ }
+\cs_new_protected:Npn \__codedoc_key_func:
+ {
+ \tl_if_head_eq_charcode:VNT \l__codedoc_index_key_tl .
+ { \__codedoc_key_pop: }
+ \__codedoc_key_drop_underscores:
+ \tl_set_eq:NN \l__codedoc_index_module_tl \l__codedoc_index_key_tl
+ \exp_args:No \__codedoc_key_trim_module:n { \token_to_str:N : }
+ \exp_args:No \__codedoc_key_trim_module:n { \token_to_str:N _ }
+ }
+\cs_new_protected:Npn \__codedoc_key_var:
+ {
+ \exp_args:Ne \tl_if_head_eq_charcode:nNTF
+ { \exp_args:No \str_tail:n \l__codedoc_index_key_tl } _
+ {
+ \str_case:en { \str_head:N \l__codedoc_index_key_tl }
+ {
+ { q } { \tl_set:Nn \l__codedoc_index_module_tl { quark } }
+ { s } { \tl_set:Nn \l__codedoc_index_module_tl { scan } }
+ }
+ \__codedoc_key_pop:
+ \__codedoc_key_pop:
+ \__codedoc_key_drop_underscores:
+ \tl_if_empty:NT \l__codedoc_index_module_tl
+ {
+ \seq_set_split:NoV \l__codedoc_tmpa_seq
+ { \token_to_str:N _ } \l__codedoc_index_key_tl
+ \seq_get_left:NN \l__codedoc_tmpa_seq \l__codedoc_index_module_tl
+ \clist_if_in:NoT \g__codedoc_non_modules_clist \l__codedoc_index_module_tl
+ {
+ \seq_get_right:NN \l__codedoc_tmpa_seq \l__codedoc_index_module_tl
+ \clist_if_in:NoT \g__codedoc_non_modules_clist \l__codedoc_index_module_tl
+ {
+ \tl_clear:N \l__codedoc_index_module_tl
+ }
+ }
+ }
+ }
+ {
+ \tl_set_eq:NN \l__codedoc_index_module_tl \l__codedoc_index_key_tl
+ \exp_args:No \__codedoc_key_trim_module:n { \token_to_str:N _ }
+ }
+ }
+\clist_new:N \g__codedoc_non_modules_clist
+\clist_gset:Ne \g__codedoc_non_modules_clist
+ {
+ \tl_to_str:n
+ {
+
+ alignment, ampersand, atsign, backslash, catcode, circumflex,
+ code, colon, document, dollar, e, empty, false, hash, inf,
+ initex, job, left, log, math, mark, max, minus, nan, nil, no,
+ novalue, other, parameter, percent, pi, recursion, right, space,
+ stop, term, tilde, tmpa, tmpb, true, underscore, zero, one, two,
+ three, four, five, six, seven, eight, nine, ten, eleven, twelve,
+ thirteen, fourteen, fifteen, sixteen, thirty, hundred
+
+ }
+ }
+\GlossaryPrologue
+ {
+ \part*{Change~History}
+ {\GlossaryParms\ttfamily\hyphenchar\font=`\-}
+ \markboth{Change~History}{Change~History}
+ \addcontentsline{toc}{part}{Change~History}
+ }
+\msg_new:nnn { l3doc } { print-changes-howto }
+ {
+ Generate~the~change~list~by~executing\\
+ \iow_indent:n
+ { makeindex~-s~gglo.ist~-o~\c_sys_jobname_str.gls~\c_sys_jobname_str.glo }
+ }
+\tl_gput_right:Nn \PrintChanges
+ { \AtEndDocument { \msg_info:nn { l3doc } { print-changes-howto } } }
+\bool_if:NTF \g__codedoc_typeset_implementation_bool
+ {
+ \RecordChanges
+ \CodelineIndex
+ \EnableCrossrefs
+ \AlsoImplementation
+ }
+ {
+ \CodelineNumbered
+ \DisableCrossrefs
+ \OnlyDescription
+ }
+%%
+%%
+%% End of file `l3doc.cls'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3docstrip.tex b/Master/texmf-dist/tex/latex-dev/l3kernel/l3docstrip.tex
new file mode 100644
index 00000000000..eda3985bd07
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3docstrip.tex
@@ -0,0 +1,25 @@
+%%
+%% This is file `l3docstrip.tex',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3docstrip.dtx (with options: `program')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File l3dosctrip.dtx
+\input docstrip %
+%%
+%%
+%% End of file `l3docstrip.tex'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88591.def b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88591.def
new file mode 100644
index 00000000000..485fa39a1df
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88591.def
@@ -0,0 +1,29 @@
+%%
+%% This is file `l3str-enc-iso88591.def',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3str-convert.dtx (with options: `iso88591')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3str-convert.dtx
+\__str_declare_eight_bit_encoding:nnnn { iso88591 } { 256 }
+ {
+ }
+ {
+ }
+%%
+%%
+%% End of file `l3str-enc-iso88591.def'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885910.def b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885910.def
new file mode 100644
index 00000000000..a7af4567560
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885910.def
@@ -0,0 +1,75 @@
+%%
+%% This is file `l3str-enc-iso885910.def',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3str-convert.dtx (with options: `iso885910')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3str-convert.dtx
+\__str_declare_eight_bit_encoding:nnnn { iso885910 } { 383 }
+ {
+ { A1 } { 0104 }
+ { A2 } { 0112 }
+ { A3 } { 0122 }
+ { A4 } { 012A }
+ { A5 } { 0128 }
+ { A6 } { 0136 }
+ { A8 } { 013B }
+ { A9 } { 0110 }
+ { AA } { 0160 }
+ { AB } { 0166 }
+ { AC } { 017D }
+ { AE } { 016A }
+ { AF } { 014A }
+ { B1 } { 0105 }
+ { B2 } { 0113 }
+ { B3 } { 0123 }
+ { B4 } { 012B }
+ { B5 } { 0129 }
+ { B6 } { 0137 }
+ { B8 } { 013C }
+ { B9 } { 0111 }
+ { BA } { 0161 }
+ { BB } { 0167 }
+ { BC } { 017E }
+ { BD } { 2015 }
+ { BE } { 016B }
+ { BF } { 014B }
+ { C0 } { 0100 }
+ { C7 } { 012E }
+ { C8 } { 010C }
+ { CA } { 0118 }
+ { CC } { 0116 }
+ { D1 } { 0145 }
+ { D2 } { 014C }
+ { D7 } { 0168 }
+ { D9 } { 0172 }
+ { E0 } { 0101 }
+ { E7 } { 012F }
+ { E8 } { 010D }
+ { EA } { 0119 }
+ { EC } { 0117 }
+ { F1 } { 0146 }
+ { F2 } { 014D }
+ { F7 } { 0169 }
+ { F9 } { 0173 }
+ { FF } { 0138 }
+ }
+ {
+ }
+%%
+%%
+%% End of file `l3str-enc-iso885910.def'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885911.def b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885911.def
new file mode 100644
index 00000000000..ec28015dd4c
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885911.def
@@ -0,0 +1,120 @@
+%%
+%% This is file `l3str-enc-iso885911.def',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3str-convert.dtx (with options: `iso885911')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3str-convert.dtx
+\__str_declare_eight_bit_encoding:nnnn { iso885911 } { 369 }
+ {
+ { A1 } { 0E01 }
+ { A2 } { 0E02 }
+ { A3 } { 0E03 }
+ { A4 } { 0E04 }
+ { A5 } { 0E05 }
+ { A6 } { 0E06 }
+ { A7 } { 0E07 }
+ { A8 } { 0E08 }
+ { A9 } { 0E09 }
+ { AA } { 0E0A }
+ { AB } { 0E0B }
+ { AC } { 0E0C }
+ { AD } { 0E0D }
+ { AE } { 0E0E }
+ { AF } { 0E0F }
+ { B0 } { 0E10 }
+ { B1 } { 0E11 }
+ { B2 } { 0E12 }
+ { B3 } { 0E13 }
+ { B4 } { 0E14 }
+ { B5 } { 0E15 }
+ { B6 } { 0E16 }
+ { B7 } { 0E17 }
+ { B8 } { 0E18 }
+ { B9 } { 0E19 }
+ { BA } { 0E1A }
+ { BB } { 0E1B }
+ { BC } { 0E1C }
+ { BD } { 0E1D }
+ { BE } { 0E1E }
+ { BF } { 0E1F }
+ { C0 } { 0E20 }
+ { C1 } { 0E21 }
+ { C2 } { 0E22 }
+ { C3 } { 0E23 }
+ { C4 } { 0E24 }
+ { C5 } { 0E25 }
+ { C6 } { 0E26 }
+ { C7 } { 0E27 }
+ { C8 } { 0E28 }
+ { C9 } { 0E29 }
+ { CA } { 0E2A }
+ { CB } { 0E2B }
+ { CC } { 0E2C }
+ { CD } { 0E2D }
+ { CE } { 0E2E }
+ { CF } { 0E2F }
+ { D0 } { 0E30 }
+ { D1 } { 0E31 }
+ { D2 } { 0E32 }
+ { D3 } { 0E33 }
+ { D4 } { 0E34 }
+ { D5 } { 0E35 }
+ { D6 } { 0E36 }
+ { D7 } { 0E37 }
+ { D8 } { 0E38 }
+ { D9 } { 0E39 }
+ { DA } { 0E3A }
+ { DF } { 0E3F }
+ { E0 } { 0E40 }
+ { E1 } { 0E41 }
+ { E2 } { 0E42 }
+ { E3 } { 0E43 }
+ { E4 } { 0E44 }
+ { E5 } { 0E45 }
+ { E6 } { 0E46 }
+ { E7 } { 0E47 }
+ { E8 } { 0E48 }
+ { E9 } { 0E49 }
+ { EA } { 0E4A }
+ { EB } { 0E4B }
+ { EC } { 0E4C }
+ { ED } { 0E4D }
+ { EE } { 0E4E }
+ { EF } { 0E4F }
+ { F0 } { 0E50 }
+ { F1 } { 0E51 }
+ { F2 } { 0E52 }
+ { F3 } { 0E53 }
+ { F4 } { 0E54 }
+ { F5 } { 0E55 }
+ { F6 } { 0E56 }
+ { F7 } { 0E57 }
+ { F8 } { 0E58 }
+ { F9 } { 0E59 }
+ { FA } { 0E5A }
+ { FB } { 0E5B }
+ }
+ {
+ { DB }
+ { DC }
+ { DD }
+ { DE }
+ }
+%%
+%%
+%% End of file `l3str-enc-iso885911.def'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885913.def b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885913.def
new file mode 100644
index 00000000000..b28db7deda2
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885913.def
@@ -0,0 +1,85 @@
+%%
+%% This is file `l3str-enc-iso885913.def',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3str-convert.dtx (with options: `iso885913')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3str-convert.dtx
+\__str_declare_eight_bit_encoding:nnnn { iso885913 } { 399 }
+ {
+ { A1 } { 201D }
+ { A5 } { 201E }
+ { A8 } { 00D8 }
+ { AA } { 0156 }
+ { AF } { 00C6 }
+ { B4 } { 201C }
+ { B8 } { 00F8 }
+ { BA } { 0157 }
+ { BF } { 00E6 }
+ { C0 } { 0104 }
+ { C1 } { 012E }
+ { C2 } { 0100 }
+ { C3 } { 0106 }
+ { C6 } { 0118 }
+ { C7 } { 0112 }
+ { C8 } { 010C }
+ { CA } { 0179 }
+ { CB } { 0116 }
+ { CC } { 0122 }
+ { CD } { 0136 }
+ { CE } { 012A }
+ { CF } { 013B }
+ { D0 } { 0160 }
+ { D1 } { 0143 }
+ { D2 } { 0145 }
+ { D4 } { 014C }
+ { D8 } { 0172 }
+ { D9 } { 0141 }
+ { DA } { 015A }
+ { DB } { 016A }
+ { DD } { 017B }
+ { DE } { 017D }
+ { E0 } { 0105 }
+ { E1 } { 012F }
+ { E2 } { 0101 }
+ { E3 } { 0107 }
+ { E6 } { 0119 }
+ { E7 } { 0113 }
+ { E8 } { 010D }
+ { EA } { 017A }
+ { EB } { 0117 }
+ { EC } { 0123 }
+ { ED } { 0137 }
+ { EE } { 012B }
+ { EF } { 013C }
+ { F0 } { 0161 }
+ { F1 } { 0144 }
+ { F2 } { 0146 }
+ { F4 } { 014D }
+ { F8 } { 0173 }
+ { F9 } { 0142 }
+ { FA } { 015B }
+ { FB } { 016B }
+ { FD } { 017C }
+ { FE } { 017E }
+ { FF } { 2019 }
+ }
+ {
+ }
+%%
+%%
+%% End of file `l3str-enc-iso885913.def'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885914.def b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885914.def
new file mode 100644
index 00000000000..a154a1c0cb5
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885914.def
@@ -0,0 +1,60 @@
+%%
+%% This is file `l3str-enc-iso885914.def',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3str-convert.dtx (with options: `iso885914')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3str-convert.dtx
+\__str_declare_eight_bit_encoding:nnnn { iso885914 } { 529 }
+ {
+ { A1 } { 1E02 }
+ { A2 } { 1E03 }
+ { A4 } { 010A }
+ { A5 } { 010B }
+ { A6 } { 1E0A }
+ { A8 } { 1E80 }
+ { AA } { 1E82 }
+ { AB } { 1E0B }
+ { AC } { 1EF2 }
+ { AF } { 0178 }
+ { B0 } { 1E1E }
+ { B1 } { 1E1F }
+ { B2 } { 0120 }
+ { B3 } { 0121 }
+ { B4 } { 1E40 }
+ { B5 } { 1E41 }
+ { B7 } { 1E56 }
+ { B8 } { 1E81 }
+ { B9 } { 1E57 }
+ { BA } { 1E83 }
+ { BB } { 1E60 }
+ { BC } { 1EF3 }
+ { BD } { 1E84 }
+ { BE } { 1E85 }
+ { BF } { 1E61 }
+ { D0 } { 0174 }
+ { D7 } { 1E6A }
+ { DE } { 0176 }
+ { F0 } { 0175 }
+ { F7 } { 1E6B }
+ { FE } { 0177 }
+ }
+ {
+ }
+%%
+%%
+%% End of file `l3str-enc-iso885914.def'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885915.def b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885915.def
new file mode 100644
index 00000000000..ca071e99247
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885915.def
@@ -0,0 +1,37 @@
+%%
+%% This is file `l3str-enc-iso885915.def',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3str-convert.dtx (with options: `iso885915')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3str-convert.dtx
+\__str_declare_eight_bit_encoding:nnnn { iso885915 } { 383 }
+ {
+ { A4 } { 20AC }
+ { A6 } { 0160 }
+ { A8 } { 0161 }
+ { B4 } { 017D }
+ { B8 } { 017E }
+ { BC } { 0152 }
+ { BD } { 0153 }
+ { BE } { 0178 }
+ }
+ {
+ }
+%%
+%%
+%% End of file `l3str-enc-iso885915.def'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885916.def b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885916.def
new file mode 100644
index 00000000000..44d4a1ae608
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso885916.def
@@ -0,0 +1,69 @@
+%%
+%% This is file `l3str-enc-iso885916.def',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3str-convert.dtx (with options: `iso885916')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3str-convert.dtx
+\__str_declare_eight_bit_encoding:nnnn { iso885916 } { 558 }
+ {
+ { A1 } { 0104 }
+ { A2 } { 0105 }
+ { A3 } { 0141 }
+ { A4 } { 20AC }
+ { A5 } { 201E }
+ { A6 } { 0160 }
+ { A8 } { 0161 }
+ { AA } { 0218 }
+ { AC } { 0179 }
+ { AE } { 017A }
+ { AF } { 017B }
+ { B2 } { 010C }
+ { B3 } { 0142 }
+ { B4 } { 017D }
+ { B5 } { 201D }
+ { B8 } { 017E }
+ { B9 } { 010D }
+ { BA } { 0219 }
+ { BC } { 0152 }
+ { BD } { 0153 }
+ { BE } { 0178 }
+ { BF } { 017C }
+ { C3 } { 0102 }
+ { C5 } { 0106 }
+ { D0 } { 0110 }
+ { D1 } { 0143 }
+ { D5 } { 0150 }
+ { D7 } { 015A }
+ { D8 } { 0170 }
+ { DD } { 0118 }
+ { DE } { 021A }
+ { E3 } { 0103 }
+ { E5 } { 0107 }
+ { F0 } { 0111 }
+ { F1 } { 0144 }
+ { F5 } { 0151 }
+ { F7 } { 015B }
+ { F8 } { 0171 }
+ { FD } { 0119 }
+ { FE } { 021B }
+ }
+ {
+ }
+%%
+%%
+%% End of file `l3str-enc-iso885916.def'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88592.def b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88592.def
new file mode 100644
index 00000000000..d54a4820c7d
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88592.def
@@ -0,0 +1,86 @@
+%%
+%% This is file `l3str-enc-iso88592.def',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3str-convert.dtx (with options: `iso88592')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3str-convert.dtx
+\__str_declare_eight_bit_encoding:nnnn { iso88592 } { 399 }
+ {
+ { A1 } { 0104 }
+ { A2 } { 02D8 }
+ { A3 } { 0141 }
+ { A5 } { 013D }
+ { A6 } { 015A }
+ { A9 } { 0160 }
+ { AA } { 015E }
+ { AB } { 0164 }
+ { AC } { 0179 }
+ { AE } { 017D }
+ { AF } { 017B }
+ { B1 } { 0105 }
+ { B2 } { 02DB }
+ { B3 } { 0142 }
+ { B5 } { 013E }
+ { B6 } { 015B }
+ { B7 } { 02C7 }
+ { B9 } { 0161 }
+ { BA } { 015F }
+ { BB } { 0165 }
+ { BC } { 017A }
+ { BD } { 02DD }
+ { BE } { 017E }
+ { BF } { 017C }
+ { C0 } { 0154 }
+ { C3 } { 0102 }
+ { C5 } { 0139 }
+ { C6 } { 0106 }
+ { C8 } { 010C }
+ { CA } { 0118 }
+ { CC } { 011A }
+ { CF } { 010E }
+ { D0 } { 0110 }
+ { D1 } { 0143 }
+ { D2 } { 0147 }
+ { D5 } { 0150 }
+ { D8 } { 0158 }
+ { D9 } { 016E }
+ { DB } { 0170 }
+ { DE } { 0162 }
+ { E0 } { 0155 }
+ { E3 } { 0103 }
+ { E5 } { 013A }
+ { E6 } { 0107 }
+ { E8 } { 010D }
+ { EA } { 0119 }
+ { EC } { 011B }
+ { EF } { 010F }
+ { F0 } { 0111 }
+ { F1 } { 0144 }
+ { F2 } { 0148 }
+ { F5 } { 0151 }
+ { F8 } { 0159 }
+ { F9 } { 016F }
+ { FB } { 0171 }
+ { FE } { 0163 }
+ { FF } { 02D9 }
+ }
+ {
+ }
+%%
+%%
+%% End of file `l3str-enc-iso88592.def'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88593.def b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88593.def
new file mode 100644
index 00000000000..d195b703b44
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88593.def
@@ -0,0 +1,64 @@
+%%
+%% This is file `l3str-enc-iso88593.def',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3str-convert.dtx (with options: `iso88593')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3str-convert.dtx
+\__str_declare_eight_bit_encoding:nnnn { iso88593 } { 384 }
+ {
+ { A1 } { 0126 }
+ { A2 } { 02D8 }
+ { A6 } { 0124 }
+ { A9 } { 0130 }
+ { AA } { 015E }
+ { AB } { 011E }
+ { AC } { 0134 }
+ { AF } { 017B }
+ { B1 } { 0127 }
+ { B6 } { 0125 }
+ { B9 } { 0131 }
+ { BA } { 015F }
+ { BB } { 011F }
+ { BC } { 0135 }
+ { BF } { 017C }
+ { C5 } { 010A }
+ { C6 } { 0108 }
+ { D5 } { 0120 }
+ { D8 } { 011C }
+ { DD } { 016C }
+ { DE } { 015C }
+ { E5 } { 010B }
+ { E6 } { 0109 }
+ { F5 } { 0121 }
+ { F8 } { 011D }
+ { FD } { 016D }
+ { FE } { 015D }
+ { FF } { 02D9 }
+ }
+ {
+ { A5 }
+ { AE }
+ { BE }
+ { C3 }
+ { D0 }
+ { E3 }
+ { F0 }
+ }
+%%
+%%
+%% End of file `l3str-enc-iso88593.def'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88594.def b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88594.def
new file mode 100644
index 00000000000..2249de98bcc
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88594.def
@@ -0,0 +1,79 @@
+%%
+%% This is file `l3str-enc-iso88594.def',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3str-convert.dtx (with options: `iso88594')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3str-convert.dtx
+\__str_declare_eight_bit_encoding:nnnn { iso88594 } { 383 }
+ {
+ { A1 } { 0104 }
+ { A2 } { 0138 }
+ { A3 } { 0156 }
+ { A5 } { 0128 }
+ { A6 } { 013B }
+ { A9 } { 0160 }
+ { AA } { 0112 }
+ { AB } { 0122 }
+ { AC } { 0166 }
+ { AE } { 017D }
+ { B1 } { 0105 }
+ { B2 } { 02DB }
+ { B3 } { 0157 }
+ { B5 } { 0129 }
+ { B6 } { 013C }
+ { B7 } { 02C7 }
+ { B9 } { 0161 }
+ { BA } { 0113 }
+ { BB } { 0123 }
+ { BC } { 0167 }
+ { BD } { 014A }
+ { BE } { 017E }
+ { BF } { 014B }
+ { C0 } { 0100 }
+ { C7 } { 012E }
+ { C8 } { 010C }
+ { CA } { 0118 }
+ { CC } { 0116 }
+ { CF } { 012A }
+ { D0 } { 0110 }
+ { D1 } { 0145 }
+ { D2 } { 014C }
+ { D3 } { 0136 }
+ { D9 } { 0172 }
+ { DD } { 0168 }
+ { DE } { 016A }
+ { E0 } { 0101 }
+ { E7 } { 012F }
+ { E8 } { 010D }
+ { EA } { 0119 }
+ { EC } { 0117 }
+ { EF } { 012B }
+ { F0 } { 0111 }
+ { F1 } { 0146 }
+ { F2 } { 014D }
+ { F3 } { 0137 }
+ { F9 } { 0173 }
+ { FD } { 0169 }
+ { FE } { 016B }
+ { FF } { 02D9 }
+ }
+ {
+ }
+%%
+%%
+%% End of file `l3str-enc-iso88594.def'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88595.def b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88595.def
new file mode 100644
index 00000000000..e54409a6e1d
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88595.def
@@ -0,0 +1,123 @@
+%%
+%% This is file `l3str-enc-iso88595.def',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3str-convert.dtx (with options: `iso88595')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3str-convert.dtx
+\__str_declare_eight_bit_encoding:nnnn { iso88595 } { 374 }
+ {
+ { A1 } { 0401 }
+ { A2 } { 0402 }
+ { A3 } { 0403 }
+ { A4 } { 0404 }
+ { A5 } { 0405 }
+ { A6 } { 0406 }
+ { A7 } { 0407 }
+ { A8 } { 0408 }
+ { A9 } { 0409 }
+ { AA } { 040A }
+ { AB } { 040B }
+ { AC } { 040C }
+ { AE } { 040E }
+ { AF } { 040F }
+ { B0 } { 0410 }
+ { B1 } { 0411 }
+ { B2 } { 0412 }
+ { B3 } { 0413 }
+ { B4 } { 0414 }
+ { B5 } { 0415 }
+ { B6 } { 0416 }
+ { B7 } { 0417 }
+ { B8 } { 0418 }
+ { B9 } { 0419 }
+ { BA } { 041A }
+ { BB } { 041B }
+ { BC } { 041C }
+ { BD } { 041D }
+ { BE } { 041E }
+ { BF } { 041F }
+ { C0 } { 0420 }
+ { C1 } { 0421 }
+ { C2 } { 0422 }
+ { C3 } { 0423 }
+ { C4 } { 0424 }
+ { C5 } { 0425 }
+ { C6 } { 0426 }
+ { C7 } { 0427 }
+ { C8 } { 0428 }
+ { C9 } { 0429 }
+ { CA } { 042A }
+ { CB } { 042B }
+ { CC } { 042C }
+ { CD } { 042D }
+ { CE } { 042E }
+ { CF } { 042F }
+ { D0 } { 0430 }
+ { D1 } { 0431 }
+ { D2 } { 0432 }
+ { D3 } { 0433 }
+ { D4 } { 0434 }
+ { D5 } { 0435 }
+ { D6 } { 0436 }
+ { D7 } { 0437 }
+ { D8 } { 0438 }
+ { D9 } { 0439 }
+ { DA } { 043A }
+ { DB } { 043B }
+ { DC } { 043C }
+ { DD } { 043D }
+ { DE } { 043E }
+ { DF } { 043F }
+ { E0 } { 0440 }
+ { E1 } { 0441 }
+ { E2 } { 0442 }
+ { E3 } { 0443 }
+ { E4 } { 0444 }
+ { E5 } { 0445 }
+ { E6 } { 0446 }
+ { E7 } { 0447 }
+ { E8 } { 0448 }
+ { E9 } { 0449 }
+ { EA } { 044A }
+ { EB } { 044B }
+ { EC } { 044C }
+ { ED } { 044D }
+ { EE } { 044E }
+ { EF } { 044F }
+ { F0 } { 2116 }
+ { F1 } { 0451 }
+ { F2 } { 0452 }
+ { F3 } { 0453 }
+ { F4 } { 0454 }
+ { F5 } { 0455 }
+ { F6 } { 0456 }
+ { F7 } { 0457 }
+ { F8 } { 0458 }
+ { F9 } { 0459 }
+ { FA } { 045A }
+ { FB } { 045B }
+ { FC } { 045C }
+ { FD } { 00A7 }
+ { FE } { 045E }
+ { FF } { 045F }
+ }
+ {
+ }
+%%
+%%
+%% End of file `l3str-enc-iso88595.def'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88596.def b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88596.def
new file mode 100644
index 00000000000..e7c6e888cc0
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88596.def
@@ -0,0 +1,109 @@
+%%
+%% This is file `l3str-enc-iso88596.def',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3str-convert.dtx (with options: `iso88596')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3str-convert.dtx
+\__str_declare_eight_bit_encoding:nnnn { iso88596 } { 344 }
+ {
+ { AC } { 060C }
+ { BB } { 061B }
+ { BF } { 061F }
+ { C1 } { 0621 }
+ { C2 } { 0622 }
+ { C3 } { 0623 }
+ { C4 } { 0624 }
+ { C5 } { 0625 }
+ { C6 } { 0626 }
+ { C7 } { 0627 }
+ { C8 } { 0628 }
+ { C9 } { 0629 }
+ { CA } { 062A }
+ { CB } { 062B }
+ { CC } { 062C }
+ { CD } { 062D }
+ { CE } { 062E }
+ { CF } { 062F }
+ { D0 } { 0630 }
+ { D1 } { 0631 }
+ { D2 } { 0632 }
+ { D3 } { 0633 }
+ { D4 } { 0634 }
+ { D5 } { 0635 }
+ { D6 } { 0636 }
+ { D7 } { 0637 }
+ { D8 } { 0638 }
+ { D9 } { 0639 }
+ { DA } { 063A }
+ { E0 } { 0640 }
+ { E1 } { 0641 }
+ { E2 } { 0642 }
+ { E3 } { 0643 }
+ { E4 } { 0644 }
+ { E5 } { 0645 }
+ { E6 } { 0646 }
+ { E7 } { 0647 }
+ { E8 } { 0648 }
+ { E9 } { 0649 }
+ { EA } { 064A }
+ { EB } { 064B }
+ { EC } { 064C }
+ { ED } { 064D }
+ { EE } { 064E }
+ { EF } { 064F }
+ { F0 } { 0650 }
+ { F1 } { 0651 }
+ { F2 } { 0652 }
+ }
+ {
+ { A1 }
+ { A2 }
+ { A3 }
+ { A5 }
+ { A6 }
+ { A7 }
+ { A8 }
+ { A9 }
+ { AA }
+ { AB }
+ { AE }
+ { AF }
+ { B0 }
+ { B1 }
+ { B2 }
+ { B3 }
+ { B4 }
+ { B5 }
+ { B6 }
+ { B7 }
+ { B8 }
+ { B9 }
+ { BA }
+ { BC }
+ { BD }
+ { BE }
+ { C0 }
+ { DB }
+ { DC }
+ { DD }
+ { DE }
+ { DF }
+ }
+%%
+%%
+%% End of file `l3str-enc-iso88596.def'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88597.def b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88597.def
new file mode 100644
index 00000000000..292b3600ee5
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88597.def
@@ -0,0 +1,108 @@
+%%
+%% This is file `l3str-enc-iso88597.def',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3str-convert.dtx (with options: `iso88597')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3str-convert.dtx
+\__str_declare_eight_bit_encoding:nnnn { iso88597 } { 498 }
+ {
+ { A1 } { 2018 }
+ { A2 } { 2019 }
+ { A4 } { 20AC }
+ { A5 } { 20AF }
+ { AA } { 037A }
+ { AF } { 2015 }
+ { B4 } { 0384 }
+ { B5 } { 0385 }
+ { B6 } { 0386 }
+ { B8 } { 0388 }
+ { B9 } { 0389 }
+ { BA } { 038A }
+ { BC } { 038C }
+ { BE } { 038E }
+ { BF } { 038F }
+ { C0 } { 0390 }
+ { C1 } { 0391 }
+ { C2 } { 0392 }
+ { C3 } { 0393 }
+ { C4 } { 0394 }
+ { C5 } { 0395 }
+ { C6 } { 0396 }
+ { C7 } { 0397 }
+ { C8 } { 0398 }
+ { C9 } { 0399 }
+ { CA } { 039A }
+ { CB } { 039B }
+ { CC } { 039C }
+ { CD } { 039D }
+ { CE } { 039E }
+ { CF } { 039F }
+ { D0 } { 03A0 }
+ { D1 } { 03A1 }
+ { D3 } { 03A3 }
+ { D4 } { 03A4 }
+ { D5 } { 03A5 }
+ { D6 } { 03A6 }
+ { D7 } { 03A7 }
+ { D8 } { 03A8 }
+ { D9 } { 03A9 }
+ { DA } { 03AA }
+ { DB } { 03AB }
+ { DC } { 03AC }
+ { DD } { 03AD }
+ { DE } { 03AE }
+ { DF } { 03AF }
+ { E0 } { 03B0 }
+ { E1 } { 03B1 }
+ { E2 } { 03B2 }
+ { E3 } { 03B3 }
+ { E4 } { 03B4 }
+ { E5 } { 03B5 }
+ { E6 } { 03B6 }
+ { E7 } { 03B7 }
+ { E8 } { 03B8 }
+ { E9 } { 03B9 }
+ { EA } { 03BA }
+ { EB } { 03BB }
+ { EC } { 03BC }
+ { ED } { 03BD }
+ { EE } { 03BE }
+ { EF } { 03BF }
+ { F0 } { 03C0 }
+ { F1 } { 03C1 }
+ { F2 } { 03C2 }
+ { F3 } { 03C3 }
+ { F4 } { 03C4 }
+ { F5 } { 03C5 }
+ { F6 } { 03C6 }
+ { F7 } { 03C7 }
+ { F8 } { 03C8 }
+ { F9 } { 03C9 }
+ { FA } { 03CA }
+ { FB } { 03CB }
+ { FC } { 03CC }
+ { FD } { 03CD }
+ { FE } { 03CE }
+ }
+ {
+ { AE }
+ { D2 }
+ }
+%%
+%%
+%% End of file `l3str-enc-iso88597.def'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88598.def b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88598.def
new file mode 100644
index 00000000000..eef8f134bb1
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88598.def
@@ -0,0 +1,96 @@
+%%
+%% This is file `l3str-enc-iso88598.def',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3str-convert.dtx (with options: `iso88598')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3str-convert.dtx
+\__str_declare_eight_bit_encoding:nnnn { iso88598 } { 308 }
+ {
+ { AA } { 00D7 }
+ { BA } { 00F7 }
+ { DF } { 2017 }
+ { E0 } { 05D0 }
+ { E1 } { 05D1 }
+ { E2 } { 05D2 }
+ { E3 } { 05D3 }
+ { E4 } { 05D4 }
+ { E5 } { 05D5 }
+ { E6 } { 05D6 }
+ { E7 } { 05D7 }
+ { E8 } { 05D8 }
+ { E9 } { 05D9 }
+ { EA } { 05DA }
+ { EB } { 05DB }
+ { EC } { 05DC }
+ { ED } { 05DD }
+ { EE } { 05DE }
+ { EF } { 05DF }
+ { F0 } { 05E0 }
+ { F1 } { 05E1 }
+ { F2 } { 05E2 }
+ { F3 } { 05E3 }
+ { F4 } { 05E4 }
+ { F5 } { 05E5 }
+ { F6 } { 05E6 }
+ { F7 } { 05E7 }
+ { F8 } { 05E8 }
+ { F9 } { 05E9 }
+ { FA } { 05EA }
+ { FD } { 200E }
+ { FE } { 200F }
+ }
+ {
+ { A1 }
+ { BF }
+ { C0 }
+ { C1 }
+ { C2 }
+ { C3 }
+ { C4 }
+ { C5 }
+ { C6 }
+ { C7 }
+ { C8 }
+ { C9 }
+ { CA }
+ { CB }
+ { CC }
+ { CD }
+ { CE }
+ { CF }
+ { D0 }
+ { D1 }
+ { D2 }
+ { D3 }
+ { D4 }
+ { D5 }
+ { D6 }
+ { D7 }
+ { D8 }
+ { D9 }
+ { DA }
+ { DB }
+ { DC }
+ { DD }
+ { DE }
+ { FB }
+ { FC }
+ }
+%%
+%%
+%% End of file `l3str-enc-iso88598.def'.
diff --git a/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88599.def b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88599.def
new file mode 100644
index 00000000000..cb640b641a3
--- /dev/null
+++ b/Master/texmf-dist/tex/latex-dev/l3kernel/l3str-enc-iso88599.def
@@ -0,0 +1,35 @@
+%%
+%% This is file `l3str-enc-iso88599.def',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% l3str-convert.dtx (with options: `iso88599')
+%%
+%% Copyright (C) 1990-2024 The LaTeX Project
+%%
+%% It may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License (LPPL), either version 1.3c of
+%% this license or (at your option) any later version. The latest
+%% version of this license is in the file:
+%%
+%% https://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% File: l3str-convert.dtx
+\__str_declare_eight_bit_encoding:nnnn { iso88599 } { 352 }
+ {
+ { D0 } { 011E }
+ { DD } { 0130 }
+ { DE } { 015E }
+ { F0 } { 011F }
+ { FD } { 0131 }
+ { FE } { 015F }
+ }
+ {
+ }
+%%
+%%
+%% End of file `l3str-enc-iso88599.def'.